Skip to content

Commit c2d0077

Browse files
Merge pull request #279 from avik-pal/ap/better_default
Use Jacobian Based Algorithms if user supplies custom Jacobian
2 parents c6c875f + 9782fd7 commit c2d0077

File tree

7 files changed

+52
-23
lines changed

7 files changed

+52
-23
lines changed

Project.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
name = "NonlinearSolve"
22
uuid = "8913a72c-1f9b-4ce2-8d82-65094dcecaec"
33
authors = ["SciML"]
4-
version = "2.8.0"
4+
version = "2.8.1"
55

66
[deps]
77
ADTypes = "47edcb42-4c32-4615-8424-f2b9edc5f35b"

src/broyden.jl

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ An implementation of `Broyden` with reseting and line search.
88
99
- `max_resets`: the maximum number of resets to perform. Defaults to `3`.
1010
- `reset_tolerance`: the tolerance for the reset check. Defaults to
11-
`sqrt(eps(eltype(u)))`.
11+
`sqrt(eps(real(eltype(u))))`.
1212
- `linesearch`: the line search algorithm to use. Defaults to [`LineSearch()`](@ref),
1313
which means that no line search is performed. Algorithms from `LineSearches.jl` can be
1414
used here directly, and they will be converted to the correct `LineSearch`. It is
@@ -67,7 +67,7 @@ function SciMLBase.__init(prob::NonlinearProblem{uType, iip}, alg::GeneralBroyde
6767
u = alias_u0 ? u0 : deepcopy(u0)
6868
fu = evaluate_f(prob, u)
6969
J⁻¹ = __init_identity_jacobian(u, fu)
70-
reset_tolerance = alg.reset_tolerance === nothing ? sqrt(eps(eltype(u))) :
70+
reset_tolerance = alg.reset_tolerance === nothing ? sqrt(eps(real(eltype(u)))) :
7171
alg.reset_tolerance
7272
reset_check = x -> abs(x) reset_tolerance
7373

src/default.jl

Lines changed: 33 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -166,7 +166,7 @@ end
166166

167167
"""
168168
RobustMultiNewton(; concrete_jac = nothing, linsolve = nothing, precs = DEFAULT_PRECS,
169-
adkwargs...)
169+
adkwargs...)
170170
171171
A polyalgorithm focused on robustness. It uses a mixture of Newton methods with different
172172
globalizing techniques (trust region updates, line searches, etc.) in order to find a
@@ -212,7 +212,7 @@ end
212212

213213
"""
214214
FastShortcutNonlinearPolyalg(; concrete_jac = nothing, linsolve = nothing,
215-
precs = DEFAULT_PRECS, adkwargs...)
215+
precs = DEFAULT_PRECS, must_use_jacobian::Val = Val(false), adkwargs...)
216216
217217
A polyalgorithm focused on balancing speed and robustness. It first tries less robust methods
218218
for more performance and then tries more robust techniques if the faster ones fail.
@@ -238,15 +238,25 @@ for more performance and then tries more robust techniques if the faster ones fa
238238
[LinearSolve.jl documentation](https://docs.sciml.ai/LinearSolve/stable/).
239239
"""
240240
function FastShortcutNonlinearPolyalg(; concrete_jac = nothing, linsolve = nothing,
241-
precs = DEFAULT_PRECS, adkwargs...)
242-
algs = (GeneralKlement(; linsolve, precs),
243-
GeneralBroyden(),
244-
NewtonRaphson(; concrete_jac, linsolve, precs, adkwargs...),
245-
NewtonRaphson(; concrete_jac, linsolve, precs, linesearch = BackTracking(),
246-
adkwargs...),
247-
TrustRegion(; concrete_jac, linsolve, precs, adkwargs...),
248-
TrustRegion(; concrete_jac, linsolve, precs,
249-
radius_update_scheme = RadiusUpdateSchemes.Bastin, adkwargs...))
241+
precs = DEFAULT_PRECS, must_use_jacobian::Val{JAC} = Val(false),
242+
adkwargs...) where {JAC}
243+
if JAC
244+
algs = (NewtonRaphson(; concrete_jac, linsolve, precs, adkwargs...),
245+
NewtonRaphson(; concrete_jac, linsolve, precs, linesearch = BackTracking(),
246+
adkwargs...),
247+
TrustRegion(; concrete_jac, linsolve, precs, adkwargs...),
248+
TrustRegion(; concrete_jac, linsolve, precs,
249+
radius_update_scheme = RadiusUpdateSchemes.Bastin, adkwargs...))
250+
else
251+
algs = (GeneralKlement(; linsolve, precs),
252+
GeneralBroyden(),
253+
NewtonRaphson(; concrete_jac, linsolve, precs, adkwargs...),
254+
NewtonRaphson(; concrete_jac, linsolve, precs, linesearch = BackTracking(),
255+
adkwargs...),
256+
TrustRegion(; concrete_jac, linsolve, precs, adkwargs...),
257+
TrustRegion(; concrete_jac, linsolve, precs,
258+
radius_update_scheme = RadiusUpdateSchemes.Bastin, adkwargs...))
259+
end
250260
return NonlinearSolvePolyAlgorithm(algs, Val(:NLS))
251261
end
252262

@@ -288,12 +298,22 @@ end
288298

289299
## Defaults
290300

301+
## TODO: In the long run we want to use an `Assumptions` API like LinearSolve to specify
302+
## the conditioning of the Jacobian and such
303+
## Defaults to a fast and robust poly algorithm in most cases. If the user went through
304+
## the trouble of specifying a custom jacobian function, we should use algorithms that
305+
## can use that!
306+
291307
function SciMLBase.__init(prob::NonlinearProblem, ::Nothing, args...; kwargs...)
292-
return SciMLBase.__init(prob, FastShortcutNonlinearPolyalg(), args...; kwargs...)
308+
must_use_jacobian = Val(prob.f.jac !== nothing)
309+
return SciMLBase.__init(prob, FastShortcutNonlinearPolyalg(; must_use_jacobian),
310+
args...; kwargs...)
293311
end
294312

295313
function SciMLBase.__solve(prob::NonlinearProblem, ::Nothing, args...; kwargs...)
296-
return SciMLBase.__solve(prob, FastShortcutNonlinearPolyalg(), args...; kwargs...)
314+
must_use_jacobian = Val(prob.f.jac !== nothing)
315+
return SciMLBase.__solve(prob, FastShortcutNonlinearPolyalg(; must_use_jacobian),
316+
args...; kwargs...)
297317
end
298318

299319
function SciMLBase.__init(prob::NonlinearLeastSquaresProblem, ::Nothing, args...; kwargs...)

src/klement.jl

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -142,7 +142,7 @@ function perform_step!(cache::GeneralKlementCache{true})
142142
mul!(cache.Jdu, J, _vec(du))
143143
cache.fu .= cache.fu2 .- cache.fu
144144
cache.fu .= _restructure(cache.fu,
145-
(_vec(cache.fu) .- cache.Jdu) ./ max.(cache.Jᵀ²du, eps(T)))
145+
(_vec(cache.fu) .- cache.Jdu) ./ max.(cache.Jᵀ²du, eps(real(T))))
146146
mul!(cache.J_cache, _vec(cache.fu), _vec(du)')
147147
cache.J_cache .*= J
148148
mul!(cache.J_cache2, cache.J_cache, J)
@@ -202,7 +202,7 @@ function perform_step!(cache::GeneralKlementCache{false})
202202
cache.Jdu = J * _vec(cache.du)
203203
cache.fu = cache.fu2 .- cache.fu
204204
cache.fu = _restructure(cache.fu,
205-
(_vec(cache.fu) .- cache.Jdu) ./ max.(cache.Jᵀ²du, eps(T)))
205+
(_vec(cache.fu) .- cache.Jdu) ./ max.(cache.Jᵀ²du, eps(real(T))))
206206
cache.J_cache = ((_vec(cache.fu) * _vec(cache.du)') .* J) * J
207207
cache.J = J .+ cache.J_cache
208208

src/lbroyden.jl

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ An implementation of `LimitedMemoryBroyden` with reseting and line search.
88
99
- `max_resets`: the maximum number of resets to perform. Defaults to `3`.
1010
- `reset_tolerance`: the tolerance for the reset check. Defaults to
11-
`sqrt(eps(eltype(u)))`.
11+
`sqrt(eps(real(eltype(u))))`.
1212
- `threshold`: the number of vectors to store in the low rank approximation. Defaults
1313
to `10`.
1414
- `linesearch`: the line search algorithm to use. Defaults to [`LineSearch()`](@ref),
@@ -82,7 +82,7 @@ function SciMLBase.__init(prob::NonlinearProblem{uType, iip}, alg::LimitedMemory
8282
threshold = min(alg.threshold, maxiters)
8383
U, Vᵀ = __init_low_rank_jacobian(u, fu, threshold)
8484
du = copy(fu)
85-
reset_tolerance = alg.reset_tolerance === nothing ? sqrt(eps(eltype(u))) :
85+
reset_tolerance = alg.reset_tolerance === nothing ? sqrt(eps(real(eltype(u)))) :
8686
alg.reset_tolerance
8787
reset_check = x -> abs(x) reset_tolerance
8888

src/utils.jl

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -302,7 +302,7 @@ _issingular(x::Number) = iszero(x)
302302
hasmethod(issingular, Tuple{T}) && return :(issingular(x))
303303
return :(__issingular(x))
304304
end
305-
__issingular(x::AbstractMatrix{T}) where {T} = cond(x) > inv(sqrt(eps(T)))
305+
__issingular(x::AbstractMatrix{T}) where {T} = cond(x) > inv(sqrt(eps(real(T))))
306306
__issingular(x) = false ## If SciMLOperator and such
307307

308308
# If factorization is LU then perform that and update the linsolve cache

test/23_test_problems.jl

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,8 @@ using NonlinearSolve, LinearAlgebra, LinearSolve, NonlinearProblemLibrary, Test
33
problems = NonlinearProblemLibrary.problems
44
dicts = NonlinearProblemLibrary.dicts
55

6-
function test_on_library(problems, dicts, alg_ops, broken_tests, ϵ = 1e-4)
6+
function test_on_library(problems, dicts, alg_ops, broken_tests, ϵ = 1e-4;
7+
skip_tests = nothing)
78
for (idx, (problem, dict)) in enumerate(zip(problems, dicts))
89
x = dict["start"]
910
res = similar(x)
@@ -15,6 +16,11 @@ function test_on_library(problems, dicts, alg_ops, broken_tests, ϵ = 1e-4)
1516
termination_condition = AbsNormTerminationMode())
1617
problem(res, sol.u, nothing)
1718

19+
skip = skip_tests !== nothing && idx in skip_tests[alg]
20+
if skip
21+
@test_skip norm(res) ϵ
22+
continue
23+
end
1824
broken = idx in broken_tests[alg] ? true : false
1925
@test norm(res)ϵ broken=broken
2026
catch
@@ -90,7 +96,10 @@ end
9096
broken_tests = Dict(alg => Int[] for alg in alg_ops)
9197
broken_tests[alg_ops[1]] = [1, 2, 4, 5, 6, 11, 12, 13, 14]
9298

93-
test_on_library(problems, dicts, alg_ops, broken_tests)
99+
skip_tests = Dict(alg => Int[] for alg in alg_ops)
100+
skip_tests[alg_ops[1]] = [22]
101+
102+
test_on_library(problems, dicts, alg_ops, broken_tests; skip_tests)
94103
end
95104

96105
@testset "GeneralKlement 23 Test Problems" begin

0 commit comments

Comments
 (0)