Tutorial: Julia Overview
Julia overview.
- 2018-08-11 Julia 0.7.0 Jeff Fessler (based on 2017 version by David Hong)
- 2019-01-20 Julia 1.0.3 and add note about line breaks
- 2020-08-05 Julia 1.5.0
- 2021-08-23 Julia 1.6.2
- 2023-09-03 Julia 1.9.2, Literate
This page comes from a single Julia file: 1-intro.jl
.
You can access the source code for such Julia documentation using the 'Edit on GitHub' link in the top right. You can view the corresponding notebook in nbviewer here: 1-intro.ipynb
, or open it in binder here: 1-intro.ipynb
.
Setup
Add the Julia packages used in this demo. Change false
to true
in the following code block if you are using any of the following packages for the first time.
if false
import Pkg
Pkg.add([
"InteractiveUtils"
"LinearAlgebra"
])
end
Tell Julia to use the following packages. Run Pkg.add()
in the preceding code block first, if needed.
# using InteractiveUtils: versioninfo
using LinearAlgebra: Diagonal, det, dot, tr
Numbers, arithmetic, types
Define a real number:
r = 3.0
3.0
Variables in Julia have a type:
typeof(r)
Float64
i = 3
3
typeof(i)
Int64
c = 3. + 2im
3.0 + 2.0im
typeof(c)
ComplexF64 (alias for Complex{Float64})
We can add, subtract, multiply and divide like usual:
4. + 5
9.0
4. - 5
-1.0
4. * 3
12.0
2. / 3
0.6666666666666666
Dividing Int
values with /
produces a Float
:
2/3
0.6666666666666666
4/2
2.0
This is different from Python 2, but similar to Python 3.
To divide integers with rounding, use ÷
instead. Type \div
then hit tab:
5 ÷ 2
2
More info about numbers here:
Vectors and matrices (i.e., arrays)
Make a vector of real numbers:
\[x = \begin{bmatrix} 1.0 \\ 3.5 \\ 2 \end{bmatrix}\]
x = [1, 3.5, 2]
3-element Vector{Float64}:
1.0
3.5
2.0
Note the type: Vector{Float64}
.
Having just one real number in the array sufficed for the array have all Float64
elements.
This is a true one-dimensional array of Float64
values.
(Matlab does not have 1D arrays; it fakes it using 2D arrays of size N × 1
.)
size(x) # returns a tuple
(3,)
length(x)
3
x_ints = [1,3,2]
3-element Vector{Int64}:
1
3
2
This is a one-dimensional array of Int64
values. We use these less often.
size(x_ints)
(3,)
length(x_ints)
3
Make a matrix using a semicolon to separate rows:
\[A = \begin{bmatrix} 1.1 & 1.2 & 1.3 \\ 2.1 & 2.2 & 2.3 \end{bmatrix}\]
A = [1.1 1.2 1.3; 2.1 2.2 2.3]
2×3 Matrix{Float64}:
1.1 1.2 1.3
2.1 2.2 2.3
This is a two-dimensional array (aka a matrix) of Float64
values.
size(A)
(2, 3)
length(A)
6
Different from Matlab, length
always returns the total number of elements.
Make vectors and matrices of all zeros.
zeros(3)
3-element Vector{Float64}:
0.0
0.0
0.0
Different from Matlab! Do not write zeros(3,1)
because Julia has proper 1D arrays. zeros(3,1)
and zeros(3)
are different!
zeros(2,3)
2×3 Matrix{Float64}:
0.0 0.0 0.0
0.0 0.0 0.0
And ones:
ones(3)
3-element Vector{Float64}:
1.0
1.0
1.0
ones(2,3)
2×3 Matrix{Float64}:
1.0 1.0 1.0
1.0 1.0 1.0
The "identity matrix" $I$ in Julia's LinearAlgebra
package is sophisticated.
Look at the following examples:
using LinearAlgebra: I
ones(3,3) - I
3×3 Matrix{Float64}:
0.0 1.0 1.0
1.0 0.0 1.0
1.0 1.0 0.0
ones(2,2) * I
2×2 Matrix{Float64}:
1.0 1.0
1.0 1.0
I(3)
3×3 LinearAlgebra.Diagonal{Bool, Vector{Bool}}:
1 ⋅ ⋅
⋅ 1 ⋅
⋅ ⋅ 1
If that $I$ seems too fancy, then you could make your own eye
function akin to Matlab as follows (but it should not be needed and it uses unnecessary memory):
eye = n -> Matrix(1.0*I(n))
eye(2)
2×2 Matrix{Float64}:
1.0 0.0
0.0 1.0
Make diagonal matrices using the Diagonal
function in LinearAlgebra
:
Diagonal(3:6)
4×4 LinearAlgebra.Diagonal{Int64, UnitRange{Int64}}:
3 ⋅ ⋅ ⋅
⋅ 4 ⋅ ⋅
⋅ ⋅ 5 ⋅
⋅ ⋅ ⋅ 6
This is far more memory efficient than Matlab's diag
command or Julia's LinearAlgebra.diagm
method. Avoid using those!
Make random vectors and matrices.
\[x = \begin{bmatrix} \mathcal{N}(0,1) \\ \mathcal{N}(0,1) \\ \mathcal{N}(0,1) \end{bmatrix} \qquad \text{i.e.,} \quad x_i \overset{\text{iid}}{\sim} \mathcal{N}(0,1)\]
\[A = \begin{bmatrix} \mathcal{N}(0,1) & \mathcal{N}(0,1) & \mathcal{N}(0,1) \\ \mathcal{N}(0,1) & \mathcal{N}(0,1) & \mathcal{N}(0,1) \end{bmatrix} \qquad \text{i.e., } \quad A_{ij} \overset{\text{iid}}{\sim} \mathcal{N}(0,1)\]
x = randn(3)
3-element Vector{Float64}:
-1.9550532464936665
-0.14826231637711607
-1.5128526102480702
A = randn(2,3)
2×3 Matrix{Float64}:
-0.585186 -0.47713 0.676879
-0.652286 0.0981847 0.0249781
Matrix operations
Indexing is done with square brackets (like in C and Python, unlike Matlab).
Index and begins at 1 (like in Matlab and counting) not 0 (like in C or Python).
A = [1.1 1.2 1.3; 2.1 2.2 2.3]
2×3 Matrix{Float64}:
1.1 1.2 1.3
2.1 2.2 2.3
A[1,1]
1.1
A[1,2:3]
2-element Vector{Float64}:
1.2
1.3
This row-slice is a one-dimensional slice (!) not a 1×2
matrix:
A[1:2,1]
2-element Vector{Float64}:
1.1
2.1
A[2,:]
3-element Vector{Float64}:
2.1
2.2
2.3
Vector dot product:
x = randn(3)
xdx = x'x
1.8413796518267749
xdx = dot(x,x)
1.8413796518267749
xdx = x'*x
1.8413796518267749
Different from Matlab! The output is a scalar, not a 1×1
"matrix:"
typeof(xdx)
Float64
Matrix times vector:
A = randn(2,3)
x = randn(3)
A*x
2-element Vector{Float64}:
1.386375158536375
-0.5639348746301314
Matrix times matrix:
A = randn(2,3)
B = randn(3,4)
A*B
2×4 Matrix{Float64}:
-1.1783 -0.807285 -0.136297 0.135519
-1.72594 -1.31452 0.0923625 0.360309
Matrix transpose (conjugate and non-conjugate):
A = 10*reshape(1:6, 2, 3) + im * reshape(1:6, 2, 3)
2×3 Matrix{Complex{Int64}}:
10+1im 30+3im 50+5im
20+2im 40+4im 60+6im
conjugate transpose, could also use adjoint(A)
:
A'
3×2 adjoint(::Matrix{Complex{Int64}}) with eltype Complex{Int64}:
10-1im 20-2im
30-3im 40-4im
50-5im 60-6im
For complex arrays, rarely do we need a non-conjugate transpose. Usually we need A'
instead. But if we do:
transpose(A) # essentially sets a flag about transpose without reordering data
3×2 transpose(::Matrix{Complex{Int64}}) with eltype Complex{Int64}:
10+1im 20+2im
30+3im 40+4im
50+5im 60+6im
Matrix determinant:
A = Diagonal(2:4)
det(A)
24
B = randn(3,3)
[det(A*B) det(A)*det(B)]
1×2 Matrix{Float64}:
6.62167 6.62167
Matrix trace:
A = ones(3,3)
tr(A) # in Matlab would be "trace(A)"
3.0
More info in Julia manual
Getting help
Julia analogue of Matlab's help
is ?
.
Type ?pwd
in the REPL to get help on the pwd
function.
It does not work in this online documentation so we use @doc
instead:
@doc pwd
pwd() -> String
Get the current working directory.
Examples
julia> pwd()
"/home/JuliaUser"
julia> cd("/home/JuliaUser/Projects/julia")
julia> pwd()
"/home/JuliaUser/Projects/julia"
- Full documentation
- Searching Julia's Github repo can sometimes also uncover similar issues.
- Lots of neat talks on their YouTube channel
- Here is an interesting one about vector transpose
Ranges
Ranges are different from (and much more efficient than) Matlab!
myrange = -2:3
-2:3
typeof(myrange)
UnitRange{Int64}
Not an Array! But it can be indexed:
myrange[1]
-2
Used often in for
loops:
for a in myrange
println(a)
end
-2
-1
0
1
2
3
Form an array by using collect
if needed (use rarely):
collect(myrange)
6-element Vector{Int64}:
-2
-1
0
1
2
3
Other ways to make ranges:
srange = 1:-1:-5
1:-1:-5
typeof(srange)
StepRange{Int64, Int64}
lrange = range(0, 2, 10)
0.0:0.2222222222222222:2.0
typeof(lrange)
StepRangeLen{Float64, Base.TwicePrecision{Float64}, Base.TwicePrecision{Float64}, Int64}
Yet another option that looks the most like linspace
:
LinRange(0,10,6)
6-element LinRange{Float64, Int64}:
0.0, 2.0, 4.0, 6.0, 8.0, 10.0
Comprehensions
A convenient way to create arrays!
comp = [i+0.1 for i in 1:5]
5-element Vector{Float64}:
1.1
2.1
3.1
4.1
5.1
comp = [10i + j for i in 1:5, j in 1:4]
5×4 Matrix{Int64}:
11 12 13 14
21 22 23 24
31 32 33 34
41 42 43 44
51 52 53 54
Defining functions
Way 1:
function f1(x,y)
z = x+y
return z
end
f1 (generic function with 1 method)
Way 2:
f2(x,y) = x+y
f2 (generic function with 1 method)
Way 3: Anonymous function:
f3 = (x,y) -> x+y
#7 (generic function with 1 method)
Functions can return multiple outputs:
function f_mult(x, y)
add = x + y
sub = x - y
return add, sub
end;
f_mult(2,3)
(5, -1)
The output is a Tuple
of the returned values:
out_tuple = f_mult(2,3)
(5, -1)
typeof(out_tuple)
Tuple{Int64, Int64}
Convenient way to split out the outputs:
out1, out2 = f_mult(2,3)
(5, -1)
out1
5
out2
-1
Broadcast
Any Julia function can be "vectorized" using "broadcast"
myquad = x -> (x+1)^2
#9 (generic function with 1 method)
myquad(1)
4
try
myquad([1,2,3]) # this does not work!
catch
"failed, as expected"
end
"failed, as expected"
This particular function was not designed to be applied to vector input arguments! But it can be used with vectors (or arrays) by adding a .
to tell Julia to apply it element-wise. This is called broadcasting.
myquad.([1,2,3])
3-element Vector{Int64}:
4
9
16
Conditionals
if
else
end
for
Generally similar to Matlab. Optional use of in
instead of =
in the for loop.
for j in 1:3
if j == 2
println("$j is a two! ^^")
else
println("$j is not a two. :(")
end
end
1 is not a two. :(
2 is a two! ^^
3 is not a two. :(
Julia has the convenient ternary operator:
mystring = 2 > 3 ? "2 is greater than 3" : "2 is not greater than 3"
"2 is not greater than 3"
Plotting
Suggested package: Plots.jl
with its default gr
backend.
Note: Usually slower the first time you plot due to precompiling. You must add
the "Plots" package first. In a regular Julia REPL you do this by using the ]
key to enter the package manager REPL, and then type add Plots
then wait.
In a Jupyter notebook, type using Pkg
then add Plots
and wait.
using Plots
backend()
Plots.GRBackend()
Plot values from a vector. (The labels are optional arguments.)
x = range(-5,5,101)
y = x.^2
plot(x, y, xlabel="x", ylabel="y", label="parabola")
heatmap
x = range(-2, 2, 101)
y = range(-1.1, 1.1, 103)
A = x.^2 .+ 30 * (y.^2)'
F = exp.(-A)
p1 = heatmap(x, y, F', # for F(x,y)
color=:grays, aspect_ratio=:equal, xlabel="x", ylabel="y", title="bump")
Using the jim
function the MIRTjim.jl
package simplifies the display of 2D images, among other features. See its examples.
Plotting functions
Plots.jl
allows you to pass in the domain and a function. It does the rest. :) This is one many examples of how Julia exploits "multiple dispatch."
plot(range(0,1,100), abs2, label="x^2")
heatmap(range(-2,2,102), range(-1.1,1.1,100),
(x,y) -> exp(-x^2-30*y^2), aspect_ratio=1)
More info about plotting at https://juliaplots.github.io
Caution: line breaks (newlines)
If you want an expression to span multiple lines, then be sure to enclose it in parentheses.
Compare the following 3 (actually 4) expressions:
x = 9
- 7
-7
y = 9 -
7
2
z = (9
- 7)
2
(x,y,z)
(9, 2, 2)
Submitting homework
This part is just for EECS 551 students at UM.
A quick example to try submitting problems.
Task: Implement a function that takes two inputs and outputs them in reverse order.
function template1(x, y)
return (y, x)
end;
template1(2, 3)
(3, 2)
Copy the above function code into a file named template1.jl
and email to eecs551@autograder.eecs.umich.edu
.
Make sure that:
- All reasonable input types can be handled. Internally trying to convert a
Float64
to anInt64
can produceInexactError
- File extension is
.jl
. Watch out for hidden extensions! - File has just the Julia function.
- (Your HW solutions can also contain
using
statements.)
An undocumented function is bad programming practice. Julia supports docstrings
for comments like this:
"""
template2(x,y)
This function reverses the order of the two input arguments.
"""
function template2(x,y)
return (y,x)
end
Main.var"Main".template2
You can see the docstring by using the ?
key or @doc
:
@doc template2
template2(x,y)
This function reverses the order of the two input arguments.
This page was generated using Literate.jl.