Lightning Round - Basic Features and Commands


In this notebook, we go through basic constructs and commands.

Competences

The user should know to start Julia in various modes (command line prompt, IJulia), how to exit, learn some features and be able to write simple programs.

Credits

This notebook is based on the slides accompanying the Lightning Round video by Alan Edelman, all part of the Julia Tutorial.

Julia resources

Julia resources are accessible through the Julia home page.

Please check packages, docs and juliacon (here you will also find links to videos from previous conferences).

Execution

To execute cell use Shift + Enter or press Play (Run cell).

To run all cells in the notebook go to Cell -> Run All

Markdown cells

Possibility to write comments / code / formulas in Markdown cells, makes Jupyter notebooks ideal for teaching and research. Text is written using Julia Markdown, which is GitHub Markdown with additional understanding of basic LaTeX.

Mastering (GitHub) Markdown is a 3-minute read, another short and very good manual is at http://daringfireball.net/projects/markdown/.

Some particulars of Julia Markdown are described in Documentation section of Julia Manual, yet another 3-minute read.

nbconvert

It is extremely easy to convert notebooks to slides, LaTeX, or PDF. For details see the documentation.

Slides

Clicking View -> Cell Toolbar -> Slideshow opens the Slide Type menu for each cell.

The slideshow is made with the command

jupyter nbconvert --to slides notebook.ipynb

The slideshow is displayed in browser with the command

jupyter nbconvert --to slides --post serve notebook.ipynb

LaTeX

jupyter nbconvert --to latex notebook.ipynb

PDF

jupyter nbconvert --to PDF notebook.ipynb

N.B. For the above conversions Pandoc needs to be installed.

Which version of Julia is running?


In [1]:
versioninfo()


Julia Version 0.6.2
Commit d386e40c17 (2017-12-13 18:08 UTC)
Platform Info:
  OS: Linux (x86_64-pc-linux-gnu)
  CPU: Pentium(R) Dual-Core  CPU      E5700  @ 3.00GHz
  WORD_SIZE: 64
  BLAS: libopenblas (USE64BITINT DYNAMIC_ARCH NO_AFFINITY Penryn)
  LAPACK: libopenblas64_
  LIBM: libopenlibm
  LLVM: libLLVM-3.9.1 (ORCJIT, penryn)

Quitting

Exiting from julia> or restarting kernel in IJulia


In [2]:
# exit()

Documentation

Documentation is well written and the starting point is http://docs.julialang.org/en/latest/

But, also remeber that Julia is open source and all routines are available on GitHub. You will learn how to make full use of this later in the course.

Punctuation review

  • [...] are for indexing, array constructors and Comprehensions
  • (...) are required for functions quit(), tic(), toc(), help()
  • {...} are for arrays
  • # is for comments

Basic indexing


In [3]:
# Matrix with random entries between 0 and 1
A=rand(5,5)


Out[3]:
5×5 Array{Float64,2}:
 0.651273  0.788237  0.926671  0.0070114  0.840558
 0.885479  0.381515  0.470443  0.319365   0.558173
 0.893232  0.643527  0.830136  0.638924   0.748683
 0.106704  0.13232   0.28214   0.508001   0.885077
 0.590179  0.59097   0.929026  0.834082   0.396741

In [4]:
A[1,1]


Out[4]:
0.6512729074930683

In [5]:
# You can index into output directly
rand(5,5)[1:2,3:4]


Out[5]:
2×2 Array{Float64,2}:
 0.950211  0.605514
 0.716064  0.634068

Indexing is elegant

If you want to compute the lower right $2\times 2$ block of $A^{10}$, in most languages you need to first compute $B=A^{10}$ and then index into $B$. In Julia, the command is simply


In [6]:
(A^10)[4:5,4:5] # Parenthesses around A^10 are necessary


Out[6]:
2×2 Array{Float64,2}:
  6614.37   9513.22
 11773.1   16932.8 

Comprehensions - elegant array constructors


In [7]:
[i for i=1:5]


Out[7]:
5-element Array{Int64,1}:
 1
 2
 3
 4
 5

In [8]:
[trace(rand(n,n)) for n=1:5]


Out[8]:
5-element Array{Float64,1}:
 0.577226
 1.10189 
 0.814006
 2.18425 
 2.77226 

In [9]:
x=1:10


