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

Commit e600ee5

Browse files
Merge pull request #41 from avik-pal/ap/lbroyden
Add limited memory broyden implementation
2 parents a748c90 + c00b3b3 commit e600ee5

File tree

4 files changed

+99
-7
lines changed

4 files changed

+99
-7
lines changed

Project.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
name = "SimpleNonlinearSolve"
22
uuid = "727e6d20-b764-4bd8-a329-72de5adea6c7"
33
authors = ["SciML"]
4-
version = "0.1.10"
4+
version = "0.1.11"
55

66
[deps]
77
ArrayInterfaceCore = "30b0a656-2188-435a-8636-2ec0e6a096e2"

src/SimpleNonlinearSolve.jl

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ include("bisection.jl")
2020
include("falsi.jl")
2121
include("raphson.jl")
2222
include("broyden.jl")
23+
include("lbroyden.jl")
2324
include("klement.jl")
2425
include("trustRegion.jl")
2526
include("ridder.jl")
@@ -52,7 +53,7 @@ SnoopPrecompile.@precompile_all_calls begin for T in (Float32, Float64)
5253
end end
5354

5455
# DiffEq styled algorithms
55-
export Bisection, Brent, Broyden, SimpleDFSane, Falsi, Klement, Ridder, SimpleNewtonRaphson,
56-
SimpleTrustRegion
56+
export Bisection, Brent, Broyden, LBroyden, SimpleDFSane, Falsi, Klement,
57+
Ridder, SimpleNewtonRaphson, SimpleTrustRegion
5758

5859
end # module

