Skip to content

Commit 932a269

Browse files
committed
test: move tests for QuasiNewton solvers
1 parent 5e9f368 commit 932a269

File tree

17 files changed

+441
-277
lines changed

17 files changed

+441
-277
lines changed
Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
name: CI (NonlinearSolveQuasiNewton)
2+
3+
on:
4+
pull_request:
5+
branches:
6+
- master
7+
paths:
8+
- "lib/NonlinearSolveQuasiNewton/**"
9+
- ".github/workflows/CI_NonlinearSolveQuasiNewton.yml"
10+
- "lib/NonlinearSolveBase/**"
11+
- "lib/SciMLJacobianOperators/**"
12+
push:
13+
branches:
14+
- master
15+
16+
concurrency:
17+
# Skip intermediate builds: always.
18+
# Cancel intermediate builds: only if it is a pull request build.
19+
group: ${{ github.workflow }}-${{ github.ref }}
20+
cancel-in-progress: ${{ startsWith(github.ref, 'refs/pull/') }}
21+
22+
jobs:
23+
test:
24+
runs-on: ${{ matrix.os }}
25+
strategy:
26+
fail-fast: false
27+
matrix:
28+
version:
29+
- "lts"
30+
- "1"
31+
os:
32+
- ubuntu-latest
33+
- macos-latest
34+
- windows-latest
35+
steps:
36+
- uses: actions/checkout@v4
37+
- uses: julia-actions/setup-julia@v2
38+
with:
39+
version: ${{ matrix.version }}
40+
- uses: actions/cache@v4
41+
env:
42+
cache-name: cache-artifacts
43+
with:
44+
path: ~/.julia/artifacts
45+
key: ${{ runner.os }}-test-${{ env.cache-name }}-${{ hashFiles('**/Project.toml') }}
46+
restore-keys: |
47+
${{ runner.os }}-test-${{ env.cache-name }}-
48+
${{ runner.os }}-test-
49+
${{ runner.os }}-
50+
- name: "Install Dependencies and Run Tests"
51+
run: |
52+
import Pkg
53+
Pkg.Registry.update()
54+
# Install packages present in subdirectories
55+
dev_pks = Pkg.PackageSpec[]
56+
for path in ("lib/SciMLJacobianOperators", "lib/NonlinearSolveBase")
57+
push!(dev_pks, Pkg.PackageSpec(; path))
58+
end
59+
Pkg.develop(dev_pks)
60+
Pkg.instantiate()
61+
Pkg.test(; coverage="user")
62+
shell: julia --color=yes --code-coverage=user --depwarn=yes --project=lib/NonlinearSolveQuasiNewton {0}
63+
- uses: julia-actions/julia-processcoverage@v1
64+
with:
65+
directories: lib/NonlinearSolveQuasiNewton/src,lib/NonlinearSolveBase/src,lib/NonlinearSolveBase/ext,lib/SciMLJacobianOperators/src
66+
- uses: codecov/codecov-action@v4
67+
with:
68+
file: lcov.info
69+
token: ${{ secrets.CODECOV_TOKEN }}
70+
verbose: true
71+
fail_ci_if_error: true

common/nlls_problem_workloads.jl

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,19 @@
1-
using SciMLBase: NonlinearLeastSquaresProblem, NonlinearFunction
1+
using SciMLBase: NonlinearLeastSquaresProblem, NonlinearFunction, NoSpecialize
22