Out[9]:
1:10

In [10]:
[ x[i]+x[i+1] for i=1:9 ]


Out[10]:
9-element Array{Int64,1}:
  3
  5
  7
  9
 11
 13
 15
 17
 19

In [11]:
z = [eye(n) for n=1:5]  # z is Array of Arrays


Out[11]:
5-element Array{Array{Float64,2},1}:
 [1.0]                                                                           
 [1.0 0.0; 0.0 1.0]                                                              
 [1.0 0.0 0.0; 0.0 1.0 0.0; 0.0 0.0 1.0]                                         
 [1.0 0.0 0.0 0.0; 0.0 1.0 0.0 0.0; 0.0 0.0 1.0 0.0; 0.0 0.0 0.0 1.0]            
 [1.0 0.0 … 0.0 0.0; 0.0 1.0 … 0.0 0.0; … ; 0.0 0.0 … 1.0 0.0; 0.0 0.0 … 0.0 1.0]

In [12]:
# First element is a 1x1 Array
z[1]


Out[12]:
1×1 Array{Float64,2}:
 1.0

In [13]:
# What is the fourth element?
z[4]


Out[13]:
4×4 Array{Float64,2}:
 1.0  0.0  0.0  0.0
 0.0  1.0  0.0  0.0
 0.0  0.0  1.0  0.0
 0.0  0.0  0.0  1.0

In [14]:
# Another example of a comprehension
A=[ i+j for i=1:5, j=1:5 ]


Out[14]:
5×5 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

In [15]:
# Notice the promotion
B=[ i+j for i=1:5, j=1.0:5 ]


Out[15]:
5×5 Array{Float64,2}:
 2.0  3.0  4.0  5.0   6.0
 3.0  4.0  5.0  6.0   7.0
 4.0  5.0  6.0  7.0   8.0
 5.0  6.0  7.0  8.0   9.0
 6.0  7.0  8.0  9.0  10.0

Commands ndims() and typeof()


In [16]:
ndims(ans)


Out[16]:
2

In [17]:
# z is a one-dimensional array
ndims(z)


Out[17]:
1

In [18]:
# Array of Arrays
typeof(z)


Out[18]:
Array{Array{Float64,2},1}

In [19]:
# z[5] is a two-dimensional array
typeof(z[5])


Out[19]:
Array{Float64,2}

In [20]:
typeof(A)


Out[20]:
Array{Int64,2}

Vectors are 1-dimensional arrays

See Multi-dimensional arrays for more.


In [21]:
v=rand(5,1) # This is 2-dimensional array


Out[21]:
5×1 Array{Float64,2}:
 0.357612
 0.444656
 0.380407
 0.936482
 0.32349 

In [22]:
vv=vec(v) # This is an 1-dimensional array or vector


Out[22]:
5-element Array{Float64,1}:
 0.357612
 0.444656
 0.380407
 0.936482
 0.32349 

In [23]:
v==vv  # Notice that they are different


Out[23]:
false

In [24]:
v-vv # Again a promotion


Out[24]:
5×1 Array{Float64,2}:
 0.0
 0.0
 0.0
 0.0
 0.0

In [25]:
w=rand(5) # This is again a vector


Out[25]:
5-element Array{Float64,1}:
 0.348688
 0.442695
 0.640844
 0.298521
 0.369625

In [26]:
Mv=[v w] # First column is a 5 x 1 matrix, second column is a vector of length 5


Out[26]:
5×2 Array{Float64,2}:
 0.357612  0.348688
 0.444656  0.442695
 0.380407  0.640844
 0.936482  0.298521
 0.32349   0.369625

In [27]:
x=Mv[:,1] # Matrix columns are extracted as vectors


Out[27]:
5-element Array{Float64,1}:
 0.357612
 0.444656
 0.380407
 0.936482
 0.32349 

In [28]:
y=Mv[:,2]


Out[28]:
5-element Array{Float64,1}:
 0.348688
 0.442695
 0.640844
 0.298521
 0.369625

In [29]:
x==v # The types differ


Out[29]:
false

In [30]:
y==w


Out[30]:
true

In [31]:
# Transpose of a matrix is a matrix
v'


Out[31]:
1×5 Array{Float64,2}:
 0.357612  0.444656  0.380407  0.936482  0.32349