src/lbroyden.jl

Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
1+
"""
2+
LBroyden(threshold::Int = 27)
3+
4+
A limited memory implementation of Broyden. This method applies the L-BFGS scheme to
5+
Broyden's method.
6+
"""
7+
Base.@kwdef struct LBroyden <: AbstractSimpleNonlinearSolveAlgorithm
8+
threshold::Int = 27
9+
end
10+
11+
@views function SciMLBase.__solve(prob::NonlinearProblem,
12+
alg::LBroyden, args...; abstol = nothing,
13+
reltol = nothing,
14+
maxiters = 1000, kwargs...)
15+
threshold = min(maxiters, alg.threshold)
16+
x = float(prob.u0)
17+
18+
if x isa Number
19+
restore_scalar = true
20+
x = [x]
21+
f = u -> prob.f(u[], prob.p)
22+
else
23+
f = Base.Fix2(prob.f, prob.p)
24+
restore_scalar = false
25+
end
26+
27+
fₙ = f(x)
28+
T = eltype(x)
29+
30+
if SciMLBase.isinplace(prob)
31+
error("LBroyden currently only supports out-of-place nonlinear problems")
32+
end
33+
34+
U = fill!(similar(x, (threshold, length(x))), zero(T))
35+
Vᵀ = fill!(similar(x, (length(x), threshold)), zero(T))
36+
37+
atol = abstol !== nothing ? abstol :
38+
real(oneunit(eltype(T))) * (eps(real(one(eltype(T)))))^(4 // 5)
39+
rtol = reltol !== nothing ? reltol : eps(real(one(eltype(T))))^(4 // 5)
40+
41+
xₙ = x
42+
xₙ₋₁ = x
43+
fₙ₋₁ = fₙ
44+
update = fₙ
45+
for i in 1:maxiters
46+
xₙ = xₙ₋₁ .+ update
47+
fₙ = f(xₙ)
48+
Δxₙ = xₙ .- xₙ₋₁
49+
Δfₙ = fₙ .- fₙ₋₁
50+
51+
if iszero(fₙ)
52+
xₙ = restore_scalar ? xₙ[] : xₙ
53+
return SciMLBase.build_solution(prob, alg, xₙ, fₙ; retcode = ReturnCode.Success)
54+
end
55+
56+
if isapprox(xₙ, xₙ₋₁; atol, rtol)
57+
xₙ = restore_scalar ? xₙ[] : xₙ
58+
return SciMLBase.build_solution(prob, alg, xₙ, fₙ; retcode = ReturnCode.Success)
59+
end
60+
61+
_U = U[1:min(threshold, i), :]
62+
_Vᵀ = Vᵀ[:, 1:min(threshold, i)]
63+
64+
vᵀ = _rmatvec(_U, _Vᵀ, Δxₙ)
65+
mvec = _matvec(_U, _Vᵀ, Δfₙ)
66+
Δxₙ = (Δxₙ .- mvec) ./ (sum(vᵀ .* Δfₙ) .+ convert(T, 1e-5))
67+
68+
Vᵀ[:, mod1(i, threshold)] .= vᵀ
69+
U[mod1(i, threshold), :] .= Δxₙ
70+
71+
update = -_matvec(U[1:min(threshold, i + 1), :], Vᵀ[:, 1:min(threshold, i + 1)], fₙ)
72+
73+
xₙ₋₁ = xₙ
74+
fₙ₋₁ = fₙ
75+
end
76+
77+
xₙ = restore_scalar ? xₙ[] : xₙ
78+
return SciMLBase.build_solution(prob, alg, xₙ, fₙ; retcode = ReturnCode.MaxIters)
79+
end
80+
81+
function _rmatvec(U::AbstractMatrix, Vᵀ::AbstractMatrix,
82+
x::Union{<:AbstractVector, <:Number})
83+
return -x .+ dropdims(sum(U .* sum(Vᵀ .* x; dims = 1)'; dims = 1); dims = 1)
84+
end
85+
86+
function _matvec(U::AbstractMatrix, Vᵀ::AbstractMatrix,
87+
x::Union{<:AbstractVector, <:Number})
88+
return -x .+ dropdims(sum(sum(x .* U'; dims = 1) .* Vᵀ; dims = 2); dims = 2)
89+
end

test/basictests.jl

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -78,7 +78,7 @@ using ForwardDiff
7878
# Immutable
7979
f, u0 = (u, p) -> u .* u .- p, @SVector[1.0, 1.0]
8080

81-
for alg in (SimpleNewtonRaphson(), Broyden(), Klement(), SimpleTrustRegion(),
81+
for alg in (SimpleNewtonRaphson(), Broyden(), LBroyden(), Klement(), SimpleTrustRegion(),
8282
SimpleDFSane())
8383
g = function (p)
8484
probN = NonlinearProblem{false}(f, csu0, p)
@@ -94,7 +94,7 @@ end
9494

9595
# Scalar
9696
f, u0 = (u, p) -> u * u - p, 1.0
97-
for alg in (SimpleNewtonRaphson(), Broyden(), Klement(), SimpleTrustRegion(),
97+
for alg in (SimpleNewtonRaphson(), Broyden(), LBroyden(), Klement(), SimpleTrustRegion(),
9898
SimpleDFSane())
9999
g = function (p)
100100
probN = NonlinearProblem{false}(f, oftype(p, u0), p)
@@ -160,13 +160,13 @@ for alg in [Bisection(), Falsi(), Ridder(), Brent()]
160160
@test ForwardDiff.jacobian(g, p) ForwardDiff.jacobian(t, p)
161161
end
162162

163-
for alg in (SimpleNewtonRaphson(), Broyden(), Klement(), SimpleTrustRegion(),
163+
for alg in (SimpleNewtonRaphson(), Broyden(), LBroyden(), Klement(), SimpleTrustRegion(),
164164
SimpleDFSane())
165165
global g, p
166166
g = function (p)
167167
probN = NonlinearProblem{false}(f, 0.5, p)
168168
sol = solve(probN, alg)
169-
return [sol.u]
169+
return [abs(sol.u)]
170170
end
171171
@test g(p) [sqrt(p[2] / p[1])]
172172
@test ForwardDiff.jacobian(g, p) ForwardDiff.jacobian(t, p)
@@ -181,6 +181,7 @@ probN = NonlinearProblem(f, u0)
181181
@test solve(probN, SimpleTrustRegion()).u[end] sqrt(2.0)
182182
@test solve(probN, SimpleTrustRegion(; autodiff = false)).u[end] sqrt(2.0)
183183
@test solve(probN, Broyden()).u[end] sqrt(2.0)
184+
@test solve(probN, LBroyden()).u[end] sqrt(2.0)
184185
@test solve(probN, Klement()).u[end] sqrt(2.0)
185186
@test solve(probN, SimpleDFSane()).u[end] sqrt(2.0)
186187

@@ -199,6 +200,7 @@ for u0 in [1.0, [1, 1.0]]
199200
@test solve(probN, SimpleTrustRegion(; autodiff = false)).u sol
200201

201202
@test solve(probN, Broyden()).u sol
203+
@test solve(probN, LBroyden()).u sol
202204
@test solve(probN, Klement()).u sol
203205
@test solve(probN, SimpleDFSane()).u sol
204206
end

0 commit comments

Comments
 (0)