1
1
"""
2
- GeneralKlement(; max_resets = 5, linsolve = nothing, linesearch = nothing,
3
- precs = DEFAULT_PRECS)
2
+ GeneralKlement(; max_resets = 100, linsolve = nothing, linesearch = nothing,
3
+ precs = DEFAULT_PRECS, alpha = true, init_jacobian::Val = Val(:identity),
4
+ autodiff = nothing)
4
5
5
6
An implementation of `Klement` with line search, preconditioning and customizable linear
6
- solves.
7
+ solves. It is recommended to use `Broyden` for most problems over this.
7
8
8
9
## Keyword Arguments
9
10
10
- - `max_resets`: the maximum number of resets to perform. Defaults to `5`.
11
+ - `max_resets`: the maximum number of resets to perform. Defaults to `100`.
12
+
11
13
- `linsolve`: the [LinearSolve.jl](https://github.com/SciML/LinearSolve.jl) used for the
12
14
linear solves within the Newton method. Defaults to `nothing`, which means it uses the
13
15
LinearSolve.jl default algorithm choice. For more information on available algorithm
@@ -19,10 +21,18 @@ solves.
19
21
- `linesearch`: the line search algorithm to use. Defaults to [`LineSearch()`](@ref),
20
22
which means that no line search is performed. Algorithms from `LineSearches.jl` can be
21
23
used here directly, and they will be converted to the correct `LineSearch`.
22
- - `init_jacobian`: the method to use for initializing the jacobian. Defaults to using the
23
- identity matrix (`Val(:identitiy)`). Alternatively, can be set to `Val(:true_jacobian)`
24
- to use the true jacobian as initialization. (Our tests suggest it is a good idea to
25
- to initialize with an identity matrix)
24
+ - `alpha`: If `init_jacobian` is set to `Val(:identity)`, then the initial Jacobian
25
+ inverse is set to be `αI`. Defaults to `1`. Can be set to `nothing` which implies
26
+ `α = max(norm(u), 1) / (2 * norm(fu))`.
27
+ - `init_jacobian`: the method to use for initializing the jacobian. Defaults to
28
+ `Val(:identity)`. Choices include:
29
+
30
+ + `Val(:identity)`: Identity Matrix.
31
+ + `Val(:true_jacobian)`: True Jacobian. Our tests suggest that this is not very
32
+ stable. Instead using `Broyden` with `Val(:true_jacobian)` gives faster and more
33
+ reliable convergence.
34
+ + `Val(:true_jacobian_diagonal)`: Diagonal of True Jacobian. This is a good choice for
35
+ differentiable problems.
26
36
- `autodiff`: determines the backend used for the Jacobian. Note that this argument is
27
37
ignored if an analytical Jacobian is passed, as that will be used instead. Defaults to
28
38
`nothing` which means that a default is selected according to the problem specification!
@@ -34,32 +44,30 @@ solves.
34
44
linsolve
35
45
precs
36
46
linesearch
47
+ alpha
37
48
end
38
49
39
- function __alg_print_modifiers (:: GeneralKlement{IJ} ) where {IJ}
50
+ function __alg_print_modifiers (alg :: GeneralKlement{IJ} ) where {IJ}
40
51
modifiers = String[]
41
52
IJ != = :identity && push! (modifiers, " init_jacobian = :$(IJ) " )
53
+ alg. alpha != = nothing && push! (modifiers, " alpha = $(alg. alpha) " )
42
54
return modifiers
43
55
end
44
56
45
- function set_linsolve (alg:: GeneralKlement{IJ, CS} , linsolve) where {IJ, CS}
46
- return GeneralKlement {IJ, CS} (alg. ad, alg. max_resets, linsolve, alg. precs,
47
- alg. linesearch)
48
- end
49
-
50
- function set_ad (alg:: GeneralKlement{IJ, CS} , ad) where {IJ, CS}
51
- return GeneralKlement {IJ, CS} (ad, alg. max_resets, alg. linsolve, alg. precs,
52
- alg. linesearch)
57
+ function set_ad (alg:: GeneralKlement{IJ, CJ} , ad) where {IJ, CJ}
58
+ return GeneralKlement {IJ, CJ} (ad, alg. max_resets, alg. linsolve, alg. precs,
59
+ alg. linesearch, alg. alpha)
53
60
end
54
61
55
- function GeneralKlement (; max_resets:: Int = 5 , linsolve = nothing ,
62
+ function GeneralKlement (; max_resets:: Int = 100 , linsolve = nothing , alpha = true ,
56
63
linesearch = nothing , precs = DEFAULT_PRECS, init_jacobian:: Val = Val (:identity ),
57
64
autodiff = nothing )
58
65
IJ = _unwrap_val (init_jacobian)
59
- @assert IJ ∈ (:identity , :true_jacobian )
66
+ @assert IJ ∈ (:identity , :true_jacobian , :true_jacobian_diagonal )
60
67
linesearch = linesearch isa LineSearch ? linesearch : LineSearch (; method = linesearch)
61
- CJ = IJ === :true_jacobian
62
- return GeneralKlement {IJ, CJ} (autodiff, max_resets, linsolve, precs, linesearch)
68
+ CJ = IJ != = :identity
69
+ return GeneralKlement {IJ, CJ} (autodiff, max_resets, linsolve, precs, linesearch,
70
+ alpha)
63
71
end
64
72
65
73
@concrete mutable struct GeneralKlementCache{iip, IJ} <: AbstractNonlinearSolveCache{iip}
79
87
J_cache_2
80
88
Jdu
81
89
Jdu_cache
90
+ alpha
91
+ alpha_initial
82
92
resets
83
93
force_stop
84
94
maxiters:: Int
@@ -102,15 +112,23 @@ function SciMLBase.__init(prob::NonlinearProblem{uType, iip}, alg_::GeneralKleme
102
112
u = __maybe_unaliased (u0, alias_u0)
103
113
fu = evaluate_f (prob, u)
104
114
115
+ alpha = __initial_alpha (alg_. alpha, u, fu, internalnorm)
116
+
105
117
if IJ === :true_jacobian
106
118
alg = get_concrete_algorithm (alg_, prob)
107
119
uf, _, J, fu_cache, jac_cache, du = jacobian_caches (alg, f, u, p, Val (iip);
108
120
lininit = Val (false ))
121
+ elseif IJ === :true_jacobian_diagonal
122
+ alg = get_concrete_algorithm (alg_, prob)
123
+ uf, _, J_cache, fu_cache, jac_cache, du = jacobian_caches (alg, f, u, p, Val (iip);
124
+ lininit = Val (false ))
125
+ J = __diag (J_cache)
109
126
elseif IJ === :identity
110
127
alg = alg_
111
128
@bb du = similar (u)
112
129
uf, fu_cache, jac_cache = nothing , nothing , nothing
113
130
J = one .(u) # Identity Init Jacobian for Klement maintains a Diagonal Structure
131
+ @bb J .*= alpha
114
132
else
115
133
error (" Invalid `init_jacobian` value" )
116
134
end
@@ -133,13 +151,14 @@ function SciMLBase.__init(prob::NonlinearProblem{uType, iip}, alg_::GeneralKleme
133
151
@bb J_cache_2 = similar (J)
134
152
@bb Jdu_cache = similar (fu)
135
153
else
136
- J_cache, J_cache_2, Jdu_cache = nothing , nothing , nothing
154
+ IJ === :identity && (J_cache = nothing )
155
+ J_cache_2, Jdu_cache = nothing , nothing
137
156
end
138
157
139
158
return GeneralKlementCache {iip, IJ} (f, alg, u, u_cache, fu, fu_cache, fu_cache_2, du, p,
140
- uf, linsolve, J, J_cache, J_cache_2, Jdu, Jdu_cache, 0 , false , maxiters ,
141
- internalnorm,
142
- ReturnCode . Default, abstol, reltol, prob, jac_cache, NLStats (1 , 0 , 0 , 0 , 0 ),
159
+ uf, linsolve, J, J_cache, J_cache_2, Jdu, Jdu_cache, alpha, alg . alpha, 0 , false ,
160
+ maxiters, internalnorm, ReturnCode . Default, abstol, reltol, prob, jac_cache ,
161
+ NLStats (1 , 0 , 0 , 0 , 0 ),
143
162
init_linesearch_cache (alg. linesearch, f, u, p, fu, Val (iip)), tc_cache, trace)
144
163
end
145
164
@@ -150,6 +169,12 @@ function perform_step!(cache::GeneralKlementCache{iip, IJ}) where {iip, IJ}
150
169
if IJ === :true_jacobian
151
170
cache. stats. nsteps == 0 && (cache. J = jacobian!! (cache. J, cache))
152
171
ill_conditioned = __is_ill_conditioned (cache. J)
172
+ elseif IJ === :true_jacobian_diagonal
173
+ if cache. stats. nsteps == 0
174
+ cache. J_cache = jacobian!! (cache. J_cache, cache)
175
+ cache. J = __get_diagonal!! (cache. J, cache. J_cache)
176
+ end
177
+ ill_conditioned = __is_ill_conditioned (cache. J)
153
178
elseif IJ === :identity
154
179
ill_conditioned = __is_ill_conditioned (cache. J)
155
180
end
@@ -162,13 +187,18 @@ function perform_step!(cache::GeneralKlementCache{iip, IJ}) where {iip, IJ}
162
187
end
163
188
if IJ === :true_jacobian && cache. stats. nsteps != 0
164
189
cache. J = jacobian!! (cache. J, cache)
165
- else
166
- cache. J = __reinit_identity_jacobian!! (cache. J)
190
+ elseif IJ === :true_jacobian_diagonal && cache. stats. nsteps != 0
191
+ cache. J_cache = jacobian!! (cache. J_cache, cache)
192
+ cache. J = __get_diagonal!! (cache. J, cache. J_cache)
193
+ elseif IJ === :identity
194
+ cache. alpha = __initial_alpha (cache. alpha, cache. alpha_initial, cache. u,
195
+ cache. fu, cache. internalnorm)
196
+ cache. J = __reinit_identity_jacobian!! (cache. J, cache. alpha)
167
197
end
168
198
cache. resets += 1
169
199
end
170
200
171
- if IJ === :identity
201
+ if cache . J isa AbstractVector || cache . J isa Number
172
202
@bb @. cache. du = cache. fu / cache. J
173
203
else
174
204
# u = u - J \ fu
@@ -193,13 +223,15 @@ function perform_step!(cache::GeneralKlementCache{iip, IJ}) where {iip, IJ}
193
223
194
224
# Update the Jacobian
195
225
@bb cache. du .*= - 1
196
- if IJ === :identity
226
+ if cache . J isa AbstractVector || cache . J isa Number
197
227
@bb @. cache. Jdu = (cache. J^ 2 ) * (cache. du^ 2 )
198
228
@bb @. cache. J += ((cache. fu - cache. fu_cache_2 - cache. J * cache. du) /
199
229
ifelse (iszero (cache. Jdu), T (1e-5 ), cache. Jdu)) * cache. du *
200
230
(cache. J^ 2 )
201
231
else
202
- @bb cache. J_cache .= cache. J' .^ 2
232
+ # Klement Updates to the Full Jacobian don't work for most problems, we should
233
+ # probably be using the Broyden Update Rule here
234
+ @bb @. cache. J_cache = cache. J' ^ 2
203
235
@bb @. cache. Jdu = cache. du^ 2
204
236
@bb cache. Jdu_cache = cache. J_cache × vec (cache. Jdu)
205
237
@bb cache. Jdu = cache. J × vec (cache. du)
@@ -217,7 +249,9 @@ function perform_step!(cache::GeneralKlementCache{iip, IJ}) where {iip, IJ}
217
249
end
218
250
219
251
function __reinit_internal! (cache:: GeneralKlementCache ; kwargs... )
220
- cache. J = __reinit_identity_jacobian!! (cache. J)
252
+ cache. alpha = __initial_alpha (cache. alpha, cache. alpha_initial, cache. u, cache. fu,
253
+ cache. internalnorm)
254
+ cache. J = __reinit_identity_jacobian!! (cache. J, cache. alpha)
221
255
cache. resets = 0
222
256
return nothing
223
257
end
0 commit comments