Let's start with a quick overview of the basic syntax, emphasising differences with Python.
Numeric values in Julia work similarly to Python:
Variables in Julia are created as in Python, with a simple assignment operation; variable names can be arbitrary unicode characters. Many may be entered in LaTeX notation, using tab substitution: type \alpha<TAB>
. There is also tab completion on partial names: \alp<TAB>
x = 3
3
y = 5
5
α = 3; ℵ = 10
10
Functions use parentheses (round brackets, ()
) around the arguments being passed. println
prints its arguments, followed by a new line. [print
omits the new line.]
println("α = ", α)
α = 3
Simple functions may be defined with a nice mathematical syntax; *
is not needed in simple expressions:
f(x) = 2x^2 + 3x + 1
g(x) = f(x) - (2x+1)*(x+1)
g (generic function with 1 method)
f(3)
28
g(3.5)
type: g: in apply, expected Function, got Float64 while loading In[11], in expression starting on line 1 in g at In[10]:2
The values of variables may be substituted into strings in a simple way using the $
operator:
# Variable substitution with $:
name = "David"
greeting = "Hello, $name"
println(greeting)
Hello, David
More complicated expressions are wrapped in parentheses:
μ = 3
println("The sine of $μ is $(sin(μ))")
The sine of 3 is 0.1411200080598672
There are numerical types with different precisions: typing Float<TAB>
or Int<TAB>
will provide a list. Currently, in arithmetic calculations types are promoted to the machine type. (This looks likely to change soon.)
Machine integers!
a = int(1e16)
a * 10
100000000000000000
a = int8(1)
b = int8(2)
a + b
3
typeof(ans)
Int64
These promotion rules are defined in int.jl
.
Arbitrary-precision integers and floating points are available through the types BigInt
and BigFloat
. The function big
converts a number into the corresponding Big
type:
big(10)
10
typeof(ans)
BigInt (constructor with 10 methods)
Note that, unlike in Python, integers are not automatically promoted to arbitrary-precision integers.
Exercise: Calculate powers of 10 using standard integers and BigInt
s
10^5
100000
10**5
syntax: use "^" instead of "**" while loading In[19], in expression starting on line 1
10^19
-8446744073709551616
ten = big(10)
10
ten^19
10000000000000000000
i = int8(10)
10
i * int8(1)
10
typeof(ans)
Int64
typemax(Int64)
9223372036854775807
typemin(Int64)
-9223372036854775808
Complex numbers are written using im
for the imaginary part:
a = 7
c = (1+3.5im) * a
7.0 + 24.5im
c.im
24.5
c.re, c.im
(7.0,24.5)
c * conj(c) # conj is a function that returns the conjugate of a complex number
649.25 + 0.0im
(Tuples behave similarly to Python.)
Rational numbers are also built into Julia; they are created using the //
operator:
3//4
3//4
typeof(ans)
Rational{Int64} (constructor with 1 method)
(big(3)//4)^50
717897987691852588770249//1267650600228229401496703205376
typeof(ans)
Rational{BigInt} (constructor with 1 method)
3//4 + 5//6
19//12
Operators are a convenient way of writing functions:
+(3, 4)
7
//(3, 4)
3//4
//
// (generic function with 8 methods)
We see that //
is a function, implemented as a series of methods. We can see what these methods are:
methods(//)
3 + -34
-31
The expression n::Integer
is a type annotation that specifies that the method applies when its first argument is of type Integer
.
Clicking on the file name takes us directly to the Julia standard library source code on GitHub where these functions are defined!
numpy
arrays¶To store several values "in one variable", we can try to imitate using a "list" as we would in Python:
l = [3, 4, 5]
3-element Array{Int64,1}: 3 4 5
typeof(l)
Array{Int64,1}
In Julia these objects are called Array
s. The curly braces indicate type parameters of the Array
type. The first is the type of element contained in the Array
(all must be of the same type) and the second the number of dimensions.
Exercise: Try to create an array in this way with elements of different types. What happens?
Exercise: What does the following syntax do?: l = {3, 4, 7.5}
l = [3., 4, 5]
3-element Array{Float64,1}: 3.0 4.0 5.0
l = [3., "a"]
2-element Array{Any,1}: 3.0 "a"
l = [3., 'a']
2-element Array{Float64,1}: 3.0 97.0
l = {3., 4, "hello", [3, 4]}
4-element Array{Any,1}: 3.0 4 "hello" [3,4]
The indices of Julia arrays are numbered starting at 1, unlike Python (where they are numbered starting at 0).
l[1]
3.0
The syntax for ranges is similar to that for Python:
l[1:2]
2-element Array{Any,1}: 3.0 4
However, the limits must be explicitly specified:
l[2:end] # Use `end` explicitly
3-element Array{Any,1}: 4 "hello" [3,4]
l[1:end-1]
3-element Array{Any,1}: 3.0 4 "hello"
l[-1]
BoundsError() while loading In[71], in expression starting on line 1
Julia Arrays
, like Python lists, but unlike numpy
arrays, are dynamic. However, the syntax is rather different from Python -- to add an element at the end of the list, we write
l = [3,4,5]
l + l
3-element Array{Int64,1}: 6 8 10
names(l)
0-element Array{Any,1}
l = [3, 4, 5]
push!(l, 7)
4-element Array{Int64,1}: 3 4 5 7
sizehint
push!
push! (generic function with 19 methods)
methods(push!)
methodswith(Array)
l = [3, 4, 5]
push!(l, 12.0)
4-element Array{Int64,1}: 3 4 5 12
12.0 == 12
true
push!(l, 12.1)
InexactError() while loading In[81], in expression starting on line 1 in push! at array.jl:457
append!(l, [10, 11, 12])
push!
replaces append
in Python. There are no methods of objects as in Python; rather, we use functions and send the object as an argument of the function.
The exclamation mark, or bang, (!
) indicates that the function modifies its argument; this is a standard convention in Julia.
Arrays which have been defined with a certain type cannot acquire elements of a different type:
l = [3, 4, 5]
push!(l, "hello")
no method convert(Type{Int64}, ASCIIString) while loading In[10], in expression starting on line 2 in push! at array.jl:457
Array
s work as mathematical vectors, with the sum of two vectors and scalar multiplication being defined:
a = [1.1, 2.2, 3.3]
b = [4.4, 5.5, 6.6]
3-element Array{Float64,1}: 4.4 5.5 6.6
a + b
3-element Array{Float64,1}: 5.5 7.7 9.9
3.5 * a
3-element Array{Float64,1}: 3.85 7.7 11.55
However, operators are, in general, not treated in an elementwise fashion (as they would be e.g. in numpy
):
a * b
no method *(Array{Float64,1}, Array{Float64,1}) while loading In[85], in expression starting on line 1
Rather, elementwise operations use a Matlab-like syntax, with an extra .
before the symbol for the operator:
a .* b
3-element Array{Float64,1}: 4.84 12.1 21.78
There are many useful operations on vectors predefined, without needing to explicitly import them.
dot(a,b) # ans is the last result
38.72
cross(a, b)
3-element Array{Float64,1}: -3.63 7.26 -3.63
norm(a)
4.115823125451335
Use help
or ?
(before the command) to obtain help:
help(dot)
?dot
transpose(a)
1x3 Array{Float64,2}: 1.1 2.2 3.3
a'
1x3 Array{Float64,2}: 1.1 2.2 3.3
M = [[2,1], [1,1]]
4-element Array{Int64,1}: 2 1 1 1
push!(l, 12.0)
2x2 Array{Int64,2}: 2 1 1 1
M = [2 1; 1 1]
2x2 Array{Int64,2}: 2 1 1 1
M = reshape([1:8], (2,2,2))
2x2x2 Array{Int64,3}: [:, :, 1] = 1 3 2 4 [:, :, 2] = 5 7 6 8
a ⋅ b
a × b
3-element Array{Float64,1}: -3.63 7.26 -3.63
I used \cdot
⋅
dot (generic function with 7 methods)
[Note that in the Julia command-line REPL, typing ?
puts it immediately into a special help mode. Similarly, ;
puts it into shell mode, in which commands are sent straight to the shell.]
:
) and add end
¶White space in Julia is not significant. Commands on one line can be separated by ;
. Blocks must finish with end
i = 0
while i < 5
print("$i\t")
i += 1
end
0 1 2 3 4
total = 0
for i = 1:10
total += i
end
println("Sum is $total")
Sum is 55
Here, 1:10
is a range object which may be iterated over.
typeof(1:10)
UnitRange{Int64} (constructor with 1 method)
We can construct an array from this by enclosing it in square brackets:
collect(1:10)
10-element Array{Int64,1}: 1 2 3 4 5 6 7 8 9 10
[1:2:10, 17]
6-element Array{Int64,1}: 1 3 5 7 9 17
Use help
or ?
to get help:
?dot
Base.dot(x, y) Compute the dot product. For complex vectors, the first vector is conjugated.
help(dot)
Base.dot(x, y) Compute the dot product. For complex vectors, the first vector is conjugated.
help("dot")
Base.dot(x, y) Compute the dot product. For complex vectors, the first vector is conjugated. Base.LinAlg.BLAS.dot(n, X, incx, Y, incy) Dot product of two vectors consisting of "n" elements of array "X" with stride "incx" and "n" elements of array "Y" with stride "incy".
Exercise: Implement the Babylonian method for calculating the square root of a positive number $y$, via the iteration $$x_{n+1} = \textstyle \frac{1}{2} (x_n + \frac{y}{x_n})$$
♆ = norm
norm (generic function with 15 methods)
♆(a)
4.115823125451335
snowman(x) = x^2
snowman (generic function with 1 method)
☃ = snowman
☃(3)
9
a = 3
a < 5 && println("Small") # evaluate the second statement only if the first is true; semantics of if-then
a > 10 || println("Small") # semantics of if not-then
Small Small
a == 3 ? println("Hello") : println("Not true")
Hello
There is an equivalent of list comprehensions in Python, as follows. Note that the array construction syntax is quite flexible.
squares = [i^2 for i in [1:2:10, 7]]
6-element Array{Any,1}: 1 9 25 49 81 49
sums = [i+j for i=1:5, j=1:5]
5x5 Array{Int64,2}: 2 3 4 5 6 3 4 5 6 7 4 5 6 7 8 5 6 7 8 9 6 7 8 9 10
sums = [i+j+k for i=1:5, j=1:5, k=1:5]
5x5x5 Array{Int64,3}: [:, :, 1] = 3 4 5 6 7 4 5 6 7 8 5 6 7 8 9 6 7 8 9 10 7 8 9 10 11 [:, :, 2] = 4 5 6 7 8 5 6 7 8 9 6 7 8 9 10 7 8 9 10 11 8 9 10 11 12 [:, :, 3] = 5 6 7 8 9 6 7 8 9 10 7 8 9 10 11 8 9 10 11 12 9 10 11 12 13 [:, :, 4] = 6 7 8 9 10 7 8 9 10 11 8 9 10 11 12 9 10 11 12 13 10 11 12 13 14 [:, :, 5] = 7 8 9 10 11 8 9 10 11 12 9 10 11 12 13 10 11 12 13 14 11 12 13 14 15
Square brackets with commas gives a one-dimensional vector. This is printed in a way that treats it as if it were a column vector (although there is in fact no difference between a one-dimensional row vector and column vector).
v = [3, 4, 5]
To create explicit matrices, Matlab-style notation is used. If we omit the commas, something different happens: we now obtain a two-dimensional Array
, i.e. a matrix, of size $1 \times n$. [Recall that in the standard notation for matrices, an $m \times n$ matrix has $m$ rows and $n$ columns.]
row_vec = [3 4 5]
We can also use the transpose operator, '
. [This is actually the conjugate-transpose operator, which also takes the complex conjugate of complex numbers. Transpose without conjugate is denoted .'
]
row_vec = [1im, 2]'
row_vec = [1im, 2].'
A complete matrix may be constructed using a semicolon (;
) to separate rows:
M = [1 2; 3 4]
2x2 Array{Int64,2}: 1 2 3 4
As in numpy
, it may also be created using a reshape
:
M = reshape([1, 2, 3, 4], (2,2))
Here, as in Python, (2,2)
denotes an (immutable) tuple:
t = (2, 2)
typeof(t)
There is an important difference in the way that Python and Julia treat slices of matrices. While in Python a one-dimensional slice in either direction returns a 1-dimensional vector, in Julia there is a difference. A vertical one-dimensional slice gives a 1-dimensional vector (a "column vector"):
M[:,1]
2-element Array{Int64,1}: 1 3
However, a horizontal one-dimensional slice produces a $1 \times n$ matrix:
M[1,:]
1x2 Array{Int64,2}: 1 2
This is the same result that is produced using the following Matlab-like syntax:
[1 2]
1x2 Array{Int64,2}: 1 2
The Mersenne Twister (pseudo-)random number generator is built-in to Julia:
rand()
0.30176459533546285
rand(5)
5-element Array{Float64,1}: 0.322047 0.858765 0.744449 0.932944 0.735366
x = rand(5, 5)
5x5 Array{Float64,2}: 0.718125 0.514504 0.485157 0.118623 0.954479 0.665983 0.223968 0.571665 0.337021 0.418121 0.388917 0.989328 0.588583 0.0660616 0.636201 0.00130766 0.277592 0.476613 0.21 0.171883 0.0978661 0.499811 0.816341 0.772805 0.207609
rand
v = [1, 2]
2-element Array{Int64,1}: 1 2
v*v
no method *(Array{Int64,1}, Array{Int64,1}) while loading In[140], in expression starting on line 1
dot(v, v)
5
M = [2 1; 1 1]
2x2 Array{Int64,2}: 2 1 1 1
dot(M, v)
no method dot(Array{Int64,2}, Array{Int64,1}) while loading In[142], in expression starting on line 1
Matrix multiplication uses the *
operator:
M * v
2-element Array{Int64,1}: 4 3
@which M*v
Exercise: Use the power method to calculate the largest eigenvalue $\lambda_1$ of the matrix $M = \begin{pmatrix} 2 & 1 \\ 1 & 1 \end{pmatrix}$. In this method, we start from an arbitrary non-zero vector $\mathbf{w}$, and repeatedly apply $M$ to it, thus calculating powers of the matrix $M$ applied to $\mathbf{w}$. The resulting vector converges to the eigenvector $\mathbf{v}_1$ corresponding to $\lambda_1$.
w = [1., 1]
M = reshape([2., 1, 1, 1], (2,2))
M, w
( 2x2 Array{Float64,2}: 2.0 1.0 1.0 1.0, [1.0,1.0])
w0 = [1., 1]
w = copy(w0)
for i in 1:10
w_new = M*w
println()
for i =1:10
t = 3
end
t
t not defined while loading In[1], in expression starting on line 4
Julia has built-in linear algebra, not only using LAPACK, but now also generic routines that work for arbitrary element types, implemented completely in Julia.
For example, given a matrix $A$, the LU-decomposition of $A$ is equivalent to Gaussian elimination; it expresses $A$ as the product $A = LU$, with $L$ a lower-triangular and $U$ an upper-triangular matrix.
This is implemented in pure Julia for arbitrary element types. When the elements are standard floating-point numbers, it uses the corresponding fast LAPACK implementation.
M = rand(100, 100)
eig(M)
(Complex{Float64}[49.4553+0.0im,2.60611+1.39241im,2.60611-1.39241im,2.80843+0.0im,1.6786+2.20359im,1.6786-2.20359im,-2.5772+0.722606im,-2.5772-0.722606im,-2.07661+1.73455im,-2.07661-1.73455im … 0.318764-0.818766im,-0.664996+0.0im,-0.262399+0.696736im,-0.262399-0.696736im,0.631772+0.341774im,0.631772-0.341774im,-0.337579+0.0im,0.0258253+0.441807im,0.0258253-0.441807im,0.244242+0.0im], 100x100 Array{Complex{Float64},2}: -0.104506+0.0im -0.0583494+0.0673774im … -0.126245+0.0im -0.110228+0.0im -0.0302002+0.0203231im 0.00971673+0.0im -0.106192+0.0im 0.0835864-0.0366953im -0.0100192+0.0im -0.101028+0.0im -0.0976258-0.0103224im -0.0208308+0.0im -0.109692+0.0im 0.0206091+0.051544im -0.0373466+0.0im -0.0927222+0.0im 0.00469629+0.0464441im … -0.0464968+0.0im -0.106839+0.0im 0.0021757+0.110256im -0.116836+0.0im -0.10324+0.0im -0.0737389-0.14787im -0.0407359+0.0im -0.102974+0.0im -0.11934-0.0301666im 0.0426221+0.0im -0.104879+0.0im 0.0237039-0.00547228im -0.00898699+0.0im -0.105285+0.0im -0.0161691+0.0600921im … 0.0082313+0.0im -0.0995482+0.0im -0.023878-0.104027im -0.0274685+0.0im -0.0994342+0.0im -0.0468258-0.044981im -0.0387807+0.0im ⋮ ⋱ -0.0954904+0.0im 0.0193279-0.017643im -0.0780521+0.0im -0.0916581+0.0im -0.066044+0.0354546im -0.153619+0.0im -0.0979165+0.0im 0.101931+0.109964im … 0.00194682+0.0im -0.107172+0.0im 0.0678739-0.0172876im 0.00680776+0.0im -0.108272+0.0im -0.100656+0.00846016im 0.0572358+0.0im -0.0913561+0.0im 0.0612352+0.0874928im 0.0822063+0.0im -0.10539+0.0im 0.00288259+0.0636149im -0.151949+0.0im -0.0925015+0.0im 0.0304961+0.00627406im … 0.058312+0.0im -0.0966832+0.0im -0.143995-0.0137428im -0.0348019+0.0im -0.101731+0.0im 0.0519957+0.00597033im -0.0907889+0.0im -0.0896939+0.0im 0.0362903-0.0129214im -0.035998+0.0im -0.0940307+0.0im -0.00657209-0.158261im -0.0286019+0.0im)
M = rand(100, 100)
M2 = map(big, M)
100x100 Array{BigFloat,2}: 3.41499732256071553138099261559545993804931640625e-01 … 6.689973747130995018750354574876837432384490966796875e-01 3.837450544904055238504270164412446320056915283203125e-01 7.06782295861154263860726132406853139400482177734375e-01 6.73779399891735852889951274846680462360382080078125e-01 8.158750410289050680745503996149636805057525634765625e-01 6.047191231529878141515155220986343920230865478515625e-01 2.68999012840712481420268886722624301910400390625e-01 3.51359116190601117324376900796778500080108642578125e-01 4.960645408074408901910601343843154609203338623046875e-01 9.30187353346197287606855752528645098209381103515625e-01 … 1.9592681156523372720812403713352978229522705078125e-01 4.1193169713166977174978455877862870693206787109375e-01 2.0328662478073766806119238026440143585205078125e-01 4.76493709114803376536428913823328912258148193359375e-01 8.88367071632467197872529141022823750972747802734375e-01 8.63301451930530650002992842928506433963775634765625e-01 8.21008010717050762394819685141555964946746826171875e-01 8.000181339706136096623367848224006593227386474609375e-01 3.88250252605438550546068654512055218219757080078125e-01 9.18804371646583906141358966124244034290313720703125e-01 … 9.625508929917956368882414608378894627094268798828125e-01 1.282382267075459214566990340244956314563751220703125e-01 7.310058552909601825575691691483370959758758544921875e-01 7.481661838307440692830141415470279753208160400390625e-01 8.349264288870987815727175984648056328296661376953125e-01 ⋮ ⋱ 6.45861874198019325632458276231773197650909423828125e-01 2.150989335177075911786914730328135192394256591796875e-01 8.3047253465137860217737397761084139347076416015625e-02 6.088991389391111841433712470461614429950714111328125e-01 2.040236468823974913533447761437855660915374755859375e-01 … 4.599328275746004113244680411298759281635284423828125e-01 2.714502378461052245484097511507570743560791015625e-01 4.2675221731670820446424841065891087055206298828125e-02 1.620864883793042832138553421827964484691619873046875e-01 3.81989856583413445179076006752438843250274658203125e-01 2.418257277111706127215029482613317668437957763671875e-01 8.273765904860841402523874421603977680206298828125e-01 3.297335386996638195711284424760378897190093994140625e-01 7.109467553999271327569431377924047410488128662109375e-01 3.470167295594233092259628392639569938182830810546875e-01 … 3.512130007274747445222828901023603975772857666015625e-01 9.44736536917150981906843298929743468761444091796875e-01 1.673222843336221732357671498903073370456695556640625e-01 6.64031380167859897056814588722772896289825439453125e-01 2.867078930370026146334794248105026781558990478515625e-01 9.403058098155445687638120944029651582241058349609375e-01 3.503450471039382119187166608753614127635955810546875e-01 9.909181693511914801320017431862652301788330078125e-02 3.346426606631125455493247500271536409854888916015625e-01
lu(M2)
( 100x100 Array{BigFloat,2}: 1e+00 … 0e+00 2.083543061729824728373762137800462284256013864405782072863835894555022090664619e-01 0e+00 1.885080284248462046020553070941008006804923664109838738230355865657787411698943e-01 0e+00 4.868390494295089913791749872572274980146677026896154096577236088185654381668904e-02 0e+00 9.308379963386380414924032310016838399893783533616283757409827477656339899757264e-01 0e+00 7.588133956951531752045254445901184138969646497514731338899505551158575891805754e-01 … 0e+00 5.266178057727190424640646456875355137119195255458693197726150378925488423692808e-02 0e+00 4.122198331411938617095188149651482955907553341364862761717983837869016180082745e-01 0e+00 4.768270046510868759568943997449232728715348268189695326045702886622947615102341e-01 0e+00 7.262472956699855818394390561425701183431889871884939446174009974067744265886207e-01 0e+00 4.897159135606284999904616348894421350490998293172493143200895359578619149888494e-01 … 0e+00 9.194470525255329080840280015506434074407586269228845733688232051880617161714118e-01 0e+00 5.669801500437603302447298467648629336522052055004847260931139087801841015280855e-01 0e+00 ⋮ ⋱ 8.325092743125319340835942125382173714655934178820894611723511198903528745790921e-01 0e+00 5.99575846903674111184857903426575819578830570089555088149829534780684898565552e-01 0e+00 4.705556231467836533274560606563136914404622521728411583796835746422943328328811e-01 … 0e+00 6.323212489337355821222728906633059988391570575895420273365638052373312599449895e-01 0e+00 6.463136385667993357235802266885210197479961139497370636575989044216656885268521e-01 0e+00 6.639246083344548585560175933677981700500964740310374618610387738425558764057364e-01 0e+00 8.310534296002742644810532677088961343742702626588530772056973456646898200119421e-02 0e+00 9.916112920437880282175608474107770616723639508546054026693021713851416081533574e-02 … 0e+00 3.965763063951899043726337854001427962884146994697338694105291529592702397043129e-01 0e+00 2.917545039865275863337548830872652081862746426685795084335557664811870353153899e-01 0e+00 1.283279261678429695656259848376865161609281298477312170163526263575172451731624e-01 0e+00 6.104155947547656981543070640316756224786274484114327613922335245871101020336498e-01 1e+00, 100x100 Array{BigFloat,2}: 9.993010137155982075540805453783832490444183349609375e-01 … 1.409297892572045629577814906951971352100372314453125e-01 0e+00 7.155612742579487919733876206897026954555154722494790178175006242995846468610666e-02 0e+00 1.132488780580699100510002528411618972832336576259989878404248337011966392263417e-01 0e+00 4.504778025722529491773090542002751344412324953450690709636621410427196447082819e-02 0e+00 6.233431183874435415550042456245419324533011989952463879362308281767234829270391e-02 0e+00 … 3.631318249243016531330675418428314100281126113341942170810688207388790162793761e-01 0e+00 2.029287842660357900704206599940314822769294458925981922655649869361624248905821e-01 0e+00 7.52105368229322982795039994345249521629118994902925956161636699630776230007012e-02 0e+00 4.406369636761867288461402103539692045041698197934802386166632976306907414893489e-01 0e+00 4.236933499688602452015765639817015802623579817516729453958923460187283582721779e-01 0e+00 … 5.197821284519048835667909474793719852300945935946894730205189079214475700368085e-01 0e+00 1.767829532273192639832024949167781724959068431739050291849191663777594732940454e+00 0e+00 -9.987903274245042009683043128635229469120006579296449027095594391828438391748962e-01 ⋮ ⋱ 0e+00 2.452094787545326669056576281040712473855057097800618302606036437896943371809676e+00 0e+00 2.424224378682565116695054070187176884395872116526132504658693528676021797028345e+00 0e+00 … 1.739952735261677244657029226964636787527858259639483093286717758727599018899551e-01 0e+00 -8.338823757841568391102445642643302505644971083904295926224433137281864464662108e-01 0e+00 1.398597251287204110883566700591731100246330289605303737831397829885026779457033e+00 0e+00 3.366917820481630468904690865376128629837566610214791987044069972956018723156938e-01 0e+00 1.416704374725771669069691596767062226456031860333416174335896646898251132981301e+00 0e+00 … 3.959281560717655532791332979444110794533750161261434478156677622153539426198889e-01 0e+00 -3.259857916254936358252594908642245701030956594119200324632141166523715083219904e-01 0e+00 -2.019555266178335762641524496293984004287939828006888972093591266537499585323791e+00 0e+00 2.742699647804872200069338994881129795068709555988360848668187543705041970312082e+00 0e+00 -9.746189261340509085756699594195457166548408350827224427205589028301701402738505e-01, [87,78,62,22,6,42,28,7,8,49 … 14,66,89,68,90,100,39,83,12,43])
lu(M)
( 100x100 Array{Float64,2}: 1.0 0.0 0.0 … 0.0 0.0 0.0 0.208354 1.0 0.0 0.0 0.0 0.0 0.188508 0.197253 1.0 0.0 0.0 0.0 0.0486839 0.498701 -0.125564 0.0 0.0 0.0 0.930838 0.695687 -0.655293 0.0 0.0 0.0 0.758813 -0.091067 0.561132 … 0.0 0.0 0.0 0.0526618 0.445214 -0.171587 0.0 0.0 0.0 0.41222 0.356765 -0.364269 0.0 0.0 0.0 0.476827 0.107381 0.338406 0.0 0.0 0.0 0.726247 0.330065 0.340706 0.0 0.0 0.0 0.489716 0.816836 -0.131986 … 0.0 0.0 0.0 0.919447 0.50482 -0.930075 0.0 0.0 0.0 0.56698 -0.112169 -0.128156 0.0 0.0 0.0 ⋮ ⋱ 0.832509 0.000309064 0.0840172 0.0 0.0 0.0 0.599576 0.331331 -0.411492 0.0 0.0 0.0 0.470556 0.264746 -0.348458 … 0.0 0.0 0.0 0.632321 -0.0624872 0.68665 0.0 0.0 0.0 0.646314 0.0203543 0.0349262 0.0 0.0 0.0 0.663925 0.257865 -0.150108 0.0 0.0 0.0 0.0831053 0.997736 -0.698928 0.0 0.0 0.0 0.0991611 0.951318 0.169188 … 0.0 0.0 0.0 0.396576 0.296518 0.133782 0.0 0.0 0.0 0.291755 0.26562 -0.185842 1.0 0.0 0.0 0.128328 0.345751 0.265363 -0.305614 1.0 0.0 0.610416 0.548097 -0.466394 -0.129163 -0.432331 1.0, 100x100 Array{Float64,2}: 0.999301 0.269395 0.513937 … 0.965834 0.335865 0.14093 0.0 0.890295 0.523864 -0.0298493 0.188389 0.0715561 0.0 0.0 0.786627 0.659953 0.222307 0.113249 0.0 0.0 0.0 0.886482 0.903205 0.0450478 0.0 0.0 0.0 -0.56174 0.0585484 0.0623343 0.0 0.0 0.0 … 0.661002 1.0115 0.363132 0.0 0.0 0.0 0.46286 0.215562 0.202929 0.0 0.0 0.0 0.0655094 -0.591076 0.0752105 0.0 0.0 0.0 -0.736783 0.138927 0.440637 0.0 0.0 0.0 0.206547 -0.0256575 0.423693 0.0 0.0 0.0 … -0.502041 -0.149553 0.519782 0.0 0.0 0.0 -1.01702 0.27332 1.76783 0.0 0.0 0.0 0.303928 -0.215946 -0.99879 ⋮ ⋱ 0.0 0.0 0.0 2.39772 -3.46131 2.45209 0.0 0.0 0.0 0.108893 -1.42097 2.42422 0.0 0.0 0.0 … -3.03719 0.900553 0.173995 0.0 0.0 0.0 -4.80897 3.18917 -0.833882 0.0 0.0 0.0 2.19005 -1.0208 1.3986 0.0 0.0 0.0 -1.26345 -1.21298 0.336692 0.0 0.0 0.0 -0.417998 0.544668 1.4167 0.0 0.0 0.0 … -0.670505 1.21561 0.395928 0.0 0.0 0.0 -1.59089 -1.34402 -0.325986 0.0 0.0 0.0 -3.35482 0.327353 -2.01956 0.0 0.0 0.0 0.0 1.52136 2.7427 0.0 0.0 0.0 0.0 0.0 -0.974619 , [87,78,62,22,6,42,28,7,8,49 … 14,66,89,68,90,100,39,83,12,43])
methods(lu)
@edit lu(M)
A Julia script, similar to a Python script, is a sequence of Julia commands placed in a file, with the termination .jl
.
From the command line, a script script.jl
can be run as
julia script.jl arg1 arg2
where arg1
and arg2
are command-line arguments.
These command-line arguments to Julia scripts are placed in the variable ARGS
as an array of strings.
Simple file input and output is easy:
outfile = open("test.txt", "w")
IOStream(<file test.txt>)
for i in 1:10
println(outfile, "The value of i is $i")
end
close(outfile)
;cat test.txt
The value of i is 1 The value of i is 2 The value of i is 3 The value of i is 4 The value of i is 5 The value of i is 6 The value of i is 7 The value of i is 8 The value of i is 9 The value of i is 10
infile = open("test.txt", "r")
IOStream(<file test.txt>)
lines = readlines(infile)
10-element Array{Union(UTF8String,ASCIIString),1}: "The value of i is 1\n" "The value of i is 2\n" "The value of i is 3\n" "The value of i is 4\n" "The value of i is 5\n" "The value of i is 6\n" "The value of i is 7\n" "The value of i is 8\n" "The value of i is 9\n" "The value of i is 10\n"
map(split, lines)
10-element Array{Array{SubString{ASCIIString},1},1}: SubString{ASCIIString}["The","value","of","i","is","1"] SubString{ASCIIString}["The","value","of","i","is","2"] SubString{ASCIIString}["The","value","of","i","is","3"] SubString{ASCIIString}["The","value","of","i","is","4"] SubString{ASCIIString}["The","value","of","i","is","5"] SubString{ASCIIString}["The","value","of","i","is","6"] SubString{ASCIIString}["The","value","of","i","is","7"] SubString{ASCIIString}["The","value","of","i","is","8"] SubString{ASCIIString}["The","value","of","i","is","9"] SubString{ASCIIString}["The","value","of","i","is","10"]
[float(line[6]) for line in map(split, lines)]
10-element Array{Any,1}: 1.0 2.0 3.0 4.0 5.0 6.0 7.0 8.0 9.0 10.0
x = rand(5,5)
5x5 Array{Float64,2}: 0.733305 0.407818 0.856017 0.17519 0.300911 0.780196 0.410316 0.747914 0.517746 0.482768 0.821524 0.282658 0.499362 0.33969 0.46995 0.127905 0.784039 0.552099 0.370477 0.000157871 0.614037 0.831996 0.557765 0.400553 0.816338
writedlm("random.txt", x)
;cat random.txt
.7333048397971658 .40781770914373294 .8560165670499627 .1751897160302176 .3009107585707549 .7801957216838906 .41031585009227367 .7479144542902101 .5177461965467203 .4827677730696496 .8215243209225078 .28265839467586185 .49936164572625463 .33968997036639403 .46994964345333146 .1279053435465234 .7840390419869261 .5520986105415162 .3704773976502098 .00015787055013616325 .6140369836251944 .8319958406211578 .5577649080752074 .4005532312183908 .8163378474918641
y = readdlm("random.txt") # note that tab completion works for files
5x5 Array{Float64,2}: 0.733305 0.407818 0.856017 0.17519 0.300911 0.780196 0.410316 0.747914 0.517746 0.482768 0.821524 0.282658 0.499362 0.33969 0.46995 0.127905 0.784039 0.552099 0.370477 0.000157871 0.614037 0.831996 0.557765 0.400553 0.816338
;ls
Arrays with any indexing.ipynb Introduction to Julia.ipynb Introduction to Julia.pdf Introduction to Julia.tex Random matrices.ipynb julia_tutorial.md
run(`echo Hello`)
Hello
Linear algebra, FFT, random numbers, special functions. Packages for optimization, ODEs etc.
Functions may be defined using the short syntax f(x) = 3x + 1
or using a longer form:
dup(x) = 2x
dup (generic function with 1 method)
function duplicate(x)
2x # no explicit "return" needed
end
duplicate (generic function with 1 method)
The last value computed in the function is automatically returned; no explicit return
statement is required.
duplicate(x) = x^2
quad2 (generic function with 1 method)
Every operator in Julia is a function. Functions are implemented by specifying their action on different types. Until now, we have written only functions that are generic, in the sense that they do not specify which type they accept, and as in Python they will work as long as the operations performed in them make sense for the input value:
duplicate(3), duplicate(3.5), duplicate(1+3im)
(6,7.0,2 + 6im)
duplicate("Hola")
syntax: unexpected , while loading In[14], in expression starting on line 1
2 * "Hola"
no method *(Int64, ASCIIString) while loading In[15], in expression starting on line 1
Note that string concatenation uses the *
operator in Julia, instead of the +
operator as in Python.
Repeating a string is thus done by raising to an integer power:
"Hello"^2
"HelloHello"
As a simple example, suppose that we wish to concatenate two strings. In Python we would write:
s1 = "Hello, "
s2 = "David"
s1 + s2
no method +(ASCIIString, ASCIIString) while loading In[17], in expression starting on line 4
However, we see that in Julia, summation is not defined for strings. What is it defined for?
+
+ (generic function with 123 methods)
We see that +
is treated as a function, and that it has a multitude of methods, which, in Julia, are specialised versions of the function that act on different types:
methods(+)
If we were unaware of the *
operator for string concatenation, we could just define our own +
for the concatenation of two strings:
+(s1::String, s2::String) = string(s1, s2)
+ (generic function with 124 methods)
"First" + " second"
"First second"
However, we cannot add a number to a string, since we have not (yet) defined it:
"The value of x is " + 3
no method +(ASCIIString, Int64) while loading In[2], in expression starting on line 1
This we can also define, using the previous new definition:
+(s::String, x::Number) = s + "$(2x)"
+ (generic function with 126 methods)
"The value of x is " + 3
"The value of x is 6"
x = 3.5
"The value of x is " + x
"The value of x is 3.5"
3 + "hello"
no method +(Int64, ASCIIString) while loading In[8], in expression starting on line 1
In fact, we can define the summation of a string with any other object:
+(s::String, x) = s + string(x)
+ (generic function with 126 methods)
"Complex " + [3,4,5]
"Complex [3,4,5]"
"a" + 3
"a6"
Number
Number
typeof(Number)
DataType
super(Int64)
Signed
super(Signed)
Integer
super(Integer)
Real
super(Real)
Number
super(Number)
Any
In this way, the concept of "function" is replaced by a "patchwork" of different definitions for objects of different types, easily modifiable by the user. This is also exactly the way to define "operator overloading" for user-defined types.
In the above, we also begin to see the power of multiple dispatch: we defined two methods (versions) of the function +
, both with the same number but different types of arguments.
A user-defined "composite type" is a collection of data. Unlike in Python, types do not "own" methods (functions internal to the type).
Rather, methods are defined separately, and are characterised by the types of all of their arguments; this is known as multiple dispatch. (Dispatch is the process of choosing which "version" of a given function to execute.)
A simple, but useful, example, is that of defining a 2D vector type. (See also the ImmutableArrays.jl
package; fixed-size arrays will later be incorporated into base Julia.)
@which 3//4
Rational(3)
3//1
6//4
3//2
im*im
-1 + 0im
immutable Vector2D # type
x::Float64
y::Float64
end
v = Vector2D(3, 4)
w = Vector2D(5, 6)
Vector2D(5.0,6.0)
v + w
no method +(Vector2D, Vector2D) while loading In[33], in expression starting on line 1
+(v::Vector2D, w::Vector2D) = Vector2D(v.x+w.x, v.y+w.y)
+ (generic function with 127 methods)
v + w
Vector2D(8.0,10.0)
*(v::Vector2D, α::Number) = Vector2D(v.x*α, v.y*α)
*(α::Number, v::Vector2D) = Vector2D(v.x*α, v.y*α)
* (generic function with 127 methods)
v * 3.5
Vector2D(10.5,14.0)
3.5 * v
no method *(Float64, Vector2D) while loading In[38], in expression starting on line 1
Exercise:
Define mathematical operations on Vector2D
. Define a particle with position and velocity in 2D. Define function move
that acts on a particle to move it over a time $\delta t$.
Here, we have used immutable
instead of type
for efficiency: the object is stored in an efficient packed form.
The equivalent of the Python __repr__
method for an object is to extend the show
method:
show
show (generic function with 89 methods)
import Base.show
show(io::IO, v::Vector2D) = print(io, "[$(v.x), $(v.y)]")
show (generic function with 90 methods)
v
[3.0, 4.0]
+(v1::Vector2D, v2::Vector2D) = Vector2D(v1.x+v2.x, v1.y+v2.y)
*(v::Vector2D, lamb::Number) = Vector2D(lamb*v.x, lamb*v.y)
* (generic function with 125 methods)
We can confirm that the new method for the function +
has indeed been defined:
methods(+)
x = Vector2D(3, 4)
y = Vector2D(5, 6)
x + y
[8.0, 10.0]
Types may have a parameter, for example:
immutable Vector2D{T <: Real}
x::T
y::T
end
T
is a type parameter.
The expression T <: Real
means that T
must be a subtype of the abstract type Real
.
We can investigate the type hierarchy with the super
function:
Integer
Integer
super(Integer)
Real
super(Real)
Number
super(Number)
Any
v = Vector2D(3., 4.)
Vector2D{Float64}(3.0,4.0)
v = Vector2D(3, 4.)
no method Vector2D{T<:Real}(Int64, Float64) while loading In[3], in expression starting on line 1
v = Vector2D(3//4, 5//6)
Vector2D{Rational{Int64}}(3//4,5//6)
show{T}(io::IO, v::Vector2D{T}) = print(io, "[$(v.x), $(v.y)]")
show (generic function with 91 methods)
v
[3//4, 5//6]
Here, the types of the two arguments were different, so there is no match for the type signature.
We can define outer constructors, defined outside the type definition itself, which allow other ways of constructing the object:
Vector2D{T}(x::T) = Vector2D(x, x)
Vector2D{T<:Real} (constructor with 2 methods)
Vector2D(3)
Vector2D{Int64}(3,3)
Let's define a particle:
type Particle
position::Vector2D{Float64}
velocity::Vector2D{Float64}
end
move(p::Particle, dt::Real) = p.position += p.velocity * dt
move (generic function with 1 method)
show(io::IO, p::Particle) = print(io, "pos: $(p.position); vel: $(p.velocity)")
show (generic function with 92 methods)
p = Particle(Vector2D(0.,0.), Vector2D(1.,1.))
pos: [0.0, 0.0]; vel: [1.0, 1.0]
+{T}(v1::Vector2D{T}, v2::Vector2D{T}) = Vector2D{T}(v1.x+v2.x, v1.y+v2.y)
*{T}(v::Vector2D{T}, lamb::Number) = Vector2D{T}(lamb*v.x, lamb*v.y)
* (generic function with 127 methods)
move(p, 0.1)
[0.1, 0.1]
p
Particle(Vector2D(0.1,0.1),Vector2D(1.0,1.0))
Now we can define a gas as a collection of particle:
Int
Int64
type Gas
particles::Vector{Particle} # Array{Particle, 1}
function Gas(N::Integer)
parts = [Particle(Vector2D(rand(2)...), Vector2D(rand(2)...)) for i in 1:N]
new(parts)
end
end
show(io::IO, g::Gas) = for i in 1:length(g.particles); \
println(io, "Particle $i: $(g.particles[i])"); end
show (generic function with 93 methods)
g = Gas(10)
Particle 1: pos: [0.8401571871406985, 0.18856970525763184]; vel: [0.4596812641022978, 0.5784262750724491] Particle 2: pos: [0.31451484655671647, 0.10567720577699169]; vel: [0.6793346556536703, 0.7043142026390625] Particle 3: pos: [0.2837859636931628, 0.5789334209946704]; vel: [0.6970907948612055, 0.9252322283977674] Particle 4: pos: [0.4880527858855739, 0.6011994939455119]; vel: [0.27086791922118336, 0.7983654060058591] Particle 5: pos: [0.40422329467131, 0.4167640334568994]; vel: [0.17875915401147457, 0.8245688498769441] Particle 6: pos: [0.11205564713828187, 0.31206386913314965]; vel: [0.9344556687726897, 0.10661012636906952] Particle 7: pos: [0.758378072195554, 0.296551074205055]; vel: [0.24019765986222263, 0.19659517334941445] Particle 8: pos: [0.19922196169683493, 0.5900826738906546]; vel: [0.2936470240291018, 0.6600190957249954] Particle 9: pos: [0.13252747366692819, 0.7307090616735368]; vel: [0.9020558731460289, 0.8512875803383082] Particle 10: pos: [0.3379179889713879, 0.06402477243763549]; vel: [0.6014263963042701, 0.22639908858998958]
move(g, 1)
g
Particle 1: pos: [0.730880319692343, 0.2796501535129359]; vel: [0.011193267298662768, 0.1896808129627281] Particle 2: pos: [1.16039593789992, 0.9441692275849607]; vel: [0.263429151377901, 0.8468877504970715] Particle 3: pos: [1.6666067582365627, 0.3961299078605012]; vel: [0.7700748108540991, 0.2196494577690271] Particle 4: pos: [0.9701729190049899, 0.7843895045657923]; vel: [0.25339292712850914, 0.35521212198092544] Particle 5: pos: [0.6973622153663408, 1.0810152148402645]; vel: [0.4550143749133051, 0.6443745244169132] Particle 6: pos: [0.7266153528207528, 0.22939062897384166]; vel: [0.23270614878986695, 0.2170317822421448] Particle 7: pos: [0.3522289676319774, 1.8952100652781094]; vel: [0.2328368405667145, 0.9154546952395797] Particle 8: pos: [0.6517052073532643, 0.7954128199341437]; vel: [0.6212587027599834, 0.40434264420206345] Particle 9: pos: [1.461020849277742, 1.0270123116841756]; vel: [0.7485545114358987, 0.23357706552863133] Particle 10: pos: [1.3454456302874394, 0.9754278188746057]; vel: [0.7429592498565307, 0.12339036567006523]
function move(g::Gas, dt::Number)
for particle in g.particles
move(particle, dt)
end
end
move (generic function with 2 methods)
move(g, 1)
[Particle(Vector2D(0.4100545643085276,0.7676363902728662),Vector2D(0.24938626268837427,0.9825967553493955)),Particle(Vector2D(0.2102452108859496,0.889693333563705),Vector2D(0.21287583861363224,0.8068570118314959)),Particle(Vector2D(0.19724340554018993,0.7904963971058772),Vector2D(0.536265203822587,0.9037289864895577)),Particle(Vector2D(0.25780964395756567,0.36770118129693063),Vector2D(0.8062880526148144,0.26950033650622607)),Particle(Vector2D(0.674431498602819,0.6854905990848916),Vector2D(0.7982268753553292,0.9381682737272479)),Particle(Vector2D(0.3107654997497411,0.00916652369829607),Vector2D(0.6903239459974226,0.3335712930840613)),Particle(Vector2D(0.8337757015793896,0.4019794592055861),Vector2D(0.8398063168102692,0.409628854228981)),Particle(Vector2D(0.8886642578985902,0.2662466340462406),Vector2D(0.36997149382773076,0.012723333186220875)),Particle(Vector2D(0.2980807091871105,0.4647730476022649),Vector2D(0.4908256919109293,0.1966075391583828)),Particle(Vector2D(0.9310095003592627,0.7354528651044734),Vector2D(0.9694676029513127,0.7690493773372564))]
g
Particle(
Vector2D(0.6594408269969019,1.7502331456222617),Vector2D(0.24938626268837427,0.9825967553493955)) Particle(Vector2D(0.42312104949958185,1.6965503453952009),Vector2D(0.21287583861363224,0.8068570118314959)) Particle(Vector2D(0.733508609362777,1.6942253835954348),Vector2D(0.536265203822587,0.9037289864895577)) Particle(Vector2D(1.06409769657238,0.6372015178031567),Vector2D(0.8062880526148144,0.26950033650622607)) Particle(Vector2D(1.4726583739581482,1.6236588728121395),Vector2D(0.7982268753553292,0.9381682737272479)) Particle(Vector2D(1.0010894457471637,0.3427378167823574),Vector2D(0.6903239459974226,0.3335712930840613)) Particle(Vector2D(1.6735820183896588,0.8116083134345671),Vector2D(0.8398063168102692,0.409628854228981)) Particle(Vector2D(1.258635751726321,0.2789699672324615),Vector2D(0.36997149382773076,0.012723333186220875)) Particle(Vector2D(0.7889064010980398,0.6613805867606477),Vector2D(0.4908256919109293,0.1966075391583828)) Particle(Vector2D(1.9004771033105754,1.5045022424417298),Vector2D(0.9694676029513127,0.7690493773372564))
M = [1 1; 0 1]
2x2 Array{Int64,2}: 1 1 0 1
M * M
2x2 Array{Int64,2}: 1 2 0 1
M^10
2x2 Array{Int64,2}: 1 10 0 1
M^(-1)
2x2 Array{Float64,2}: 1.0 -1.0 0.0 1.0
inv(M)
2x2 Array{Float64,2}: 1.0 -1.0 0.0 1.0
det(M)
1
norm(M)
1.618033988749895
?norm
Base.norm(A[, p]) Compute the "p"-norm of a vector or the operator norm of a matrix "A", defaulting to the "p=2"-norm. For vectors, "p" can assume any numeric value (even though not all values produce a mathematically valid vector norm). In particular, "norm(A, Inf)" returns the largest value in "abs(A)", whereas "norm(A, -Inf)" returns the smallest. For matrices, valid values of "p" are "1", "2", or "Inf". (Note that for sparse matrices, "p=2" is currently not implemented.) Use "vecnorm()" to compute the Frobenius norm.
lamb, vv = eig(M)
([1.0,1.0], 2x2 Array{Float64,2}: 1.0 -1.0 0.0 2.22045e-16)
methods(Symmetric)
Exercise: Generate a random matrix of $1000 \times 1000$ gaussian random variates. Calculate its eigenvalues and plot them. Calculate the differences between consecutive eigenvalues and plot a histogram of them.
N = 1000
M = randn(N, N)
M = Symmetric(M);
typeof(M)
Symmetric{Float64} (constructor with 2 methods)
@which dot([3], [4])
help("dot")
INFO: Loading help data...
Base.dot(x, y) Compute the dot product. For complex vectors, the first vector is conjugated. Base.LinAlg.BLAS.dot(n, X, incx, Y, incy) Dot product of two vectors consisting of "n" elements of array "X" with stride "incx" and "n" elements of array "Y" with stride "incy".
@time lamb, vv = eig(M);
elapsed time: 0.495310114 seconds (24377616 bytes allocated)
using PyPlot
INFO: Loading help data...
plot(lamb, "o-")
1-element Array{Any,1}: PyObject <matplotlib.lines.Line2D object at 0x10a503250>
PyPlot.figure(figsize=(8,4))
plot(lamb, "o-")
1-element Array{Any,1}: PyObject <matplotlib.lines.Line2D object at 0x12739cad0>
differences = diff(lamb);
plot(differences, "o-")
1-element Array{Any,1}: PyObject <matplotlib.lines.Line2D object at 0x1039fe650>
h = hist(d, 100)
(0.0:0.002:0.14400000000000002,[23,67,129,145,186,148,169,163,133,136 … 0,0,0,0,0,0,0,0,0,1])
plot(collect(h[1][1:end-1]), h[2])
1-element Array{Any,1}: PyObject <matplotlib.lines.Line2D object at 0x127a18350>
requires vs include vs using vs import
g.particles
10-element Array{Particle,1}: Particle(Vector2D(1.1970706607995785,0.9626230979634414),Vector2D(0.860883456513649,0.6912433760368852)) Particle(Vector2D(1.053173102651149,0.1511931905677396),Vector2D(0.8340780458653039,0.25711513963745936)) Particle(Vector2D(0.23418507182780868,0.1636419234893668),Vector2D(0.5955322025693717,0.24991956880512323)) Particle(Vector2D(0.42255575858509486,0.5273931990785453),Vector2D(0.2006799940853723,0.6328159954800312)) Particle(Vector2D(0.3068915889399675,0.13605825532312857),Vector2D(0.43637626808391783,0.18840541521552945)) Particle(Vector2D(1.261065364493417,0.7019570824715238),Vector2D(0.8829407354236436,0.8559843409264631)) Particle(Vector2D(0.19304010783643638,0.6135945878339716),Vector2D(0.5699319437166632,0.633292636020014)) Particle(Vector2D(0.6975851395549426,0.35822475426018596),Vector2D(0.9632833403270651,0.6328280131492559)) Particle(Vector2D(1.1112601539035263,0.5851179879294491),Vector2D(0.9151617509797421,0.664341462715691)) Particle(Vector2D(0.3128717288502572,1.2580516751593864),Vector2D(0.37515387127035216,0.9028796885418442))
for particle in g.particles
move(particle, 1)
end
g.particles
10-element Array{Particle,1}: Particle(Vector2D(2.057954117313227,1.6538664740003266),Vector2D(0.860883456513649,0.6912433760368852)) Particle(Vector2D(1.887251148516453,0.40830833020519897),Vector2D(0.8340780458653039,0.25711513963745936)) Particle(Vector2D(0.8297172743971803,0.41356149229449),Vector2D(0.5955322025693717,0.24991956880512323)) Particle(Vector2D(0.6232357526704672,1.1602091945585764),Vector2D(0.2006799940853723,0.6328159954800312)) Particle(Vector2D(0.7432678570238853,0.324463670538658),Vector2D(0.43637626808391783,0.18840541521552945)) Particle(Vector2D(2.1440060999170605,1.557941423397987),Vector2D(0.8829407354236436,0.8559843409264631)) Particle(Vector2D(0.7629720515530996,1.2468872238539856),Vector2D(0.5699319437166632,0.633292636020014)) Particle(Vector2D(1.6608684798820077,0.9910527674094418),Vector2D(0.9632833403270651,0.6328280131492559)) Particle(Vector2D(2.0264219048832683,1.2494594506451402),Vector2D(0.9151617509797421,0.664341462715691)) Particle(Vector2D(0.6880256001206093,2.1609313637012306),Vector2D(0.37515387127035216,0.9028796885418442))