11# Sadly `Broyden` is taken up by SimpleNonlinearSolve.jl
22"""
3- GeneralBroyden(; max_resets = 3, linesearch = nothing, reset_tolerance = nothing)
3+ GeneralBroyden(; max_resets = 100, linesearch = nothing, reset_tolerance = nothing,
4+ init_jacobian::Val = Val(:identity), autodiff = nothing, alpha = nothing)
45
56An implementation of `Broyden` with resetting and line search.
67
78## Arguments
89
9- - `max_resets`: the maximum number of resets to perform. Defaults to `3`.
10+ - `max_resets`: the maximum number of resets to perform. Defaults to `100`.
11+
1012 - `reset_tolerance`: the tolerance for the reset check. Defaults to
1113 `sqrt(eps(real(eltype(u))))`.
1214 - `linesearch`: the line search algorithm to use. Defaults to [`LineSearch()`](@ref),
1315 which means that no line search is performed. Algorithms from `LineSearches.jl` can be
1416 used here directly, and they will be converted to the correct `LineSearch`. It is
1517 recommended to use [LiFukushimaLineSearch](@ref) -- a derivative free linesearch
1618 specifically designed for Broyden's method.
19+ - `alpha`: If `init_jacobian` is set to `Val(:identity)`, then the initial Jacobian
20+ inverse is set to be `(αI)⁻¹`. Defaults to `nothing` which implies
21+ `α = max(norm(u), 1) / (2 * norm(fu))`.
22+ - `init_jacobian`: the method to use for initializing the jacobian. Defaults to
23+ `Val(:identity)`. Choices include:
24+
25+ + `Val(:identity)`: Identity Matrix.
26+ + `Val(:true_jacobian)`: True Jacobian. This is a good choice for differentiable
27+ problems.
28+ - `autodiff`: determines the backend used for the Jacobian. Note that this argument is
29+ ignored if an analytical Jacobian is passed, as that will be used instead. Defaults to
30+ `nothing` which means that a default is selected according to the problem specification!
31+ Valid choices are types from ADTypes.jl. (Used if `init_jacobian = Val(:true_jacobian)`)
32+ - `update_rule`: Update Rule for the Jacobian. Choices are:
33+
34+ + `Val(:good_broyden)`: Good Broyden's Update Rule
35+ + `Val(:bad_broyden)`: Bad Broyden's Update Rule
36+ + `Val(:diagonal)`: Only update the diagonal of the Jacobian. This algorithm may be
37+ useful for specific problems, but whether it will work may depend strongly on the
38+ problem.
1739"""
18- @concrete struct GeneralBroyden <: AbstractNewtonAlgorithm{false, Nothing}
40+ @concrete struct GeneralBroyden{IJ, UR, CJ, AD} <: AbstractNewtonAlgorithm{CJ, AD}
41+ ad:: AD
1942 max_resets:: Int
2043 reset_tolerance
2144 linesearch
45+ alpha
46+ end
47+
48+ function __alg_print_modifiers (alg:: GeneralBroyden{IJ, UR} ) where {IJ, UR}
49+ modifiers = String[]
50+ IJ != = :identity && push! (modifiers, " init_jacobian = Val(:$(IJ) )" )
51+ UR != = :good_broyden && push! (modifiers, " update_rule = Val(:$(UR) )" )
52+ alg. alpha != = nothing && push! (modifiers, " alpha = $(alg. alpha) " )
53+ return modifiers
54+ end
55+
56+ function set_ad (alg:: GeneralBroyden{IJ, UR, CJ} , ad) where {IJ, UR, CJ}
57+ return GeneralBroyden {IJ, UR, CJ} (ad, alg. max_resets, alg. reset_tolerance,
58+ alg. linesearch, alg. alpha)
2259end
2360
24- function GeneralBroyden (; max_resets = 3 , linesearch = nothing ,
25- reset_tolerance = nothing )
61+ function GeneralBroyden (; max_resets = 100 , linesearch = nothing , reset_tolerance = nothing ,
62+ init_jacobian:: Val = Val (:identity ), autodiff = nothing , alpha = nothing ,
63+ update_rule = Val (:good_broyden ))
64+ UR = _unwrap_val (update_rule)
65+ @assert UR ∈ (:good_broyden , :bad_broyden , :diagonal )
66+ IJ = _unwrap_val (init_jacobian)
67+ @assert IJ ∈ (:identity , :true_jacobian )
2668 linesearch = linesearch isa LineSearch ? linesearch : LineSearch (; method = linesearch)
27- return GeneralBroyden (max_resets, reset_tolerance, linesearch)
69+ CJ = IJ === :true_jacobian
70+ return GeneralBroyden {IJ, UR, CJ} (autodiff, max_resets, reset_tolerance, linesearch,
71+ alpha)
2872end
2973
30- @concrete mutable struct GeneralBroydenCache{iip} <: AbstractNonlinearSolveCache{iip}
74+ @concrete mutable struct GeneralBroydenCache{iip, IJ, UR} < :
75+ AbstractNonlinearSolveCache{iip}
3176 f
3277 alg
3378 u
3782 fu_cache
3883 dfu
3984 p
85+ uf
4086 J⁻¹
87+ J⁻¹_cache
4188 J⁻¹dfu
89+ inv_alpha
90+ alpha_initial
4291 force_stop:: Bool
4392 resets:: Int
4493 max_resets:: Int
4998 reltol
5099 reset_tolerance
51100 reset_check
101+ jac_cache
52102 prob
53103 stats:: NLStats
54104 ls_cache
55105 tc_cache
56106 trace
57107end
58108
59- function SciMLBase. __init (prob:: NonlinearProblem{uType, iip} , alg :: GeneralBroyden , args ... ;
60- alias_u0 = false , maxiters = 1000 , abstol = nothing , reltol = nothing ,
109+ function SciMLBase. __init (prob:: NonlinearProblem{uType, iip} , alg_ :: GeneralBroyden{IJ, UR} ,
110+ args ... ; alias_u0 = false , maxiters = 1000 , abstol = nothing , reltol = nothing ,
61111 termination_condition = nothing , internalnorm:: F = DEFAULT_NORM,
62- kwargs... ) where {uType, iip, F}
112+ kwargs... ) where {uType, iip, F, IJ, UR }
63113 @unpack f, u0, p = prob
64114 u = __maybe_unaliased (u0, alias_u0)
65115 fu = evaluate_f (prob, u)
66116 @bb du = copy (u)
67- J⁻¹ = __init_identity_jacobian (u, fu)
117+
118+ inv_alpha = __initial_inv_alpha (alg_. alpha, u, fu, internalnorm)
119+
120+ if IJ === :true_jacobian
121+ alg = get_concrete_algorithm (alg_, prob)
122+ uf, _, J, fu_cache, jac_cache, du = jacobian_caches (alg, f, u, p, Val (iip);
123+ lininit = Val (false ))
124+ if UR === :diagonal
125+ J⁻¹_cache = J
126+ J⁻¹ = __diag (J)
127+ else
128+ J⁻¹_cache = nothing
129+ J⁻¹ = J
130+ end
131+ elseif IJ === :identity
132+ alg = alg_
133+ @bb du = similar (u)
134+ uf, fu_cache, jac_cache, J⁻¹_cache = nothing , nothing , nothing , nothing
135+ if UR === :diagonal
136+ J⁻¹ = one .(fu)
137+ @bb J⁻¹ .*= inv_alpha
138+ else
139+ J⁻¹ = __init_identity_jacobian (u, fu, inv_alpha)
140+ end
141+ end
142+
68143 reset_tolerance = alg. reset_tolerance === nothing ? sqrt (eps (real (eltype (u)))) :
69144 alg. reset_tolerance
70145 reset_check = x -> abs (x) ≤ reset_tolerance
71146
72147 @bb u_cache = copy (u)
73- @bb fu_cache = copy (fu)
74- @bb dfu = similar (fu)
148+ @bb dfu = copy (fu)
75149 @bb J⁻¹dfu = similar (u)
76150
77151 abstol, reltol, tc_cache = init_termination_cache (abstol, reltol, fu, u,
78152 termination_condition)
79- trace = init_nonlinearsolve_trace (alg, u, fu, J⁻¹, du; uses_jac_inverse = Val ( true ),
80- kwargs... )
153+ trace = init_nonlinearsolve_trace (alg, u, fu, ApplyArray (__zero, J⁻¹) , du;
154+ uses_jac_inverse = Val ( true ), kwargs... )
81155
82- return GeneralBroydenCache {iip} (f, alg, u, u_cache, du, fu, fu_cache, dfu, p,
83- J⁻¹, J⁻¹dfu, false , 0 , alg. max_resets, maxiters, internalnorm, ReturnCode. Default,
84- abstol, reltol, reset_tolerance, reset_check, prob, NLStats (1 , 0 , 0 , 0 , 0 ),
156+ return GeneralBroydenCache {iip, IJ, UR} (f, alg, u, u_cache, du, fu, fu_cache, dfu, p,
157+ uf, J⁻¹, J⁻¹_cache, J⁻¹dfu, inv_alpha, alg. alpha, false , 0 , alg. max_resets,
158+ maxiters, internalnorm, ReturnCode. Default, abstol, reltol, reset_tolerance,
159+ reset_check, jac_cache, prob, NLStats (1 , 0 , 0 , 0 , 0 ),
85160 init_linesearch_cache (alg. linesearch, f, u, p, fu, Val (iip)), tc_cache, trace)
86161end
87162
88- function perform_step! (cache:: GeneralBroydenCache{iip} ) where {iip}
163+ function perform_step! (cache:: GeneralBroydenCache{iip, IJ, UR } ) where {iip, IJ, UR }
89164 T = eltype (cache. u)
90165
91- @bb cache. du = cache. J⁻¹ × vec (cache. fu)
166+ if IJ === :true_jacobian && cache. stats. nsteps == 0
167+ if UR === :diagonal
168+ cache. J⁻¹_cache = __safe_inv (jacobian!! (cache. J⁻¹_cache, cache))
169+ cache. J⁻¹ = __get_diagonal!! (cache. J⁻¹, cache. J⁻¹_cache)
170+ else
171+ cache. J⁻¹ = __safe_inv (jacobian!! (cache. J⁻¹, cache))
172+ end
173+ end
174+
175+ if UR === :diagonal
176+ @bb @. cache. du = cache. J⁻¹ * cache. fu
177+ else
178+ @bb cache. du = cache. J⁻¹ × vec (cache. fu)
179+ end
92180 α = perform_linesearch! (cache. ls_cache, cache. u, cache. du)
93181 @bb axpy! (- α, cache. du, cache. u)
94182
@@ -100,33 +188,62 @@ function perform_step!(cache::GeneralBroydenCache{iip}) where {iip}
100188 cache. force_stop && return nothing
101189
102190 # Update the inverse jacobian
103- @bb @. cache. dfu = cache. fu - cache. fu_cache
191+ @bb @. cache. dfu = cache. fu - cache. dfu
104192
105193 if all (cache. reset_check, cache. du) || all (cache. reset_check, cache. dfu)
106194 if cache. resets ≥ cache. max_resets
107195 cache. retcode = ReturnCode. ConvergenceFailure
108196 cache. force_stop = true
109197 return nothing
110198 end
111- cache. J⁻¹ = __reinit_identity_jacobian!! (cache. J⁻¹)
199+ if IJ === :true_jacobian
200+ if UR === :diagonal
201+ cache. J⁻¹_cache = __safe_inv (jacobian!! (cache. J⁻¹_cache, cache))
202+ cache. J⁻¹ = __get_diagonal!! (cache. J⁻¹, cache. J⁻¹_cache)
203+ else
204+ cache. J⁻¹ = __safe_inv (jacobian!! (cache. J⁻¹, cache))
205+ end
206+ else
207+ cache. inv_alpha = __initial_inv_alpha (cache. inv_alpha, cache. alpha_initial,
208+ cache. u, cache. fu, cache. internalnorm)
209+ cache. J⁻¹ = __reinit_identity_jacobian!! (cache. J⁻¹, cache. inv_alpha)
210+ end
112211 cache. resets += 1
113212 else
114213 @bb cache. du .*= - 1
115- @bb cache. J⁻¹dfu = cache. J⁻¹ × vec (cache. dfu)
116- @bb cache. u_cache = transpose (cache. J⁻¹) × vec (cache. du)
117- denom = dot (cache. du, cache. J⁻¹dfu)
118- @bb @. cache. du = (cache. du - cache. J⁻¹dfu) / ifelse (iszero (denom), T (1e-5 ), denom)
119- @bb cache. J⁻¹ += vec (cache. du) × transpose (_vec (cache. u_cache))
214+ if UR === :good_broyden
215+ @bb cache. J⁻¹dfu = cache. J⁻¹ × vec (cache. dfu)
216+ @bb cache. u_cache = transpose (cache. J⁻¹) × vec (cache. du)
217+ denom = dot (cache. du, cache. J⁻¹dfu)
218+ @bb @. cache. du = (cache. du - cache. J⁻¹dfu) /
219+ ifelse (iszero (denom), T (1e-5 ), denom)
220+ @bb cache. J⁻¹ += vec (cache. du) × transpose (_vec (cache. u_cache))
221+ elseif UR === :bad_broyden
222+ @bb cache. J⁻¹dfu = cache. J⁻¹ × vec (cache. dfu)
223+ dfu_norm = cache. internalnorm (cache. dfu)^ 2
224+ @bb @. cache. du = (cache. du - cache. J⁻¹dfu) /
225+ ifelse (iszero (dfu_norm), T (1e-5 ), dfu_norm)
226+ @bb cache. J⁻¹ += vec (cache. du) × transpose (_vec (cache. dfu))
227+ elseif UR === :diagonal
228+ @bb @. cache. J⁻¹dfu = cache. du * cache. J⁻¹ * cache. dfu
229+ denom = sum (cache. J⁻¹dfu)
230+ @bb @. cache. J⁻¹ += (cache. du - cache. J⁻¹ * cache. dfu) * cache. du * cache. J⁻¹ /
231+ ifelse (iszero (denom), T (1e-5 ), denom)
232+ else
233+ error (" update_rule = Val(:$(UR) ) is not implemented for Broyden." )
234+ end
120235 end
121236
122- @bb copyto! (cache. fu_cache , cache. fu)
237+ @bb copyto! (cache. dfu , cache. fu)
123238 @bb copyto! (cache. u_cache, cache. u)
124239
125240 return nothing
126241end
127242
128243function __reinit_internal! (cache:: GeneralBroydenCache ; kwargs... )
129- cache. J⁻¹ = __reinit_identity_jacobian!! (cache. J⁻¹)
244+ cache. inv_alpha = __initial_inv_alpha (cache. inv_alpha, cache. alpha_initial, cache. u,
245+ cache. fu, cache. internalnorm)
246+ cache. J⁻¹ = __reinit_identity_jacobian!! (cache. J⁻¹, cache. inv_alpha)
130247 cache. resets = 0
131248 return nothing
132249end
0 commit comments