In [32]:
# Transpose of a vector is a RowVector
w'


Out[32]:
1×5 RowVector{Float64,Array{Float64,1}}:
 0.348688  0.442695  0.640844  0.298521  0.369625

1D and 2D arrays


In [33]:
w=1.0:5


Out[33]:
1.0:1.0:5.0

In [34]:
A*w


Out[34]:
5-element Array{Float64,1}:
  70.0
  85.0
 100.0
 115.0
 130.0

In [35]:
w=collect(1.0:5)


Out[35]:
5-element Array{Float64,1}:
 1.0
 2.0
 3.0
 4.0
 5.0

In [36]:
A*w  # This returns a 1-dimensional array


Out[36]:
5-element Array{Float64,1}:
  70.0
  85.0
 100.0
 115.0
 130.0

In [37]:
A*v # This returns a 2-dimensional array - v is a 5 x 1 array


Out[37]:
5×1 Array{Float64,2}:
 10.1942
 12.6368
 15.0795
 17.5221
 19.9648

Discussion

Such behavior is due to the fact that Julia has vectors as a special type. Pros? Cons?

What is matrix $\times$ vector?

What is the result of

$$ C[i,j]=A[i,:]*B[:,j]$$

In [38]:
B=[A[i,:]*A[:,j] for i=1:5, j=1:5]


DimensionMismatch("Cannot multiply two vectors")