33
nonlinear_functions = (
4-
(NonlinearFunction{false}((u, p) -> (u .^ 2 .- p)[1:1]), [0.1, 0.0]),
5-
(NonlinearFunction{false}((u, p) -> vcat(u .* u .- p, u .* u .- p)), [0.1, 0.1]),
4+
(NonlinearFunction{false, NoSpecialize}((u, p) -> (u .^ 2 .- p)[1:1]), [0.1, 0.0]),
65
(
7-
NonlinearFunction{true}(
6+
NonlinearFunction{false, NoSpecialize}((u, p) -> vcat(u .* u .- p, u .* u .- p)),
7+
[0.1, 0.1]
8+
),
9+
(
10+
NonlinearFunction{true, NoSpecialize}(
811
(du, u, p) -> du[1] = u[1] * u[1] - p, resid_prototype = zeros(1)
912
),
1013
[0.1, 0.0]
1114
),
1215
(
13-
NonlinearFunction{true}(
16+
NonlinearFunction{true, NoSpecialize}(
1417
(du, u, p) -> du .= vcat(u .* u .- p, u .* u .- p), resid_prototype = zeros(4)
1518
),
1619
[0.1, 0.1]

common/nonlinear_problem_workloads.jl

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
1-
using SciMLBase: NonlinearProblem, NonlinearFunction
1+
using SciMLBase: NonlinearProblem, NonlinearFunction, NoSpecialize
22

33
nonlinear_functions = (
4-
(NonlinearFunction{false}((u, p) -> u .* u .- p), 0.1),
5-
(NonlinearFunction{false}((u, p) -> u .* u .- p), [0.1]),
6-
(NonlinearFunction{true}((du, u, p) -> du .= u .* u .- p), [0.1])
4+
(NonlinearFunction{false, NoSpecialize}((u, p) -> u .* u .- p), 0.1),
5+
(NonlinearFunction{false, NoSpecialize}((u, p) -> u .* u .- p), [0.1]),
6+
(NonlinearFunction{true, NoSpecialize}((du, u, p) -> du .= u .* u .- p), [0.1])
77
)
88

99
nonlinear_problems = NonlinearProblem[]

lib/NonlinearSolveBase/ext/NonlinearSolveBaseLinearSolveExt.jl

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,8 +26,10 @@ function (cache::LinearSolveJLCache)(;
2626
if cache.precs === nothing
2727
Pl, Pr = nothing, nothing
2828
else
29-
Pl, Pr = cache.precs(cache.lincache.A, du, linu, p, nothing,
30-
A !== nothing, Plprev, Prprev, cachedata)
29+
Pl, Pr = cache.precs(
30+
cache.lincache.A, du, linu, p, nothing,
31+
A !== nothing, Plprev, Prprev, cachedata
32+
)
3133
end
3234

3335
if Pl !== nothing || Pr !== nothing

lib/NonlinearSolveBase/src/NonlinearSolveBase.jl

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ using DifferentiationInterface: DifferentiationInterface, Constant
1111
using EnzymeCore: EnzymeCore
1212
using FastClosures: @closure
1313
using FunctionProperties: hasbranching
14-
using LinearAlgebra: LinearAlgebra, Diagonal, norm, ldiv!, diagind, pinv
14+
using LinearAlgebra: LinearAlgebra, Diagonal, norm, ldiv!, diagind
1515
using Markdown: @doc_str
1616
using MaybeInplace: @bb
1717
using Preferences: @load_preference

lib/NonlinearSolveBase/src/tracing.jl

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -203,10 +203,13 @@ function update_trace!(
203203
if show_now || store_now
204204
entry = if trace.trace_level.trace_mode isa Val{:minimal}
205205
NonlinearSolveTraceEntry(trace.prob, iter, fu, δu .* α, missing, missing)
206-
elseif trace.trace_level.trace_mode isa Val{:condition_number}
207-
NonlinearSolveTraceEntry(trace.prob, iter, fu, δu .* α, J, missing)
208206
else
209-
NonlinearSolveTraceEntry(trace.prob, iter, fu, δu .* α, J, u)
207+
J = convert(AbstractArray, J)
208+
if trace.trace_level.trace_mode isa Val{:condition_number}
209+
NonlinearSolveTraceEntry(trace.prob, iter, fu, δu .* α, J, missing)
210+
else
211+
NonlinearSolveTraceEntry(trace.prob, iter, fu, δu .* α, J, u)
212+
end
210213
end
211214
show_now && show(stdout, MIME"text/plain"(), entry)
212215
store_now && push!(trace.history, entry)
@@ -224,7 +227,7 @@ function update_trace!(cache, α = true; uses_jac_inverse = Val(false))
224227
trace, cache.nsteps + 1, get_u(cache), get_fu(cache), nothing, cache.du, α
225228
)
226229
else
227-
J = uses_jac_inverse isa Val{true} ? pinv(J) : J
230+
J = uses_jac_inverse isa Val{true} ? Utils.Pinv(cache.J) : cache.J
228231
update_trace!(trace, cache.nsteps + 1, get_u(cache), get_fu(cache), J, cache.du, α)
229232
end
230233
end

lib/NonlinearSolveBase/src/utils.jl

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
module Utils
22

33
using ArrayInterface: ArrayInterface
4+
using ConcreteStructs: @concrete
45
using FastClosures: @closure
56
using LinearAlgebra: LinearAlgebra, Diagonal, Symmetric, norm, dot, cond, diagind, pinv
67
using MaybeInplace: @bb
@@ -15,6 +16,17 @@ is_extension_loaded(::Val) = false
1516

1617
fast_scalar_indexing(xs...) = all(ArrayInterface.fast_scalar_indexing, xs)
1718

19+
@concrete struct Pinv
20+
J
21+
end
22+
23+
function Base.convert(::Type{AbstractArray}, A::Pinv)
24+
hasmethod(pinv, Tuple{typeof(A.J)}) && return pinv(A.J)
25+
@warn "`pinv` not defined for $(typeof(A.J)). Jacobian will not be inverted when \
26+
tracing." maxlog=1
27+
return A.J
28+
end
29+
1830
function nonallocating_isapprox(x::Number, y::Number; atol = false,
1931
rtol = atol > 0 ? false : sqrt(eps(promote_type(typeof(x), typeof(y)))))
2032
return isapprox(x, y; atol, rtol)
@@ -223,7 +235,11 @@ end
223235

224236
make_identity!!(::T, α) where {T <: Number} = T(α)
225237
function make_identity!!(A::AbstractVector{T}, α) where {T}
226-
@bb @. A = T(α)
238+
if ArrayInterface.can_setindex(A)
239+
@. A = α
240+
else
241+
A = one.(A) .* α
242+
end
227243
return A
228244
end
229245
function make_identity!!(::SMatrix{S1, S2, T, L}, α) where {S1, S2, T, L}

lib/NonlinearSolveQuasiNewton/Project.toml

Lines changed: 19 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,6 @@ ArrayInterface = "4fba245c-0d91-5ea0-9b3e-6abc04ee57a9"
88
CommonSolve = "38540f10-b2f7-11e9-35d8-d573e4eb0ff2"
99
ConcreteStructs = "2569d6c7-a4a2-43d3-a901-331e8e4be471"
1010
DiffEqBase = "2b5f629d-d688-5b77-993f-72d75c75574e"
11-
LineSearch = "87fe0de2-c867-4266-b59a-2f0a94fc965b"
1211
LinearAlgebra = "37e2e46d-f89d-539d-b4ee-838fcccc9c8e"
1312
LinearSolve = "7ed4a6bd-45f5-4d41-b270-4a48e9bafcae"
1413
MaybeInplace = "bb5d69b7-63fc-4a16-80bd-7e42200c7bdb"
@@ -20,16 +19,22 @@ SciMLOperators = "c0aeaf25-5076-4817-a8d5-81caf7dfa961"
2019
StaticArraysCore = "1e83bf80-4336-4d27-bf5d-d5a4f845583c"
2120

2221
[compat]
22+
ADTypes = "1.9.0"
2323
Aqua = "0.8"
2424
ArrayInterface = "7.16.0"
25+
BenchmarkTools = "1.5.0"
2526
CommonSolve = "0.2.4"
2627
ConcreteStructs = "0.2.3"
2728
DiffEqBase = "6.155.3"
29+
Enzyme = "0.13.12"
2830
ExplicitImports = "1.5"
31+
FiniteDiff = "2.26.0"
32+
ForwardDiff = "0.10.36"
2933
Hwloc = "3"
3034
InteractiveUtils = "<0.0.1, 1"
3135
LineSearch = "0.1.4"
32-
LinearAlgebra = "1.11.0"
36+
LineSearches = "7.3.0"
37+
LinearAlgebra = "1.10"
3338
LinearSolve = "2.36.1"
3439
MaybeInplace = "0.1.4"
3540
NonlinearProblemLibrary = "0.1.2"
@@ -41,20 +46,31 @@ Reexport = "1"
4146
SciMLBase = "2.54"
4247
SciMLOperators = "0.3.11"
4348
StableRNGs = "1"
49+
StaticArrays = "1.9.8"
4450
StaticArraysCore = "1.4.3"
4551
Test = "1.10"
52+
Zygote = "0.6.72"
4653
julia = "1.10"
4754

4855
[extras]
56+
ADTypes = "47edcb42-4c32-4615-8424-f2b9edc5f35b"
4957
Aqua = "4c88cf16-eb10-579e-8560-4a9242c79595"
58+
BenchmarkTools = "6e4b80f9-dd63-53aa-95a3-0cdb28fa8baf"
59+
Enzyme = "7da242da-08ed-463a-9acd-ee780be4f1d9"
5060
ExplicitImports = "7d51a73a-1435-4ff3-83d9-f097790105c7"
61+
FiniteDiff = "6a86dc24-6348-571c-b903-95158fe2bd41"
62+
ForwardDiff = "f6369f11-7733-5829-9624-2563aa707210"
5163
Hwloc = "0e44f5e4-bd66-52a0-8798-143a42290a1d"
5264
InteractiveUtils = "b77e0a4c-d291-57a0-90e8-8db25a27a240"
65+
LineSearch = "87fe0de2-c867-4266-b59a-2f0a94fc965b"
66+
LineSearches = "d3d80556-e9d4-5f37-9878-2ab0fcc64255"
5367
NonlinearProblemLibrary = "b7050fa9-e91f-4b37-bcee-a89a063da141"
5468
Pkg = "44cfe95a-1eb2-52ea-b672-e2afdf69b78f"
5569
ReTestItems = "817f1d60-ba6b-4fd5-9520-3cf149f6a823"
5670
StableRNGs = "860ef19b-820b-49d6-a774-d7a799459cd3"
71+
StaticArrays = "90137ffa-7385-5640-81b9-e52037218182"
5772
Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40"
73+
Zygote = "e88e6eb3-aa80-5325-afca-941959d7151f"
5874

5975
[targets]
60-
test = ["Aqua", "ExplicitImports", "Hwloc", "InteractiveUtils", "NonlinearProblemLibrary", "Pkg", "ReTestItems", "StableRNGs", "Test"]
76+
test = ["ADTypes", "Aqua", "BenchmarkTools", "Enzyme", "ExplicitImports", "FiniteDiff", "ForwardDiff", "Hwloc", "InteractiveUtils", "LineSearch", "LineSearches", "NonlinearProblemLibrary", "Pkg", "ReTestItems", "StableRNGs", "StaticArrays", "Test", "Zygote"]

lib/NonlinearSolveQuasiNewton/src/broyden.jl

Lines changed: 6 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -148,16 +148,14 @@ function InternalAPI.solve!(
148148
@bb @. cache.dfu = fu - cache.dfu
149149
J⁻¹_diag = Utils.restructure(cache.dfu, diag(J⁻¹))
150150
if cache.rule isa GoodBroydenUpdateRule
151-
@bb @. J⁻¹_diag = J⁻¹_diag * cache.dfu * du
152-
denom = sum(J⁻¹_diag)
153-
@bb @. J⁻¹_diag = J⁻¹_diag +
154-
(du - J⁻¹_diag * cache.dfu) * du * J⁻¹_diag /
155-
ifelse(iszero(denom), T(1e-5), denom)
151+
@bb @. cache.J⁻¹dfu = J⁻¹_diag * cache.dfu * du
152+
denom = sum(cache.J⁻¹dfu)
153+
@bb @. J⁻¹_diag += (du - cache.J⁻¹dfu) * du * J⁻¹_diag /
154+
ifelse(iszero(denom), T(1e-5), denom)
156155
else
157156
denom = cache.internalnorm(cache.dfu)^2
158-
@bb @. J⁻¹_diag = J⁻¹_diag +
159-
(du - J⁻¹_diag * cache.dfu) * cache.dfu /
160-
ifelse(iszero(denom), T(1e-5), denom)
157+
@bb @. J⁻¹_diag += (du - J⁻¹_diag * cache.dfu) * cache.dfu /
158+
ifelse(iszero(denom), T(1e-5), denom)
161159
end
162160
@bb copyto!(cache.dfu, fu)
163161
return Diagonal(vec(J⁻¹_diag))

lib/NonlinearSolveQuasiNewton/src/initialization.jl

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -166,6 +166,10 @@ function InternalAPI.init(
166166
J = BroydenLowRankJacobian(fu, u; alg.threshold, alpha = α)
167167
else
168168
threshold = min(Utils.unwrap_val(alg.threshold), maxiters)
169+
if threshold > length(u)
170+
@warn "`threshold` is larger than the size of the state, which may cause \
171+
numerical instability. Consider reducing `threshold`."
172+
end
169173
J = BroydenLowRankJacobian(fu, u; threshold, alpha = α)
170174
end
171175
return InitializedApproximateJacobianCache(
@@ -240,9 +244,9 @@ function LinearAlgebra.mul!(y::AbstractVector, J::BroydenLowRankJacobian, x::Abs
240244
@. y = -J.alpha * x
241245
return y
242246
end
243-
_, U, Vᵀ = get_components(J)
247+
cache, U, Vᵀ = get_components(J)
244248
@bb cache = Vᵀ × x
245-
mul!(y, U, cache)
249+
LinearAlgebra.mul!(y, U, cache)
246250
@bb @. y -= J.alpha * x
247251
return y
248252
end
@@ -258,9 +262,9 @@ function LinearAlgebra.mul!(y::AbstractVector, x::AbstractVector, J::BroydenLowR
258262
@. y = -J.alpha * x
259263
return y
260264
end
261-
_, U, Vᵀ = get_components(J)
265+
cache, U, Vᵀ = get_components(J)
262266
@bb cache = transpose(U) × x
263-
mul!(y, transpose(Vᵀ), cache)
267+
LinearAlgebra.mul!(y, transpose(Vᵀ), cache)
264268
@bb @. y -= J.alpha * x
265269
return y
266270
end

0 commit comments

Comments
 (0)