11"""
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)
45
56An 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.
78
89## Keyword Arguments
910
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+
1113 - `linsolve`: the [LinearSolve.jl](https://github.com/SciML/LinearSolve.jl) used for the
1214 linear solves within the Newton method. Defaults to `nothing`, which means it uses the
1315 LinearSolve.jl default algorithm choice. For more information on available algorithm
@@ -19,10 +21,18 @@ solves.
1921 - `linesearch`: the line search algorithm to use. Defaults to [`LineSearch()`](@ref),
2022 which means that no line search is performed. Algorithms from `LineSearches.jl` can be
2123 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.
2636 - `autodiff`: determines the backend used for the Jacobian. Note that this argument is
2737 ignored if an analytical Jacobian is passed, as that will be used instead. Defaults to
2838 `nothing` which means that a default is selected according to the problem specification!
@@ -34,32 +44,30 @@ solves.
3444 linsolve
3545 precs
3646 linesearch
47+ alpha
3748end
3849
39- function __alg_print_modifiers (:: GeneralKlement{IJ} ) where {IJ}
50+ function __alg_print_modifiers (alg :: GeneralKlement{IJ} ) where {IJ}
4051 modifiers = String[]
4152 IJ != = :identity && push! (modifiers, " init_jacobian = :$(IJ) " )
53+ alg. alpha != = nothing && push! (modifiers, " alpha = $(alg. alpha) " )
4254 return modifiers
4355end
4456
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)
5360end
5461
55- function GeneralKlement (; max_resets:: Int = 5 , linsolve = nothing ,
62+ function GeneralKlement (; max_resets:: Int = 100 , linsolve = nothing , alpha = true ,
5663 linesearch = nothing , precs = DEFAULT_PRECS, init_jacobian:: Val = Val (:identity ),
5764 autodiff = nothing )
5865 IJ = _unwrap_val (init_jacobian)
59- @assert IJ ∈ (:identity , :true_jacobian )
66+ @assert IJ ∈ (:identity , :true_jacobian , :true_jacobian_diagonal )
6067 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)
6371end
6472
6573@concrete mutable struct GeneralKlementCache{iip, IJ} <: AbstractNonlinearSolveCache{iip}
7987 J_cache_2
8088 Jdu
8189 Jdu_cache
90+ alpha
91+ alpha_initial
8292 resets
8393 force_stop
8494 maxiters:: Int
@@ -102,15 +112,23 @@ function SciMLBase.__init(prob::NonlinearProblem{uType, iip}, alg_::GeneralKleme
102112 u = __maybe_unaliased (u0, alias_u0)
103113 fu = evaluate_f (prob, u)
104114
115+ alpha = __initial_alpha (alg_. alpha, u, fu, internalnorm)
116+
105117 if IJ === :true_jacobian
106118 alg = get_concrete_algorithm (alg_, prob)
107119 uf, _, J, fu_cache, jac_cache, du = jacobian_caches (alg, f, u, p, Val (iip);
108120 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)
109126 elseif IJ === :identity
110127 alg = alg_
111128 @bb du = similar (u)
112129 uf, fu_cache, jac_cache = nothing , nothing , nothing
113130 J = one .(u) # Identity Init Jacobian for Klement maintains a Diagonal Structure
131+ @bb J .*= alpha
114132 else
115133 error (" Invalid `init_jacobian` value" )
116134 end
@@ -133,13 +151,14 @@ function SciMLBase.__init(prob::NonlinearProblem{uType, iip}, alg_::GeneralKleme
133151 @bb J_cache_2 = similar (J)
134152 @bb Jdu_cache = similar (fu)
135153 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
137156 end
138157
139158 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 ),
143162 init_linesearch_cache (alg. linesearch, f, u, p, fu, Val (iip)), tc_cache, trace)
144163end
145164
@@ -150,6 +169,12 @@ function perform_step!(cache::GeneralKlementCache{iip, IJ}) where {iip, IJ}
150169 if IJ === :true_jacobian
151170 cache. stats. nsteps == 0 && (cache. J = jacobian!! (cache. J, cache))
152171 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)
153178 elseif IJ === :identity
154179 ill_conditioned = __is_ill_conditioned (cache. J)
155180 end
@@ -162,13 +187,18 @@ function perform_step!(cache::GeneralKlementCache{iip, IJ}) where {iip, IJ}
162187 end
163188 if IJ === :true_jacobian && cache. stats. nsteps != 0
164189 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)
167197 end
168198 cache. resets += 1
169199 end
170200
171- if IJ === :identity
201+ if cache . J isa AbstractVector || cache . J isa Number
172202 @bb @. cache. du = cache. fu / cache. J
173203 else
174204 # u = u - J \ fu
@@ -193,13 +223,15 @@ function perform_step!(cache::GeneralKlementCache{iip, IJ}) where {iip, IJ}
193223
194224 # Update the Jacobian
195225 @bb cache. du .*= - 1
196- if IJ === :identity
226+ if cache . J isa AbstractVector || cache . J isa Number
197227 @bb @. cache. Jdu = (cache. J^ 2 ) * (cache. du^ 2 )
198228 @bb @. cache. J += ((cache. fu - cache. fu_cache_2 - cache. J * cache. du) /
199229 ifelse (iszero (cache. Jdu), T (1e-5 ), cache. Jdu)) * cache. du *
200230 (cache. J^ 2 )
201231 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
203235 @bb @. cache. Jdu = cache. du^ 2
204236 @bb cache. Jdu_cache = cache. J_cache × vec (cache. Jdu)
205237 @bb cache. Jdu = cache. J × vec (cache. du)
@@ -217,7 +249,9 @@ function perform_step!(cache::GeneralKlementCache{iip, IJ}) where {iip, IJ}
217249end
218250
219251function __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)
221255 cache. resets = 0
222256 return nothing
223257end
0 commit comments