f(z) = let
= z
x,y ^3 + y, x-y]
[xend
= [0.1, 0.3] x0
2-element Vector{Float64}:
0.1
0.3
The spirit of this simple tutorial consists in learning how to write simple solution algorithms. For each algorithm, test that it works, using simple test functions whose solution is known.
Write a function fixed_point(f::Function, x0::Float64)
which computes the fixed point of f
starting from initial point x0
.
Write a function bisection(f::Function, a::Float64, b::Float64)
which computes a zero of function f
within (a,b)
using a bisection method.
Write a function golden(f::Function, a::Float64, b::Float64)
which computes a zero of function f
within (a,b)
using a golden ratio method.
Write a function zero_newton(f::Function, x0::Float64)
which computes the zero of function f
starting from initial point x0
.
Add an option zero_newton(f::Function, x0::Float64, backtracking=true)
which computes the zero of function f
starting from initial point x0
using backtracking in each iteration.
Write a function min_gd(f::Function, x0::Float64)
which computes the minimum of function f
using gradient descent. Assume f
returns a scalar and a gradient.
Write a function min_nr(f::Function, x0::Float64)
which computes the minimum of function f
using Newton-Raphson method. Assume f
returns a scalar, a gradient, and a hessian.
Write a method zero_newton(f::Function, x0::Vector{Float64})
which computes the zero of a vector valued function f
starting from initial point x0
.
f(z) = let
= z
x,y ^3 + y, x-y]
[xend
= [0.1, 0.3] x0
2-element Vector{Float64}:
0.1
0.3
f(x0)
2-element Vector{Float64}:
0.301
-0.19999999999999998
using ForwardDiff
jacobian(f, x0) ForwardDiff.
2×2 Matrix{Float64}:
0.03 1.0
1.0 -1.0
function zero_newton(f, x0; maxit=10, verbose=false, τ_η=1e-10, τ_ϵ=1e-10)
# local x1
= x0
x1
for it ∈ 1:maxit
= f(x0)
f0 = maximum(abs.(f0))
ϵ if ϵ < τ_ϵ
println("Iteration $(it): ϵ=$(ϵ) ") : nothing
verbose ? return x0
end
= ForwardDiff.jacobian(f, x0)
J0
= x0 - J0 \ f0
x1
= maximum(abs.(x1-x0))
η if η<τ_η
return x1
end
# cond ? do : otherwise
println("Iteration $(it): ϵ=$(ϵ) : η=$(η)") : nothing
verbose ?
= x1
x0
end
return x1
end
zero_newton (generic function with 1 method)
zero_newton(f, x0; verbose=true)
Iteration 1: ϵ=0.301 : η=0.2980582524271845
Iteration 2: ϵ=0.0019417548939487858 : η=0.00194173293071458
Iteration 3: ϵ=1.4642100930805857e-8 : η=1.4642100930805848e-8
Iteration 4: ϵ=6.617444900424222e-24
2-element Vector{Float64}:
6.617444900424222e-24
6.617444900424222e-24
Add an method zero_newton(f::Function, x0::Vector{Float64}, backtracking=true)
which computes the zero of function f
starting from initial point x0
using backtracking in each iteration.
function zero_newton(f, x0; maxit=10, verbose=false, τ_η=1e-10, τ_ϵ=1e-10, backtrack=true)
# local x1
= x0
x1
for it ∈ 1:maxit
= f(x0)
f0 = maximum(abs.(f0))
ϵ if ϵ < τ_ϵ
println("Iteration $(it): ϵ=$(ϵ) ") : nothing
verbose ? return x0
end
= ForwardDiff.jacobian(f, x0)
J0
# Newton steap
= - J0 \ f0
Δ
if !backtrack
= x0 + Δ
x1
else
for i = 0:10
= x0 + 2.0^(-i)*Δ
x1
# test function for new guess
= f(x1)
f1 1 = maximum(abs.(f1))
ϵ
if ϵ1<ϵ
# new guess is good
break
end
end
end
= maximum(abs.(x1-x0))
η if η<τ_η
return x1
end
# cond ? do : otherwise
println("Iteration $(it): ϵ=$(ϵ) : η=$(η)") : nothing
verbose ?
= x1
x0
end
return x1
end
zero_newton (generic function with 1 method)
zero_newton(f, x0, verbose=true, backtrack=true)
Iteration 1: ϵ=0.301 : η=0.2980582524271845
Iteration 2: ϵ=0.0019417548939487858 : η=0.00194173293071458
Iteration 3: ϵ=1.4642100930805857e-8 : η=1.4642100930805848e-8
Iteration 4: ϵ=6.617444900424222e-24
2-element Vector{Float64}:
6.617444900424222e-24
6.617444900424222e-24
Add a method zero_newton(f::Function, x0::Vector{Float64}, backtracking=true, lb=Vector{Float64})
which computes the zero of function f
starting from initial point x0
taking complementarity constraint into account x>=lb
using the Fischer-Burmeister method.
g(z) = let
=z
x,y-x, x^2+y^2-1 ]
[ yend
= [0.0, -10000] lb
2-element Vector{Float64}:
0.0
-10000.0
φ_FB(a,b) = a+b-sqrt(a^2+b^2) # min
φ_FB (generic function with 1 method)
function ncp(f, x0, lb; args...)
# the dot calls φ_FB component-wise on its vector arguments
# fun(u) = φ_FB.(f(u), u - lb)
= u-> φ_FB.(f(u), u - lb)
fun
zero_newton(fun, x0; args...)
end
ncp (generic function with 2 methods)
x0
2-element Vector{Float64}:
0.1
0.3
ncp(g, x0, lb; maxit=100, verbose=true)
Iteration 1: ϵ=0.900040498785529 : η=0.8449227125187968
Iteration 2: ϵ=0.34501875850946817 : η=0.17623432346721998
Iteration 3: ϵ=0.04599075111946149 : η=0.022412443670669857
Iteration 4: ϵ=0.0005761770207755035 : η=0.0002880043594630788
Iteration 5: ϵ=8.413735486101359e-8 : η=4.2068675654149956e-8
Iteration 6: ϵ=0.0
2-element Vector{Float64}:
-5.460886251568741e-17
1.0000000000002816
ncp(g, [0.6, 0.6], lb; maxit=100, verbose=true)
Iteration 1: ϵ=0.2800039197663864 : η=0.11666503345865797
Iteration 2: ϵ=0.027217503327847226 : η=0.009494525381928298
Iteration 3: ϵ=0.0001802549850253854 : η=6.37240192483679e-5
Iteration 4: ϵ=8.119968697428703e-9 : η=2.8708424526513454e-9
Iteration 5: ϵ=0.0
2-element Vector{Float64}:
0.7071067811866388
0.7071067811866388