Skip to content

Commit 93f0954

Browse files
committed
Multiple Broyden Update Rules
1 parent 0dd6194 commit 93f0954

File tree

3 files changed

+51
-18
lines changed

3 files changed

+51
-18
lines changed

src/NonlinearSolve.jl

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -117,6 +117,7 @@ function Base.show(io::IO, alg::AbstractNonlinearSolveAlgorithm)
117117
push!(modifiers, "linesearch = $(nameof(typeof(alg.linesearch)))()")
118118
end
119119
end
120+
append!(modifiers, __alg_print_modifiers(alg))
120121
if __getproperty(alg, Val(:radius_update_scheme)) !== nothing
121122
push!(modifiers, "radius_update_scheme = $(alg.radius_update_scheme)")
122123
end
@@ -125,6 +126,8 @@ function Base.show(io::IO, alg::AbstractNonlinearSolveAlgorithm)
125126
return nothing
126127
end
127128

129+
__alg_print_modifiers(_) = String[]
130+
128131
function SciMLBase.__solve(prob::Union{NonlinearProblem, NonlinearLeastSquaresProblem},
129132
alg::AbstractNonlinearSolveAlgorithm, args...; kwargs...)
130133
cache = init(prob, alg, args...; kwargs...)

src/broyden.jl

Lines changed: 42 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ An implementation of `Broyden` with resetting and line search.
88
## Arguments
99
1010
- `max_resets`: the maximum number of resets to perform. Defaults to `3`.
11+
1112
- `reset_tolerance`: the tolerance for the reset check. Defaults to
1213
`sqrt(eps(real(eltype(u))))`.
1314
- `linesearch`: the line search algorithm to use. Defaults to [`LineSearch()`](@ref),
@@ -22,29 +23,44 @@ An implementation of `Broyden` with resetting and line search.
2223
ignored if an analytical Jacobian is passed, as that will be used instead. Defaults to
2324
`nothing` which means that a default is selected according to the problem specification!
2425
Valid choices are types from ADTypes.jl. (Used if `init_jacobian = Val(:true_jacobian)`)
26+
- `update_rule`: Update Rule for the Jacobian. Choices are:
27+
28+
+ `Val(:good_broyden)`: Good Broyden's Update Rule
29+
+ `Val(:bad_broyden)`: Bad Broyden's Update Rule
2530
"""
26-
@concrete struct GeneralBroyden{IJ, CJ, AD} <: AbstractNewtonAlgorithm{CJ, AD}
31+
@concrete struct GeneralBroyden{IJ, UR, CJ, AD} <: AbstractNewtonAlgorithm{CJ, AD}
2732
ad::AD
2833
max_resets::Int
2934
reset_tolerance
3035
linesearch
3136
end
3237

33-
function set_ad(alg::GeneralBroyden{IJ, CJ}, ad) where {IJ, CJ}
34-
return GeneralBroyden{IJ, CJ}(ad, alg.max_resets, alg.reset_tolerance, alg.linesearch)
38+
function __alg_print_modifiers(::GeneralBroyden{IJ, UR}) where {IJ, UR}
39+
modifiers = String[]
40+
IJ !== :identity && push!(modifiers, "init_jacobian = :$(IJ)")
41+
UR !== :good_broyden && push!(modifiers, "update_rule = :$(UR)")
42+
return modifiers
3543
end
3644

37-
function GeneralBroyden(; max_resets = 3, linesearch = nothing,
38-
reset_tolerance = nothing, init_jacobian::Val = Val(:identity),
39-
autodiff = nothing)
45+
function set_ad(alg::GeneralBroyden{IJ, UR, CJ}, ad) where {IJ, UR, CJ}
46+
return GeneralBroyden{IJ, UR, CJ}(ad, alg.max_resets, alg.reset_tolerance,
47+
alg.linesearch)
48+
end
49+
50+
function GeneralBroyden(; max_resets = 3, linesearch = nothing, reset_tolerance = nothing,
51+
init_jacobian::Val = Val(:identity), autodiff = nothing,
52+
update_rule = Val(:good_broyden))
53+
UR = _unwrap_val(update_rule)
54+
@assert UR (:good_broyden, :bad_broyden)
4055
IJ = _unwrap_val(init_jacobian)
4156
@assert IJ (:identity, :true_jacobian)
4257
linesearch = linesearch isa LineSearch ? linesearch : LineSearch(; method = linesearch)
4358
CJ = IJ === :true_jacobian
44-
return GeneralBroyden{IJ, CJ}(autodiff, max_resets, reset_tolerance, linesearch)
59+
return GeneralBroyden{IJ, UR, CJ}(autodiff, max_resets, reset_tolerance, linesearch)
4560
end
4661

47-
@concrete mutable struct GeneralBroydenCache{iip, IJ} <: AbstractNonlinearSolveCache{iip}
62+
@concrete mutable struct GeneralBroydenCache{iip, IJ, UR} <:
63+
AbstractNonlinearSolveCache{iip}
4864
f
4965
alg
5066
u
@@ -75,10 +91,10 @@ end
7591
trace
7692
end
7793

78-
function SciMLBase.__init(prob::NonlinearProblem{uType, iip}, alg_::GeneralBroyden{IJ},
94+
function SciMLBase.__init(prob::NonlinearProblem{uType, iip}, alg_::GeneralBroyden{IJ, UR},
7995
args...; alias_u0 = false, maxiters = 1000, abstol = nothing, reltol = nothing,
8096
termination_condition = nothing, internalnorm::F = DEFAULT_NORM,
81-
kwargs...) where {uType, iip, F, IJ}
97+
kwargs...) where {uType, iip, F, IJ, UR}
8298
@unpack f, u0, p = prob
8399
u = __maybe_unaliased(u0, alias_u0)
84100
fu = evaluate_f(prob, u)
@@ -109,14 +125,14 @@ function SciMLBase.__init(prob::NonlinearProblem{uType, iip}, alg_::GeneralBroyd
109125
trace = init_nonlinearsolve_trace(alg, u, fu, J⁻¹, du; uses_jac_inverse = Val(true),
110126
kwargs...)
111127

112-
return GeneralBroydenCache{iip, IJ}(f, alg, u, u_cache, du, fu, fu_cache, dfu, p, uf,
113-
J⁻¹, J⁻¹dfu, false, 0, alg.max_resets, maxiters, internalnorm, ReturnCode.Default,
114-
abstol, reltol, reset_tolerance, reset_check, jac_cache, prob,
128+
return GeneralBroydenCache{iip, IJ, UR}(f, alg, u, u_cache, du, fu, fu_cache, dfu, p,
129+
uf, J⁻¹, J⁻¹dfu, false, 0, alg.max_resets, maxiters, internalnorm,
130+
ReturnCode.Default, abstol, reltol, reset_tolerance, reset_check, jac_cache, prob,
115131
NLStats(1, 0, 0, 0, 0),
116132
init_linesearch_cache(alg.linesearch, f, u, p, fu, Val(iip)), tc_cache, trace)
117133
end
118134

119-
function perform_step!(cache::GeneralBroydenCache{iip, IJ}) where {iip, IJ}
135+
function perform_step!(cache::GeneralBroydenCache{iip, IJ, UR}) where {iip, IJ, UR}
120136
T = eltype(cache.u)
121137

122138
if IJ === :true_jacobian && cache.stats.nsteps == 0
@@ -152,10 +168,18 @@ function perform_step!(cache::GeneralBroydenCache{iip, IJ}) where {iip, IJ}
152168
else
153169
@bb cache.du .*= -1
154170
@bb cache.J⁻¹dfu = cache.J⁻¹ × vec(cache.dfu)
155-
@bb cache.u_cache = transpose(cache.J⁻¹) × vec(cache.du)
156-
denom = dot(cache.du, cache.J⁻¹dfu)
157-
@bb @. cache.du = (cache.du - cache.J⁻¹dfu) / ifelse(iszero(denom), T(1e-5), denom)
158-
@bb cache.J⁻¹ += vec(cache.du) × transpose(_vec(cache.u_cache))
171+
if UR === :good_broyden
172+
@bb cache.u_cache = transpose(cache.J⁻¹) × vec(cache.du)
173+
denom = dot(cache.du, cache.J⁻¹dfu)
174+
@bb @. cache.du = (cache.du - cache.J⁻¹dfu) /
175+
ifelse(iszero(denom), T(1e-5), denom)
176+
@bb cache.J⁻¹ += vec(cache.du) × transpose(_vec(cache.u_cache))
177+
elseif UR === :bad_broyden
178+
dfu_norm = norm(cache.dfu)^2
179+
@bb @. cache.du = (cache.du - cache.J⁻¹dfu) /
180+
ifelse(iszero(dfu_norm), T(1e-5), dfu_norm)
181+
@bb cache.J⁻¹ += vec(cache.du) × transpose(_vec(cache.dfu))
182+
end
159183
end
160184

161185
@bb copyto!(cache.dfu, cache.fu)

src/klement.jl

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,12 @@ solves.
3636
linesearch
3737
end
3838

39+
function __alg_print_modifiers(::GeneralKlement{IJ}) where {IJ}
40+
modifiers = String[]
41+
IJ !== :identity && push!(modifiers, "init_jacobian = :$(IJ)")
42+
return modifiers
43+
end
44+
3945
function set_linsolve(alg::GeneralKlement{IJ, CS}, linsolve) where {IJ, CS}
4046
return GeneralKlement{IJ, CS}(alg.ad, alg.max_resets, linsolve, alg.precs,
4147
alg.linesearch)

0 commit comments

Comments
 (0)