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

Commit 4bc5f24

Browse files
committed
Add limited memory broyden implementation
1 parent a748c90 commit 4bc5f24

File tree

2 files changed

+86
-2
lines changed

2 files changed

+86
-2
lines changed

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

0 commit comments

Comments
 (0)