"""
Multplies I by J by K
"""
function stupid_loop(I,J,K)
t = 0.0
for i=1:I
for j=1:J
for k = 1:K
t += 1.0
end
end
end
return t
endstupid_loop
Some useful links from QuantEcon:
Excellent resources at: julialang - checkout JuliaAcademy, it’s free
How I learnt: interpreted code is slow, so vectorize your coe.
stupid_loop
Code is translated to LLVM code then to instructions for the processor. Note that processor instructions are shorter than LLVM code.
; Function Signature: stupid_loop(Int64, Int64, Int64) ; @ /home/pablo/Teaching/polytechnique/eco309/tutorials/jl_notebook_cell_df34fa98e69747e1a8f8a730347b8e2f_W4sZmlsZQ==.jl:4 within `stupid_loop` define double @julia_stupid_loop_6611(i64 signext %"I::Int64", i64 signext %"J::Int64", i64 signext %"K::Int64") #0 { top: ; @ /home/pablo/Teaching/polytechnique/eco309/tutorials/jl_notebook_cell_df34fa98e69747e1a8f8a730347b8e2f_W4sZmlsZQ==.jl within `stupid_loop` %".I::Int64" = call i64 @llvm.smax.i64(i64 %"I::Int64", i64 0) ; @ /home/pablo/Teaching/polytechnique/eco309/tutorials/jl_notebook_cell_df34fa98e69747e1a8f8a730347b8e2f_W4sZmlsZQ==.jl:6 within `stupid_loop` ; ┌ @ range.jl:904 within `iterate` ; │┌ @ range.jl:681 within `isempty` ; ││┌ @ operators.jl:379 within `>` ; │││┌ @ int.jl:83 within `<` %0 = icmp slt i64 %"I::Int64", 1 ; └└└└ br i1 %0, label %L85, label %L16.preheader L16.preheader: ; preds = %top %".J::Int64" = call i64 @llvm.smax.i64(i64 %"J::Int64", i64 0) %1 = icmp slt i64 %"J::Int64", 1 %".K::Int64" = call i64 @llvm.smax.i64(i64 %"K::Int64", i64 0) %2 = icmp slt i64 %"K::Int64", 1 ; @ /home/pablo/Teaching/polytechnique/eco309/tutorials/jl_notebook_cell_df34fa98e69747e1a8f8a730347b8e2f_W4sZmlsZQ==.jl:7 within `stupid_loop` %or.cond = select i1 %1, i1 true, i1 %2 br i1 %or.cond, label %L85, label %L16.preheader49 L16.preheader49: ; preds = %L16.preheader ; @ /home/pablo/Teaching/polytechnique/eco309/tutorials/jl_notebook_cell_df34fa98e69747e1a8f8a730347b8e2f_W4sZmlsZQ==.jl:8 within `stupid_loop` %3 = add nsw i64 %".K::Int64", -1 br label %L16 L16: ; preds = %L74.loopexit.split, %L16.preheader49 %value_phi3 = phi i64 [ %11, %L74.loopexit.split ], [ 1, %L16.preheader49 ] %value_phi4 = phi double [ %.lcssa, %L74.loopexit.split ], [ 0.000000e+00, %L16.preheader49 ] br label %L33 L33: ; preds = %L63.loopexit, %L16 %value_phi11 = phi double [ %.lcssa, %L63.loopexit ], [ %value_phi4, %L16 ] %value_phi12 = phi i64 [ %10, %L63.loopexit ], [ 1, %L16 ] ; @ /home/pablo/Teaching/polytechnique/eco309/tutorials/jl_notebook_cell_df34fa98e69747e1a8f8a730347b8e2f_W4sZmlsZQ==.jl:10 within `stupid_loop` %xtraiter = and i64 %".K::Int64", 3 %4 = icmp ult i64 %3, 3 br i1 %4, label %L63.loopexit.unr-lcssa, label %L33.new L33.new: ; preds = %L33 %unroll_iter = and i64 %".K::Int64", 9223372036854775804 br label %L50 L50: ; preds = %L50, %L33.new %value_phi19 = phi double [ %value_phi11, %L33.new ], [ %8, %L50 ] %niter = phi i64 [ 0, %L33.new ], [ %niter.next.3, %L50 ] ; @ /home/pablo/Teaching/polytechnique/eco309/tutorials/jl_notebook_cell_df34fa98e69747e1a8f8a730347b8e2f_W4sZmlsZQ==.jl:9 within `stupid_loop` ; ┌ @ float.jl:491 within `+` %5 = fadd double %value_phi19, 1.000000e+00 %6 = fadd double %5, 1.000000e+00 %7 = fadd double %6, 1.000000e+00 %8 = fadd double %7, 1.000000e+00 ; └ ; @ /home/pablo/Teaching/polytechnique/eco309/tutorials/jl_notebook_cell_df34fa98e69747e1a8f8a730347b8e2f_W4sZmlsZQ==.jl:10 within `stupid_loop` %niter.next.3 = add i64 %niter, 4 %niter.ncmp.3 = icmp eq i64 %niter.next.3, %unroll_iter br i1 %niter.ncmp.3, label %L63.loopexit.unr-lcssa, label %L50 L63.loopexit.unr-lcssa: ; preds = %L50, %L33 %.lcssa.ph = phi double [ undef, %L33 ], [ %8, %L50 ] %value_phi19.unr = phi double [ %value_phi11, %L33 ], [ %8, %L50 ] %lcmp.mod.not = icmp eq i64 %xtraiter, 0 br i1 %lcmp.mod.not, label %L63.loopexit, label %L50.epil L50.epil: ; preds = %L50.epil, %L63.loopexit.unr-lcssa %value_phi19.epil = phi double [ %9, %L50.epil ], [ %value_phi19.unr, %L63.loopexit.unr-lcssa ] %epil.iter = phi i64 [ %epil.iter.next, %L50.epil ], [ 0, %L63.loopexit.unr-lcssa ] ; @ /home/pablo/Teaching/polytechnique/eco309/tutorials/jl_notebook_cell_df34fa98e69747e1a8f8a730347b8e2f_W4sZmlsZQ==.jl:9 within `stupid_loop` ; ┌ @ float.jl:491 within `+` %9 = fadd double %value_phi19.epil, 1.000000e+00 ; └ ; @ /home/pablo/Teaching/polytechnique/eco309/tutorials/jl_notebook_cell_df34fa98e69747e1a8f8a730347b8e2f_W4sZmlsZQ==.jl:10 within `stupid_loop` %epil.iter.next = add i64 %epil.iter, 1 %epil.iter.cmp.not = icmp eq i64 %epil.iter.next, %xtraiter br i1 %epil.iter.cmp.not, label %L63.loopexit, label %L50.epil L63.loopexit: ; preds = %L50.epil, %L63.loopexit.unr-lcssa ; @ /home/pablo/Teaching/polytechnique/eco309/tutorials/jl_notebook_cell_df34fa98e69747e1a8f8a730347b8e2f_W4sZmlsZQ==.jl:9 within `stupid_loop` ; ┌ @ float.jl:491 within `+` %.lcssa = phi double [ %.lcssa.ph, %L63.loopexit.unr-lcssa ], [ %9, %L50.epil ] ; └ ; @ /home/pablo/Teaching/polytechnique/eco309/tutorials/jl_notebook_cell_df34fa98e69747e1a8f8a730347b8e2f_W4sZmlsZQ==.jl:11 within `stupid_loop` ; ┌ @ range.jl:908 within `iterate` ; │┌ @ promotion.jl:639 within `==` %.not.not37 = icmp eq i64 %value_phi12, %".J::Int64" ; │└ %10 = add nuw i64 %value_phi12, 1 ; └ br i1 %.not.not37, label %L74.loopexit.split, label %L33 L74.loopexit.split: ; preds = %L63.loopexit ; @ /home/pablo/Teaching/polytechnique/eco309/tutorials/jl_notebook_cell_df34fa98e69747e1a8f8a730347b8e2f_W4sZmlsZQ==.jl:12 within `stupid_loop` ; ┌ @ range.jl:908 within `iterate` ; │┌ @ promotion.jl:639 within `==` %.not.not38 = icmp eq i64 %value_phi3, %".I::Int64" ; │└ %11 = add nuw i64 %value_phi3, 1 ; └ br i1 %.not.not38, label %L85, label %L16 L85: ; preds = %L74.loopexit.split, %L16.preheader, %top %value_phi34 = phi double [ 0.000000e+00, %top ], [ 0.000000e+00, %L16.preheader ], [ %.lcssa, %L74.loopexit.split ] ; @ /home/pablo/Teaching/polytechnique/eco309/tutorials/jl_notebook_cell_df34fa98e69747e1a8f8a730347b8e2f_W4sZmlsZQ==.jl:13 within `stupid_loop` ret double %value_phi34 }
.text
.file "stupid_loop"
.section .rodata.cst8,"aM",@progbits,8
.p2align 3, 0x0 # -- Begin function julia_stupid_loop_7565
.LCPI0_0:
.quad 0x3ff0000000000000 # double 1
.section .ltext,"axl",@progbits
.globl julia_stupid_loop_7565
.p2align 4, 0x90
.type julia_stupid_loop_7565,@function
julia_stupid_loop_7565: # @julia_stupid_loop_7565
; Function Signature: stupid_loop(Int64, Int64, Int64)
; ┌ @ /home/pablo/Teaching/econobits/jl_notebook_cell_df34fa98e69747e1a8f8a730347b8e2f_W4sZmlsZQ==.jl:4 within `stupid_loop`
# %bb.0: # %top
#DEBUG_VALUE: stupid_loop:I <- $rdi
#DEBUG_VALUE: stupid_loop:J <- $rsi
#DEBUG_VALUE: stupid_loop:K <- $rdx
xor eax, eax
vxorpd xmm0, xmm0, xmm0
; │ @ /home/pablo/Teaching/econobits/jl_notebook_cell_df34fa98e69747e1a8f8a730347b8e2f_W4sZmlsZQ==.jl within `stupid_loop`
test rdi, rdi
cmovg rax, rdi
; │ @ /home/pablo/Teaching/econobits/jl_notebook_cell_df34fa98e69747e1a8f8a730347b8e2f_W4sZmlsZQ==.jl:6 within `stupid_loop`
jle .LBB0_28
# %bb.1: # %L16.preheader
xor ecx, ecx
test rsi, rsi
cmovg rcx, rsi
; │ @ /home/pablo/Teaching/econobits/jl_notebook_cell_df34fa98e69747e1a8f8a730347b8e2f_W4sZmlsZQ==.jl:7 within `stupid_loop`
jle .LBB0_28
# %bb.2: # %L16.preheader
test rdx, rdx
jle .LBB0_28
# %bb.3: # %L16.preheader36
push rbp
mov rbp, rsp
push r14
push rbx
mov rdi, rdx
sar rdi, 63
andn rdx, rdi, rdx
; │ @ /home/pablo/Teaching/econobits/jl_notebook_cell_df34fa98e69747e1a8f8a730347b8e2f_W4sZmlsZQ==.jl:8 within `stupid_loop`
lea rdi, [rdx - 1]
mov r8, rdx
and r8, -8
mov r9d, edx
and r9d, 7
vxorpd xmm0, xmm0, xmm0
mov r10d, 1
movabs r11, offset .LCPI0_0
vmovsd xmm1, qword ptr [r11] # xmm1 = mem[0],zero
movabs r11, 9223372036854775806
and r11, rcx
jmp .LBB0_4
.p2align 4, 0x90
.LBB0_26: # %L74.loopexit.split
# in Loop: Header=BB0_4 Depth=1
; │ @ /home/pablo/Teaching/econobits/jl_notebook_cell_df34fa98e69747e1a8f8a730347b8e2f_W4sZmlsZQ==.jl:12 within `stupid_loop`
; │┌ @ range.jl:921 within `iterate`
lea rbx, [r10 + 1]
; ││┌ @ promotion.jl:637 within `==`
cmp r10, rax
mov r10, rbx
; │└└
je .LBB0_27
.LBB0_4: # %L16
# =>This Loop Header: Depth=1
# Child Loop BB0_6 Depth 2
# Child Loop BB0_8 Depth 3
# Child Loop BB0_11 Depth 3
# Child Loop BB0_14 Depth 3
# Child Loop BB0_17 Depth 3
# Child Loop BB0_22 Depth 2
# Child Loop BB0_25 Depth 2
; │ @ /home/pablo/Teaching/econobits/jl_notebook_cell_df34fa98e69747e1a8f8a730347b8e2f_W4sZmlsZQ==.jl:8 within `stupid_loop`
cmp rsi, 1
jne .LBB0_5
.LBB0_19: # %L74.loopexit.split.unr-lcssa
# in Loop: Header=BB0_4 Depth=1
; │ @ /home/pablo/Teaching/econobits/jl_notebook_cell_df34fa98e69747e1a8f8a730347b8e2f_W4sZmlsZQ==.jl:10 within `stupid_loop`
test cl, 1
je .LBB0_26
# %bb.20: # %L33.epil.preheader
# in Loop: Header=BB0_4 Depth=1
cmp rdi, 7
jb .LBB0_23
# %bb.21: # %L33.new.epil
# in Loop: Header=BB0_4 Depth=1
mov rbx, r8
.p2align 4, 0x90
.LBB0_22: # %L50.epil39
# Parent Loop BB0_4 Depth=1
# => This Inner Loop Header: Depth=2
; │ @ /home/pablo/Teaching/econobits/jl_notebook_cell_df34fa98e69747e1a8f8a730347b8e2f_W4sZmlsZQ==.jl:9 within `stupid_loop`
; │┌ @ float.jl:495 within `+`
vaddsd xmm0, xmm0, xmm1
vaddsd xmm0, xmm0, xmm1
vaddsd xmm0, xmm0, xmm1
vaddsd xmm0, xmm0, xmm1
vaddsd xmm0, xmm0, xmm1
vaddsd xmm0, xmm0, xmm1
vaddsd xmm0, xmm0, xmm1
vaddsd xmm0, xmm0, xmm1
; │└
; │ @ /home/pablo/Teaching/econobits/jl_notebook_cell_df34fa98e69747e1a8f8a730347b8e2f_W4sZmlsZQ==.jl:10 within `stupid_loop`
add rbx, -8
jne .LBB0_22
.LBB0_23: # %L63.loopexit.unr-lcssa.epil
# in Loop: Header=BB0_4 Depth=1
test dl, 7
je .LBB0_26
# %bb.24: # %L50.epil.epil.preheader
# in Loop: Header=BB0_4 Depth=1
mov rbx, r9
.p2align 4, 0x90
.LBB0_25: # %L50.epil.epil
# Parent Loop BB0_4 Depth=1
# => This Inner Loop Header: Depth=2
; │ @ /home/pablo/Teaching/econobits/jl_notebook_cell_df34fa98e69747e1a8f8a730347b8e2f_W4sZmlsZQ==.jl:9 within `stupid_loop`
; │┌ @ float.jl:495 within `+`
vaddsd xmm0, xmm0, xmm1
; │└
; │ @ /home/pablo/Teaching/econobits/jl_notebook_cell_df34fa98e69747e1a8f8a730347b8e2f_W4sZmlsZQ==.jl:10 within `stupid_loop`
dec rbx
jne .LBB0_25
jmp .LBB0_26
.p2align 4, 0x90
.LBB0_5: # %L16.new
# in Loop: Header=BB0_4 Depth=1
xor ebx, ebx
jmp .LBB0_6
.p2align 4, 0x90
.LBB0_18: # %L63.loopexit.1
# in Loop: Header=BB0_6 Depth=2
; │ @ /home/pablo/Teaching/econobits/jl_notebook_cell_df34fa98e69747e1a8f8a730347b8e2f_W4sZmlsZQ==.jl:11 within `stupid_loop`
add rbx, 2
cmp rbx, r11
je .LBB0_19
.LBB0_6: # %L33
# Parent Loop BB0_4 Depth=1
# => This Loop Header: Depth=2
# Child Loop BB0_8 Depth 3
# Child Loop BB0_11 Depth 3
# Child Loop BB0_14 Depth 3
# Child Loop BB0_17 Depth 3
; │ @ /home/pablo/Teaching/econobits/jl_notebook_cell_df34fa98e69747e1a8f8a730347b8e2f_W4sZmlsZQ==.jl:10 within `stupid_loop`
cmp rdi, 7
jb .LBB0_9
# %bb.7: # %L33.new
# in Loop: Header=BB0_6 Depth=2
mov r14, r8
.p2align 4, 0x90
.LBB0_8: # %L50
# Parent Loop BB0_4 Depth=1
# Parent Loop BB0_6 Depth=2
# => This Inner Loop Header: Depth=3
; │ @ /home/pablo/Teaching/econobits/jl_notebook_cell_df34fa98e69747e1a8f8a730347b8e2f_W4sZmlsZQ==.jl:9 within `stupid_loop`
; │┌ @ float.jl:495 within `+`
vaddsd xmm0, xmm0, xmm1
vaddsd xmm0, xmm0, xmm1
vaddsd xmm0, xmm0, xmm1
vaddsd xmm0, xmm0, xmm1
vaddsd xmm0, xmm0, xmm1
vaddsd xmm0, xmm0, xmm1
vaddsd xmm0, xmm0, xmm1
vaddsd xmm0, xmm0, xmm1
; │└
; │ @ /home/pablo/Teaching/econobits/jl_notebook_cell_df34fa98e69747e1a8f8a730347b8e2f_W4sZmlsZQ==.jl:10 within `stupid_loop`
add r14, -8
jne .LBB0_8
.LBB0_9: # %L63.loopexit.unr-lcssa
# in Loop: Header=BB0_6 Depth=2
test dl, 7
je .LBB0_12
# %bb.10: # %L50.epil.preheader
# in Loop: Header=BB0_6 Depth=2
mov r14, r9
.p2align 4, 0x90
.LBB0_11: # %L50.epil
# Parent Loop BB0_4 Depth=1
# Parent Loop BB0_6 Depth=2
# => This Inner Loop Header: Depth=3
; │ @ /home/pablo/Teaching/econobits/jl_notebook_cell_df34fa98e69747e1a8f8a730347b8e2f_W4sZmlsZQ==.jl:9 within `stupid_loop`
; │┌ @ float.jl:495 within `+`
vaddsd xmm0, xmm0, xmm1
; │└
; │ @ /home/pablo/Teaching/econobits/jl_notebook_cell_df34fa98e69747e1a8f8a730347b8e2f_W4sZmlsZQ==.jl:10 within `stupid_loop`
dec r14
jne .LBB0_11
.LBB0_12: # %L63.loopexit
# in Loop: Header=BB0_6 Depth=2
cmp rdi, 7
jb .LBB0_15
# %bb.13: # %L33.new.1
# in Loop: Header=BB0_6 Depth=2
mov r14, r8
.p2align 4, 0x90
.LBB0_14: # %L50.1
# Parent Loop BB0_4 Depth=1
# Parent Loop BB0_6 Depth=2
# => This Inner Loop Header: Depth=3
; │ @ /home/pablo/Teaching/econobits/jl_notebook_cell_df34fa98e69747e1a8f8a730347b8e2f_W4sZmlsZQ==.jl:9 within `stupid_loop`
; │┌ @ float.jl:495 within `+`
vaddsd xmm0, xmm0, xmm1
vaddsd xmm0, xmm0, xmm1
vaddsd xmm0, xmm0, xmm1
vaddsd xmm0, xmm0, xmm1
vaddsd xmm0, xmm0, xmm1
vaddsd xmm0, xmm0, xmm1
vaddsd xmm0, xmm0, xmm1
vaddsd xmm0, xmm0, xmm1
; │└
; │ @ /home/pablo/Teaching/econobits/jl_notebook_cell_df34fa98e69747e1a8f8a730347b8e2f_W4sZmlsZQ==.jl:10 within `stupid_loop`
add r14, -8
jne .LBB0_14
.LBB0_15: # %L63.loopexit.unr-lcssa.1
# in Loop: Header=BB0_6 Depth=2
test dl, 7
je .LBB0_18
# %bb.16: # %L50.epil.1.preheader
# in Loop: Header=BB0_6 Depth=2
mov r14, r9
.p2align 4, 0x90
.LBB0_17: # %L50.epil.1
# Parent Loop BB0_4 Depth=1
# Parent Loop BB0_6 Depth=2
# => This Inner Loop Header: Depth=3
; │ @ /home/pablo/Teaching/econobits/jl_notebook_cell_df34fa98e69747e1a8f8a730347b8e2f_W4sZmlsZQ==.jl:9 within `stupid_loop`
; │┌ @ float.jl:495 within `+`
vaddsd xmm0, xmm0, xmm1
; │└
; │ @ /home/pablo/Teaching/econobits/jl_notebook_cell_df34fa98e69747e1a8f8a730347b8e2f_W4sZmlsZQ==.jl:10 within `stupid_loop`
dec r14
jne .LBB0_17
jmp .LBB0_18
.LBB0_27:
pop rbx
pop r14
pop rbp
.LBB0_28: # %L85
; │ @ /home/pablo/Teaching/econobits/jl_notebook_cell_df34fa98e69747e1a8f8a730347b8e2f_W4sZmlsZQ==.jl:13 within `stupid_loop`
ret
.Lfunc_end0:
.size julia_stupid_loop_7565, .Lfunc_end0-julia_stupid_loop_7565
; └
# -- End function
.type ".L+Core.Float64#7567",@object # @"+Core.Float64#7567"
.section .lrodata,"al",@progbits
.p2align 3, 0x0
".L+Core.Float64#7567":
.quad ".L+Core.Float64#7567.jit"
.size ".L+Core.Float64#7567", 8
.set ".L+Core.Float64#7567.jit", 123748261767136
.size ".L+Core.Float64#7567.jit", 8
.section ".note.GNU-stack","",@progbits
Assignement operator is = (equality is ==, identity is ===)
1.23
Default semantic is pass-by-reference:
To work on a copy: copy or deepcopy
Base.Meta.ParseError: ParseError:
# Error @ /home/pablo/Teaching/econobits/jl_notebook_cell_df34fa98e69747e1a8f8a730347b8e2f_X31sZmlsZQ==.jl:1:1
?typeof
╙ ── not a unary operator
ParseError:
# Error @ /home/pablo/Teaching/econobits/jl_notebook_cell_df34fa98e69747e1a8f8a730347b8e2f_X31sZmlsZQ==.jl:1:1
?typeof
╙ ── not a unary operator
Stacktrace:
[1] top-level scope
@ ~/Teaching/econobits/jl_notebook_cell_df34fa98e69747e1a8f8a730347b8e2f_X31sZmlsZQ==.jl:1
Equality
Identity
Boolean operator
"2 + 2 = 4"
MethodError: MethodError: no method matching +(::String, ::String)
The function `+` exists, but no method is defined for this combination of argument types.
String concatenation is performed with * (See also: https://docs.julialang.org/en/v1/manual/strings/#man-concatenation).
Closest candidates are:
+(::Any, ::Any, !Matched::Any, !Matched::Any...)
@ Base operators.jl:642
+(!Matched::BitMatrix, !Matched::LinearAlgebra.UniformScaling)
@ LinearAlgebra ~/.julia/juliaup/julia-1.12.4+0.x64.linux.gnu/share/julia/stdlib/v1.12/LinearAlgebra/src/uniformscaling.jl:154
+(!Matched::Bool, !Matched::Complex{Bool})
@ Base complex.jl:308
...
MethodError: no method matching +(::String, ::String)
The function `+` exists, but no method is defined for this combination of argument types.
String concatenation is performed with * (See also: https://docs.julialang.org/en/v1/manual/strings/#man-concatenation).
Closest candidates are:
+(::Any, ::Any, !Matched::Any, !Matched::Any...)
@ Base operators.jl:642
+(!Matched::BitMatrix, !Matched::LinearAlgebra.UniformScaling)
@ LinearAlgebra ~/.julia/juliaup/julia-1.12.4+0.x64.linux.gnu/share/julia/stdlib/v1.12/LinearAlgebra/src/uniformscaling.jl:154
+(!Matched::Bool, !Matched::Complex{Bool})
@ Base complex.jl:308
...
Stacktrace:
[1] top-level scope
@ ~/Teaching/econobits/jl_notebook_cell_df34fa98e69747e1a8f8a730347b8e2f_Y103sZmlsZQ==.jl:2
Julia has one-dimensional arrays. They are also called Vector.
2d arrays are also called matrices… and can be used for matrix multiplications.
Vectorized operations take a ., even comparisons:
Elements are always accessed with square brackets:
Conditions
x is negative
While
For loops
Boston
New York
Philadelphia
("Boston", "MA")
("New York", "NY")
("Philadelphia", "PA")
Boston, MA
New York, NY
Philadelphia, PA
Basic functions
Optional arguments
Keyword arguments
g (generic function with 1 method)
One liners:
Anonymous function:
A composite type is a collection of named fields that can be treated as a single value. They bear a passing resemblance to MATLAB structs.
All fields must be declared ahead of time. The double colon, ::, constrains a field to contain values of a certain type. This is optional for any field.
When a type with \(n\) fields is defined, a constructor (function that creates an instance of that type) that takes \(n\) ordered arguments is automatically created. Additional constructors can be defined for convenience.
Parameter(0.9, identity, "\beta", "Discount rate")
Parameter
Parameter
MethodError: Cannot `convert` an object of type String to an object of type Function Closest candidates are: convert(::Type{T}, ::T) where T at essentials.jl:205 Stacktrace: [1] Parameter(value::Float64, transformation::String, tex_label::String, description::String) @ Main ./In[35]:3 [2] Parameter(value::Float64, transformation::String) @ Main ./In[50]:2 [3] top-level scope @ In[51]:1 [4] eval @ ./boot.jl:360 [inlined] [5] include_string(mapexpr::typeof(REPL.softscope), mod::Module, code::String, filename::String) @ Base ./loading.jl:1094
Parameter
Parameter(0.5, identity, "\alpha", "No description available")
Now the function Parameter has two different methods with different signatures:
by default structures in Julia are non-mutable
LoadError: setfield! immutable struct of type Parameter cannot be changed
setfield! immutable struct of type Parameter cannot be changed
Stacktrace:
[1] setproperty!(x::Parameter, f::Symbol, v::Float64)
@ Base ./Base.jl:34
[2] top-level scope
@ In[77]:1
[3] eval
@ ./boot.jl:360 [inlined]
[4] include_string(mapexpr::typeof(REPL.softscope), mod::Module, code::String, filename::String)
@ Base ./loading.jl:1094
Parameterized types are data types that are defined to handle values identically regardless of the type of those values.
Arrays are a familiar example. An Array{T,1} is a one-dimensional array filled with objects of any type T (e.g. Float64, String).
MethodError: no method matching Duple(::Int64, ::Float64)
Closest candidates are:
Duple(::T, ::T) where T at In[29]:3
Stacktrace:
[1] top-level scope
@ In[30]:1
[2] eval
@ ./boot.jl:373 [inlined]
[3] include_string(mapexpr::typeof(REPL.softscope), mod::Module, code::String, filename::String)
@ Base ./loading.jl:1196
This single declaration defines an unlimited number of new types: Duple{String}, Duple{Float64}, etc. are all immediately usable.
We can also restrict the type parameter T using the type hierarchy.
LoadError: MethodError: no method matching PlanarCoordinate(::String, ::String)
MethodError: no method matching PlanarCoordinate(::String, ::String)
Stacktrace:
[1] top-level scope
@ In[49]:1
[2] eval
@ ./boot.jl:373 [inlined]
[3] include_string(mapexpr::typeof(REPL.softscope), mod::Module, code::String, filename::String)
@ Base ./loading.jl:1196
You can write all your code without thinking about types at all. If you do this, however, you’ll be missing out on some of the biggest benefits of using Julia.
If you understand types, you can:
Even if you only use built-in functions and types, your code still takes advantage of Julia’s type system. That’s why it’s important to understand what types are and how to use them.
# Example: writing type-stable functions
function sumofsins_unstable(n::Integer)
sum = 0:: Integer
for i in 1:n
sum += sin(3.4)
end
return sum
end
function sumofsins_stable(n::Integer)
sum = 0.0 :: Float64
for i in 1:n
sum += sin(3.4)
end
return sum
end
# Compile and run
sumofsins_unstable(Int(1e5))
sumofsins_stable(Int(1e5))-25554.110202663698
In sumofsins_stable, the compiler is guaranteed that sum is of type Float64 throughout; therefore, it saves time and memory. On the other hand, in sumofsins_unstable, the compiler must check the type of sum at each iteration of the loop. Let’s look at the LLVM intermediate representation.
So far we have defined functions over argument lists of any type. Methods allow us to define functions “piecewise”. For any set of input arguments, we can define a method, a definition of one possible behavior for a function.
print_type (generic function with 1 method)
print_type (generic function with 2 methods)
print_type (generic function with 3 methods)
Julia uses multiple dispatch to decide which method of a function to execute when a function is applied. In particular, Julia compares the types of all arguments to the signatures of the function’s methods in order to choose the applicable one, not just the first (hence “multiple”).
MethodError: MethodError: no method matching print_type(::Array{Int64,1})
Closest candidates are:
print_type(!Matched::String) at In[53]:3
print_type(!Matched::Number) at In[51]:3
print_type(!Matched::Number, !Matched::Number) at In[54]:3
MethodError: no method matching print_type(::Array{Int64,1})
Closest candidates are:
print_type(!Matched::String) at In[53]:3
print_type(!Matched::Number) at In[51]:3
print_type(!Matched::Number, !Matched::Number) at In[54]:3
Stacktrace:
[1] top-level scope at In[58]:1
Julia supports a short function definition for one-liners
As well as a special syntax for anonymous functions
As we’ve seen, you can use Julia just like you use MATLAB and get faster code. However, to write faster and better code, attempt to write in a “Julian” manner:
How is Julia so fast? Julia is just-in-time (JIT) compiled, which means (according to this StackExchange answer):
A JIT compiler runs after the program has started and compiles the code (usually bytecode or some kind of VM instructions) on the fly (or just-in-time, as it’s called) into a form that’s usually faster, typically the host CPU’s native instruction set. A JIT has access to dynamic runtime information whereas a standard compiler doesn’t and can make better optimizations like inlining functions that are used frequently.
This is in contrast to a traditional compiler that compiles all the code to machine language before the program is first run.
In particular, Julia uses type information at runtime to optimize how your code is compiled. This is why writing type-stable code makes such a difference in speed!
Taken from QuantEcon’s Julia Essentials and Vectors, Arrays, and Matrices lectures.
Consider the polynomial \[p(x) = \sum_{i=0}^n a_0 x^0\] Using enumerate, write a function p such that p(x, coeff) computes the value of the polynomial with coefficients coeff evaluated at x.
Write a function solve_discrete_lyapunov that solves the discrete Lyapunov equation \[S = ASA' + \Sigma \Sigma'\] using the iterative procedure \[S_0 = \Sigma \Sigma'\] \[S_{t+1} = A S_t A' + \Sigma \Sigma'\] taking in as arguments the \(n \times n\) matrix \(A\), the \(n \times k\) matrix \(\Sigma\), and a number of iterations.