Stacktrace:
 [1] *(::Array{Int64,1}, ::Array{Int64,1}) at ./linalg/rowvector.jl:184
 [2] (::##11#12)(::Tuple{Int64,Int64}) at ./<missing>:0
 [3] collect(::Base.Generator{Base.Iterators.Prod2{UnitRange{Int64},UnitRange{Int64}},##11#12}) at ./array.jl:475
 [4] include_string(::String, ::String) at ./loading.jl:522

In [39]:
# Rows and columsn are both 1D vectors - must use dot product
B=[A[i,:]A[:,j] for i=1:5, j=1:5]


Out[39]:
5×5 Array{Int64,2}:
  90  110  130  150  170
 110  135  160  185  210
 130  160  190  220  250
 150  185  220  255  290
 170  210  250  290  330

In [40]:
inv(B)


Out[40]:
5×5 Array{Float64,2}:
  8.79609e12   1.75922e13  -3.51844e13  -1.75922e13   2.63883e13
 -2.63883e13  -0.421875     3.51844e13   3.51844e13  -4.39805e13
  1.75922e13  -3.51844e13   3.51844e13  -3.51844e13   1.75922e13
  8.79609e12  -0.0         -3.51844e13   3.51844e13  -8.79609e12
 -8.79609e12   1.75922e13   0.0         -1.75922e13   8.79609e12

ones(), eye() and zeros()

Notice that the output type depends on the argument. This is a general Julia feature called Multiple dispatch and will be explained later in more detail.


In [41]:
# The output type depends on the argument. Float64 is the default.
ones(3,5), ones(5), ones(rand(1:3,4,6))


Out[41]:
([1.0 1.0 … 1.0 1.0; 1.0 1.0 … 1.0 1.0; 1.0 1.0 … 1.0 1.0], [1.0, 1.0, 1.0, 1.0, 1.0], [1 1 … 1 1; 1 1 … 1 1; 1 1 … 1 1; 1 1 … 1 1])

In [42]:
rand(1:3,4,6)


Out[42]:
4×6 Array{Int64,2}:
 3  2  2  3  3  1
 2  1  2  1  1  3
 1  1  1  1  3  1
 3  3  3  2  2  1

In [43]:
zeros(3,5)


Out[43]:
3×5 Array{Float64,2}:
 0.0  0.0  0.0  0.0  0.0
 0.0  0.0  0.0  0.0  0.0
 0.0  0.0  0.0  0.0  0.0

In [44]:
zeros(5)


Out[44]:
5-element Array{Float64,1}:
 0.0
 0.0
 0.0
 0.0
 0.0

In [45]:
zeros(rand(1:3,4,6))


Out[45]:
4×6 Array{Int64,2}:
 0  0  0  0  0  0
 0  0  0  0  0  0
 0  0  0  0  0  0
 0  0  0  0  0  0

In [46]:
eye(4)


Out[46]:
4×4 Array{Float64,2}:
 1.0  0.0  0.0  0.0
 0.0  1.0  0.0  0.0
 0.0  0.0  1.0  0.0
 0.0  0.0  0.0  1.0

In [47]:
eye(Int,4)


Out[47]:
4×4 Array{Int64,2}:
 1  0  0  0
 0  1  0  0
 0  0  1  0
 0  0  0  1

In [48]:
eye(Int32,4)


Out[48]:
4×4 Array{Int32,2}:
 1  0  0  0
 0  1  0  0
 0  0  1  0
 0  0  0  1

In [49]:
complex(eye(4))


Out[49]:
4×4 Array{Complex{Float64},2}:
 1.0+0.0im  0.0+0.0im  0.0+0.0im  0.0+0.0im
 0.0+0.0im  1.0+0.0im  0.0+0.0im  0.0+0.0im
 0.0+0.0im  0.0+0.0im  1.0+0.0im  0.0+0.0im
 0.0+0.0im  0.0+0.0im  0.0+0.0im  1.0+0.0im

Complex numbers

i is too valuable symbol for loops, so Julia uses im for the complex unit.


In [50]:
im


Out[50]:
im

In [51]:
2im


Out[51]:
0 + 2im

In [52]:
typeof(ans)


Out[52]:
Complex{Int64}

In [53]:
typeof(2.0im)


Out[53]:
Complex{Float64}

In [54]:
# Another way of defining complex numbers
complex(3,4)


Out[54]:
3 + 4im

In [55]:
# If one of the arguments if Float64, so is the 
# entire number - promotion!
complex(3,4.0)


Out[55]:
3.0 + 4.0im

In [56]:
# This produces an error (like in any other language)
sqrt(-1)


DomainError:
sqrt will only return a complex result if called with a complex argument. Try sqrt(complex(x)).

Stacktrace:
 [1] sqrt(::Int64) at ./math.jl:434
 [2] include_string(::String, ::String) at ./loading.jl:522

In [57]:
# and this is fine.
sqrt(complex(-1))


Out[57]:
0.0 + 1.0im

Ternary operator

Let us define our version of the sign function


In [58]:
si(x) = (x>0) ? 1 : -1


Out[58]:
si (generic function with 1 method)

In [59]:
si(-13)


Out[59]:
-1

This is equivalent to:


In [60]:
function si(x)
    if x>0
        return 1
    else
        return -1
    end
end


Out[60]:
si (generic function with 1 method)

In [61]:
si(pi-8), si(0), si(0.0)


Out[61]:
(-1, -1, -1)

The expressions can be nested:


In [62]:
# now si(0) is 0
si(x) = (x>0) ? 1 : ((x<0) ? -1: 0)


Out[62]:
si (generic function with 1 method)

In [63]:
# '\pi Tab' produces π and means π
si(π-8), si(0)


Out[63]:
(-1, 0)

Typing

Special mathematical (LaTeX) symbols can be used (like $\alpha$, $\Xi$, $\pi$, $\oplus$, $\cdot$, etc.). The symbol in both, the notebook and command line version, is produced by writing LaTeX command followed by <Tab>.

Subscripts and superscripts are written as e.g., x_m<TAB>, x\^3<TAB>.


In [64]:
Ξ = 8; Ψ  = 6; Γ = Ξ  Ψ


Out[64]:
48

In [65]:
typeof(Γ)


Out[65]:
Int64

In [67]:
ω₁=7; xᵏ=23
ω₁*xᵏ


Out[67]:
161

Writing a program and running a file

Special feature of Julia is that the results of commands are not displayed, unless explicitely required.

To display results you can use commands @show or println() (or many others, see the Text I/O in the manual.)

Consider the file deploy.jl with the following code

n=int(ARGS[1])          # take one integer argument
println(rand(1:n,n,n))  # generate and print n x n matrix of random integers between 1 and n
@show b=3               # set b to 3 and show the result
c=4                     # set c to 4

Running the program in the shell gives

$ julia deploy.jl 5
[1 3 2 4 1
 5 3 1 1 4
 5 4 2 2 5
 3 1 2 3 4
 4 4 5 4 4]
b = 3 => 3

Notice that the result of the last command (c) is not displayed.

You can, of course, also run the above command in the Console tab of JuliaBox. To do this, you first have to change the directory

cd Julia-Course/src

Similarly, the program can be converted to executable and run directly, without referencing julia in the command line. The refernece to julia must be added in the first line, as in the file deploy1.jl:

#!/usr/bin/julia
n=int(ARGS[1])
println(rand(1:n,n,n))
@show b=3
c=4

In the shell do:

$ chmod +x deploy1.jl
$ ./deploy1.jl 5
[4 5 3 2 5
 4 2 1 5 1
 3 2 4 5 1
 2 4 4 3 1
 3 4 5 3 3]
b = 3 => 3

Finally, to run the same program in julia shell or IJulia, the input has to be changed, as in the file deploy2.jl:

n=int(readline(STDIN))
println(rand(1:n,n,n))
@show b=3
c=4

Notice that now the result of the last line is displayed by default - in this case it is 4, the values of c. The output of the random matrix and of b is forced.


In [68]:
include("deploy2.jl")


STDIN> 5
[4 4 4 3 5; 1 1 3 2 2; 5 1 5 5 3; 4 5 4 1 4; 1 2 3 1 1]
b = 3 = 3
Out[68]:
4

Running external programs and unix pipe

run() - calling external program


In [69]:
?run


search: run trunc truncate round rounding RoundUp RoundDown RoundToZero

Out[69]:
run(command, args...)

Run a command object, constructed with backticks. Throws an error if anything goes wrong, including the process exiting with a non-zero status.

Notice, that this is not a gret help, Julia has much better commands for this.


In [70]:
# This calls the unix Calendar program
run(`cal`)


     April 2018     
Su Mo Tu We Th Fr Sa
 1  2  3  4  5  6  7 
 8  9 10 11 12 13 14 
15 16 17 18 19 20 21 
22 23 24 25 26 27 28 
29 30                
                     

In [71]:
# The pipe is '|>' instead of usual '|'
run(pipeline(`cal`,`grep Sa`))


Su Mo Tu We Th Fr Sa

ccall() - calling C program


In [72]:
?ccall


search: ccall AbstractChannel

Out[72]:
ccall((symbol, library) or function_pointer, ReturnType, (ArgumentType1, ...), ArgumentValue1, ...)

Call function in C-exported shared library, specified by (function name, library) tuple, where each component is a string or symbol.

Note that the argument type tuple must be a literal tuple, and not a tuple-valued variable or expression. Alternatively, ccall may also be used to call a function pointer, such as one returned by dlsym.

Each ArgumentValue to the ccall will be converted to the corresponding ArgumentType, by automatic insertion of calls to unsafe_convert(ArgumentType, cconvert(ArgumentType, ArgumentValue)). (See also the documentation for each of these functions for further details.) In most cases, this simply results in a call to convert(ArgumentType, ArgumentValue).


In [73]:
# Simple version
ccall(:clock,Int,())


Out[73]:
32230547

In [74]:
path = ccall((:getenv, "libc"), Cstring, (Cstring,), "SHELL")


Out[74]:
Cstring(0x00007ffe306492d3)

In [75]:
unsafe_string(path) # Human readable version


Out[75]:
"/bin/bash"

Task() and Channel

Julia has a control flow feature that allows computations to be suspended and resumed in a flexible manner (see Tasks in the manual).


In [76]:
function stepbystep(c::Channel)
    put!(c, "start")
    for n=1:3
        put!(c,n^2)    
    end
    put!(c,"stop")
end


Out[76]:
stepbystep (generic function with 1 method)

In [77]:
c1=Channel(stepbystep)


Out[77]:
Channel{Any}(sz_max:0,sz_curr:1)

In [78]:
take!(c1)


Out[78]:
"start"

In [79]:
take!(c1)


Out[79]:
1

In [80]:
take!(c1)


Out[80]:
4

In [81]:
take!(c1)


Out[81]:
9

In [82]:
take!(c1)


Out[82]:
"stop"

In [83]:
# Guess what comes next?
take!(c1)


InvalidStateException("Channel is closed.", :closed)

Stacktrace:
 [1] check_channel_state(::Channel{Any}) at ./channels.jl:131
 [2] take_unbuffered(::Channel{Any}) at ./channels.jl:329
 [3] take!(::Channel{Any}) at ./channels.jl:317
 [4] include_string(::String, ::String) at ./loading.jl:522

In [ ]: