From 802fc9539e2625dc64f161bb56741d1ce882f7e8 Mon Sep 17 00:00:00 2001 From: oscarddssmith Date: Fri, 28 Mar 2025 11:56:28 -0400 Subject: [PATCH 1/3] move downstream tests downstream --- Project.toml | 7 +- test/downstream/initialization.jl | 353 +++++++++++++++++++++++++++++- test/{ => downstream}/traits.jl | 0 test/runtests.jl | 9 +- 4 files changed, 356 insertions(+), 13 deletions(-) rename test/{ => downstream}/traits.jl (100%) diff --git a/Project.toml b/Project.toml index 52641172bb..c70c5a686c 100644 --- a/Project.toml +++ b/Project.toml @@ -73,7 +73,6 @@ MLStyle = "0.4.17" Makie = "0.20, 0.21, 0.22" Markdown = "1.10" Moshi = "0.3" -NonlinearSolve = "3, 4" PartialFunctions = "1.1" PrecompileTools = "1.2" Preferences = "1.3" @@ -100,11 +99,8 @@ julia = "1.10" Aqua = "4c88cf16-eb10-579e-8560-4a9242c79595" ChainRulesCore = "d360d2e6-b24c-11e9-a2a3-2a2ae2dbcce4" DataFrames = "a93c6f00-e57d-5684-b7b6-d8193f3e46c0" -DelayDiffEq = "bcd4f6db-9728-5f36-b5f7-82caef46ccdb" ForwardDiff = "f6369f11-7733-5829-9624-2563aa707210" MLStyle = "d8e11817-5142-5d16-987a-aa16d5891078" -NonlinearSolve = "8913a72c-1f9b-4ce2-8d82-65094dcecaec" -OrdinaryDiffEq = "1dea7af3-3e70-54e6-95c3-0bf5283fa5ed" PartialFunctions = "570af359-4316-4cb7-8c74-252c00c2016b" Pkg = "44cfe95a-1eb2-52ea-b672-e2afdf69b78f" Plots = "91a5bcdd-55d7-5caf-9e0b-520d859cae80" @@ -113,11 +109,10 @@ SafeTestsets = "1bc83da4-3b8d-516f-aca4-4fe02f6d838f" Serialization = "9e88b42a-f829-5b0c-bbe9-9e923198166b" StableRNGs = "860ef19b-820b-49d6-a774-d7a799459cd3" StaticArrays = "90137ffa-7385-5640-81b9-e52037218182" -StochasticDiffEq = "789caeaf-c7a9-5a7d-9973-96adeb23e2a0" Tables = "bd369af6-aec1-5ad0-b16a-f7cc5008161c" Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40" UnicodePlots = "b8865327-cd53-5732-bb35-84acbb429228" Zygote = "e88e6eb3-aa80-5325-afca-941959d7151f" [targets] -test = ["Pkg", "Plots", "UnicodePlots", "SafeTestsets", "Serialization", "Test", "StableRNGs", "StaticArrays", "StochasticDiffEq", "Aqua", "Zygote", "PartialFunctions", "DataFrames", "NonlinearSolve", "OrdinaryDiffEq", "ForwardDiff", "Tables", "MLStyle"] +test = ["Pkg", "Plots", "UnicodePlots", "SafeTestsets", "Serialization", "Test", "StableRNGs", "StaticArrays", "Aqua", "Zygote", "PartialFunctions", "DataFrames", "ForwardDiff", "Tables", "MLStyle"] diff --git a/test/downstream/initialization.jl b/test/downstream/initialization.jl index 0101d3cb9e..c5bbb2a443 100644 --- a/test/downstream/initialization.jl +++ b/test/downstream/initialization.jl @@ -1,7 +1,8 @@ using ModelingToolkit, NonlinearSolve, OrdinaryDiffEq, Sundials, SciMLBase, Test using SymbolicIndexingInterface using ModelingToolkit: t_nounits as t, D_nounits as D - +using StochasticDiffEq, OrdinaryDiffEq, NonlinearSolve, SymbolicIndexingInterface, + LinearAlgebra, Test @testset "CheckInit" begin abstol = 1e-10 @testset "Sundials + DAEProblem" begin @@ -76,3 +77,353 @@ end prob = ODEProblem(pend, [x => 1, y => 0, λ => 2], (0.0, 1.5), [g => 1]) @test occursin("Initialization status: OVERDETERMINED", sprint(summary, prob)) end + + +@testset "CheckInit" begin + @testset "ODEProblem" begin + function rhs(u, p, t) + return [u[1] * t, u[1]^2 - u[2]^2] + end + function rhs!(du, u, p, t) + du[1] = u[1] * t + du[2] = u[1]^2 - u[2]^2 + end + + oopfn = ODEFunction{false}(rhs, mass_matrix = [1 0; 0 0]) + iipfn = ODEFunction{true}(rhs!, mass_matrix = [1 0; 0 0]) + + @testset "Inplace = $(SciMLBase.isinplace(f))" for f in [oopfn, iipfn] + prob = ODEProblem(f, [1.0, 1.0], (0.0, 1.0)) + integ = init(prob) + u0, _, success = SciMLBase.get_initial_values( + prob, integ, f, SciMLBase.CheckInit(), + Val(SciMLBase.isinplace(f)); abstol = 1e-10) + @test success + @test u0 == prob.u0 + + integ.u[2] = 2.0 + @test_throws SciMLBase.CheckInitFailureError SciMLBase.get_initial_values( + prob, integ, f, SciMLBase.CheckInit(), + Val(SciMLBase.isinplace(f)); abstol = 1e-10) + end + + @testset "With I mass matrix" begin + function rhs(u, p, t) + return u + end + prob = ODEProblem(ODEFunction(rhs; mass_matrix = I), ones(2), (0.0, 1.0)) + integ = init(prob) + u0, _, success = SciMLBase.get_initial_values( + prob, integ, prob.f, SciMLBase.CheckInit(), + Val(false); abstol = 1e-10 + ) + @test success + @test u0 == prob.u0 + end + end + + @testset "DAEProblem" begin + function daerhs(du, u, p, t) + return [du[1] - u[1] * t - p, u[1]^2 - u[2]^2] + end + function daerhs!(resid, du, u, p, t) + resid[1] = du[1] - u[1] * t - p + resid[2] = u[1]^2 - u[2]^2 + end + + oopfn = DAEFunction{false}(daerhs) + iipfn = DAEFunction{true}(daerhs!) + + @testset "Inplace = $(SciMLBase.isinplace(f))" for f in [oopfn, iipfn] + prob = DAEProblem(f, [1.0, 0.0], [1.0, 1.0], (0.0, 1.0), 1.0) + integ = init(prob, DImplicitEuler()) + u0, _, success = SciMLBase.get_initial_values( + prob, integ, f, SciMLBase.CheckInit(), + Val(SciMLBase.isinplace(f)); abstol = 1e-10) + @test success + @test u0 == prob.u0 + + integ.u[2] = 2.0 + @test_throws SciMLBase.CheckInitFailureError SciMLBase.get_initial_values( + prob, integ, f, SciMLBase.CheckInit(), + Val(SciMLBase.isinplace(f)); abstol = 1e-10) + + integ.u[2] = 1.0 + integ.du[1] = 2.0 + @test_throws SciMLBase.CheckInitFailureError SciMLBase.get_initial_values( + prob, integ, f, SciMLBase.CheckInit(), + Val(SciMLBase.isinplace(f)); abstol = 1e-10) + end + end + + @testset "SDEProblem" begin + mm_A = [1 0 0; 0 1 0; 0 0 0] + function sdef!(du, u, p, t) + du[1] = u[1] + du[2] = u[2] + du[3] = u[1] + u[2] + u[3] - 1 + end + function sdef(u, p, t) + du = similar(u) + sdef!(du, u, p, t) + du + end + + function g!(du, u, p, t) + @. du = 0.1 + end + function g(u, p, t) + du = similar(u) + g!(du, u, p, t) + du + end + iipfn = SDEFunction{true}(sdef!, g!; mass_matrix = mm_A) + oopfn = SDEFunction{false}(sdef, g; mass_matrix = mm_A) + + @testset "Inplace = $(SciMLBase.isinplace(f))" for f in [oopfn, iipfn] + prob = SDEProblem(f, [1.0, 1.0, -1.0], (0.0, 1.0)) + integ = init(prob, ImplicitEM()) + u0, _, success = SciMLBase.get_initial_values( + prob, integ, f, SciMLBase.CheckInit(), + Val(SciMLBase.isinplace(f)); abstol = 1e-10) + @test success + @test u0 == prob.u0 + + integ.u[2] = 2.0 + @test_throws SciMLBase.CheckInitFailureError SciMLBase.get_initial_values( + prob, integ, f, SciMLBase.CheckInit(), + Val(SciMLBase.isinplace(f)); abstol = 1e-10) + end + end +end + +@testset "OverrideInit" begin + function rhs2(u, p, t) + return [u[1] * t + p, u[1]^2 - u[2]^2] + end + + @testset "No-op without `initialization_data`" begin + prob = ODEProblem(rhs2, [1.0, 2.0], (0.0, 1.0), 1.0) + @test SciMLBase.initialization_status(prob) === nothing + integ = init(prob) + integ.u[2] = 3.0 + u0, p, success = SciMLBase.get_initial_values( + prob, integ, prob.f, SciMLBase.OverrideInit(), Val(false)) + @test u0 ≈ [1.0, 3.0] + @test success + end + + # unknowns are u[2], p. Parameter is u[1] + initprob = NonlinearProblem([1.0, 1.0], [1.0]) do x, _u1 + u2, p = x + u1 = _u1[1] + return [u1^2 - u2^2, p^2 - 2p + 1] + end + update_initializeprob! = function (iprob, integ) + iprob.p[1] = integ.u[1] + end + initprobmap = function (nlsol) + return [parameter_values(nlsol)[1], nlsol.u[1]] + end + initprobpmap = function (_, nlsol) + return nlsol.u[2] + end + initialization_data = SciMLBase.OverrideInitData( + initprob, update_initializeprob!, initprobmap, initprobpmap) + fn = ODEFunction(rhs2; initialization_data) + prob = ODEProblem(fn, [2.0, 0.0], (0.0, 1.0), 0.0) + @test SciMLBase.initialization_status(prob) == SciMLBase.FULLY_DETERMINED + integ = init(prob; initializealg = NoInit()) + + @testset "Errors without `nlsolve_alg`" begin + @test_throws SciMLBase.OverrideInitMissingAlgorithm SciMLBase.get_initial_values( + prob, integ, fn, SciMLBase.OverrideInit(), Val(false)) + end + + abstol = 1e-10 + reltol = 1e-10 + @testset "Solves" begin + @testset "with explicit alg" begin + u0, p, success = SciMLBase.get_initial_values( + prob, integ, fn, SciMLBase.OverrideInit(), + Val(false); nlsolve_alg = NewtonRaphson(), abstol, reltol) + + @test u0 ≈ [2.0, 2.0] + @test p ≈ 1.0 + @test success + + initprob.p[1] = 1.0 + end + @testset "with alg in `OverrideInit`" begin + u0, p, success = SciMLBase.get_initial_values( + prob, integ, fn, + SciMLBase.OverrideInit(; nlsolve = NewtonRaphson(), abstol, reltol), + Val(false)) + + @test u0 ≈ [2.0, 2.0] + @test p ≈ 1.0 + @test success + + initprob.p[1] = 1.0 + end + @testset "with trivial problem and no alg" begin + iprob = NonlinearProblem((u, p) -> 0.0, nothing, 1.0) + iprobmap = (_) -> [1.0, 1.0] + initdata = SciMLBase.OverrideInitData(iprob, nothing, iprobmap, nothing) + _fn = ODEFunction(rhs2; initialization_data = initdata) + _prob = ODEProblem(_fn, [2.0, 0.0], (0.0, 1.0), 1.0) + _integ = init(_prob; initializealg = NoInit()) + + u0, p, success = SciMLBase.get_initial_values( + _prob, _integ, _fn, SciMLBase.OverrideInit(), Val(false); abstol, reltol) + + @test u0 ≈ [1.0, 1.0] + @test p ≈ 1.0 + @test success + end + @testset "with kwargs provided to `get_initial_values`" begin + u0, p, success = SciMLBase.get_initial_values( + prob, integ, fn, SciMLBase.OverrideInit(), + Val(false); nlsolve_alg = NewtonRaphson(), abstol, reltol, u0 = [-1.0, 1.0]) + @test u0 ≈ [2.0, -2.0] + @test p ≈ 1.0 + @test success + end + end + + @testset "Solves with non-integrator value provider" begin + _integ = ProblemState(; u = integ.u, p = parameter_values(integ), t = integ.t) + u0, p, success = SciMLBase.get_initial_values( + prob, _integ, fn, SciMLBase.OverrideInit(), + Val(false); nlsolve_alg = NewtonRaphson(), abstol, reltol) + + @test u0 ≈ [2.0, 2.0] + @test p ≈ 1.0 + @test success + + initprob.p[1] = 1.0 + end + + @testset "Solves without `update_initializeprob!`" begin + initdata = SciMLBase.@set initialization_data.update_initializeprob! = nothing + fn = ODEFunction(rhs2; initialization_data = initdata) + prob = ODEProblem(fn, [2.0, 0.0], (0.0, 1.0), 0.0) + integ = init(prob; initializealg = NoInit()) + + u0, p, success = SciMLBase.get_initial_values( + prob, integ, fn, SciMLBase.OverrideInit(), + Val(false); nlsolve_alg = NewtonRaphson(), abstol, reltol) + @test u0 ≈ [1.0, 1.0] + @test p ≈ 1.0 + @test success + end + + @testset "Solves without `initializeprobmap`" begin + initdata = SciMLBase.@set initialization_data.initializeprobmap = nothing + fn = ODEFunction(rhs2; initialization_data = initdata) + prob = ODEProblem(fn, [2.0, 0.0], (0.0, 1.0), 0.0) + integ = init(prob; initializealg = NoInit()) + + u0, p, success = SciMLBase.get_initial_values( + prob, integ, fn, SciMLBase.OverrideInit(), + Val(false); nlsolve_alg = NewtonRaphson(), abstol, reltol) + + @test u0 ≈ [2.0, 0.0] + @test p ≈ 1.0 + @test success + end + + @testset "Solves without `initializeprobpmap`" begin + initdata = SciMLBase.@set initialization_data.initializeprobpmap = nothing + fn = ODEFunction(rhs2; initialization_data = initdata) + prob = ODEProblem(fn, [2.0, 0.0], (0.0, 1.0), 0.0) + integ = init(prob; initializealg = NoInit()) + + u0, p, success = SciMLBase.get_initial_values( + prob, integ, fn, SciMLBase.OverrideInit(), + Val(false); nlsolve_alg = NewtonRaphson(), abstol, reltol) + + @test u0 ≈ [2.0, 2.0] + @test p ≈ 0.0 + @test success + end + + @testset "Initialization status for `SCCNonlinearProblem`" begin + initprob = SCCNonlinearProblem([initprob], [Returns(nothing)]) + initialization_data = SciMLBase.OverrideInitData( + initprob, nothing, nothing, nothing) + fn = ODEFunction(rhs2; initialization_data) + prob = ODEProblem(fn, [2.0, 0.0], (0.0, 1.0), 0.0) + @test SciMLBase.initialization_status(prob) == SciMLBase.FULLY_DETERMINED + end + + @testset "Trivial initialization" begin + initprob = NonlinearProblem(Returns(nothing), nothing, [1.0]) + update_initializeprob! = function (iprob, integ) + # just to access the current time and use it as a number, so this errors + # if run on a problem with `current_time(prob) === nothing` + iprob.p[1] = current_time(integ) + 1 + iprob.p[1] = state_values(integ)[1] + end + initprobmap = function (nlsol) + u1 = parameter_values(nlsol)[1] + return [u1, u1] + end + initprobpmap = function (_, nlsol) + return 0.0 + end + initialization_data = SciMLBase.OverrideInitData( + initprob, update_initializeprob!, initprobmap, initprobpmap) + fn = ODEFunction(rhs2; initialization_data) + prob = ODEProblem(fn, [2.0, 0.0], (0.0, 1.0), 0.0) + @test SciMLBase.initialization_status(prob) == SciMLBase.FULLY_DETERMINED + integ = init(prob; initializealg = NoInit()) + + u0, p, success = SciMLBase.get_initial_values( + prob, integ, fn, SciMLBase.OverrideInit(), Val(false) + ) + @test u0 ≈ [2.0, 2.0] + @test p ≈ 0.0 + @test success + + @testset "Doesn't run in `remake` if `tspan == (nothing, nothing)`" begin + prob = ODEProblem(fn, [2.0, 0.0], (nothing, nothing), 0.0) + @test_nowarn remake(prob) + end + end + + @testset "Initialization status for overdetermined case" begin + initfn = NonlinearFunction(; resid_prototype = ones(3)) do u, p + return [u[1] - 1.0, u[2] - 1.0, u[1] * u[2] - 1.0] + end + initprob = NonlinearLeastSquaresProblem(initfn, ones(2)) + initialization_data = SciMLBase.OverrideInitData( + initprob, nothing, nothing, nothing) + fn = ODEFunction(rhs2; initialization_data) + prob = ODEProblem(fn, [2.0, 0.0], (0.0, 1.0), 0.0) + @test SciMLBase.initialization_status(prob) == SciMLBase.OVERDETERMINED + end + + @testset "Initialization status for underdetermined case" begin + initfn = NonlinearFunction(; resid_prototype = ones(1)) do u, p + return [u[1] - 1.0] + end + initprob = NonlinearLeastSquaresProblem(initfn, ones(2)) + initialization_data = SciMLBase.OverrideInitData( + initprob, nothing, nothing, nothing) + fn = ODEFunction(rhs2; initialization_data) + prob = ODEProblem(fn, [2.0, 0.0], (0.0, 1.0), 0.0) + @test SciMLBase.initialization_status(prob) == SciMLBase.UNDERDETERMINED + end +end + +@testset "NoInit" begin + prob = ODEProblem(ones(2), (0.0, 1.0), ones(2)) do u, p, t + return u + end + u, p, success = SciMLBase.get_initial_values( + prob, prob, prob.f, SciMLBase.NoInit(), Val(true)) + @test u == ones(2) + @test p == ones(2) + @test success +end diff --git a/test/traits.jl b/test/downstream/traits.jl similarity index 100% rename from test/traits.jl rename to test/downstream/traits.jl diff --git a/test/runtests.jl b/test/runtests.jl index 766dbe6750..22c02c0f5b 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -39,9 +39,6 @@ end @time @safetestset "Integrator interface" begin include("integrator_tests.jl") end - @time @safetestset "Table Traits" begin - include("traits.jl") - end @time @safetestset "Ensemble functionality" begin include("ensemble_tests.jl") end @@ -60,9 +57,6 @@ end @time @safetestset "Serialization tests" begin include("serialization_tests.jl") end - @time @safetestset "Initialization" begin - include("initialization.jl") - end @time @safetestset "Clocks" begin include("clock.jl") end @@ -124,6 +118,9 @@ end @time @safetestset "Initialization" begin include("downstream/initialization.jl") end + @time @safetestset "Table Traits" begin + include("downstream/traits.jl") + end end if !is_APPVEYOR && (GROUP == "Downstream" || GROUP == "SymbolicIndexingInterface") From 0f85f42aecfb9eb5896fa383c1a808553aab5458 Mon Sep 17 00:00:00 2001 From: oscarddssmith Date: Fri, 28 Mar 2025 13:10:00 -0400 Subject: [PATCH 2/3] add test that downstream tests are in downstream --- Project.toml | 4 +--- test/aqua.jl | 8 ++++++++ test/downstream/Project.toml | 2 ++ 3 files changed, 11 insertions(+), 3 deletions(-) diff --git a/Project.toml b/Project.toml index c70c5a686c..8f39e2fbf7 100644 --- a/Project.toml +++ b/Project.toml @@ -60,7 +60,6 @@ ChainRules = "1.58.0" ChainRulesCore = "1.18" CommonSolve = "0.2.4" ConstructionBase = "1.5" -DataFrames = "1.6" Distributed = "1.10" DocStringExtensions = "0.9" EnumX = "1" @@ -98,7 +97,6 @@ julia = "1.10" [extras] Aqua = "4c88cf16-eb10-579e-8560-4a9242c79595" ChainRulesCore = "d360d2e6-b24c-11e9-a2a3-2a2ae2dbcce4" -DataFrames = "a93c6f00-e57d-5684-b7b6-d8193f3e46c0" ForwardDiff = "f6369f11-7733-5829-9624-2563aa707210" MLStyle = "d8e11817-5142-5d16-987a-aa16d5891078" PartialFunctions = "570af359-4316-4cb7-8c74-252c00c2016b" @@ -115,4 +113,4 @@ UnicodePlots = "b8865327-cd53-5732-bb35-84acbb429228" Zygote = "e88e6eb3-aa80-5325-afca-941959d7151f" [targets] -test = ["Pkg", "Plots", "UnicodePlots", "SafeTestsets", "Serialization", "Test", "StableRNGs", "StaticArrays", "Aqua", "Zygote", "PartialFunctions", "DataFrames", "ForwardDiff", "Tables", "MLStyle"] +test = ["Aqua", "ForwardDiff", "MLStyle", "PartialFunctions", "Pkg", "Plots", "SafeTestsets", "Serialization", "StableRNGs", "StaticArrays", "Tables", "Test", "UnicodePlots", "Zygote"] diff --git a/test/aqua.jl b/test/aqua.jl index 32708ce632..d3bdc52408 100644 --- a/test/aqua.jl +++ b/test/aqua.jl @@ -1,6 +1,14 @@ using Test using SciMLBase using Aqua +using Pkg + +# yes this is horrible, we'll fix it when Pkg or Base provides a decent API +manifest = Pkg.Types.EnvCache().manifest +# these are good sentinels to test whether someone has added a heavy SciML package to the test deps +if haskey(manifest.deps, "NonlinearSolveBase") || haskey(manifest.deps, "DiffEqBase") + error("Don't put Downstream Packages in non Downstream CI") +end # https://github.com/JuliaArrays/FillArrays.jl/pull/163 @test isempty(detect_ambiguities(SciMLBase)) diff --git a/test/downstream/Project.toml b/test/downstream/Project.toml index 5e65ebbf4f..b4adf6b1c9 100644 --- a/test/downstream/Project.toml +++ b/test/downstream/Project.toml @@ -1,5 +1,6 @@ [deps] BoundaryValueDiffEq = "764a87c0-6b3e-53db-9096-fe964310641d" +DataFrames = "a93c6f00-e57d-5684-b7b6-d8193f3e46c0" DelayDiffEq = "bcd4f6db-9728-5f36-b5f7-82caef46ccdb" DiffEqCallbacks = "459566f4-90b8-5000-8ac3-15dfb0a30def" ForwardDiff = "f6369f11-7733-5829-9624-2563aa707210" @@ -30,6 +31,7 @@ Zygote = "e88e6eb3-aa80-5325-afca-941959d7151f" [compat] BoundaryValueDiffEq = "5" +DataFrames = "1.6" DelayDiffEq = "5" DiffEqCallbacks = "3, 4" ForwardDiff = "0.10" From 437e55146bc60bd3a455f329ab76bb9198ba804f Mon Sep 17 00:00:00 2001 From: Oscar Smith Date: Fri, 28 Mar 2025 14:10:28 -0400 Subject: [PATCH 3/3] run downgrade on 1.10 --- .github/workflows/Downgrade.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/Downgrade.yml b/.github/workflows/Downgrade.yml index cf17c8d8fa..920d987017 100644 --- a/.github/workflows/Downgrade.yml +++ b/.github/workflows/Downgrade.yml @@ -15,7 +15,7 @@ jobs: runs-on: ubuntu-latest strategy: matrix: - version: ['1'] + version: ['min'] steps: - uses: actions/checkout@v4 - uses: julia-actions/setup-julia@v2