The only way Julia can utalize the jit compiler is when code is written in functions. Therefore, much of the Julia code one writes will be within functions. Here is the basic syntax for defining functions.
In [1]:
function foo(x, y)
w = x + y
z = sin(2 * w)
w ./ z, cos(w) # last line is what gets returned, multiple return values separated by comma, could use return
end
Out[1]:
Note: the last line is what gets returned (you can add the keywork return at the end if you want but it is unnecessary). Also note that commas on the last line is used for multiple outputs.
In [2]:
a, b = foo(5.0, 7.0) # functions called with parenthesis
a
Out[2]:
In [3]:
a, b = foo(ones(2,2), rand(2,2))
b
Out[3]:
If you have a short function, you can use one line syntax
In [4]:
foo2(a, b) = 1 + 2a + 5b^4 # Notice that `2a` gets parsed as `2*a`
Out[4]:
You can also "pipe" functions.
In [5]:
a = sin(cos(exp(5)))
b = 5 |> exp |> cos |> sin
a == b
Out[5]:
These are convient for piping and map. Note, in Julia 0.4 un-named functions are slow since they do not get jit compiled. This is changed in Julia 0.5 so un-named functions are fast.
In [6]:
x -> sin(x^2)
Out[6]:
In [7]:
y = 1.9 |> cos |> z -> foo2(z,10) |> log
Out[7]:
In [8]:
map(x -> cos(x^2), [π/4,π/2,π])
Out[8]:
In [9]:
function foo!(x)
x[:] = zeros(size(x))
return nothing # optional
end
Out[9]:
In [10]:
a = [1 0; 0 1]
foo!(a)
a
Out[10]:
Notice, however, that the following code doesn't mutate x since the line x = zeros(size(x)) rebinds local name x.
In [11]:
function foo!(x)
x = zeros(size(x))
return nothing # optional
end
a = [1 0; 0 1]
foo!(a)
a
Out[11]:
In [12]:
function fop(x, base = 10)
x = x^2
return base * x
x # this is ignored
end
Out[12]:
In [13]:
fop(1)
Out[13]:
In [14]:
fop(1, 2)
Out[14]:
In [15]:
function tot(x, y; style=0, width=1, color=3)
x + y + style + width/color
end
Out[15]:
In [16]:
tot(1,2)
Out[16]:
In [17]:
tot(1,2, width = 3)
Out[17]:
In [18]:
tot(1, 2; width = 3) # the semicolon in this case is un-necssary
Out[18]:
In [19]:
bez(a, b, c...) = println("$c")
bez(2,3, 4, 5, 6)
In [20]:
args = [4, 5]
fop(args...) # calls fob(args[1], args[2])
Out[20]:
Splatting works nicely with variable length arguments and dictionaries.
In [21]:
bez2(c...) = bez(1, 2, c...) # the second c is a spat, the first makes a variable length arg
bez2(4,5,6,7,8)
Splatting a dic for named arguments
In [22]:
dargs = Dict(:style => 5, :width => 3, :color => 1) # :style is of symbol type
tot(4, 5; dargs...) # now the ; is required
Out[22]:
In [23]:
function clos(data)
# withing the function scope, data acts like a global variable
function loglike(μ)
-0.5 * sumabs2(data .- μ)
end
function updatedata(val)
push!(data, val)
end
loglike, updatedata # return the two functions
end
Out[23]:
In [24]:
like1, updatedata1 = clos(rand(10))
Out[24]:
Now the data is closed off to any mutations other than those given by updatedata.
In [25]:
using PyPlot
[like1(μ) for μ=0.1:.1:3] |> x -> plot(0.1:.1:3, x)
Out[25]:
In [26]:
updatedata1(10) # add 10 to the data set
Out[26]:
In [27]:
[like1(μ) for μ=0.1:.1:3] |> x -> plot(0.1:.1:3, x)
Out[27]:
Since for loops in Julia are fast, you often find yourself allocating arrays and filling the entries with a loop.
In [28]:
A = Array(Int64, (10,10)) # initialized 10x10 array of Int64's
for i = 1:10
for j = 1:10
A[j, i] = i + j
end
end
A
Out[28]:
You can equivalently use the following more terse syntax for nested loops.
In [29]:
A = Array(Int64, (10,10)) # initialized 10x10 array of Int64's
for i = 1:10, j = 1:10
A[j, i] = i + j
end
A
Out[29]:
Note: Julia stores it's arrays in column major order so it is fastest to order the for loop so the rows are the inner-most loop. This avoids the pointer from jumping around the memory layout.
In cases where you don't need the explicit row and column index you can use the iterator eachindex which automatically sets up the loop in the correct order. Here is an example.
In [30]:
A = zeros(1_000, 1_000)
B = rand(1_000, 1_000)
for i in eachindex(A,B)
A[i] = sin(B[i])
end
A
Out[30]:
Here is the basic if,else conditional.
In [31]:
x = 2
if x == 5
print(1)
else
print(2)
end
There is also a nice way to do a quick if, else variable assignment
In [32]:
x = 2
y = x < 2 ? x : 0.0
y
Out[32]:
When using this operation in a function, be sure to make it type stable
In [33]:
x = 2
y = x < 2 ? x : zero(x)
y
Out[33]:
In [34]:
x = 2.0
y = x < 2 ? x : zero(x)
y
Out[34]:
These can also be chained.
In [35]:
x = 4.0
y = x < 2 ? 2 :
x < 3 ? 3 :
x < 4 ? 4 : Inf
y
Out[35]:
In [36]:
[sin(x) for x = 1:3]
Out[36]:
In [37]:
[abs(i - j) for i = [4, 7, 8], j = 1:3]
Out[37]:
It is generally a good idea to prefix the types of the elements of a comprehension
In [38]:
Float64[abs(i - j) for i = [4, 7, 8], j = 1:3]
Out[38]:
In [39]:
Array{Float64,1}[rand(i) for i in [1,2,3]]
Out[39]:
Easiy multiple assignment
In [40]:
a, b, c = 1, 2, 3
Out[40]:
Indexing arrays can happen at the end
In [41]:
a = (rand(10, 10) * rand(10, 10))[2, 2]
Out[41]:
String interpolation is easy
In [42]:
a = 5
"The variable a is assigned the value $a."
Out[42]:
In [43]:
a = "ethan"
"The variable a is assigned the value $a."
Out[43]:
In [44]:
for a in ["baz", "boo", "bar"] # loop over a vector of strings
println("The variable a is assigned the value $a")
end
Dictionaries
In [45]:
a = Dict("bar" => 10, "baz" => 5, "boo" =>1)
Out[45]:
In [46]:
a["baz"]
Out[46]:
Immutable tuples
In [47]:
tul = (4, 5, 6)
tul[2]
Out[47]:
Immutable means the entries can not be mutated
In [48]:
tul[2] = 7
Sets
In [49]:
As= Set([2,3,4,4,5,6])
Bs = Set([2,3,4])
Bs ∩ As
Out[49]:
In [50]:
Bs ∪ As
Out[50]:
Parallel computing is fully distributed (i.e. not on shared memory) out of the box so you can immediatly perform calculations spread accross multiple computers. This also means you need to explicity send definitions to workers.
# Define the workers on multiple servers:
# 5 on the server hilbert and 5 on the server gumbel.
jobs = [
"hilbert",
"hilbert",
"hilbert",
"hilbert",
"hilbert",
"gumbel",
"gumbel",
"gumbel",
"gumbel",
"gumbel"
]
# now launch the workers
addprocs(jobs)
# we need to define `foo` and `parameter` on each worker
@everywhere function foo(x)
sum(x.^2)
end
@everywhere const parameter = 3.3
# now send out 1000 jobs (which will be automatically distributed to our 5 workers).
# Each job returns foo(x) to the main node which are combined by vcat.
out = @parallel (vcat) for r in 1:1_000
x = rand(100)
foo(x)
end
# close all workers (except for the main node)
closeprocs()
for ν in [0.8, 1.2, 2.2], ρ in [0.05, 0.2], xmax in [1.2, 1.4]
run(`julia scripts/script1d/script1d.jl $ν $ρ $σ $prdc_sim $xmax`)
end
In [ ]: