1
1
# Sadly `Broyden` is taken up by SimpleNonlinearSolve.jl
2
2
"""
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)
4
5
5
6
An implementation of `Broyden` with resetting and line search.
6
7
7
8
## Arguments
8
9
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
+
10
12
- `reset_tolerance`: the tolerance for the reset check. Defaults to
11
13
`sqrt(eps(real(eltype(u))))`.
12
14
- `linesearch`: the line search algorithm to use. Defaults to [`LineSearch()`](@ref),
13
15
which means that no line search is performed. Algorithms from `LineSearches.jl` can be
14
16
used here directly, and they will be converted to the correct `LineSearch`. It is
15
17
recommended to use [LiFukushimaLineSearch](@ref) -- a derivative free linesearch
16
18
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.
17
39
"""
18
- @concrete struct GeneralBroyden <: AbstractNewtonAlgorithm{false, Nothing}
40
+ @concrete struct GeneralBroyden{IJ, UR, CJ, AD} <: AbstractNewtonAlgorithm{CJ, AD}
41
+ ad:: AD
19
42
max_resets:: Int
20
43
reset_tolerance
21
44
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)
22
59
end
23
60
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 )
26
68
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)
28
72
end
29
73
30
- @concrete mutable struct GeneralBroydenCache{iip} <: AbstractNonlinearSolveCache{iip}
74
+ @concrete mutable struct GeneralBroydenCache{iip, IJ, UR} < :
75
+ AbstractNonlinearSolveCache{iip}
31
76
f
32
77
alg
33
78
u
37
82
fu_cache
38
83
dfu
39
84
p
85
+ uf
40
86
J⁻¹
87
+ J⁻¹_cache
41
88
J⁻¹dfu
89
+ inv_alpha
90
+ alpha_initial
42
91
force_stop:: Bool
43
92
resets:: Int
44
93
max_resets:: Int
49
98
reltol
50
99
reset_tolerance
51
100
reset_check
101
+ jac_cache
52
102
prob
53
103
stats:: NLStats
54
104
ls_cache
55
105
tc_cache
56
106
trace
57
107
end
58
108
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 ,
61
111
termination_condition = nothing , internalnorm:: F = DEFAULT_NORM,
62
- kwargs... ) where {uType, iip, F}
112
+ kwargs... ) where {uType, iip, F, IJ, UR }
63
113
@unpack f, u0, p = prob
64
114
u = __maybe_unaliased (u0, alias_u0)
65
115
fu = evaluate_f (prob, u)
66
116
@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
+
68
143
reset_tolerance = alg. reset_tolerance === nothing ? sqrt (eps (real (eltype (u)))) :
69
144
alg. reset_tolerance
70
145
reset_check = x -> abs (x) ≤ reset_tolerance
71
146
72
147
@bb u_cache = copy (u)
73
- @bb fu_cache = copy (fu)
74
- @bb dfu = similar (fu)
148
+ @bb dfu = copy (fu)
75
149
@bb J⁻¹dfu = similar (u)
76
150
77
151
abstol, reltol, tc_cache = init_termination_cache (abstol, reltol, fu, u,
78
152
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... )
81
155
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 ),
85
160
init_linesearch_cache (alg. linesearch, f, u, p, fu, Val (iip)), tc_cache, trace)
86
161
end
87
162
88
- function perform_step! (cache:: GeneralBroydenCache{iip} ) where {iip}
163
+ function perform_step! (cache:: GeneralBroydenCache{iip, IJ, UR } ) where {iip, IJ, UR }
89
164
T = eltype (cache. u)
90
165
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
92
180
α = perform_linesearch! (cache. ls_cache, cache. u, cache. du)
93
181
@bb axpy! (- α, cache. du, cache. u)
94
182
@@ -100,33 +188,62 @@ function perform_step!(cache::GeneralBroydenCache{iip}) where {iip}
100
188
cache. force_stop && return nothing
101
189
102
190
# Update the inverse jacobian
103
- @bb @. cache. dfu = cache. fu - cache. fu_cache
191
+ @bb @. cache. dfu = cache. fu - cache. dfu
104
192
105
193
if all (cache. reset_check, cache. du) || all (cache. reset_check, cache. dfu)
106
194
if cache. resets ≥ cache. max_resets
107
195
cache. retcode = ReturnCode. ConvergenceFailure
108
196
cache. force_stop = true
109
197
return nothing
110
198
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
112
211
cache. resets += 1
113
212
else
114
213
@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
120
235
end
121
236
122
- @bb copyto! (cache. fu_cache , cache. fu)
237
+ @bb copyto! (cache. dfu , cache. fu)
123
238
@bb copyto! (cache. u_cache, cache. u)
124
239
125
240
return nothing
126
241
end
127
242
128
243
function __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)
130
247
cache. resets = 0
131
248
return nothing
132
249
end
0 commit comments