Skip to content
This repository was archived by the owner on Jul 25, 2023. It is now read-only.

Commit 68d985a

Browse files
committed
upload solutions to JuliaIntro
1 parent 13ea6a4 commit 68d985a

15 files changed

+808
-0
lines changed
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
using BenchmarkTools
2+
3+
function cos_approx(x, N)
4+
# approximation of cosine via power series expansion
5+
# inputs:
6+
# - x : argument of cosine
7+
# - N : truncation order of the power series approximation
8+
# outputs:
9+
# - cos_val : approximation of cos(x)
10+
cos_val = 0
11+
for n in 0:N
12+
cos_val += (-1)^n * x^(2n)/(2factorial(n))
13+
end
14+
return cos_val
15+
end
16+
17+
@btime cos_approx($/3),$(10))
18+
@btime cos($/3))
19+
@btime cos/3)
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
import timeit
2+
import numpy as np
3+
from math import factorial
4+
5+
6+
def cos_approx(x,order):
7+
return sum((-1)**n * x**(2*n)/(2*factorial(n)) for n in range(order+1))
8+
9+
a = 3.14/3
10+
order = 10
11+
def benchmark_cos_approx():
12+
cos_approx(a,order)
13+
pass
14+
15+
def benchmark_cos():
16+
np.cos(a)
17+
pass
18+
19+
execution_time = timeit.Timer(benchmark_cos_approx).timeit(number = 100000)/100000
20+
print("Execution of cos_approx took " + str(execution_time*1e9) + " ns")
21+
22+
execution_time = timeit.Timer(benchmark_cos).timeit(number = 100000)/100000
23+
print("Execution of np.cos took " + str(execution_time*1e9) + " ns")
24+
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
import Base.+
2+
3+
struct MyComplex
4+
Re
5+
Im
6+
end
7+
8+
function my_times(a::MyComplex, b::MyComplex)
9+
real_part = a.Re * b.Re - a.Im * b.Im
10+
imaginary_part = a.Re * b.Im + a.Im * b.Re
11+
return MyComplex(real_part, imaginary_part)
12+
end
13+
14+
# test-case
15+
x = MyComplex(1.0, 2.0)
16+
y = MyComplex(3.0, 4.0)
17+
z = my_times(x,y)
18+
19+
# compare to Julia's built in complex arithmetic!
20+
x_julia = 1.0 + 1.0*im # defines a complex number: 1 + 1i
21+
y_julia = 0.5 + 3.0*im # defines a complex number: 0.5 + 3i
22+
z_julia = x_julia * y_julia
23+
24+
# Implement function my_add that adds
25+
# two objects of type MyComplex and returns the result as such
26+
function my_add(a::MyComplex,b::MyComplex)
27+
return MyComplex(a.Re+b.Re, a.Im+b.Im)
28+
end
29+
30+
# overload Julia's + function
31+
function +(a::MyComplex, b::MyComplex)
32+
return my_add(a, b)
33+
end
34+
35+
z = x + y
36+
sum([x,y])
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
cd(@__DIR__)
2+
include("dual_number_arithmetic.jl")
3+
4+
# The way we set things up, we can also evaluate gradients of vector-valued functions!
5+
# As an example, we consider Motzkin's polynomial:
6+
Motzkin(x,y) = x^4*y^4 + x^2*y^4 - 3*x^2*y^2 + 1
7+
Motzkin_gradient(x,y) = [4*x^3*y^4 + 2*x*y^4 - 6*x*y^2;
8+
4*x^4*y^3 + 4*x^2*y^3 - 6*x^2*y]
9+
10+
x = DualNumber(rand(), [1.0, 0])
11+
y = DualNumber(rand(), [0, 1.0])
12+
13+
# check if the result is correct:
14+
@show Motzkin(x,y).val == Motzkin(x.val, y.val)
15+
@show Motzkin(x,y).grad == Motzkin_gradient(x.val,y.val)
Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
import Base: +, -, *, ^, /, sin, cos, show
2+
3+
#################################
4+
##### Defining our DualNumber type
5+
#################################
6+
struct DualNumber{Tval,Tgrad}
7+
val::Tval
8+
grad::Tgrad
9+
end
10+
11+
#################################
12+
##### product rule
13+
#################################
14+
function *(a::DualNumber, b::DualNumber)
15+
return DualNumber(a.val*b.val, b.val*a.grad + a.val*b.grad) # product rule
16+
end
17+
18+
function *(a::Number, b::DualNumber)
19+
return DualNumber(a*b.val, a*b.grad) # complete
20+
end
21+
22+
function *(a::DualNumber, b::Number)
23+
return b*a # complete
24+
end
25+
26+
#################################
27+
##### chain rule for powers
28+
#################################
29+
function ^(a::DualNumber, b::Number)
30+
return DualNumber(a.val^b, b*a.val^(b-1)*a.grad)
31+
end
32+
33+
#################################
34+
##### quotient rule
35+
#################################
36+
function /(a::DualNumber, b::DualNumber)
37+
return DualNumber(a.val/b.val, a.grad/b.val - a.val*b.grad/b.val^2 )
38+
end
39+
40+
function /(a::DualNumber, b::Number)
41+
return DualNumber(a.val/b, a.grad/b)
42+
end
43+
44+
#################################
45+
##### addition
46+
#################################
47+
function +(a::DualNumber, b::DualNumber)
48+
return DualNumber(a.val+b.val, a.grad + b.grad) # complete
49+
end
50+
51+
function +(a::DualNumber, b::Number)
52+
return DualNumber(a.val + b, a.grad) # complete
53+
end
54+
55+
function +(a::Number, b::DualNumber)
56+
return b+a # complete
57+
end
58+
59+
#################################
60+
##### Chain Rule for sin/cos
61+
#################################
62+
function sin(a::DualNumber)
63+
return DualNumber(sin(a.val), cos(a.val)*a.grad)
64+
end
65+
66+
function cos(a::DualNumber)
67+
return DualNumber(cos(a.val), -sin(a.val)*a.grad)
68+
end
69+
70+
#################################
71+
##### subtraction of dual numbers
72+
#################################
73+
function -(a::DualNumber)
74+
return DualNumber(-a.val, -a.grad)
75+
end
76+
77+
function -(a::DualNumber, b::T) where T <: Union{DualNumber, Number}
78+
return a + (-b)
79+
end
80+
81+
function -(a::Number, b::DualNumber)
82+
return a + (-b)
83+
end
84+
85+
#################################
86+
##### We are suckers for neat printouts in the REPL!
87+
#################################
88+
show(io::IO, a::DualNumber) = println(io, "$(a.val) + $(a.grad)ϵ")
Lines changed: 120 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,120 @@
1+
cd(@__DIR__)
2+
include("dual_number_arithmetic.jl")
3+
4+
using CairoMakie
5+
function lotka_volterra(x,α)
6+
# right-hand-side of lotka-volterra system
7+
# inputs:
8+
# - x : current state x[1] = population of prey,
9+
# x[2] = populaiton of predator
10+
# - α : hunting efficiency of predator species
11+
# returns:
12+
# - dx/dt : rate of change of population sizes
13+
14+
dx = [x[1] - x[1]*x[2];
15+
-2*x[2] + α*x[1]*x[2]]
16+
return dx
17+
end
18+
19+
# or when messing around with unicode symbols ....
20+
function lotka_volterra(x,α)
21+
# right-hand-side of lotka-volterra system
22+
# inputs:
23+
# - x : current state x[1] = population of prey,
24+
# x[2] = populaiton of predator
25+
# - α : hunting efficiency of predator species
26+
# returns:
27+
# - dx/dt : rate of change of population sizes
28+
29+
🐇, 🐺 = x
30+
d🐇 = 🐇 - α*🐇*🐺
31+
d🐺 = -🐺 + α*🐇*🐺
32+
return [d🐇, d🐺]
33+
end
34+
35+
function simulate_lotka_volterra(x0,α,h,N)
36+
# simulator for lotka volterra system (explicit Euler)
37+
# inputs:
38+
# - x0 : initial condition
39+
# - α : hunting efficiency of predator
40+
# - h : step size
41+
# - N : number of steps
42+
# outputs:
43+
# - trajectory : Vector of population sizes, entry for each time point visisted
44+
# during integration
45+
# - time_points: Vector of time points visisted during integration
46+
trajectory = [x0]
47+
time_points = range(0, N*h, length=N+1)
48+
for i in 1:N
49+
x_current = trajectory[end] # current state
50+
x_hat = x_current + h*lotka_volterra(x_current,α) # explicit euler prediction
51+
push!(trajectory, x_hat)
52+
end
53+
return trajectory, time_points
54+
end
55+
56+
# initial condition for the simulation
57+
x0 = [1.0, 0.25]
58+
# step size
59+
h = 0.01
60+
# time horizon (0.0, N*h)
61+
N = 1500
62+
# parameter range for the predator efficiency
63+
α_range = 1.2:0.01:2.8
64+
65+
# gradient prediction by automatic differentiation
66+
#
67+
# please complete the
68+
# to that end, recall that
69+
# x0_dual = [x0[1] + ∂x0[1]/∂α * ϵ, x0[2] + ∂x0[2]/∂α * ϵ]
70+
# α_dual = [α + ∂α/∂α * ϵ]
71+
# since we want to compute the sensitivity of the final state wrt. parameter α
72+
#α = DualNumber(1.5,?)
73+
#x0_dual = [DualNumber(?,?), DualNumber(?,?)]
74+
α = DualNumber(1.5, 1.0)
75+
x0_dual = [DualNumber(x0[1], 0.0), DualNumber(x0[2],0.0)]
76+
77+
78+
# propagate dual numbers through simulation:
79+
dual_trajectory, time_points = simulate_lotka_volterra(x0_dual, α, h, N)
80+
81+
##############
82+
### No more modifications needed beyond this point
83+
##############
84+
85+
# General Lotka-Volterra system oscilaltions
86+
trajectory, time_points = simulate_lotka_volterra(x0, 0.25, h, N)
87+
88+
fig = Figure(fontsize=18);
89+
ax_prey = Axis(fig[1,1], ylabel = "prey population", xlabel = "time")
90+
ax_predator = Axis(fig[2,1], ylabel = "predator population", xlabel = "time")
91+
lines!(ax_prey, time_points, [x[1].val for x in dual_trajectory])
92+
lines!(ax_predator, time_points, [x[2].val for x in dual_trajectory])
93+
display(fig)
94+
95+
# parameter dependence of the final state x(N*h) on α
96+
x_final = []
97+
for α in α_range
98+
trajectory, time_points = simulate_lotka_volterra(x0, α, h, N)
99+
push!(x_final, trajectory[end])
100+
end
101+
102+
fig = Figure(fontsize=18);
103+
ax_prey = Axis(fig[1,1], ylabel = "prey population at $(N*h) months", xlabel = "α")
104+
ax_predator = Axis(fig[2,1], ylabel = "predator population at $(N*h) months", xlabel = "α")
105+
lines!(ax_prey, α_range, [x[1] for x in x_final])
106+
lines!(ax_predator, α_range, [x[2] for x in x_final])
107+
scatter!(ax_prey, [α.val], [dual_trajectory[end][1].val], color = :red)
108+
lines!(ax_prey, [α.val-0.2, α.val+0.2],
109+
[dual_trajectory[end][1].val - 0.2*dual_trajectory[end][1].grad,
110+
dual_trajectory[end][1].val + 0.2*dual_trajectory[end][1].grad],
111+
color = :red)
112+
113+
scatter!(ax_predator, [α.val], [dual_trajectory[end][2].val], color = :red)
114+
lines!(ax_predator, [α.val-0.2, α.val+0.2],
115+
[dual_trajectory[end][2].val - 0.2*dual_trajectory[end][2].grad,
116+
dual_trajectory[end][2].val + 0.2*dual_trajectory[end][2].grad],
117+
color = :red)
118+
display(fig)
119+
120+
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
cd(@__DIR__)
2+
include("dual_number_arithmetic.jl")
3+
using CairoMakie
4+
5+
# Simone's test function
6+
func(x) = cos(x)+x*sin(x)
7+
func_dot(x) = x*cos(x)
8+
9+
# points at which we want to evaluate Simone's test function and its derivative
10+
x_range = range(-6, 6, length = 100)
11+
12+
# evaluate Simone's test function for each x in x_range
13+
vals = func.(x_range)
14+
15+
# evaluate Simeone's test function derivative for each x in x_range
16+
grads = func_dot.(x_range)
17+
18+
# Now we initialize the dual numbers for the evaluation of the derivative
19+
# of Simone's test function as
20+
x_dual = [DualNumber(x, 1.0) for x in x_range[1:10:end]]
21+
22+
# evaluate Simone's test function for each x in x_dual and save the result in dual_result
23+
dual_result = func.(x_dual)
24+
25+
#############################################################
26+
# plotting
27+
#############################################################
28+
fig = Figure(fontsize=18);
29+
axs = [Axis(fig[1,1], xlabel = L"x", ylabel = L"f(x)"),
30+
Axis(fig[1,2], xlabel = L"x", ylabel = L"\frac{df}{dx}(x)")]
31+
32+
lines!(axs[1], x_range, func.(x_range), color = :black, linestyle=:dash, linewidth=4,label="Analytical")
33+
lines!(axs[2], x_range, func_dot.(x_range), color = :black, linestyle=:dash, linewidth=4)
34+
scatter!(axs[1], [x.val for x in x_dual], [x.val for x in dual_result], color = :red, linewidth=2, label = "AD")
35+
scatter!(axs[2], [x.val for x in x_dual], [x.grad for x in dual_result], color = :red, linewidth=2)
36+
axislegend(axs[1])
37+
display(fig)

day_9/JuliaIntro/solutions/Ex4/__init__.py

Whitespace-only changes.
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
a = [[1/5],
2+
[3/40, 9/40],
3+
[44/45, -56/15, 32/9],
4+
[19372/6561, -25360/2187, 64448/6561, -212/729],
5+
[9017/3168, -355/33, 46732/5247, 49/176, -5103/18656],
6+
[35/384, 0, 500/1113, 125/192, -2187/6784, 11/84]]
7+
b = [35/384, 0, 500/1113, 125/192, -2187/6784, 11/84, 0]
8+
c = [0, 1/5, 3/10, 4/5, 8/9, 1, 1]
9+
b_control = [5179/57600, 0, 7571/16695, 393/640, -92097/339200, 187/2100, 1/40]

0 commit comments

Comments
 (0)