Skip to content
This repository was archived by the owner on May 15, 2025. It is now read-only.

Commit 667c4b2

Browse files
Merge pull request #105 from avik-pal/ap/minor_fixes
Add compat entries and change default norm
2 parents 8d9a5d3 + b4fe845 commit 667c4b2

File tree

12 files changed

+76
-67
lines changed

12 files changed

+76
-67
lines changed

Project.toml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,11 +18,14 @@ SciMLBase = "0bca4576-84f4-4d90-8ffe-ffa030f20462"
1818
StaticArraysCore = "1e83bf80-4336-4d27-bf5d-d5a4f845583c"
1919

2020
[compat]
21+
ADTypes = "0.2"
2122
ArrayInterface = "7"
23+
ConcreteStructs = "0.2"
2224
DiffEqBase = "6.126"
2325
FiniteDiff = "2"
2426
ForwardDiff = "0.10.3"
2527
LinearAlgebra = "1.9"
28+
MaybeInplace = "0.1"
2629
PrecompileTools = "1"
2730
Reexport = "1"
2831
SciMLBase = "2.7"

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ sol = solve(probB, ITP())
4040

4141
For more details on the bracketing methods, refer to the [Tutorials](https://docs.sciml.ai/NonlinearSolve/stable/tutorials/nonlinear/#Using-Bracketing-Methods) and detailed [APIs](https://docs.sciml.ai/NonlinearSolve/stable/api/simplenonlinearsolve/#Solver-API)
4242

43-
## Breaking Changes in v2
43+
## Breaking Changes in v1.0.0
4444

4545
- Batched solvers have been removed in favor of `BatchedArrays.jl`. Stay tuned for detailed
4646
tutorials on how to use `BatchedArrays.jl` with `NonlinearSolve` & `SimpleNonlinearSolve`

src/nlsolve/broyden.jl

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,9 +7,9 @@ and static array problems.
77
struct SimpleBroyden <: AbstractSimpleNonlinearSolveAlgorithm end
88

99
function SciMLBase.__solve(prob::NonlinearProblem, alg::SimpleBroyden, args...;
10-
abstol = nothing, reltol = nothing, maxiters = 1000,
10+
abstol = nothing, reltol = nothing, maxiters = 1000, alias_u0 = false,
1111
termination_condition = nothing, kwargs...)
12-
@bb x = copy(float(prob.u0))
12+
x = __maybe_unaliased(prob.u0, alias_u0)
1313
fx = _get_fx(prob, x)
1414

1515
@bb xo = copy(x)

src/nlsolve/dfsane.jl

Lines changed: 18 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -51,9 +51,9 @@ Computation, 75, 1429-1448.](https://www.researchgate.net/publication/220576479_
5151
end
5252

5353
function SciMLBase.__solve(prob::NonlinearProblem, alg::SimpleDFSane, args...;
54-
abstol = nothing, reltol = nothing, maxiters = 1000,
54+
abstol = nothing, reltol = nothing, maxiters = 1000, alias_u0 = false,
5555
termination_condition = nothing, kwargs...)
56-
x = float(copy(prob.u0))
56+
x = __maybe_unaliased(prob.u0, alias_u0)
5757
fx = _get_fx(prob, x)
5858
T = eltype(x)
5959

@@ -76,11 +76,10 @@ function SciMLBase.__solve(prob::NonlinearProblem, alg::SimpleDFSane, args...;
7676
history_f_k = fill(fx_norm, M)
7777

7878
# Generate the cache
79+
@bb x_cache = similar(x)
7980
@bb d = copy(x)
8081
@bb xo = copy(x)
81-
@bb x_cache = copy(x)
8282
@bb δx = copy(x)
83-
@bb fxo = copy(fx)
8483
@bb δf = copy(fx)
8584

8685
k = 0
@@ -91,50 +90,52 @@ function SciMLBase.__solve(prob::NonlinearProblem, alg::SimpleDFSane, args...;
9190
# Line search direction
9291
@bb @. d = -σ_k * fx
9392

94-
η = η_strategy(f_1, k, x, fx)
93+
η = η_strategy(f_1, k + 1, x, fx)
9594
f_bar = maximum(history_f_k)
9695
α_p = α_1
9796
α_m = α_1
9897

99-
@bb @. x += α_p * d
98+
@bb @. x_cache = x + α_p * d
10099

101-
fx = __eval_f(prob, fx, x)
100+
fx = __eval_f(prob, fx, x_cache)
102101
fx_norm_new = NONLINEARSOLVE_DEFAULT_NORM(fx)^nexp
103102

104103
while k < maxiters
105-
fx_norm_new (f_bar + η - γ * α_p^2 * fx_norm) && break
104+
Bool(fx_norm_new (f_bar + η - γ * α_p^2 * fx_norm)) && break
106105

107-
α_p = α_p^2 * fx_norm / (fx_norm_new + (T(2) * α_p - T(1)) * fx_norm)
108-
@bb @. x -= α_m * d
106+
α_tp = α_p^2 * fx_norm / (fx_norm_new + (T(2) * α_p - T(1)) * fx_norm)
107+
@bb @. x_cache = x - α_m * d
109108

110-
fx = __eval_f(prob, fx, x)
109+
fx = __eval_f(prob, fx, x_cache)
111110
fx_norm_new = NONLINEARSOLVE_DEFAULT_NORM(fx)^nexp
112111

113-
fx_norm_new (f_bar + η - γ * α_m^2 * fx_norm) && break
112+
Bool(fx_norm_new (f_bar + η - γ * α_m^2 * fx_norm)) && break
114113

115114
α_tm = α_m^2 * fx_norm / (fx_norm_new + (T(2) * α_m - T(1)) * fx_norm)
116-
α_p = clamp(α_p, τ_min * α_p, τ_max * α_p)
115+
α_p = clamp(α_tp, τ_min * α_p, τ_max * α_p)
117116
α_m = clamp(α_tm, τ_min * α_m, τ_max * α_m)
118-
@bb @. x += α_p * d
117+
@bb @. x_cache = x + α_p * d
119118

120-
fx = __eval_f(prob, fx, x)
119+
fx = __eval_f(prob, fx, x_cache)
121120
fx_norm_new = NONLINEARSOLVE_DEFAULT_NORM(fx)^nexp
122121

123122
k += 1
124123
end
125124

125+
@bb copyto!(x, x_cache)
126+
126127
tc_sol = check_termination(tc_cache, fx, x, xo, prob, alg)
127128
tc_sol !== nothing && return tc_sol
128129

129130
# Update spectral parameter
130131
@bb @. δx = x - xo
131-
@bb @. δf = fx - fxo
132+
@bb @. δf = fx - δf
132133

133134
σ_k = dot(δx, δx) / dot(δx, δf)
134135

135136
# Take step
136137
@bb copyto!(xo, x)
137-
@bb copyto!(fxo, fx)
138+
@bb copyto!(δf, fx)
138139
fx_norm = fx_norm_new
139140

140141
# Store function value

src/nlsolve/halley.jl

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,18 +14,22 @@ A low-overhead implementation of Halley's Method.
1414
1515
- `autodiff`: determines the backend used for the Hessian. Defaults to
1616
`AutoForwardDiff()`. Valid choices are `AutoForwardDiff()` or `AutoFiniteDiff()`.
17+
18+
!!! warning
19+
20+
Inplace Problems are currently not supported by this method.
1721
"""
1822
@kwdef @concrete struct SimpleHalley <: AbstractNewtonAlgorithm
1923
autodiff = AutoForwardDiff()
2024
end
2125

2226
function SciMLBase.__solve(prob::NonlinearProblem, alg::SimpleHalley, args...;
23-
abstol = nothing, reltol = nothing, maxiters = 1000,
27+
abstol = nothing, reltol = nothing, maxiters = 1000, alias_u0 = false,
2428
termination_condition = nothing, kwargs...)
2529
isinplace(prob) &&
2630
error("SimpleHalley currently only supports out-of-place nonlinear problems")
2731

28-
x = copy(float(prob.u0))
32+
x = __maybe_unaliased(prob.u0, alias_u0)
2933
fx = _get_fx(prob, x)
3034
T = eltype(x)
3135

src/nlsolve/klement.jl

Lines changed: 15 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -7,9 +7,9 @@ method is non-allocating on scalar and static array problems.
77
struct SimpleKlement <: AbstractSimpleNonlinearSolveAlgorithm end
88

99
function SciMLBase.__solve(prob::NonlinearProblem, alg::SimpleKlement, args...;
10-
abstol = nothing, reltol = nothing, maxiters = 1000,
10+
abstol = nothing, reltol = nothing, maxiters = 1000, alias_u0 = false,
1111
termination_condition = nothing, kwargs...)
12-
x = float(prob.u0)
12+
x = __maybe_unaliased(prob.u0, alias_u0)
1313
T = eltype(x)
1414
fx = _get_fx(prob, x)
1515

@@ -21,13 +21,12 @@ function SciMLBase.__solve(prob::NonlinearProblem, alg::SimpleKlement, args...;
2121
@bb δx = copy(x)
2222
@bb fprev = copy(fx)
2323
@bb xo = copy(x)
24-
@bb δf = copy(fx)
2524
@bb d = copy(x)
2625

2726
J = __init_identity_jacobian(fx, x)
28-
@bb J_cache = copy(J)
29-
@bb δx² = copy(x)
30-
@bb J_cache2 = copy(J)
27+
@bb J_cache = similar(J)
28+
@bb δx² = similar(x)
29+
@bb J_cache2 = similar(J)
3130
@bb F = copy(J)
3231

3332
for _ in 1:maxiters
@@ -45,10 +44,11 @@ function SciMLBase.__solve(prob::NonlinearProblem, alg::SimpleKlement, args...;
4544
# Singularity test
4645
if !issuccess(F_)
4746
J = __init_identity_jacobian!!(J)
47+
@bb copyto!(F, J)
4848
if setindex_trait(J) === CanSetindex()
49-
lu!(J; check = false)
49+
F_ = lu!(F; check = false)
5050
else
51-
J = lu(J; check = false)
51+
F_ = lu(F; check = false)
5252
end
5353
end
5454
end
@@ -67,23 +67,18 @@ function SciMLBase.__solve(prob::NonlinearProblem, alg::SimpleKlement, args...;
6767
tc_sol !== nothing && return tc_sol
6868

6969
@bb δx .*= -1
70-
@bb @. δf = fx - fprev
71-
72-
# Prevent division by 0
70+
@bb J_cache .= transpose(J) .^ 2
7371
@bb @. δx² = δx^2
74-
@bb @. J_cache = J^2
75-
@bb d = transpose(J_cache) × vec(δx²)
76-
@bb @. d = max(d, singular_tol)
77-
72+
@bb d = J_cache × vec(δx²)
7873
@bb δx² = J × vec(δx)
79-
@bb @. δf = (δf - δx²) / d
80-
81-
_vδf, _vδx = _vec(δf), _vec(δx)
82-
@bb J_cache = _vδf × transpose(_vδx)
74+
@bb @. fprev = (fx - fprev - δx²) / ifelse(iszero(d), singular_tol, d)
75+
@bb J_cache = vec(fprev) × transpose(_vec(δx))
8376
@bb @. J_cache *= J
8477
@bb J_cache2 = J_cache × J
85-
8678
@bb @. J += J_cache2
79+
80+
@bb copyto!(fprev, fx)
81+
@bb copyto!(xo, x)
8782
end
8883

8984
return build_solution(prob, alg, x, fx; retcode = ReturnCode.MaxIters)

src/nlsolve/lbroyden.jl

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,9 +22,9 @@ function SimpleLimitedMemoryBroyden(; threshold::Union{Val, Int} = Val(27))
2222
end
2323

2424
@views function SciMLBase.__solve(prob::NonlinearProblem, alg::SimpleLimitedMemoryBroyden,
25-
args...; abstol = nothing, reltol = nothing, maxiters = 1000,
25+
args...; abstol = nothing, reltol = nothing, maxiters = 1000, alias_u0 = false,
2626
termination_condition = nothing, kwargs...)
27-
@bb x = copy(float(prob.u0))
27+
x = __maybe_unaliased(prob.u0, alias_u0)
2828
threshold = __get_threshold(alg)
2929
η = min(SciMLBase._unwrap_val(threshold), maxiters)
3030

src/nlsolve/raphson.jl

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -24,8 +24,8 @@ const SimpleGaussNewton = SimpleNewtonRaphson
2424

2525
function SciMLBase.__solve(prob::Union{NonlinearProblem, NonlinearLeastSquaresProblem},
2626
alg::SimpleNewtonRaphson, args...; abstol = nothing, reltol = nothing,
27-
maxiters = 1000, termination_condition = nothing, kwargs...)
28-
@bb x = copy(float(prob.u0))
27+
maxiters = 1000, termination_condition = nothing, alias_u0 = false, kwargs...)
28+
x = __maybe_unaliased(prob.u0, alias_u0)
2929
fx = _get_fx(prob, x)
3030
@bb xo = copy(x)
3131
J, jac_cache = jacobian_cache(alg.autodiff, prob.f, fx, x, prob.p)
@@ -37,9 +37,7 @@ function SciMLBase.__solve(prob::Union{NonlinearProblem, NonlinearLeastSquaresPr
3737
fx, dfx = value_and_jacobian(alg.autodiff, prob.f, fx, x, prob.p, jac_cache; J)
3838

3939
if i == 1
40-
if iszero(fx)
41-
return build_solution(prob, alg, x, fx; retcode = ReturnCode.Success)
42-
end
40+
iszero(fx) && build_solution(prob, alg, x, fx; retcode = ReturnCode.Success)
4341
else
4442
# Termination Checks
4543
tc_sol = check_termination(tc_cache, fx, x, xo, prob, alg)

src/nlsolve/trustRegion.jl

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -49,9 +49,9 @@ scalar and static array problems.
4949
end
5050

5151
function SciMLBase.__solve(prob::NonlinearProblem, alg::SimpleTrustRegion, args...;
52-
abstol = nothing, reltol = nothing, maxiters = 1000,
52+
abstol = nothing, reltol = nothing, maxiters = 1000, alias_u0 = false,
5353
termination_condition = nothing, kwargs...)
54-
@bb x = copy(float(prob.u0))
54+
x = __maybe_unaliased(prob.u0, alias_u0)
5555
T = eltype(real(x))
5656
Δₘₐₓ = T(alg.max_trust_radius)
5757
Δ = T(alg.initial_trust_radius)
@@ -85,7 +85,6 @@ function SciMLBase.__solve(prob::NonlinearProblem, alg::SimpleTrustRegion, args.
8585
@bb= copy(x)
8686
dogleg_cache = (; δsd, δN_δsd, δN)
8787

88-
F = fx
8988
for k in 1:maxiters
9089
# Solve the trust region subproblem.
9190
δ = dogleg_method!!(dogleg_cache, ∇f, fx, g, Δ)
@@ -97,19 +96,19 @@ function SciMLBase.__solve(prob::NonlinearProblem, alg::SimpleTrustRegion, args.
9796

9897
# Compute the ratio of the actual to predicted reduction.
9998
@bb= H × vec(δ)
100-
r = (fₖ₊₁ - fₖ) / (dot', g) + dot', Hδ) / T(2))
99+
r = (fₖ₊₁ - fₖ) / (dot(δ, g) + dot(δ, Hδ) / T(2))
101100

102101
# Update the trust region radius.
103-
if r < η₂
102+
if r η₂
103+
shrink_counter = 0
104+
else
104105
Δ = t₁ * Δ
105106
shrink_counter += 1
106107
shrink_counter > max_shrink_times && return build_solution(prob, alg, x, fx;
107108
retcode = ReturnCode.ConvergenceFailure)
108-
else
109-
shrink_counter = 0
110109
end
111110

112-
if r > η₁
111+
if r η₁
113112
# Termination Checks
114113
tc_sol = check_termination(tc_cache, fx, x, xo, prob, alg)
115114
tc_sol !== nothing && return tc_sol
@@ -144,6 +143,7 @@ function dogleg_method!!(cache, J, f, g, Δ)
144143
@bb δsd .= g
145144
@bb @. δsd *= -1
146145
norm_δsd = norm(δsd)
146+
147147
if (norm_δsd Δ)
148148
@bb @. δsd *= Δ / norm_δsd
149149
return δsd

src/utils.jl

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -286,14 +286,14 @@ function check_termination(tc_cache, fx, x, xo, prob, alg)
286286
end
287287
function check_termination(tc_cache, fx, x, xo, prob, alg,
288288
::AbstractNonlinearTerminationMode)
289-
if tc_cache(fx, x, xo)
289+
if Bool(tc_cache(fx, x, xo))
290290
return build_solution(prob, alg, x, fx; retcode = ReturnCode.Success)
291291
end
292292
return nothing
293293
end
294294
function check_termination(tc_cache, fx, x, xo, prob, alg,
295295
::AbstractSafeNonlinearTerminationMode)
296-
if tc_cache(fx, x, xo)
296+
if Bool(tc_cache(fx, x, xo))
297297
if tc_cache.retcode == NonlinearSafeTerminationReturnCode.Success
298298
retcode = ReturnCode.Success
299299
elseif tc_cache.retcode == NonlinearSafeTerminationReturnCode.PatienceTermination
@@ -309,7 +309,7 @@ function check_termination(tc_cache, fx, x, xo, prob, alg,
309309
end
310310
function check_termination(tc_cache, fx, x, xo, prob, alg,
311311
::AbstractSafeBestNonlinearTerminationMode)
312-
if tc_cache(fx, x, xo)
312+
if Bool(tc_cache(fx, x, xo))
313313
if tc_cache.retcode == NonlinearSafeTerminationReturnCode.Success
314314
retcode = ReturnCode.Success
315315
elseif tc_cache.retcode == NonlinearSafeTerminationReturnCode.PatienceTermination
@@ -335,3 +335,11 @@ end
335335

336336
@inline __eval_f(prob, fx, x) = isinplace(prob) ? (prob.f(fx, x, prob.p); fx) :
337337
prob.f(x, prob.p)
338+
339+
# Unalias
340+
@inline __maybe_unaliased(x::Union{Number, SArray}, ::Bool) = x
341+
@inline function __maybe_unaliased(x::AbstractArray, alias::Bool)
342+
# Spend time coping iff we will mutate the array
343+
(alias || !ArrayInterface.can_setindex(typeof(x))) && return x
344+
return deepcopy(x)
345+
end

test/23_test_problems.jl

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,7 @@ end
6161
alg_ops = (SimpleDFSane(),)
6262

6363
broken_tests = Dict(alg => Int[] for alg in alg_ops)
64-
broken_tests[alg_ops[1]] = [1, 2, 3, 4, 5, 6, 7, 9, 11, 12, 13, 15, 16, 17, 21, 22]
64+
broken_tests[alg_ops[1]] = [1, 2, 3, 4, 5, 6, 11, 21]
6565

6666
test_on_library(problems, dicts, alg_ops, broken_tests)
6767
end
@@ -82,7 +82,7 @@ end
8282
alg_ops = (SimpleKlement(),)
8383

8484
broken_tests = Dict(alg => Int[] for alg in alg_ops)
85-
broken_tests[alg_ops[1]] = [1, 2, 4, 5, 6, 7, 9, 10, 11, 12, 13, 19, 21, 22]
85+
broken_tests[alg_ops[1]] = [1, 2, 4, 5, 6, 7, 11, 13, 22]
8686

8787
test_on_library(problems, dicts, alg_ops, broken_tests)
8888
end

test/basictests.jl

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -198,11 +198,11 @@ end
198198
res = benchmark_nlsolve_oop(quadratic_f, @SVector[1.0, 1.0], p)
199199

200200
if any(x -> isnan(x) || x <= 1e-5 || x >= 1e5, res)
201-
@test_broken all(res .≈ sqrt(p))
201+
@test_broken all(abs.(res) .≈ sqrt(p))
202202
@test_broken abs.(ForwardDiff.derivative(p -> benchmark_nlsolve_oop(quadratic_f,
203203
@SVector[1.0, 1.0], p).u[end], p)) 1 / (2 * sqrt(p))
204204
else
205-
@test all(res .≈ sqrt(p))
205+
@test all(abs.(res) .≈ sqrt(p))
206206
@test isapprox(abs.(ForwardDiff.derivative(p -> benchmark_nlsolve_oop(quadratic_f,
207207
@SVector[1.0, 1.0], p).u[end], p)), 1 / (2 * sqrt(p)))
208208
end
@@ -213,12 +213,12 @@ end
213213
for p in 1.0:0.1:100.0
214214
res = benchmark_nlsolve_oop(quadratic_f, 1.0, p)
215215

216-
if any(x -> isnan(x) || x <= 1e-5 || x >= 1e5, res)
217-
@test_broken all(res . sqrt(p))
216+
if any(x -> isnan(x), res)
217+
@test_broken abs(res.u) sqrt(p)
218218
@test_broken abs.(ForwardDiff.derivative(p -> benchmark_nlsolve_oop(quadratic_f,
219219
1.0, p).u, p)) 1 / (2 * sqrt(p))
220220
else
221-
@test all(res . sqrt(p))
221+
@test abs(res.u) sqrt(p)
222222
@test isapprox(abs.(ForwardDiff.derivative(p -> benchmark_nlsolve_oop(quadratic_f,
223223
1.0, p).u, p)), 1 / (2 * sqrt(p)))
224224
end

0 commit comments

Comments
 (0)