http://julialang.org, https://github.com/tkelman, https://github.com/kbarbary
In [1]:
# type => by-reference semantics,
# immutable => by-value semantics
immutable DualNumber{T}
value::T
epsilon::T
end
# type parameter {T} constrains typeof(value) == typeof(epsilon)
In [2]:
# import addition and multiplication from Base to extend them with new methods
import Base: +, *
# one-liner function definition:
+(x::DualNumber, y::DualNumber) = DualNumber(x.value + y.value, x.epsilon + y.epsilon)
# longer form function definition:
function *(x::DualNumber, y::DualNumber)
return DualNumber(x.value * y.value, x.value * y.epsilon + y.value * x.epsilon)
end
Out[2]:
In [3]:
# compare LLVM and native code generated for DualNumber{Int64} vs DualNumber{Float64}
x_i64 = DualNumber(1, 2)
Out[3]:
In [4]:
y_i64 = DualNumber(3, 4)
Out[4]:
In [5]:
@code_llvm x_i64 * y_i64
In [6]:
@code_native x_i64 * y_i64
In [7]:
x_f64 = DualNumber(1.0, 2.0)
Out[7]:
In [8]:
y_f64 = DualNumber(3.0, 4.0)
Out[8]:
In [9]:
@code_llvm x_f64 * y_f64
In [10]:
@code_native x_f64 * y_f64
In [11]:
# custom printing for a type can be achieved by extending the `show` function:
function Base.show(io::IO, x::DualNumber)
print(io, string(x.value, "+", x.epsilon, "ε"))
end
DualNumber(1, 2)
Out[11]:
In [12]:
# To use DualNumber in linear algebra operations, just need to define `zero` value
Base.zero{T}(::Type{DualNumber{T}}) = DualNumber(zero(T), zero(T))
Out[12]:
In [13]:
values1 = rand(1:10, 6, 4);
epsilon1 = rand(1:10, 6, 4);
dualmat1 = map(DualNumber, values1, epsilon1)
Out[13]:
In [14]:
values2 = rand(1:10, 4, 3);
epsilon2 = rand(1:10, 4, 3);
dualmat2 = map(DualNumber, values2, epsilon2)
Out[14]:
In [15]:
# matrix multiplication
dualmat1 * dualmat2
Out[15]:
In [16]:
# elementwise multiplication
dualmat1 .* dualmat1
Out[16]:
In [17]:
# calling into C libraries
ccall(:sin, Float64, (Float64,), 1.0)
Out[17]:
In [18]:
buffer = Array(UInt8, 25)
ccall(:sprintf, Void, (Ptr{UInt8}, Ptr{UInt8}, Ptr{UInt8}), buffer, "%s\n", "Hello from C sprintf!")
len = ccall(:strlen, Cint, (Ptr{UInt8},), buffer)
bytestring(pointer(buffer), len)
Out[18]:
In [19]:
# calling into Python libraries - https://github.com/stevengj/PyCall.jl
Pkg.add("PyCall")
using PyCall
@pyimport scipy.optimize as so
# (see http://www.juliaopt.org for Julia optimization packages)
so.newton(x -> cos(x) - 3x, 1)
Out[19]:
In [21]:
# access, and modify, the syntax tree of code as a data structure
ex = :(cos(x) - 3x)
dump(ex)
In [6]:
# macros begin with @, take expression structure of input,
# modify and return a different expression to execute
ex2 = copy(ex);
ex2.args[1] = :*
show(ex2)
In [23]:
macro multiply(foo)
foo.args[1] = :*
return foo
end
@multiply cos(1) - 3
Out[23]:
In [24]:
cos(1) - 3
Out[24]:
In [25]:
cos(1) * 3
Out[25]:
In [ ]: