Skip to content
This repository was archived by the owner on May 15, 2025. It is now read-only.

Commit be9e2d2

Browse files
committed
Implemented Broyden and tests
1 parent 468a204 commit be9e2d2

File tree

3 files changed

+77
-42
lines changed

3 files changed

+77
-42
lines changed

Project.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ version = "0.1.4"
77
ArrayInterfaceCore = "30b0a656-2188-435a-8636-2ec0e6a096e2"
88
FiniteDiff = "6a86dc24-6348-571c-b903-95158fe2bd41"
99
ForwardDiff = "f6369f11-7733-5829-9624-2563aa707210"
10+
LinearAlgebra = "37e2e46d-f89d-539d-b4ee-838fcccc9c8e"
1011
Reexport = "189a3867-3050-52da-a836-e630ba90ab69"
1112
SciMLBase = "0bca4576-84f4-4d90-8ffe-ffa030f20462"
1213
SnoopPrecompile = "66db9d55-30c0-4569-8b51-7e840670fc0c"

src/broyden.jl

Lines changed: 29 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -5,13 +5,19 @@
55
struct Broyden <: AbstractSimpleNonlinearSolveAlgorithm end
66

77
function SciMLBase.solve(prob::NonlinearProblem,
8-
alg::Broyden, args...; abstol = nothing,
9-
reltol = nothing,
10-
maxiters = 1000, kwargs...)
8+
alg::Broyden, args...; abstol = nothing,
9+
reltol = nothing,
10+
maxiters = 1000, kwargs...)
11+
1112
f = Base.Fix2(prob.f, prob.p)
12-
xₙ = float(prob.u0)
13-
T = typeof(xₙ)
14-
J⁻¹ = Matrix{T}(I, length(xₙ), length(xₙ))
13+
x = float(prob.u0)
14+
fₙ = f(x)
15+
T = eltype(x)
16+
if length(x) > 1
17+
J⁻¹ = Matrix{T}(I, length(x), length(x))
18+
else
19+
J⁻¹ = 1
20+
end
1521

1622
if SciMLBase.isinplace(prob)
1723
error("Broyden currently only supports out-of-place nonlinear problems")
@@ -21,23 +27,30 @@ function SciMLBase.solve(prob::NonlinearProblem,
2127
real(oneunit(eltype(T))) * (eps(real(one(eltype(T)))))^(4 // 5)
2228
rtol = reltol !== nothing ? reltol : eps(real(one(eltype(T))))^(4 // 5)
2329

24-
for _ in 1:maxiters
25-
# TODO check if nameing with heavy use of subscrips is ok
30+
xₙ = x
31+
xₙ₋₁ = x
32+
fₙ₋₁ = fₙ
33+
for n in 1:maxiters
34+
35+
xₙ = xₙ₋₁ - J⁻¹ * fₙ₋₁
2636
fₙ = f(xₙ)
27-
xₙ₊₁ = xₙ + J⁻¹ * fₙ
28-
Δxₙ = xₙ₊₁ - xₙ
29-
Δfₙ = f(xₙ₊₁) - fₙ
30-
J⁻¹ .+= ((Δxₙ .- J⁻¹ * Δfₙ) ./ (Δxₙ' * J⁻¹ * Δfₙ)) * (Δxₙ' * J⁻¹)
37+
Δxₙ = xₙ - xₙ₋₁
38+
Δfₙ = fₙ - fₙ₋₁
39+
J⁻¹ += ((Δxₙ - J⁻¹ * Δfₙ) ./ (Δxₙ' * J⁻¹ * Δfₙ)) * (Δxₙ' * J⁻¹)
3140

3241
iszero(fₙ) &&
33-
return SciMLBase.build_solution(prob, alg, xₙ₊₁, fₙ; retcode = ReturnCode.Success)
42+
return SciMLBase.build_solution(prob, alg, xₙ, fₙ;
43+
retcode = ReturnCode.Success)
3444

35-
if isapprox(xₙ₊₁, xₙ, atol = atol, rtol = rtol)
36-
return SciMLBase.build_solution(prob, alg, xₙ₊₁, fₙ; retcode = ReturnCode.Success)
45+
if isapprox(xₙ, xₙ₋₁, atol = atol, rtol = rtol)
46+
return SciMLBase.build_solution(prob, alg, xₙ, fₙ;
47+
retcode = ReturnCode.Success)
3748
end
38-
xₙ = xₙ₊₁
49+
xₙ₋₁ = xₙ
50+
fₙ₋₁ = fₙ
3951
end
4052

4153
@show xₙ, fₙ
54+
4255
return SciMLBase.build_solution(prob, alg, xₙ, fₙ; retcode = ReturnCode.MaxIters)
4356
end

test/basictests.jl

Lines changed: 47 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ using StaticArrays
33
using BenchmarkTools
44
using Test
55

6+
# SimpleNewtonRaphson
67
function benchmark_scalar(f, u0)
78
probN = NonlinearProblem{false}(f, u0)
89
sol = (solve(probN, SimpleNewtonRaphson()))
@@ -23,38 +24,53 @@ sol = benchmark_scalar(sf, csu0)
2324

2425
@test (@ballocated benchmark_scalar(sf, csu0)) == 0
2526

27+
# Broyden
28+
function benchmark_scalar(f, u0)
29+
probN = NonlinearProblem{false}(f, u0)
30+
sol = (solve(probN, Broyden()))
31+
end
32+
33+
sol = benchmark_scalar(sf, csu0)
34+
@test sol.retcode === ReturnCode.Success
35+
@test sol.u * sol.u - 2 < 1e-9
36+
@test (@ballocated benchmark_scalar(sf, csu0)) == 0
37+
2638
# AD Tests
2739
using ForwardDiff
2840

2941
# Immutable
3042
f, u0 = (u, p) -> u .* u .- p, @SVector[1.0, 1.0]
3143

32-
g = function (p)
33-
probN = NonlinearProblem{false}(f, csu0, p)
34-
sol = solve(probN, SimpleNewtonRaphson(), tol = 1e-9)
35-
return sol.u[end]
36-
end
44+
for alg in [SimpleNewtonRaphson(), Broyden()]
45+
g = function (p)
46+
probN = NonlinearProblem{false}(f, csu0, p)
47+
sol = solve(probN, alg, tol = 1e-9)
48+
return sol.u[end]
49+
end
3750

38-
for p in 1.0:0.1:100.0
39-
@test g(p) sqrt(p)
40-
@test ForwardDiff.derivative(g, p) 1 / (2 * sqrt(p))
51+
for p in 1.1:0.1:100.0
52+
@test g(p) sqrt(p)
53+
@test ForwardDiff.derivative(g, p) 1 / (2 * sqrt(p))
54+
end
4155
end
4256

4357
# Scalar
4458
f, u0 = (u, p) -> u * u - p, 1.0
59+
for alg in [SimpleNewtonRaphson(), Broyden()]
4560

46-
# SimpleNewtonRaphson
47-
g = function (p)
48-
probN = NonlinearProblem{false}(f, oftype(p, u0), p)
49-
sol = solve(probN, SimpleNewtonRaphson())
50-
return sol.u
51-
end
52-
53-
@test ForwardDiff.derivative(g, 1.0) 0.5
61+
g = function (p)
62+
probN = NonlinearProblem{false}(f, oftype(p, u0), p)
63+
sol = solve(probN, alg)
64+
return sol.u
65+
end
5466

55-
for p in 1.1:0.1:100.0
56-
@test g(p) sqrt(p)
67+
p = 1.1
5768
@test ForwardDiff.derivative(g, p) 1 / (2 * sqrt(p))
69+
70+
for p in 1.1:0.1:100.0
71+
@test g(p) sqrt(p)
72+
@test ForwardDiff.derivative(g, p) 1 / (2 * sqrt(p))
73+
end
5874
end
5975

6076
tspan = (1.0, 20.0)
@@ -77,24 +93,26 @@ for alg in [Bisection(), Falsi()]
7793
global g, p
7894
g = function (p)
7995
probN = IntervalNonlinearProblem{false}(f, tspan, p)
80-
sol = solve(probN, Bisection()) # TODO check if "alg" should replace "Bisection()"
96+
sol = solve(probN, alg)
8197
return [sol.left]
8298
end
8399

84100
@test g(p) [sqrt(p[2] / p[1])]
85101
@test ForwardDiff.jacobian(g, p) ForwardDiff.jacobian(t, p)
86102
end
87103

88-
gnewton = function (p)
89-
probN = NonlinearProblem{false}(f, 0.5, p)
90-
sol = solve(probN, SimpleNewtonRaphson())
91-
return [sol.u]
104+
for alg in [SimpleNewtonRaphson(), Broyden()]
105+
global g, p
106+
g = function (p)
107+
probN = NonlinearProblem{false}(f, 0.5, p)
108+
sol = solve(probN, alg)
109+
return [sol.u]
110+
end
111+
@test g(p) [sqrt(p[2] / p[1])]
112+
@test ForwardDiff.jacobian(g, p) ForwardDiff.jacobian(t, p)
92113
end
93-
@test gnewton(p) [sqrt(p[2] / p[1])]
94-
@test ForwardDiff.jacobian(gnewton, p) ForwardDiff.jacobian(t, p)
95114

96115
# Error Checks
97-
98116
f, u0 = (u, p) -> u .* u .- 2.0, @SVector[1.0, 1.0]
99117
probN = NonlinearProblem(f, u0)
100118

@@ -103,6 +121,8 @@ probN = NonlinearProblem(f, u0)
103121
@test solve(probN, SimpleNewtonRaphson(; autodiff = false)).u[end] sqrt(2.0)
104122
@test solve(probN, SimpleNewtonRaphson(; autodiff = false)).u[end] sqrt(2.0)
105123
# TODO check why the 2 lines above are identical
124+
@test solve(probN, Broyden()).u[end] sqrt(2.0)
125+
@test solve(probN, Broyden(); immutable = false).u[end] sqrt(2.0)
106126

107127
for u0 in [1.0, [1, 1.0]]
108128
local f, probN, sol
@@ -114,6 +134,7 @@ for u0 in [1.0, [1, 1.0]]
114134
@test solve(probN, SimpleNewtonRaphson()).u sol
115135
@test solve(probN, SimpleNewtonRaphson()).u sol
116136
@test solve(probN, SimpleNewtonRaphson(; autodiff = false)).u sol
137+
@test solve(probN, Broyden()).u sol
117138
end
118139

119140
# Bisection Tests

0 commit comments

Comments
 (0)