From f49c30828a13fe5a083b8dea89b09ab6679c4ecb Mon Sep 17 00:00:00 2001 From: Avik Pal Date: Wed, 30 Oct 2024 16:30:20 -0400 Subject: [PATCH] test: fix more of NonlinearSolve tests --- .buildkite/pipeline.yml | 4 +- .../workflows/CI_BracketingNonlinearSolve.yml | 2 +- .github/workflows/CI_NonlinearSolve.yml | 10 +- .github/workflows/CI_NonlinearSolveBase.yml | 2 +- .../workflows/CI_NonlinearSolveFirstOrder.yml | 2 +- .../CI_NonlinearSolveQuasiNewton.yml | 2 +- .../CI_NonlinearSolveSpectralMethods.yml | 2 +- .../workflows/CI_SciMLJacobianOperators.yml | 2 +- .github/workflows/Downgrade.yml | 1 - Project.toml | 2 +- .../src/NonlinearSolveBase.jl | 2 +- lib/NonlinearSolveBase/src/timer_outputs.jl | 22 + lib/NonlinearSolveFirstOrder/Project.toml | 4 +- src/NonlinearSolve.jl | 3 +- src/helpers.jl | 21 - test/23_test_problems_tests.jl | 144 ++++++ test/core/23_test_problems_tests.jl | 144 ------ test/core/forward_ad_tests.jl | 116 ----- test/core/nlls_tests.jl | 21 - test/core_tests.jl | 416 ++++++++++++++++++ test/{gpu/core_tests.jl => cuda_tests.jl} | 0 test/forward_ad_tests.jl | 116 +++++ test/misc/aliasing_tests.jl | 25 -- test/misc/ensemble_tests.jl | 21 - test/misc/exotic_type_tests.jl | 27 -- test/misc/linsolve_switching_tests.jl | 28 -- test/misc/matrix_resizing_tests.jl | 31 -- test/misc/noinit_caching_tests.jl | 23 - test/misc/polyalg_tests.jl | 256 ----------- test/misc/qa_tests.jl | 28 -- .../mtk_cache_indexing_tests.jl | 3 +- test/qa_tests.jl | 24 + test/runtests.jl | 16 +- .../{nlls_tests.jl => least_squares_tests.jl} | 0 34 files changed, 756 insertions(+), 764 deletions(-) create mode 100644 test/23_test_problems_tests.jl delete mode 100644 test/core/23_test_problems_tests.jl delete mode 100644 test/core/forward_ad_tests.jl delete mode 100644 test/core/nlls_tests.jl create mode 100644 test/core_tests.jl rename test/{gpu/core_tests.jl => cuda_tests.jl} (100%) create mode 100644 test/forward_ad_tests.jl delete mode 100644 test/misc/aliasing_tests.jl delete mode 100644 test/misc/ensemble_tests.jl delete mode 100644 test/misc/exotic_type_tests.jl delete mode 100644 test/misc/linsolve_switching_tests.jl delete mode 100644 test/misc/matrix_resizing_tests.jl delete mode 100644 test/misc/noinit_caching_tests.jl delete mode 100644 test/misc/polyalg_tests.jl delete mode 100644 test/misc/qa_tests.jl rename test/{downstream => }/mtk_cache_indexing_tests.jl (98%) create mode 100644 test/qa_tests.jl rename test/wrappers/{nlls_tests.jl => least_squares_tests.jl} (100%) diff --git a/.buildkite/pipeline.yml b/.buildkite/pipeline.yml index 4292dbdda..b74c820cc 100644 --- a/.buildkite/pipeline.yml +++ b/.buildkite/pipeline.yml @@ -14,7 +14,7 @@ steps: Pkg.Registry.update(); # Install packages present in subdirectories dev_pks = Pkg.PackageSpec[]; - for path in ("lib/SciMLJacobianOperators", "lib/BracketingNonlinearSolve", "lib/NonlinearSolveBase", "lib/SimpleNonlinearSolve") + for path in ("lib/SciMLJacobianOperators", "lib/BracketingNonlinearSolve", "lib/NonlinearSolveBase", "lib/SimpleNonlinearSolve", "lib/NonlinearSolveFirstOrder", "lib/NonlinearSolveSpectralMethods", "lib/NonlinearSolveQuasiNewton") push!(dev_pks, Pkg.PackageSpec(; path)); end Pkg.develop(dev_pks); @@ -42,7 +42,7 @@ steps: Pkg.Registry.update(); # Install packages present in subdirectories dev_pks = Pkg.PackageSpec[]; - for path in ("lib/NonlinearSolveBase", "lib/BracketingNonlinearSolve") + for path in ("lib/NonlinearSolveBase", "lib/BracketingNonlinearSolve", "lib/SciMLJacobianOperators") push!(dev_pks, Pkg.PackageSpec(; path)) end Pkg.develop(dev_pks); diff --git a/.github/workflows/CI_BracketingNonlinearSolve.yml b/.github/workflows/CI_BracketingNonlinearSolve.yml index 69c1f7746..e83060bdb 100644 --- a/.github/workflows/CI_BracketingNonlinearSolve.yml +++ b/.github/workflows/CI_BracketingNonlinearSolve.yml @@ -26,7 +26,7 @@ jobs: fail-fast: false matrix: version: - - "lts" + - "1.10" - "1" os: - ubuntu-latest diff --git a/.github/workflows/CI_NonlinearSolve.yml b/.github/workflows/CI_NonlinearSolve.yml index 0ea05487c..24be53295 100644 --- a/.github/workflows/CI_NonlinearSolve.yml +++ b/.github/workflows/CI_NonlinearSolve.yml @@ -14,6 +14,9 @@ on: - "lib/BracketingNonlinearSolve/**" - "lib/NonlinearSolveBase/**" - "lib/SimpleNonlinearSolve/**" + - "lib/NonlinearSolveFirstOrder/**" + - "lib/NonlinearSolveSpectralMethods/**" + - "lib/NonlinearSolveQuasiNewton/**" push: branches: - master @@ -33,10 +36,9 @@ jobs: group: - Core - Downstream - - Misc - Wrappers version: - - "lts" + - "1.10" - "1" os: - ubuntu-latest @@ -63,7 +65,7 @@ jobs: Pkg.Registry.update() # Install packages present in subdirectories dev_pks = Pkg.PackageSpec[] - for path in ("lib/SciMLJacobianOperators", "lib/BracketingNonlinearSolve", "lib/NonlinearSolveBase", "lib/SimpleNonlinearSolve") + for path in ("lib/SciMLJacobianOperators", "lib/BracketingNonlinearSolve", "lib/NonlinearSolveBase", "lib/SimpleNonlinearSolve", "lib/NonlinearSolveFirstOrder", "lib/NonlinearSolveSpectralMethods", "lib/NonlinearSolveQuasiNewton") push!(dev_pks, Pkg.PackageSpec(; path)) end Pkg.develop(dev_pks) @@ -74,7 +76,7 @@ jobs: GROUP: ${{ matrix.group }} - uses: julia-actions/julia-processcoverage@v1 with: - directories: src,ext,lib/SciMLJacobianOperators/src + directories: src,ext,lib/SciMLJacobianOperators/src,lib/BracketingNonlinearSolve/src,lib/NonlinearSolveBase/src,lib/NonlinearSolveBase/ext,lib/SimpleNonlinearSolve/src,lib/NonlinearSolveFirstOrder/src,lib/NonlinearSolveSpectralMethods/src,lib/NonlinearSolveQuasiNewton/src - uses: codecov/codecov-action@v4 with: file: lcov.info diff --git a/.github/workflows/CI_NonlinearSolveBase.yml b/.github/workflows/CI_NonlinearSolveBase.yml index 9b9f0d959..0ba7c11d2 100644 --- a/.github/workflows/CI_NonlinearSolveBase.yml +++ b/.github/workflows/CI_NonlinearSolveBase.yml @@ -25,7 +25,7 @@ jobs: fail-fast: false matrix: version: - - "lts" + - "1.10" - "1" os: - ubuntu-latest diff --git a/.github/workflows/CI_NonlinearSolveFirstOrder.yml b/.github/workflows/CI_NonlinearSolveFirstOrder.yml index cf9bd427b..8b93788b1 100644 --- a/.github/workflows/CI_NonlinearSolveFirstOrder.yml +++ b/.github/workflows/CI_NonlinearSolveFirstOrder.yml @@ -26,7 +26,7 @@ jobs: fail-fast: false matrix: version: - - "lts" + - "1.10" - "1" os: - ubuntu-latest diff --git a/.github/workflows/CI_NonlinearSolveQuasiNewton.yml b/.github/workflows/CI_NonlinearSolveQuasiNewton.yml index 5d6024b06..1e3815ea9 100644 --- a/.github/workflows/CI_NonlinearSolveQuasiNewton.yml +++ b/.github/workflows/CI_NonlinearSolveQuasiNewton.yml @@ -26,7 +26,7 @@ jobs: fail-fast: false matrix: version: - - "lts" + - "1.10" - "1" os: - ubuntu-latest diff --git a/.github/workflows/CI_NonlinearSolveSpectralMethods.yml b/.github/workflows/CI_NonlinearSolveSpectralMethods.yml index 80f9b0e9d..7999ee906 100644 --- a/.github/workflows/CI_NonlinearSolveSpectralMethods.yml +++ b/.github/workflows/CI_NonlinearSolveSpectralMethods.yml @@ -26,7 +26,7 @@ jobs: fail-fast: false matrix: version: - - "lts" + - "1.10" - "1" os: - ubuntu-latest diff --git a/.github/workflows/CI_SciMLJacobianOperators.yml b/.github/workflows/CI_SciMLJacobianOperators.yml index 96ce9db15..3f243a8b3 100644 --- a/.github/workflows/CI_SciMLJacobianOperators.yml +++ b/.github/workflows/CI_SciMLJacobianOperators.yml @@ -24,7 +24,7 @@ jobs: fail-fast: false matrix: version: - - "lts" + - "1.10" - "1" os: - ubuntu-latest diff --git a/.github/workflows/Downgrade.yml b/.github/workflows/Downgrade.yml index 83223e2f6..6d619f9ab 100644 --- a/.github/workflows/Downgrade.yml +++ b/.github/workflows/Downgrade.yml @@ -21,7 +21,6 @@ jobs: group: - Core - Downstream - - Misc - Wrappers steps: - uses: actions/checkout@v4 diff --git a/Project.toml b/Project.toml index 768a46005..f817aba9f 100644 --- a/Project.toml +++ b/Project.toml @@ -78,7 +78,7 @@ InteractiveUtils = "<0.0.1, 1" LeastSquaresOptim = "0.8.5" LineSearch = "0.1.4" LineSearches = "7.3" -LinearAlgebra = "1.11.0" +LinearAlgebra = "1.10" LinearSolve = "2.36.1" MINPACK = "1.2" MPI = "0.20.22" diff --git a/lib/NonlinearSolveBase/src/NonlinearSolveBase.jl b/lib/NonlinearSolveBase/src/NonlinearSolveBase.jl index 7cb1cffaf..a3ac4614d 100644 --- a/lib/NonlinearSolveBase/src/NonlinearSolveBase.jl +++ b/lib/NonlinearSolveBase/src/NonlinearSolveBase.jl @@ -3,7 +3,7 @@ module NonlinearSolveBase using Compat: @compat using ConcreteStructs: @concrete using FastClosures: @closure -using Preferences: @load_preference +using Preferences: @load_preference, @set_preferences! using ADTypes: ADTypes, AbstractADType, AutoSparse, NoSparsityDetector, KnownJacobianSparsityDetector diff --git a/lib/NonlinearSolveBase/src/timer_outputs.jl b/lib/NonlinearSolveBase/src/timer_outputs.jl index 6a65f1bab..56bb2a95c 100644 --- a/lib/NonlinearSolveBase/src/timer_outputs.jl +++ b/lib/NonlinearSolveBase/src/timer_outputs.jl @@ -31,3 +31,25 @@ end @static if !TIMER_OUTPUTS_ENABLED @inline reset_timer!(::Nothing) = nothing end + +""" + enable_timer_outputs() + +Enable `TimerOutput` for all `NonlinearSolve` algorithms. This is useful for debugging +but has some overhead, so it is disabled by default. +""" +function enable_timer_outputs() + @set_preferences!("enable_timer_outputs" => true) + @info "Timer Outputs Enabled. Restart the Julia session for this to take effect." +end + +""" + disable_timer_outputs() + +Disable `TimerOutput` for all `NonlinearSolve` algorithms. This should be used when +`NonlinearSolve` is being used in performance-critical code. +""" +function disable_timer_outputs() + @set_preferences!("enable_timer_outputs" => false) + @info "Timer Outputs Disabled. Restart the Julia session for this to take effect." +end diff --git a/lib/NonlinearSolveFirstOrder/Project.toml b/lib/NonlinearSolveFirstOrder/Project.toml index 1a6420468..b323a7f8e 100644 --- a/lib/NonlinearSolveFirstOrder/Project.toml +++ b/lib/NonlinearSolveFirstOrder/Project.toml @@ -52,6 +52,7 @@ Reexport = "1" SciMLBase = "2.54" SciMLJacobianOperators = "0.1.0" Setfield = "1.1.1" +SparseArrays = "1.10" SparseConnectivityTracer = "0.6.8" SparseMatrixColorings = "0.4.8" StableRNGs = "1" @@ -75,6 +76,7 @@ NonlinearProblemLibrary = "b7050fa9-e91f-4b37-bcee-a89a063da141" Pkg = "44cfe95a-1eb2-52ea-b672-e2afdf69b78f" Random = "9a3f8284-a2c9-5f02-9a11-845980a1fd5c" ReTestItems = "817f1d60-ba6b-4fd5-9520-3cf149f6a823" +SparseArrays = "2f01184e-e22b-5df5-ae63-d93ebab69eaf" SparseConnectivityTracer = "9f842d2f-2579-4b1d-911e-f412cf18a3f5" SparseMatrixColorings = "0a514795-09f3-496d-8182-132a7b665d35" StableRNGs = "860ef19b-820b-49d6-a774-d7a799459cd3" @@ -83,4 +85,4 @@ Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40" Zygote = "e88e6eb3-aa80-5325-afca-941959d7151f" [targets] -test = ["Aqua", "BandedMatrices", "BenchmarkTools", "Enzyme", "ExplicitImports", "Hwloc", "InteractiveUtils", "LineSearch", "LineSearches", "NonlinearProblemLibrary", "Pkg", "Random", "ReTestItems", "SparseConnectivityTracer", "SparseMatrixColorings", "StableRNGs", "StaticArrays", "Test", "Zygote"] +test = ["Aqua", "BandedMatrices", "BenchmarkTools", "Enzyme", "ExplicitImports", "Hwloc", "InteractiveUtils", "LineSearch", "LineSearches", "NonlinearProblemLibrary", "Pkg", "Random", "ReTestItems", "SparseArrays", "SparseConnectivityTracer", "SparseMatrixColorings", "StableRNGs", "StaticArrays", "Test", "Zygote"] diff --git a/src/NonlinearSolve.jl b/src/NonlinearSolve.jl index 74dea2342..87bc2a5b3 100644 --- a/src/NonlinearSolve.jl +++ b/src/NonlinearSolve.jl @@ -12,7 +12,8 @@ using DiffEqBase: DiffEqBase # Needed for `init` / `solve` dispatches using LinearAlgebra: LinearAlgebra, norm using LineSearch: BackTracking using NonlinearSolveBase: NonlinearSolveBase, InternalAPI, AbstractNonlinearSolveAlgorithm, - AbstractNonlinearSolveCache, Utils, L2_NORM + AbstractNonlinearSolveCache, Utils, L2_NORM, + enable_timer_outputs, disable_timer_outputs using Preferences: set_preferences! using SciMLBase: SciMLBase, NLStats, ReturnCode, AbstractNonlinearProblem, NonlinearProblem, diff --git a/src/helpers.jl b/src/helpers.jl index b28ef01dd..e69de29bb 100644 --- a/src/helpers.jl +++ b/src/helpers.jl @@ -1,21 +0,0 @@ -""" - enable_timer_outputs() - -Enable `TimerOutput` for all `NonlinearSolve` algorithms. This is useful for debugging -but has some overhead, so it is disabled by default. -""" -function enable_timer_outputs() - set_preferences!(NonlinearSolveBase, "enable_timer_outputs" => true; force = true) - @info "Timer Outputs Enabled. Restart the Julia session for this to take effect." -end - -""" - disable_timer_outputs() - -Disable `TimerOutput` for all `NonlinearSolve` algorithms. This should be used when -`NonlinearSolve` is being used in performance-critical code. -""" -function disable_timer_outputs() - set_preferences!(NonlinearSolveBase, "enable_timer_outputs" => false; force = true) - @info "Timer Outputs Disabled. Restart the Julia session for this to take effect." -end diff --git a/test/23_test_problems_tests.jl b/test/23_test_problems_tests.jl new file mode 100644 index 000000000..6246cbecb --- /dev/null +++ b/test/23_test_problems_tests.jl @@ -0,0 +1,144 @@ +# @testsetup module RobustnessTesting +# using NonlinearSolve, LinearAlgebra, LinearSolve, NonlinearProblemLibrary, Test + +# problems = NonlinearProblemLibrary.problems +# dicts = NonlinearProblemLibrary.dicts + +# function test_on_library( +# problems, dicts, alg_ops, broken_tests, ϵ = 1e-4; skip_tests = nothing) +# for (idx, (problem, dict)) in enumerate(zip(problems, dicts)) +# x = dict["start"] +# res = similar(x) +# nlprob = NonlinearProblem(problem, copy(x)) +# @testset "$idx: $(dict["title"])" begin +# for alg in alg_ops +# try +# sol = solve(nlprob, alg; maxiters = 10000) +# problem(res, sol.u, nothing) + +# skip = skip_tests !== nothing && idx in skip_tests[alg] +# if skip +# @test_skip norm(res, Inf) ≤ ϵ +# continue +# end +# broken = idx in broken_tests[alg] ? true : false +# @test norm(res, Inf)≤ϵ broken=broken +# catch err +# @error err +# broken = idx in broken_tests[alg] ? true : false +# if broken +# @test false broken=true +# else +# @test 1 == 2 +# end +# end +# end +# end +# end +# end + +# export test_on_library, problems, dicts +# end + +# @testitem "PolyAlgorithms" setup=[RobustnessTesting] tags=[:core] begin +# alg_ops = (RobustMultiNewton(), FastShortcutNonlinearPolyalg()) + +# broken_tests = Dict(alg => Int[] for alg in alg_ops) +# broken_tests[alg_ops[1]] = [] +# broken_tests[alg_ops[2]] = [] + +# test_on_library(problems, dicts, alg_ops, broken_tests) +# end + +# @testitem "NewtonRaphson" setup=[RobustnessTesting] tags=[:core] begin +# alg_ops = (NewtonRaphson(),) + +# broken_tests = Dict(alg => Int[] for alg in alg_ops) +# broken_tests[alg_ops[1]] = [1] + +# test_on_library(problems, dicts, alg_ops, broken_tests) +# end + +# @testitem "TrustRegion" setup=[RobustnessTesting] tags=[:core] begin +# alg_ops = (TrustRegion(; radius_update_scheme = RadiusUpdateSchemes.Simple), +# TrustRegion(; radius_update_scheme = RadiusUpdateSchemes.Fan), +# TrustRegion(; radius_update_scheme = RadiusUpdateSchemes.Hei), +# TrustRegion(; radius_update_scheme = RadiusUpdateSchemes.Yuan), +# TrustRegion(; radius_update_scheme = RadiusUpdateSchemes.Bastin), +# TrustRegion(; radius_update_scheme = RadiusUpdateSchemes.NLsolve)) + +# broken_tests = Dict(alg => Int[] for alg in alg_ops) +# broken_tests[alg_ops[1]] = [11, 21] +# broken_tests[alg_ops[2]] = [11, 21] +# broken_tests[alg_ops[3]] = [11, 21] +# broken_tests[alg_ops[4]] = [8, 11, 21] +# broken_tests[alg_ops[5]] = [21] +# broken_tests[alg_ops[6]] = [11, 21] + +# test_on_library(problems, dicts, alg_ops, broken_tests) +# end + +# @testitem "LevenbergMarquardt" setup=[RobustnessTesting] tags=[:core] begin +# using LinearSolve + +# alg_ops = (LevenbergMarquardt(), LevenbergMarquardt(; α_geodesic = 0.1), +# LevenbergMarquardt(; linsolve = CholeskyFactorization())) + +# broken_tests = Dict(alg => Int[] for alg in alg_ops) +# broken_tests[alg_ops[1]] = [11, 21] +# broken_tests[alg_ops[2]] = [11, 21] +# broken_tests[alg_ops[3]] = [11, 21] + +# test_on_library(problems, dicts, alg_ops, broken_tests) +# end + +# @testitem "DFSane" setup=[RobustnessTesting] tags=[:core] begin +# alg_ops = (DFSane(),) + +# broken_tests = Dict(alg => Int[] for alg in alg_ops) +# broken_tests[alg_ops[1]] = [1, 2, 3, 5, 21] + +# test_on_library(problems, dicts, alg_ops, broken_tests) +# end + +# @testitem "Broyden" setup=[RobustnessTesting] tags=[:core] begin +# alg_ops = (Broyden(), Broyden(; init_jacobian = Val(:true_jacobian)), +# Broyden(; update_rule = Val(:bad_broyden)), +# Broyden(; init_jacobian = Val(:true_jacobian), update_rule = Val(:bad_broyden))) + +# broken_tests = Dict(alg => Int[] for alg in alg_ops) +# if Sys.isapple() +# broken_tests[alg_ops[1]] = [1, 5, 11] +# broken_tests[alg_ops[2]] = [1, 5, 8, 11, 18] +# broken_tests[alg_ops[3]] = [1, 5, 6, 9, 11] +# broken_tests[alg_ops[4]] = [5, 6, 8, 11] +# else +# broken_tests[alg_ops[1]] = [1, 5, 11, 15] +# broken_tests[alg_ops[2]] = [1, 5, 8, 11, 18] +# broken_tests[alg_ops[3]] = [1, 5, 9, 11] +# broken_tests[alg_ops[4]] = [5, 6, 8, 11] +# end + +# test_on_library(problems, dicts, alg_ops, broken_tests, Sys.isapple() ? 1e-3 : 1e-4) +# end + +# @testitem "Klement" setup=[RobustnessTesting] tags=[:core] begin +# alg_ops = (Klement(), Klement(; init_jacobian = Val(:true_jacobian_diagonal))) + +# broken_tests = Dict(alg => Int[] for alg in alg_ops) +# broken_tests[alg_ops[1]] = [1, 2, 4, 5, 11, 18, 22] +# broken_tests[alg_ops[2]] = [2, 4, 5, 7, 18, 22] + +# test_on_library(problems, dicts, alg_ops, broken_tests) +# end + +# @testitem "PseudoTransient" setup=[RobustnessTesting] tags=[:core] begin +# # PT relies on the root being a stable equilibrium for convergence, so it won't work on +# # most problems +# alg_ops = (PseudoTransient(),) + +# broken_tests = Dict(alg => Int[] for alg in alg_ops) +# broken_tests[alg_ops[1]] = [1, 2, 3, 11, 15, 16] + +# test_on_library(problems, dicts, alg_ops, broken_tests) +# end diff --git a/test/core/23_test_problems_tests.jl b/test/core/23_test_problems_tests.jl deleted file mode 100644 index 7648db53f..000000000 --- a/test/core/23_test_problems_tests.jl +++ /dev/null @@ -1,144 +0,0 @@ -@testsetup module RobustnessTesting -using NonlinearSolve, LinearAlgebra, LinearSolve, NonlinearProblemLibrary, Test - -problems = NonlinearProblemLibrary.problems -dicts = NonlinearProblemLibrary.dicts - -function test_on_library( - problems, dicts, alg_ops, broken_tests, ϵ = 1e-4; skip_tests = nothing) - for (idx, (problem, dict)) in enumerate(zip(problems, dicts)) - x = dict["start"] - res = similar(x) - nlprob = NonlinearProblem(problem, copy(x)) - @testset "$idx: $(dict["title"])" begin - for alg in alg_ops - try - sol = solve(nlprob, alg; maxiters = 10000) - problem(res, sol.u, nothing) - - skip = skip_tests !== nothing && idx in skip_tests[alg] - if skip - @test_skip norm(res, Inf) ≤ ϵ - continue - end - broken = idx in broken_tests[alg] ? true : false - @test norm(res, Inf)≤ϵ broken=broken - catch err - @error err - broken = idx in broken_tests[alg] ? true : false - if broken - @test false broken=true - else - @test 1 == 2 - end - end - end - end - end -end - -export test_on_library, problems, dicts -end - -@testitem "PolyAlgorithms" setup=[RobustnessTesting] tags=[:core] begin - alg_ops = (RobustMultiNewton(), FastShortcutNonlinearPolyalg()) - - broken_tests = Dict(alg => Int[] for alg in alg_ops) - broken_tests[alg_ops[1]] = [] - broken_tests[alg_ops[2]] = [] - - test_on_library(problems, dicts, alg_ops, broken_tests) -end - -@testitem "NewtonRaphson" setup=[RobustnessTesting] tags=[:core] begin - alg_ops = (NewtonRaphson(),) - - broken_tests = Dict(alg => Int[] for alg in alg_ops) - broken_tests[alg_ops[1]] = [1] - - test_on_library(problems, dicts, alg_ops, broken_tests) -end - -@testitem "TrustRegion" setup=[RobustnessTesting] tags=[:core] begin - alg_ops = (TrustRegion(; radius_update_scheme = RadiusUpdateSchemes.Simple), - TrustRegion(; radius_update_scheme = RadiusUpdateSchemes.Fan), - TrustRegion(; radius_update_scheme = RadiusUpdateSchemes.Hei), - TrustRegion(; radius_update_scheme = RadiusUpdateSchemes.Yuan), - TrustRegion(; radius_update_scheme = RadiusUpdateSchemes.Bastin), - TrustRegion(; radius_update_scheme = RadiusUpdateSchemes.NLsolve)) - - broken_tests = Dict(alg => Int[] for alg in alg_ops) - broken_tests[alg_ops[1]] = [11, 21] - broken_tests[alg_ops[2]] = [11, 21] - broken_tests[alg_ops[3]] = [11, 21] - broken_tests[alg_ops[4]] = [8, 11, 21] - broken_tests[alg_ops[5]] = [21] - broken_tests[alg_ops[6]] = [11, 21] - - test_on_library(problems, dicts, alg_ops, broken_tests) -end - -@testitem "LevenbergMarquardt" setup=[RobustnessTesting] tags=[:core] begin - using LinearSolve - - alg_ops = (LevenbergMarquardt(), LevenbergMarquardt(; α_geodesic = 0.1), - LevenbergMarquardt(; linsolve = CholeskyFactorization())) - - broken_tests = Dict(alg => Int[] for alg in alg_ops) - broken_tests[alg_ops[1]] = [11, 21] - broken_tests[alg_ops[2]] = [11, 21] - broken_tests[alg_ops[3]] = [11, 21] - - test_on_library(problems, dicts, alg_ops, broken_tests) -end - -@testitem "DFSane" setup=[RobustnessTesting] tags=[:core] begin - alg_ops = (DFSane(),) - - broken_tests = Dict(alg => Int[] for alg in alg_ops) - broken_tests[alg_ops[1]] = [1, 2, 3, 5, 21] - - test_on_library(problems, dicts, alg_ops, broken_tests) -end - -@testitem "Broyden" setup=[RobustnessTesting] tags=[:core] begin - alg_ops = (Broyden(), Broyden(; init_jacobian = Val(:true_jacobian)), - Broyden(; update_rule = Val(:bad_broyden)), - Broyden(; init_jacobian = Val(:true_jacobian), update_rule = Val(:bad_broyden))) - - broken_tests = Dict(alg => Int[] for alg in alg_ops) - if Sys.isapple() - broken_tests[alg_ops[1]] = [1, 5, 11] - broken_tests[alg_ops[2]] = [1, 5, 8, 11, 18] - broken_tests[alg_ops[3]] = [1, 5, 6, 9, 11] - broken_tests[alg_ops[4]] = [5, 6, 8, 11] - else - broken_tests[alg_ops[1]] = [1, 5, 11, 15] - broken_tests[alg_ops[2]] = [1, 5, 8, 11, 18] - broken_tests[alg_ops[3]] = [1, 5, 9, 11] - broken_tests[alg_ops[4]] = [5, 6, 8, 11] - end - - test_on_library(problems, dicts, alg_ops, broken_tests, Sys.isapple() ? 1e-3 : 1e-4) -end - -@testitem "Klement" setup=[RobustnessTesting] tags=[:core] begin - alg_ops = (Klement(), Klement(; init_jacobian = Val(:true_jacobian_diagonal))) - - broken_tests = Dict(alg => Int[] for alg in alg_ops) - broken_tests[alg_ops[1]] = [1, 2, 4, 5, 11, 18, 22] - broken_tests[alg_ops[2]] = [2, 4, 5, 7, 18, 22] - - test_on_library(problems, dicts, alg_ops, broken_tests) -end - -@testitem "PseudoTransient" setup=[RobustnessTesting] tags=[:core] begin - # PT relies on the root being a stable equilibrium for convergence, so it won't work on - # most problems - alg_ops = (PseudoTransient(),) - - broken_tests = Dict(alg => Int[] for alg in alg_ops) - broken_tests[alg_ops[1]] = [1, 2, 3, 11, 15, 16] - - test_on_library(problems, dicts, alg_ops, broken_tests) -end diff --git a/test/core/forward_ad_tests.jl b/test/core/forward_ad_tests.jl deleted file mode 100644 index 24711b9d9..000000000 --- a/test/core/forward_ad_tests.jl +++ /dev/null @@ -1,116 +0,0 @@ -@testsetup module ForwardADTesting -using Reexport, NonlinearSolve -@reexport using ForwardDiff, MINPACK, NLsolve, StaticArrays, Sundials, LinearAlgebra - -test_f!(du, u, p) = (@. du = u^2 - p) -test_f(u, p) = (@. u^2 - p) - -jacobian_f(::Number, p) = 1 / (2 * √p) -jacobian_f(::Number, p::Number) = 1 / (2 * √p) -jacobian_f(u, p::Number) = one.(u) .* (1 / (2 * √p)) -jacobian_f(u, p::AbstractArray) = diagm(vec(@. 1 / (2 * √p))) - -function solve_with(::Val{mode}, u, alg) where {mode} - f = if mode === :iip - solve_iip(p) = solve(NonlinearProblem(test_f!, u, p), alg).u - elseif mode === :iip_cache - function solve_iip_init(p) - cache = SciMLBase.init(NonlinearProblem(test_f!, u, p), alg) - return SciMLBase.solve!(cache).u - end - elseif mode === :oop - solve_oop(p) = solve(NonlinearProblem(test_f, u, p), alg).u - elseif mode === :oop_cache - function solve_oop_init(p) - cache = SciMLBase.init(NonlinearProblem(test_f, u, p), alg) - return SciMLBase.solve!(cache).u - end - end - return f -end - -__compatible(::Any, ::Val{:oop}) = true -__compatible(::Any, ::Val{:oop_cache}) = true -__compatible(::Number, ::Val{:iip}) = false -__compatible(::AbstractArray, ::Val{:iip}) = true -__compatible(::StaticArray, ::Val{:iip}) = false -__compatible(::Number, ::Val{:iip_cache}) = false -__compatible(::AbstractArray, ::Val{:iip_cache}) = true -__compatible(::StaticArray, ::Val{:iip_cache}) = false - -__compatible(::Any, ::Number) = true -__compatible(::Number, ::AbstractArray) = false -__compatible(u::AbstractArray, p::AbstractArray) = size(u) == size(p) - -__compatible(u::Number, ::SciMLBase.AbstractNonlinearAlgorithm) = true -__compatible(u::Number, ::Union{CMINPACK, NLsolveJL, KINSOL}) = true -__compatible(u::AbstractArray, ::SciMLBase.AbstractNonlinearAlgorithm) = true -__compatible(u::AbstractArray{T, N}, ::KINSOL) where {T, N} = N == 1 # Need to be fixed upstream -__compatible(u::StaticArray{S, T, N}, ::KINSOL) where {S <: Tuple, T, N} = false -__compatible(u::StaticArray, ::SciMLBase.AbstractNonlinearAlgorithm) = true -__compatible(u::StaticArray, ::Union{CMINPACK, NLsolveJL, KINSOL}) = false -__compatible(u, ::Nothing) = true - -__compatible(::Any, ::Any) = true -__compatible(::CMINPACK, ::Val{:iip_cache}) = false -__compatible(::CMINPACK, ::Val{:oop_cache}) = false -__compatible(::NLsolveJL, ::Val{:iip_cache}) = false -__compatible(::NLsolveJL, ::Val{:oop_cache}) = false -__compatible(::KINSOL, ::Val{:iip_cache}) = false -__compatible(::KINSOL, ::Val{:oop_cache}) = false - -export test_f!, test_f, jacobian_f, solve_with, __compatible -end - -@testitem "ForwardDiff.jl Integration" setup=[ForwardADTesting] tags=[:core] begin - for alg in (NewtonRaphson(), TrustRegion(), LevenbergMarquardt(), - PseudoTransient(; alpha_initial = 10.0), Broyden(), Klement(), DFSane(), - nothing, NLsolveJL(), CMINPACK(), KINSOL(; globalization_strategy = :LineSearch)) - us = (2.0, @SVector[1.0, 1.0], [1.0, 1.0], ones(2, 2), @SArray ones(2, 2)) - - alg isa CMINPACK && Sys.isapple() && continue - - @testset "Scalar AD" begin - for p in 1.0:0.1:100.0, u0 in us, mode in (:iip, :oop, :iip_cache, :oop_cache) - __compatible(u0, alg) || continue - __compatible(u0, Val(mode)) || continue - __compatible(alg, Val(mode)) || continue - - sol = solve(NonlinearProblem(test_f, u0, p), alg) - if SciMLBase.successful_retcode(sol) - gs = abs.(ForwardDiff.derivative(solve_with(Val{mode}(), u0, alg), p)) - gs_true = abs.(jacobian_f(u0, p)) - if !(isapprox(gs, gs_true, atol = 1e-5)) - @error "ForwardDiff Failed for u0=$(u0) and p=$(p) with $(alg)" forwardiff_gradient=gs true_gradient=gs_true - else - @test abs.(gs)≈abs.(gs_true) atol=1e-5 - end - end - end - end - - @testset "Jacobian" begin - for u0 in us, - p in ([2.0, 1.0], [2.0 1.0; 3.0 4.0]), - mode in (:iip, :oop, :iip_cache, :oop_cache) - - __compatible(u0, p) || continue - __compatible(u0, alg) || continue - __compatible(u0, Val(mode)) || continue - __compatible(alg, Val(mode)) || continue - - sol = solve(NonlinearProblem(test_f, u0, p), alg) - if SciMLBase.successful_retcode(sol) - gs = abs.(ForwardDiff.jacobian(solve_with(Val{mode}(), u0, alg), p)) - gs_true = abs.(jacobian_f(u0, p)) - if !(isapprox(gs, gs_true, atol = 1e-5)) - @show sol.retcode, sol.u - @error "ForwardDiff Failed for u0=$(u0) and p=$(p) with $(alg)" forwardiff_jacobian=gs true_jacobian=gs_true - else - @test abs.(gs)≈abs.(gs_true) atol=1e-5 - end - end - end - end - end -end diff --git a/test/core/nlls_tests.jl b/test/core/nlls_tests.jl deleted file mode 100644 index 85f4ba4ba..000000000 --- a/test/core/nlls_tests.jl +++ /dev/null @@ -1,21 +0,0 @@ - -# TODO: Test polyalg here - -@testitem "NLLS Analytic Jacobian" tags=[:core] begin - dataIn = 1:10 - f(x, p) = x[1] * dataIn .^ 2 .+ x[2] * dataIn .+ x[3] - dataOut = f([1, 2, 3], nothing) + 0.1 * randn(10, 1) - - resid(x, p) = f(x, p) - dataOut - jac(x, p) = [dataIn .^ 2 dataIn ones(10, 1)] - x0 = [1, 1, 1] - - prob = NonlinearLeastSquaresProblem(resid, x0) - sol1 = solve(prob) - - nlfunc = NonlinearFunction(resid; jac) - prob = NonlinearLeastSquaresProblem(nlfunc, x0) - sol2 = solve(prob) - - @test sol1.u ≈ sol2.u -end diff --git a/test/core_tests.jl b/test/core_tests.jl new file mode 100644 index 000000000..301b0b389 --- /dev/null +++ b/test/core_tests.jl @@ -0,0 +1,416 @@ +@testitem "NLLS Analytic Jacobian" tags=[:core] begin + dataIn = 1:10 + f(x, p) = x[1] * dataIn .^ 2 .+ x[2] * dataIn .+ x[3] + dataOut = f([1, 2, 3], nothing) + 0.1 * randn(10, 1) + + resid(x, p) = f(x, p) - dataOut + jac(x, p) = [dataIn .^ 2 dataIn ones(10, 1)] + x0 = [1, 1, 1] + + prob = NonlinearLeastSquaresProblem(resid, x0) + sol1 = solve(prob) + + nlfunc = NonlinearFunction(resid; jac) + prob = NonlinearLeastSquaresProblem(nlfunc, x0) + sol2 = solve(prob) + + @test sol1.u ≈ sol2.u +end + +@testitem "Basic PolyAlgorithms" tags=[:core] begin + f(u, p) = u .* u .- 2 + u0 = [1.0, 1.0] + + prob = NonlinearProblem(f, u0) + + polyalgs = ( + RobustMultiNewton(), FastShortcutNonlinearPolyalg(), nothing, missing, + NonlinearSolvePolyAlgorithm((Broyden(), LimitedMemoryBroyden())) + ) + + @testset "Direct Solve" begin + @testset for alg in polyalgs + alg = alg === missing ? () : (alg,) + sol = solve(prob, alg...; abstol = 1e-9) + @test SciMLBase.successful_retcode(sol) + err = maximum(abs, f(sol.u, 2.0)) + @test err < 1e-9 + end + end + + @testset "Caching Interface" begin + @testset for alg in polyalgs + alg = alg === missing ? () : (alg,) + cache = init(prob, alg...; abstol = 1e-9) + solver = solve!(cache) + @test SciMLBase.successful_retcode(solver) + end + end + + @testset "Step Interface" begin + @testset for alg in polyalgs + alg = alg === missing ? () : (alg,) + cache = init(prob, alg...; abstol = 1e-9) + for i in 1:10000 + step!(cache) + cache.force_stop && break + end + @test SciMLBase.successful_retcode(cache.retcode) + end + end +end + +@testitem "PolyAlgorithms Autodiff" tags=[:core] begin + cache = zeros(2) + function f(du, u, p) + cache .= u .* u + du .= cache .- 2 + end + u0 = [1.0, 1.0] + probN = NonlinearProblem{true}(f, u0) + + custom_polyalg = NonlinearSolvePolyAlgorithm(( + Broyden(; autodiff = AutoFiniteDiff()), LimitedMemoryBroyden()) + ) + + # Uses the `__solve` function + @test_throws MethodError solve(probN; abstol = 1e-9) + @test_throws MethodError solve(probN, RobustMultiNewton()) + + sol = solve(probN, RobustMultiNewton(; autodiff = AutoFiniteDiff())) + @test SciMLBase.successful_retcode(sol) + + sol = solve( + probN, FastShortcutNonlinearPolyalg(; autodiff = AutoFiniteDiff()); abstol = 1e-9 + ) + @test SciMLBase.successful_retcode(sol) + + quadratic_f(u::Float64, p) = u^2 - p + + prob = NonlinearProblem(quadratic_f, 2.0, 4.0) + + @test_throws MethodError solve(prob) + @test_throws MethodError solve(prob, RobustMultiNewton()) + + sol = solve(prob, RobustMultiNewton(; autodiff = AutoFiniteDiff())) + @test SciMLBase.successful_retcode(sol) +end + +@testitem "PolyAlgorithm Aliasing" tags=[:core] begin + using NonlinearProblemLibrary + + # Use a problem that the initial solvers cannot solve and cause the initial value to + # diverge. If we don't alias correctly, all the subsequent algorithms will also fail. + prob = NonlinearProblemLibrary.nlprob_23_testcases["Generalized Rosenbrock function"].prob + u0 = copy(prob.u0) + prob = remake(prob; u0 = copy(u0)) + + # If aliasing is not handled properly this will diverge + sol = solve( + prob; abstol = 1e-6, alias_u0 = true, + termination_condition = AbsNormTerminationMode(Base.Fix1(maximum, abs)) + ) + + @test sol.u === prob.u0 + @test SciMLBase.successful_retcode(sol.retcode) + + prob = remake(prob; u0 = copy(u0)) + + cache = init( + prob; abstol = 1e-6, alias_u0 = true, + termination_condition = AbsNormTerminationMode(Base.Fix1(maximum, abs)) + ) + sol = solve!(cache) + + @test sol.u === prob.u0 + @test SciMLBase.successful_retcode(sol.retcode) +end + +@testitem "Ensemble Nonlinear Problems" tags=[:core] begin + prob_func(prob, i, repeat) = remake(prob; u0 = prob.u0[:, i]) + + prob_nls_oop = NonlinearProblem((u, p) -> u .* u .- p, rand(4, 128), 2.0) + prob_nls_iip = NonlinearProblem((du, u, p) -> du .= u .* u .- p, rand(4, 128), 2.0) + prob_nlls_oop = NonlinearLeastSquaresProblem((u, p) -> u .^ 2 .- p, rand(4, 128), 2.0) + prob_nlls_iip = NonlinearLeastSquaresProblem( + NonlinearFunction{true}((du, u, p) -> du .= u .^ 2 .- p; resid_prototype = rand(4)), + rand(4, 128), 2.0 + ) + + for prob in (prob_nls_oop, prob_nls_iip, prob_nlls_oop, prob_nlls_iip) + ensembleprob = EnsembleProblem(prob; prob_func) + + for ensemblealg in (EnsembleThreads(), EnsembleSerial()) + sim = solve(ensembleprob, nothing, ensemblealg; trajectories = size(prob.u0, 2)) + @test all(SciMLBase.successful_retcode, sim.u) + end + end +end + +@testitem "BigFloat Support" tags=[:core] begin + using LinearAlgebra + + fn_iip = NonlinearFunction{true}((du, u, p) -> du .= u .* u .- p) + fn_oop = NonlinearFunction{false}((u, p) -> u .* u .- p) + + u0 = BigFloat[1.0, 1.0, 1.0] + prob_iip_bf = NonlinearProblem{true}(fn_iip, u0, BigFloat(2)) + prob_oop_bf = NonlinearProblem{false}(fn_oop, u0, BigFloat(2)) + + for alg in (NewtonRaphson(), Broyden(), Klement(), DFSane(), TrustRegion()) + sol = solve(prob_oop_bf, alg) + @test norm(sol.resid, Inf) < 1e-6 + @test SciMLBase.successful_retcode(sol.retcode) + + sol = solve(prob_iip_bf, alg) + @test norm(sol.resid, Inf) < 1e-6 + @test SciMLBase.successful_retcode(sol.retcode) + end +end + +@testitem "Singular Exception: Issue #153" tags=[:core] begin + function f(du, u, p) + s1, s1s2, s2 = u + k1, c1, Δt = p + + du[1] = -0.25 * c1 * k1 * s1 * s2 + du[2] = 0.25 * c1 * k1 * s1 * s2 + du[3] = -0.25 * c1 * k1 * s1 * s2 + end + + prob = NonlinearProblem(f, [2.0, 2.0, 2.0], [1.0, 2.0, 2.5]) + sol = solve(prob; abstol = 1e-9) + @test SciMLBase.successful_retcode(sol) +end + +@testitem "Simple Scalar Problem: Issue #187" tags=[:core] begin + using NaNMath + + # https://github.com/SciML/NonlinearSolve.jl/issues/187 + # If we use a General Nonlinear Solver the solution might go out of the domain! + ff_interval(u, p) = 0.5 / 1.5 * NaNMath.log.(u ./ (1.0 .- u)) .- 2.0 * u .+ 1.0 + + uspan = (0.02, 0.1) + prob = IntervalNonlinearProblem(ff_interval, uspan) + sol = solve(prob; abstol = 1e-9) + @test SciMLBase.successful_retcode(sol) + + u0 = 0.06 + p = 2.0 + prob = NonlinearProblem(ff_interval, u0, p) + sol = solve(prob; abstol = 1e-9) + @test SciMLBase.successful_retcode(sol) +end + +# Shooting Problem: Taken from BoundaryValueDiffEq.jl +# Testing for Complex Valued Root Finding. For Complex valued inputs we drop some of the +# algorithms which dont support those. +@testitem "Complex Valued Problems: Single-Shooting" tags=[:core] begin + using OrdinaryDiffEqTsit5 + + function ode_func!(du, u, p, t) + du[1] = u[2] + du[2] = -u[1] + return nothing + end + + function objective_function!(resid, u0, p) + odeprob = ODEProblem{true}(ode_func!, u0, (0.0, 100.0), p) + sol = solve(odeprob, Tsit5(), abstol = 1e-9, reltol = 1e-9, verbose = false) + resid[1] = sol(0.0)[1] + resid[2] = sol(100.0)[1] - 1.0 + return nothing + end + + prob = NonlinearProblem{true}(objective_function!, [0.0, 1.0] .+ 1im) + sol = solve(prob; abstol = 1e-10) + @test SciMLBase.successful_retcode(sol) + # This test is not meant to return success but test that all the default solvers can handle + # complex valued problems + @test_nowarn solve(prob; abstol = 1e-19, maxiters = 10) + @test_nowarn solve( + prob, RobustMultiNewton(eltype(prob.u0)); abstol = 1e-19, maxiters = 10 + ) +end + +@testitem "No AD" tags=[:core] begin + no_ad_fast = FastShortcutNonlinearPolyalg(autodiff = AutoFiniteDiff()) + no_ad_robust = RobustMultiNewton(autodiff = AutoFiniteDiff()) + no_ad_algs = Set([no_ad_fast, no_ad_robust, no_ad_fast.algs..., no_ad_robust.algs...]) + + @testset "Inplace" begin + f_iip = Base.Experimental.@opaque (du, u, p) -> du .= u .* u .- p + u0 = [0.5] + prob = NonlinearProblem(f_iip, u0, 1.0) + for alg in no_ad_algs + sol = solve(prob, alg) + @test isapprox(only(sol.u), 1.0) + @test SciMLBase.successful_retcode(sol.retcode) + end + end + + @testset "Out of Place" begin + f_oop = Base.Experimental.@opaque (u, p) -> u .* u .- p + u0 = [0.5] + prob = NonlinearProblem{false}(f_oop, u0, 1.0) + for alg in no_ad_algs + sol = solve(prob, alg) + @test isapprox(only(sol.u), 1.0) + @test SciMLBase.successful_retcode(sol.retcode) + end + end +end + +@testitem "Infeasible" tags=[:core] begin + using LinearAlgebra, StaticArrays + + # this is infeasible + function f1_infeasible!(out, u, p) + μ = 3.986004415e14 + x = 7000.0e3 + y = -6.970561549987071e-9 + z = -3.784706123246018e-9 + v_x = 8.550491684548064e-12 + u[1] + v_y = 6631.60076191005 + u[2] + v_z = 3600.665431405663 + u[3] + r = @SVector [x, y, z] + v = @SVector [v_x, v_y, v_z] + h = cross(r, v) + ev = cross(v, h) / μ - r / norm(r) + i = acos(h[3] / norm(h)) + e = norm(ev) + a = 1 / (2 / norm(r) - (norm(v)^2 / μ)) + out .= [a - 42.0e6, e - 1e-5, i - 1e-5] + return nothing + end + + function f1_infeasible(u, p) + μ = 3.986004415e14 + x = 7000.0e3 + y = -6.970561549987071e-9 + z = -3.784706123246018e-9 + v_x = 8.550491684548064e-12 + u[1] + v_y = 6631.60076191005 + u[2] + v_z = 3600.665431405663 + u[3] + r = [x, y, z] + v = [v_x, v_y, v_z] + h = cross(r, v) + ev = cross(v, h) / μ - r / norm(r) + i = acos(h[3] / norm(h)) + e = norm(ev) + a = 1 / (2 / norm(r) - (norm(v)^2 / μ)) + return [a - 42.0e6, e - 1e-5, i - 1e-5] + end + + u0 = [0.0, 0.0, 0.0] + prob = NonlinearProblem(f1_infeasible!, u0) + sol = solve(prob) + + @test all(!isnan, sol.u) + @test !SciMLBase.successful_retcode(sol.retcode) + @inferred solve(prob) + + u0 = [0.0, 0.0, 0.0] + prob = NonlinearProblem(f1_infeasible, u0) + sol = solve(prob) + + @test all(!isnan, sol.u) + @test !SciMLBase.successful_retcode(sol.retcode) + @inferred solve(prob) + + u0 = @SVector [0.0, 0.0, 0.0] + prob = NonlinearProblem(f1_infeasible, u0) + + sol = solve(prob) + @test all(!isnan, sol.u) + @test !SciMLBase.successful_retcode(sol.retcode) +end + +@testitem "NoInit Caching" tags=[:core] begin + using LinearAlgebra + + solvers = [ + SimpleNewtonRaphson(), SimpleTrustRegion(), SimpleDFSane() + ] + + prob = NonlinearProblem((u, p) -> u .^ 2 .- p, [0.1, 0.3], 2.0) + + for alg in solvers + cache = init(prob, alg) + sol = solve!(cache) + @test SciMLBase.successful_retcode(sol) + @test norm(sol.resid, Inf) ≤ 1e-6 + + reinit!(cache; p = 5.0) + @test cache.prob.p == 5.0 + sol = solve!(cache) + @test SciMLBase.successful_retcode(sol) + @test norm(sol.resid, Inf) ≤ 1e-6 + @test norm(sol.u .^ 2 .- 5.0, Inf) ≤ 1e-6 + end +end + +@testitem "Out-of-place Matrix Resizing" tags=[:core] begin + using StableRNGs + + ff(u, p) = u .* u .- p + u0 = rand(StableRNG(0), 2, 2) + p = 2.0 + vecprob = NonlinearProblem(ff, vec(u0), p) + prob = NonlinearProblem(ff, u0, p) + + for alg in ( + NewtonRaphson(), TrustRegion(), LevenbergMarquardt(), + PseudoTransient(), RobustMultiNewton(), FastShortcutNonlinearPolyalg(), + Broyden(), Klement(), LimitedMemoryBroyden(; threshold = 2) + ) + @test vec(solve(prob, alg).u) == solve(vecprob, alg).u + end +end + +@testitem "Inplace Matrix Resizing" tags=[:core] begin + using StableRNGs + + fiip(du, u, p) = (du .= u .* u .- p) + u0 = rand(StableRNG(0), 2, 2) + p = 2.0 + vecprob = NonlinearProblem(fiip, vec(u0), p) + prob = NonlinearProblem(fiip, u0, p) + + for alg in ( + NewtonRaphson(), TrustRegion(), LevenbergMarquardt(), + PseudoTransient(), RobustMultiNewton(), FastShortcutNonlinearPolyalg(), + Broyden(), Klement(), LimitedMemoryBroyden(; threshold = 2) + ) + @test vec(solve(prob, alg).u) == solve(vecprob, alg).u + end +end + +@testitem "Singular Systems -- Auto Linear Solve Switching" tags=[:core] begin + using LinearAlgebra + + function f!(du, u, p) + du[1] = 2u[1] - 2 + du[2] = (u[1] - 4u[2])^2 + 0.1 + end + + u0 = [0.0, 0.0] # Singular Jacobian at u0 + + prob = NonlinearProblem(f!, u0) + + sol = solve(prob) # This doesn't have a root so let's just test the switching + @test sol.u≈[1.0, 0.25] atol=1e-3 rtol=1e-3 + + function nlls!(du, u, p) + du[1] = 2u[1] - 2 + du[2] = (u[1] - 4u[2])^2 + 0.1 + du[3] = 0 + end + + u0 = [0.0, 0.0] + + prob = NonlinearProblem(NonlinearFunction(nlls!, resid_prototype = zeros(3)), u0) + + solve(prob) + @test sol.u≈[1.0, 0.25] atol=1e-3 rtol=1e-3 +end diff --git a/test/gpu/core_tests.jl b/test/cuda_tests.jl similarity index 100% rename from test/gpu/core_tests.jl rename to test/cuda_tests.jl diff --git a/test/forward_ad_tests.jl b/test/forward_ad_tests.jl new file mode 100644 index 000000000..e40b0a91d --- /dev/null +++ b/test/forward_ad_tests.jl @@ -0,0 +1,116 @@ +# @testsetup module ForwardADTesting +# using Reexport, NonlinearSolve +# @reexport using ForwardDiff, MINPACK, NLsolve, StaticArrays, Sundials, LinearAlgebra + +# test_f!(du, u, p) = (@. du = u^2 - p) +# test_f(u, p) = (@. u^2 - p) + +# jacobian_f(::Number, p) = 1 / (2 * √p) +# jacobian_f(::Number, p::Number) = 1 / (2 * √p) +# jacobian_f(u, p::Number) = one.(u) .* (1 / (2 * √p)) +# jacobian_f(u, p::AbstractArray) = diagm(vec(@. 1 / (2 * √p))) + +# function solve_with(::Val{mode}, u, alg) where {mode} +# f = if mode === :iip +# solve_iip(p) = solve(NonlinearProblem(test_f!, u, p), alg).u +# elseif mode === :iip_cache +# function solve_iip_init(p) +# cache = SciMLBase.init(NonlinearProblem(test_f!, u, p), alg) +# return SciMLBase.solve!(cache).u +# end +# elseif mode === :oop +# solve_oop(p) = solve(NonlinearProblem(test_f, u, p), alg).u +# elseif mode === :oop_cache +# function solve_oop_init(p) +# cache = SciMLBase.init(NonlinearProblem(test_f, u, p), alg) +# return SciMLBase.solve!(cache).u +# end +# end +# return f +# end + +# __compatible(::Any, ::Val{:oop}) = true +# __compatible(::Any, ::Val{:oop_cache}) = true +# __compatible(::Number, ::Val{:iip}) = false +# __compatible(::AbstractArray, ::Val{:iip}) = true +# __compatible(::StaticArray, ::Val{:iip}) = false +# __compatible(::Number, ::Val{:iip_cache}) = false +# __compatible(::AbstractArray, ::Val{:iip_cache}) = true +# __compatible(::StaticArray, ::Val{:iip_cache}) = false + +# __compatible(::Any, ::Number) = true +# __compatible(::Number, ::AbstractArray) = false +# __compatible(u::AbstractArray, p::AbstractArray) = size(u) == size(p) + +# __compatible(u::Number, ::SciMLBase.AbstractNonlinearAlgorithm) = true +# __compatible(u::Number, ::Union{CMINPACK, NLsolveJL, KINSOL}) = true +# __compatible(u::AbstractArray, ::SciMLBase.AbstractNonlinearAlgorithm) = true +# __compatible(u::AbstractArray{T, N}, ::KINSOL) where {T, N} = N == 1 # Need to be fixed upstream +# __compatible(u::StaticArray{S, T, N}, ::KINSOL) where {S <: Tuple, T, N} = false +# __compatible(u::StaticArray, ::SciMLBase.AbstractNonlinearAlgorithm) = true +# __compatible(u::StaticArray, ::Union{CMINPACK, NLsolveJL, KINSOL}) = false +# __compatible(u, ::Nothing) = true + +# __compatible(::Any, ::Any) = true +# __compatible(::CMINPACK, ::Val{:iip_cache}) = false +# __compatible(::CMINPACK, ::Val{:oop_cache}) = false +# __compatible(::NLsolveJL, ::Val{:iip_cache}) = false +# __compatible(::NLsolveJL, ::Val{:oop_cache}) = false +# __compatible(::KINSOL, ::Val{:iip_cache}) = false +# __compatible(::KINSOL, ::Val{:oop_cache}) = false + +# export test_f!, test_f, jacobian_f, solve_with, __compatible +# end + +# @testitem "ForwardDiff.jl Integration" setup=[ForwardADTesting] tags=[:core] begin +# for alg in (NewtonRaphson(), TrustRegion(), LevenbergMarquardt(), +# PseudoTransient(; alpha_initial = 10.0), Broyden(), Klement(), DFSane(), +# nothing, NLsolveJL(), CMINPACK(), KINSOL(; globalization_strategy = :LineSearch)) +# us = (2.0, @SVector[1.0, 1.0], [1.0, 1.0], ones(2, 2), @SArray ones(2, 2)) + +# alg isa CMINPACK && Sys.isapple() && continue + +# @testset "Scalar AD" begin +# for p in 1.0:0.1:100.0, u0 in us, mode in (:iip, :oop, :iip_cache, :oop_cache) +# __compatible(u0, alg) || continue +# __compatible(u0, Val(mode)) || continue +# __compatible(alg, Val(mode)) || continue + +# sol = solve(NonlinearProblem(test_f, u0, p), alg) +# if SciMLBase.successful_retcode(sol) +# gs = abs.(ForwardDiff.derivative(solve_with(Val{mode}(), u0, alg), p)) +# gs_true = abs.(jacobian_f(u0, p)) +# if !(isapprox(gs, gs_true, atol = 1e-5)) +# @error "ForwardDiff Failed for u0=$(u0) and p=$(p) with $(alg)" forwardiff_gradient=gs true_gradient=gs_true +# else +# @test abs.(gs)≈abs.(gs_true) atol=1e-5 +# end +# end +# end +# end + +# @testset "Jacobian" begin +# for u0 in us, +# p in ([2.0, 1.0], [2.0 1.0; 3.0 4.0]), +# mode in (:iip, :oop, :iip_cache, :oop_cache) + +# __compatible(u0, p) || continue +# __compatible(u0, alg) || continue +# __compatible(u0, Val(mode)) || continue +# __compatible(alg, Val(mode)) || continue + +# sol = solve(NonlinearProblem(test_f, u0, p), alg) +# if SciMLBase.successful_retcode(sol) +# gs = abs.(ForwardDiff.jacobian(solve_with(Val{mode}(), u0, alg), p)) +# gs_true = abs.(jacobian_f(u0, p)) +# if !(isapprox(gs, gs_true, atol = 1e-5)) +# @show sol.retcode, sol.u +# @error "ForwardDiff Failed for u0=$(u0) and p=$(p) with $(alg)" forwardiff_jacobian=gs true_jacobian=gs_true +# else +# @test abs.(gs)≈abs.(gs_true) atol=1e-5 +# end +# end +# end +# end +# end +# end diff --git a/test/misc/aliasing_tests.jl b/test/misc/aliasing_tests.jl deleted file mode 100644 index 78b8ec798..000000000 --- a/test/misc/aliasing_tests.jl +++ /dev/null @@ -1,25 +0,0 @@ -@testitem "PolyAlgorithm Aliasing" tags=[:misc] begin - using NonlinearProblemLibrary - - # Use a problem that the initial solvers cannot solve and cause the initial value to - # diverge. If we don't alias correctly, all the subsequent algorithms will also fail. - prob = NonlinearProblemLibrary.nlprob_23_testcases["Generalized Rosenbrock function"].prob - u0 = copy(prob.u0) - prob = remake(prob; u0 = copy(u0)) - - # If aliasing is not handled properly this will diverge - sol = solve(prob; abstol = 1e-6, alias_u0 = true, - termination_condition = AbsNormTerminationMode(Base.Fix1(maximum, abs))) - - @test sol.u === prob.u0 - @test SciMLBase.successful_retcode(sol.retcode) - - prob = remake(prob; u0 = copy(u0)) - - cache = init(prob; abstol = 1e-6, alias_u0 = true, - termination_condition = AbsNormTerminationMode(Base.Fix1(maximum, abs))) - sol = solve!(cache) - - @test sol.u === prob.u0 - @test SciMLBase.successful_retcode(sol.retcode) -end diff --git a/test/misc/ensemble_tests.jl b/test/misc/ensemble_tests.jl deleted file mode 100644 index c034bf6bc..000000000 --- a/test/misc/ensemble_tests.jl +++ /dev/null @@ -1,21 +0,0 @@ -@testitem "Ensemble Nonlinear Problems" tags=[:misc] begin - using NonlinearSolve - - prob_func(prob, i, repeat) = remake(prob; u0 = prob.u0[:, i]) - - prob_nls_oop = NonlinearProblem((u, p) -> u .* u .- p, rand(4, 128), 2.0) - prob_nls_iip = NonlinearProblem((du, u, p) -> du .= u .* u .- p, rand(4, 128), 2.0) - prob_nlls_oop = NonlinearLeastSquaresProblem((u, p) -> u .^ 2 .- p, rand(4, 128), 2.0) - prob_nlls_iip = NonlinearLeastSquaresProblem( - NonlinearFunction{true}((du, u, p) -> du .= u .^ 2 .- p; resid_prototype = rand(4)), - rand(4, 128), 2.0) - - for prob in (prob_nls_oop, prob_nls_iip, prob_nlls_oop, prob_nlls_iip) - ensembleprob = EnsembleProblem(prob; prob_func) - - for ensemblealg in (EnsembleThreads(), EnsembleSerial()) - sim = solve(ensembleprob, nothing, ensemblealg; trajectories = size(prob.u0, 2)) - @test all(SciMLBase.successful_retcode, sim.u) - end - end -end diff --git a/test/misc/exotic_type_tests.jl b/test/misc/exotic_type_tests.jl deleted file mode 100644 index 57a9c28ed..000000000 --- a/test/misc/exotic_type_tests.jl +++ /dev/null @@ -1,27 +0,0 @@ -# File for different types of exotic types -@testsetup module NonlinearSolveExoticTypeTests -using NonlinearSolve - -fn_iip = NonlinearFunction{true}((du, u, p) -> du .= u .* u .- p) -fn_oop = NonlinearFunction{false}((u, p) -> u .* u .- p) - -u0 = BigFloat[1.0, 1.0, 1.0] -prob_iip_bf = NonlinearProblem{true}(fn_iip, u0, BigFloat(2)) -prob_oop_bf = NonlinearProblem{false}(fn_oop, u0, BigFloat(2)) - -export fn_iip, fn_oop, u0, prob_iip_bf, prob_oop_bf -end - -@testitem "BigFloat Support" tags=[:misc] setup=[NonlinearSolveExoticTypeTests] begin - using NonlinearSolve, LinearAlgebra - - for alg in [NewtonRaphson(), Broyden(), Klement(), DFSane(), TrustRegion()] - sol = solve(prob_oop_bf, alg) - @test norm(sol.resid, Inf) < 1e-6 - @test SciMLBase.successful_retcode(sol.retcode) - - sol = solve(prob_iip_bf, alg) - @test norm(sol.resid, Inf) < 1e-6 - @test SciMLBase.successful_retcode(sol.retcode) - end -end diff --git a/test/misc/linsolve_switching_tests.jl b/test/misc/linsolve_switching_tests.jl deleted file mode 100644 index 34d10e294..000000000 --- a/test/misc/linsolve_switching_tests.jl +++ /dev/null @@ -1,28 +0,0 @@ -@testitem "Singular Systems -- Auto Linear Solve Switching" tags=[:misc] begin - using LinearSolve, NonlinearSolve - - function f!(du, u, p) - du[1] = 2u[1] - 2 - du[2] = (u[1] - 4u[2])^2 + 0.1 - end - - u0 = [0.0, 0.0] # Singular Jacobian at u0 - - prob = NonlinearProblem(f!, u0) - - sol = solve(prob) # This doesn't have a root so let's just test the switching - @test sol.u≈[1.0, 0.25] atol=1e-3 rtol=1e-3 - - function nlls!(du, u, p) - du[1] = 2u[1] - 2 - du[2] = (u[1] - 4u[2])^2 + 0.1 - du[3] = 0 - end - - u0 = [0.0, 0.0] - - prob = NonlinearProblem(NonlinearFunction(nlls!, resid_prototype = zeros(3)), u0) - - solve(prob) - @test sol.u≈[1.0, 0.25] atol=1e-3 rtol=1e-3 -end diff --git a/test/misc/matrix_resizing_tests.jl b/test/misc/matrix_resizing_tests.jl deleted file mode 100644 index 2aa8151ce..000000000 --- a/test/misc/matrix_resizing_tests.jl +++ /dev/null @@ -1,31 +0,0 @@ -@testitem "Out-of-place Matrix Resizing" tags=[:misc] begin - using StableRNGs - - ff(u, p) = u .* u .- p - u0 = rand(StableRNG(0), 2, 2) - p = 2.0 - vecprob = NonlinearProblem(ff, vec(u0), p) - prob = NonlinearProblem(ff, u0, p) - - for alg in (NewtonRaphson(), TrustRegion(), LevenbergMarquardt(), - PseudoTransient(), RobustMultiNewton(), FastShortcutNonlinearPolyalg(), - Broyden(), Klement(), LimitedMemoryBroyden(; threshold = 2)) - @test vec(solve(prob, alg).u) == solve(vecprob, alg).u - end -end - -@testitem "Inplace Matrix Resizing" tags=[:misc] begin - using StableRNGs - - fiip(du, u, p) = (du .= u .* u .- p) - u0 = rand(StableRNG(0), 2, 2) - p = 2.0 - vecprob = NonlinearProblem(fiip, vec(u0), p) - prob = NonlinearProblem(fiip, u0, p) - - for alg in (NewtonRaphson(), TrustRegion(), LevenbergMarquardt(), - PseudoTransient(), RobustMultiNewton(), FastShortcutNonlinearPolyalg(), - Broyden(), Klement(), LimitedMemoryBroyden(; threshold = 2)) - @test vec(solve(prob, alg).u) == solve(vecprob, alg).u - end -end diff --git a/test/misc/noinit_caching_tests.jl b/test/misc/noinit_caching_tests.jl deleted file mode 100644 index 4ab4138f8..000000000 --- a/test/misc/noinit_caching_tests.jl +++ /dev/null @@ -1,23 +0,0 @@ -@testitem "NoInit Caching" tags=[:misc] begin - using LinearAlgebra - import NLsolve, NLSolvers - - solvers = [SimpleNewtonRaphson(), SimpleTrustRegion(), SimpleDFSane(), NLsolveJL(), - NLSolversJL(NLSolvers.LineSearch(NLSolvers.Newton(), NLSolvers.Backtracking()))] - - prob = NonlinearProblem((u, p) -> u .^ 2 .- p, [0.1, 0.3], 2.0) - - for alg in solvers - cache = init(prob, alg) - sol = solve!(cache) - @test SciMLBase.successful_retcode(sol) - @test norm(sol.resid, Inf) ≤ 1e-6 - - reinit!(cache; p = 5.0) - @test cache.prob.p == 5.0 - sol = solve!(cache) - @test SciMLBase.successful_retcode(sol) - @test norm(sol.resid, Inf) ≤ 1e-6 - @test norm(sol.u .^ 2 .- 5.0, Inf) ≤ 1e-6 - end -end diff --git a/test/misc/polyalg_tests.jl b/test/misc/polyalg_tests.jl deleted file mode 100644 index a106ace44..000000000 --- a/test/misc/polyalg_tests.jl +++ /dev/null @@ -1,256 +0,0 @@ -@testitem "Basic PolyAlgorithms" tags=[:misc] begin - f(u, p) = u .* u .- 2 - u0 = [1.0, 1.0] - probN = NonlinearProblem{false}(f, u0) - - custom_polyalg = NonlinearSolvePolyAlgorithm((Broyden(), LimitedMemoryBroyden())) - - # Uses the `__solve` function - solver = solve(probN; abstol = 1e-9) - @test SciMLBase.successful_retcode(solver) - solver = solve(probN, RobustMultiNewton(); abstol = 1e-9) - @test SciMLBase.successful_retcode(solver) - solver = solve(probN, FastShortcutNonlinearPolyalg(); abstol = 1e-9) - @test SciMLBase.successful_retcode(solver) - solver = solve(probN, custom_polyalg; abstol = 1e-9) - @test SciMLBase.successful_retcode(solver) - - # Test the caching interface - cache = init(probN; abstol = 1e-9) - solver = solve!(cache) - @test SciMLBase.successful_retcode(solver) - cache = init(probN, RobustMultiNewton(); abstol = 1e-9) - solver = solve!(cache) - @test SciMLBase.successful_retcode(solver) - cache = init(probN, FastShortcutNonlinearPolyalg(); abstol = 1e-9) - solver = solve!(cache) - @test SciMLBase.successful_retcode(solver) - cache = init(probN, custom_polyalg; abstol = 1e-9) - solver = solve!(cache) - @test SciMLBase.successful_retcode(solver) - - # Test the step interface - cache = init(probN; abstol = 1e-9) - for i in 1:10000 - step!(cache) - cache.force_stop && break - end - @test SciMLBase.successful_retcode(cache.retcode) - cache = init(probN, RobustMultiNewton(); abstol = 1e-9) - for i in 1:10000 - step!(cache) - cache.force_stop && break - end - @test SciMLBase.successful_retcode(cache.retcode) - cache = init(probN, FastShortcutNonlinearPolyalg(); abstol = 1e-9) - for i in 1:10000 - step!(cache) - cache.force_stop && break - end - @test SciMLBase.successful_retcode(cache.retcode) - cache = init(probN, custom_polyalg; abstol = 1e-9) - for i in 1:10000 - step!(cache) - cache.force_stop && break - end -end - -@testitem "Testing #153 Singular Exception" tags=[:misc] begin - # https://github.com/SciML/NonlinearSolve.jl/issues/153 - function f(du, u, p) - s1, s1s2, s2 = u - k1, c1, Δt = p - - du[1] = -0.25 * c1 * k1 * s1 * s2 - du[2] = 0.25 * c1 * k1 * s1 * s2 - du[3] = -0.25 * c1 * k1 * s1 * s2 - end - - prob = NonlinearProblem(f, [2.0, 2.0, 2.0], [1.0, 2.0, 2.5]) - sol = solve(prob; abstol = 1e-9) - @test SciMLBase.successful_retcode(sol) -end - -@testitem "PolyAlgorithms Autodiff" tags=[:misc] begin - cache = zeros(2) - function f(du, u, p) - cache .= u .* u - du .= cache .- 2 - end - u0 = [1.0, 1.0] - probN = NonlinearProblem{true}(f, u0) - - custom_polyalg = NonlinearSolvePolyAlgorithm(( - Broyden(; autodiff = AutoFiniteDiff()), LimitedMemoryBroyden())) - - # Uses the `__solve` function - @test_throws MethodError solve(probN; abstol = 1e-9) - @test_throws MethodError solve(probN, RobustMultiNewton(); abstol = 1e-9) - sol = solve(probN, RobustMultiNewton(; autodiff = AutoFiniteDiff()); abstol = 1e-9) - @test SciMLBase.successful_retcode(sol) - sol = solve( - probN, FastShortcutNonlinearPolyalg(; autodiff = AutoFiniteDiff()); abstol = 1e-9) - @test SciMLBase.successful_retcode(sol) - sol = solve(probN, custom_polyalg; abstol = 1e-9) - @test SciMLBase.successful_retcode(sol) - - quadratic_f(u::Float64, p) = u^2 - p - - prob = NonlinearProblem(quadratic_f, 2.0, 4.0) - - @test_throws MethodError solve(prob) - @test_throws MethodError solve(prob, RobustMultiNewton()) - sol = solve(prob, RobustMultiNewton(; autodiff = AutoFiniteDiff())) - @test SciMLBase.successful_retcode(sol) -end - -@testitem "Simple Scalar Problem #187" tags=[:misc] begin - using NaNMath - - # https://github.com/SciML/NonlinearSolve.jl/issues/187 - # If we use a General Nonlinear Solver the solution might go out of the domain! - ff_interval(u, p) = 0.5 / 1.5 * NaNMath.log.(u ./ (1.0 .- u)) .- 2.0 * u .+ 1.0 - - uspan = (0.02, 0.1) - prob = IntervalNonlinearProblem(ff_interval, uspan) - sol = solve(prob; abstol = 1e-9) - @test SciMLBase.successful_retcode(sol) - - u0 = 0.06 - p = 2.0 - prob = NonlinearProblem(ff_interval, u0, p) - sol = solve(prob; abstol = 1e-9) - @test SciMLBase.successful_retcode(sol) -end - -# Shooting Problem: Taken from BoundaryValueDiffEq.jl -# Testing for Complex Valued Root Finding. For Complex valued inputs we drop some of the -# algorithms which dont support those. -@testitem "Complex Valued Problems: Single-Shooting" tags=[:misc] begin - using OrdinaryDiffEqTsit5 - - function ode_func!(du, u, p, t) - du[1] = u[2] - du[2] = -u[1] - return nothing - end - - function objective_function!(resid, u0, p) - odeprob = ODEProblem{true}(ode_func!, u0, (0.0, 100.0), p) - sol = solve(odeprob, Tsit5(), abstol = 1e-9, reltol = 1e-9, verbose = false) - resid[1] = sol(0.0)[1] - resid[2] = sol(100.0)[1] - 1.0 - return nothing - end - - prob = NonlinearProblem{true}(objective_function!, [0.0, 1.0] .+ 1im) - sol = solve(prob; abstol = 1e-10) - @test SciMLBase.successful_retcode(sol) - # This test is not meant to return success but test that all the default solvers can handle - # complex valued problems - @test_nowarn solve(prob; abstol = 1e-19, maxiters = 10) - @test_nowarn solve( - prob, RobustMultiNewton(eltype(prob.u0)); abstol = 1e-19, maxiters = 10) -end - -@testitem "No AD" tags=[:misc] begin - no_ad_fast = FastShortcutNonlinearPolyalg(autodiff = AutoFiniteDiff()) - no_ad_robust = RobustMultiNewton(autodiff = AutoFiniteDiff()) - no_ad_algs = Set([no_ad_fast, no_ad_robust, no_ad_fast.algs..., no_ad_robust.algs...]) - - @testset "Inplace" begin - f_iip = Base.Experimental.@opaque (du, u, p) -> du .= u .* u .- p - u0 = [0.5] - prob = NonlinearProblem(f_iip, u0, 1.0) - for alg in no_ad_algs - sol = solve(prob, alg) - @test isapprox(only(sol.u), 1.0) - @test SciMLBase.successful_retcode(sol.retcode) - end - end - - @testset "Out of Place" begin - f_oop = Base.Experimental.@opaque (u, p) -> u .* u .- p - u0 = [0.5] - prob = NonlinearProblem{false}(f_oop, u0, 1.0) - for alg in no_ad_algs - sol = solve(prob, alg) - @test isapprox(only(sol.u), 1.0) - @test SciMLBase.successful_retcode(sol.retcode) - end - end -end - -@testsetup module InfeasibleFunction -using LinearAlgebra, StaticArrays - -# this is infeasible -function f1_infeasible!(out, u, p) - μ = 3.986004415e14 - x = 7000.0e3 - y = -6.970561549987071e-9 - z = -3.784706123246018e-9 - v_x = 8.550491684548064e-12 + u[1] - v_y = 6631.60076191005 + u[2] - v_z = 3600.665431405663 + u[3] - r = @SVector [x, y, z] - v = @SVector [v_x, v_y, v_z] - h = cross(r, v) - ev = cross(v, h) / μ - r / norm(r) - i = acos(h[3] / norm(h)) - e = norm(ev) - a = 1 / (2 / norm(r) - (norm(v)^2 / μ)) - out .= [a - 42.0e6, e - 1e-5, i - 1e-5] - return nothing -end - -# this is unfeasible -function f1_infeasible(u, p) - μ = 3.986004415e14 - x = 7000.0e3 - y = -6.970561549987071e-9 - z = -3.784706123246018e-9 - v_x = 8.550491684548064e-12 + u[1] - v_y = 6631.60076191005 + u[2] - v_z = 3600.665431405663 + u[3] - r = [x, y, z] - v = [v_x, v_y, v_z] - h = cross(r, v) - ev = cross(v, h) / μ - r / norm(r) - i = acos(h[3] / norm(h)) - e = norm(ev) - a = 1 / (2 / norm(r) - (norm(v)^2 / μ)) - return [a - 42.0e6, e - 1e-5, i - 1e-5] -end - -export f1_infeasible!, f1_infeasible -end - -@testitem "[IIP] Infeasible" setup=[InfeasibleFunction] tags=[:misc] begin - u0 = [0.0, 0.0, 0.0] - prob = NonlinearProblem(f1_infeasible!, u0) - sol = solve(prob) - - @test all(!isnan, sol.u) - @test !SciMLBase.successful_retcode(sol.retcode) - @inferred solve(prob) -end - -@testitem "[OOP] Infeasible" setup=[InfeasibleFunction] tags=[:misc] begin - using LinearAlgebra, StaticArrays - - u0 = [0.0, 0.0, 0.0] - prob = NonlinearProblem(f1_infeasible, u0) - sol = solve(prob) - - @test all(!isnan, sol.u) - @test !SciMLBase.successful_retcode(sol.retcode) - @inferred solve(prob) - - u0 = @SVector [0.0, 0.0, 0.0] - prob = NonlinearProblem(f1_infeasible, u0) - - sol = solve(prob) - @test all(!isnan, sol.u) - @test !SciMLBase.successful_retcode(sol.retcode) -end diff --git a/test/misc/qa_tests.jl b/test/misc/qa_tests.jl deleted file mode 100644 index 6aafd4024..000000000 --- a/test/misc/qa_tests.jl +++ /dev/null @@ -1,28 +0,0 @@ -@testitem "Aqua" tags=[:misc] begin - using NonlinearSolve, SimpleNonlinearSolve, Aqua - - Aqua.find_persistent_tasks_deps(NonlinearSolve) - Aqua.test_ambiguities(NonlinearSolve; recursive = false) - Aqua.test_deps_compat(NonlinearSolve) - Aqua.test_piracies(NonlinearSolve, - treat_as_own = [NonlinearProblem, NonlinearLeastSquaresProblem, - SimpleNonlinearSolve.AbstractSimpleNonlinearSolveAlgorithm]) - Aqua.test_project_extras(NonlinearSolve) - # Timer Outputs needs to be enabled via Preferences - Aqua.test_stale_deps(NonlinearSolve; ignore = [:TimerOutputs]) - Aqua.test_unbound_args(NonlinearSolve) - Aqua.test_undefined_exports(NonlinearSolve) -end - -@testitem "Explicit Imports" tags=[:misc] begin - using NonlinearSolve, ADTypes, SimpleNonlinearSolve, SciMLBase - import BandedMatrices, FastLevenbergMarquardt, FixedPointAcceleration, - LeastSquaresOptim, MINPACK, NLsolve, NLSolvers, SIAMFANLEquations, SpeedMapping - - using ExplicitImports - - @test check_no_implicit_imports(NonlinearSolve; - skip = (NonlinearSolve, Base, Core, SimpleNonlinearSolve, SciMLBase)) === nothing - @test check_no_stale_explicit_imports(NonlinearSolve) === nothing - @test check_all_qualified_accesses_via_owners(NonlinearSolve) === nothing -end diff --git a/test/downstream/mtk_cache_indexing_tests.jl b/test/mtk_cache_indexing_tests.jl similarity index 98% rename from test/downstream/mtk_cache_indexing_tests.jl rename to test/mtk_cache_indexing_tests.jl index d29cb1da7..3109d1612 100644 --- a/test/downstream/mtk_cache_indexing_tests.jl +++ b/test/mtk_cache_indexing_tests.jl @@ -13,7 +13,8 @@ @testset "$integtype" for (alg, integtype) in [ (NewtonRaphson(), NonlinearSolve.GeneralizedFirstOrderAlgorithmCache), (FastShortcutNonlinearPolyalg(), NonlinearSolve.NonlinearSolvePolyAlgorithmCache), - (SimpleNewtonRaphson(), NonlinearSolve.NonlinearSolveNoInitCache)] + (SimpleNewtonRaphson(), NonlinearSolve.NonlinearSolveNoInitCache) + ] nint = init(nlprob, alg) @test nint isa integtype diff --git a/test/qa_tests.jl b/test/qa_tests.jl new file mode 100644 index 000000000..ffac5c7bb --- /dev/null +++ b/test/qa_tests.jl @@ -0,0 +1,24 @@ +@testitem "Aqua" tags=[:misc] begin + using NonlinearSolve, SimpleNonlinearSolve, Aqua + + Aqua.test_all(NonlinearSolve; ambiguities = false, piracies = false) + Aqua.test_ambiguities(NonlinearSolve; recursive = false) + Aqua.test_piracies(NonlinearSolve, + treat_as_own = [ + NonlinearProblem, NonlinearLeastSquaresProblem, + SimpleNonlinearSolve.AbstractSimpleNonlinearSolveAlgorithm + ] + ) +end + +@testitem "Explicit Imports" tags=[:misc] begin + using NonlinearSolve, ADTypes, SimpleNonlinearSolve, SciMLBase + import FastLevenbergMarquardt, FixedPointAcceleration, LeastSquaresOptim, MINPACK, + NLsolve, NLSolvers, SIAMFANLEquations, SpeedMapping + + using ExplicitImports + + @test check_no_implicit_imports(NonlinearSolve; skip = (Base, Core)) === nothing + @test check_no_stale_explicit_imports(NonlinearSolve) === nothing + @test check_all_qualified_accesses_via_owners(NonlinearSolve) === nothing +end diff --git a/test/runtests.jl b/test/runtests.jl index 33ca5da7a..59a43c2f9 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -10,13 +10,19 @@ const EXTRA_PKGS = Pkg.PackageSpec[] length(EXTRA_PKGS) ≥ 1 && Pkg.add(EXTRA_PKGS) const RETESTITEMS_NWORKERS = parse( - Int, get(ENV, "RETESTITEMS_NWORKERS", string(min(Hwloc.num_physical_cores(), 4)))) + Int, get(ENV, "RETESTITEMS_NWORKERS", string(min(Hwloc.num_physical_cores(), 4))) +) const RETESTITEMS_NWORKER_THREADS = parse(Int, - get(ENV, "RETESTITEMS_NWORKER_THREADS", - string(max(Hwloc.num_virtual_cores() ÷ RETESTITEMS_NWORKERS, 1)))) + get( + ENV, "RETESTITEMS_NWORKER_THREADS", + string(max(Hwloc.num_virtual_cores() ÷ RETESTITEMS_NWORKERS, 1)) + ) +) @info "Running tests for group: $(GROUP) with $(RETESTITEMS_NWORKERS) workers" -ReTestItems.runtests(NonlinearSolve; tags = (GROUP == "all" ? nothing : [Symbol(GROUP)]), +ReTestItems.runtests( + NonlinearSolve; tags = (GROUP == "all" ? nothing : [Symbol(GROUP)]), nworkers = RETESTITEMS_NWORKERS, nworker_threads = RETESTITEMS_NWORKER_THREADS, - testitem_timeout = 3600) + testitem_timeout = 3600 +) diff --git a/test/wrappers/nlls_tests.jl b/test/wrappers/least_squares_tests.jl similarity index 100% rename from test/wrappers/nlls_tests.jl rename to test/wrappers/least_squares_tests.jl