Skip to content

Commit 5020ff5

Browse files
singhharsh1708Harsh Singh
andauthored
feat: add AllocCheck.jl allocation tests across all solver packages (#3383)
* feat: add AllocCheck.jl allocation tests across all solver packages Add perform_step! allocation tests for all subpackages using AllocCheck.jl. Passing solvers are tested without broken=true; known-allocating solvers are marked broken=true to pin current state for future fixes. Core infrastructure Allocation tests run before JET in all runtests.jl to avoid code invalidation. * feat: add allocation tests for Linear, StabilizedIRK, and AMF * fix: use Project.toml for AllocCheck dep, positive TEST_GROUP conditions, fix BDF regression * fix: mark Core alloccheck tests as broken=true * fix: add SafeTestsets to AMF extras, mark Core alloccheck tests broken=true * fix: remove docstrings from allocation tests, remove AllocCheck from compat, fix Core alloccheck * fix: delete stray Core test/Project.toml, skip Aqua extras compat, rebase onto upstream * fix: remove broken=true from passing alloc tests, delete stray Core Project.toml, skip Aqua extras compat * fix: isolate AllocCheck in test/qa/ sub-env per GPU test pattern, revert Aqua deps_compat changes * fix: correct TOML syntax error (double-quote) in test targets across all subpackages * fix: remove orphaned sources.DiffEqBase from qa/Project.toml, add Pkg to AMF test targets Julia 1.12 rejects [sources.*] entries not listed in [deps] or [extras]. DiffEqBase resolves transitively through OrdinaryDiffEqCore's sources. Also adds missing Pkg to OrdinaryDiffEqAMF test extras/targets. * fix: wrap check_allocs in try-catch for StabilizedRK large SVector solvers ROCK2/4 and ESERK solvers use SVector{46}/SVector{50} fields in their caches. AllocCheck's LLVM analysis throws MethodError when encountering these very large static array constructors. Wrap check_allocs in try-catch to skip gracefully. --------- Co-authored-by: Harsh Singh <harsh@Harshs-MacBook-Air.local>
1 parent 0f1657b commit 5020ff5

131 files changed

Lines changed: 2396 additions & 234 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

lib/ImplicitDiscreteSolve/Project.toml

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,6 @@ OrdinaryDiffEqSDIRK = "2d112036-d095-4a1e-ab9a-08536f3ecdbf"
2020
SafeTestsets = "1bc83da4-3b8d-516f-aca4-4fe02f6d838f"
2121
Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40"
2222
Aqua = "4c88cf16-eb10-579e-8560-4a9242c79595"
23-
AllocCheck = "9b6a8646-10ed-4001-bbdc-1d2f46dfbb1a"
2423

2524
[sources]
2625
DiffEqBase = {path = "../DiffEqBase"}
@@ -39,7 +38,6 @@ NonlinearSolveFirstOrder = "1.9.0, 2"
3938
SymbolicIndexingInterface = "0.3.38"
4039
JET = "0.9.18, 0.10.4"
4140
julia = "1.10"
42-
AllocCheck = "0.2"
4341
DiffEqBase = "6.194"
4442
Reexport = "1.2"
4543

lib/OrdinaryDiffEqAMF/Project.toml

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ SciMLOperators = "c0aeaf25-5076-4817-a8d5-81caf7dfa961"
1313

1414
[compat]
1515
LinearAlgebra = "1.10"
16+
SafeTestsets = "0.1.0"
1617
LinearSolve = "3.46"
1718
OrdinaryDiffEqCore = "3.30"
1819
Reexport = "1.2"
@@ -25,8 +26,10 @@ path = "../OrdinaryDiffEqCore"
2526

2627
[extras]
2728
OrdinaryDiffEqRosenbrock = "43230ef6-c299-4910-a778-202eb28ce4ce"
29+
Pkg = "44cfe95a-1eb2-52ea-b672-e2afdf69b78f"
30+
SafeTestsets = "1bc83da4-3b8d-516f-aca4-4fe02f6d838f"
2831
SciMLSensitivity = "1ed8b502-d754-442c-8d5d-10ac956f44a1"
2932
Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40"
3033

3134
[targets]
32-
test = ["OrdinaryDiffEqRosenbrock", "SciMLSensitivity", "Test"]
35+
test = ["OrdinaryDiffEqRosenbrock", "Pkg", "SafeTestsets", "SciMLSensitivity", "Test"]
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
using OrdinaryDiffEqAMF
2+
using OrdinaryDiffEqRosenbrock
3+
using OrdinaryDiffEqCore
4+
using SciMLOperators: MatrixOperator
5+
using AllocCheck
6+
using Test
7+
8+
@testset "AMF Allocation Tests" begin
9+
function f!(du, u, p, t)
10+
du[1] = -0.5 * u[1]
11+
du[2] = -1.5 * u[2]
12+
end
13+
14+
J_op = MatrixOperator([-0.5 0.0; 0.0 -1.5])
15+
amf_func = build_amf_function(f!; jac = J_op)
16+
prob = ODEProblem(amf_func, [1.0, 1.0], (0.0, 1.0))
17+
18+
@testset "AMF(ROS34PW1a) perform_step! Static Analysis" begin
19+
integrator = init(
20+
prob, AMF(ROS34PW1a), dt = 0.1, save_everystep = false,
21+
abstol = 1.0e-6, reltol = 1.0e-6
22+
)
23+
step!(integrator)
24+
25+
cache = integrator.cache
26+
allocs = check_allocs(
27+
OrdinaryDiffEqCore.perform_step!,
28+
(typeof(integrator), typeof(cache))
29+
)
30+
31+
@test length(allocs) == 0 broken = true
32+
33+
if length(allocs) > 0
34+
println(
35+
"AllocCheck found $(length(allocs)) allocation sites in AMF(ROS34PW1a) perform_step!"
36+
)
37+
else
38+
println("AMF(ROS34PW1a) perform_step! appears allocation-free with AllocCheck")
39+
end
40+
end
41+
end
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
[deps]
2+
AllocCheck = "9b6a8646-10ed-4001-bbdc-1d2f46dfbb1a"
3+
OrdinaryDiffEqAMF = "08082164-f6a1-4363-b3df-3aa6fcf571ad"
4+
OrdinaryDiffEqCore = "bbf590c4-e513-4bbe-9b18-05decba2e5d8"
5+
SciMLBase = "0bca4576-84f4-4d90-8ffe-ffa030f20462"
6+
SciMLOperators = "c0aeaf25-5076-4817-a8d5-81caf7dfa961"
7+
Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40"
8+
9+
[sources.OrdinaryDiffEqAMF]
10+
path = "../../"
11+
12+
[sources.OrdinaryDiffEqCore]
13+
path = "../../../OrdinaryDiffEqCore"
14+
15+
[compat]
16+
AllocCheck = "0.2"
17+
OrdinaryDiffEqAMF = "1"
18+
OrdinaryDiffEqCore = "3"
19+
SciMLBase = "2"
20+
SciMLOperators = "1"
21+
julia = "1.10"
Lines changed: 26 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,29 @@
1+
using Pkg
12
using Test
3+
using SafeTestsets
24

3-
@testset "OrdinaryDiffEqAMF" begin
4-
include("test_pollu.jl")
5-
include("test_fd2d.jl")
6-
include("test_adjoint_fd2d.jl")
5+
const TEST_GROUP = get(ENV, "ODEDIFFEQ_TEST_GROUP", "ALL")
6+
const ORIGINAL_PROJECT = dirname(Base.active_project())
7+
8+
function activate_qa_env()
9+
Pkg.activate(joinpath(@__DIR__, "qa"))
10+
Pkg.instantiate()
11+
end
12+
13+
# Run functional tests
14+
if TEST_GROUP == "Core" || TEST_GROUP == "ALL"
15+
@testset "OrdinaryDiffEqAMF" begin
16+
include("test_pollu.jl")
17+
include("test_fd2d.jl")
18+
include("test_adjoint_fd2d.jl")
19+
end
20+
end
21+
22+
# Run QA tests (AllocCheck) - skip on pre-release Julia
23+
# Allocation tests must run before JET because JET's static analysis
24+
# invalidates compiled code and causes spurious runtime allocations.
25+
if (TEST_GROUP == "QA" || TEST_GROUP == "ALL") && isempty(VERSION.prerelease)
26+
activate_qa_env()
27+
@time @safetestset "Allocation Tests" include("allocation_tests.jl")
28+
Pkg.activate(ORIGINAL_PROJECT)
729
end

lib/OrdinaryDiffEqAdamsBashforthMoulton/Project.toml

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,6 @@ Random = "9a3f8284-a2c9-5f02-9a11-845980a1fd5c"
2323
DiffEqDevTools = "f3b72e0c-5b89-59e1-b016-84e28bfd966d"
2424
ODEProblemLibrary = "fdc4e326-1af4-4b90-96e7-779fcce2daa5"
2525
Aqua = "4c88cf16-eb10-579e-8560-4a9242c79595"
26-
AllocCheck = "9b6a8646-10ed-4001-bbdc-1d2f46dfbb1a"
2726
SafeTestsets = "1bc83da4-3b8d-516f-aca4-4fe02f6d838f"
2827

2928
[sources]
@@ -46,7 +45,6 @@ julia = "1.10"
4645
JET = "0.9, 0.11"
4746
RecursiveArrayTools = "3.52"
4847
ODEProblemLibrary = "1"
49-
AllocCheck = "0.2"
5048
DiffEqBase = "6.194"
5149
Reexport = "1.2"
5250
SafeTestsets = "0.1.0"
Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
using OrdinaryDiffEqAdamsBashforthMoulton
2+
using OrdinaryDiffEqCore
3+
using SciMLBase: FullSpecialize
4+
using AllocCheck
5+
using Test
6+
7+
@testset "AdamsBashforthMoulton Allocation Tests" begin
8+
function simple_system!(du, u, p, t)
9+
du[1] = -0.5 * u[1]
10+
du[2] = -1.5 * u[2]
11+
end
12+
13+
# Use FullSpecialize to avoid FunctionWrappers dynamic dispatch noise
14+
prob = ODEProblem{true, FullSpecialize}(simple_system!, [1.0, 1.0], (0.0, 10.0))
15+
16+
# Fixed-step Adams-Bashforth and Adams-Bashforth-Moulton methods
17+
ab_solvers = [AB3(), AB4(), AB5(), ABM32(), ABM43(), ABM54()]
18+
19+
# Variable-coefficient Adams methods — recompute g coefficients each step
20+
vcab_solvers = [VCAB3(), VCAB4(), VCAB5(), VCABM3(), VCABM4(), VCABM5(), VCABM()]
21+
22+
@testset "ABM perform_step! Static Analysis" begin
23+
for solver in ab_solvers
24+
@testset "$(typeof(solver)) perform_step! allocation check" begin
25+
integrator = init(
26+
prob, solver, dt = 0.1, save_everystep = false, adaptive = false
27+
)
28+
# Multistep methods need history: advance past startup steps
29+
for _ in 1:5
30+
step!(integrator)
31+
end
32+
33+
cache = integrator.cache
34+
allocs = check_allocs(
35+
OrdinaryDiffEqCore.perform_step!,
36+
(typeof(integrator), typeof(cache))
37+
)
38+
39+
@test length(allocs) == 0 broken = true
40+
41+
if length(allocs) > 0
42+
println(
43+
"AllocCheck found $(length(allocs)) allocation sites in $(typeof(solver)) perform_step!"
44+
)
45+
else
46+
println(
47+
"$(typeof(solver)) perform_step! appears allocation-free with AllocCheck"
48+
)
49+
end
50+
end
51+
end
52+
53+
for solver in vcab_solvers
54+
@testset "$(typeof(solver)) perform_step! allocation check" begin
55+
integrator = init(
56+
prob, solver, dt = 0.1, save_everystep = false,
57+
abstol = 1.0e-6, reltol = 1.0e-6
58+
)
59+
for _ in 1:5
60+
step!(integrator)
61+
end
62+
63+
cache = integrator.cache
64+
allocs = check_allocs(
65+
OrdinaryDiffEqCore.perform_step!,
66+
(typeof(integrator), typeof(cache))
67+
)
68+
69+
@test length(allocs) == 0 broken = true
70+
71+
if length(allocs) > 0
72+
println(
73+
"AllocCheck found $(length(allocs)) allocation sites in $(typeof(solver)) perform_step!"
74+
)
75+
else
76+
println(
77+
"$(typeof(solver)) perform_step! appears allocation-free with AllocCheck"
78+
)
79+
end
80+
end
81+
end
82+
end
83+
end
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
[deps]
2+
AllocCheck = "9b6a8646-10ed-4001-bbdc-1d2f46dfbb1a"
3+
OrdinaryDiffEqAdamsBashforthMoulton = "89bda076-bce5-4f1c-845f-551c83cdda9a"
4+
OrdinaryDiffEqCore = "bbf590c4-e513-4bbe-9b18-05decba2e5d8"
5+
SciMLBase = "0bca4576-84f4-4d90-8ffe-ffa030f20462"
6+
Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40"
7+
8+
[sources.OrdinaryDiffEqAdamsBashforthMoulton]
9+
path = "../../"
10+
11+
[sources.OrdinaryDiffEqCore]
12+
path = "../../../OrdinaryDiffEqCore"
13+
14+
[compat]
15+
AllocCheck = "0.2"
16+
OrdinaryDiffEqAdamsBashforthMoulton = "1"
17+
OrdinaryDiffEqCore = "3"
18+
SciMLBase = "2"
19+
julia = "1.10"
Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,28 @@
1+
using Pkg
12
using SafeTestsets
23

34
const TEST_GROUP = get(ENV, "ODEDIFFEQ_TEST_GROUP", "ALL")
5+
const ORIGINAL_PROJECT = dirname(Base.active_project())
6+
7+
function activate_qa_env()
8+
Pkg.activate(joinpath(@__DIR__, "qa"))
9+
Pkg.instantiate()
10+
end
411

512
# Run functional tests
6-
if TEST_GROUP != "QA"
13+
if TEST_GROUP == "Core" || TEST_GROUP == "ALL"
714
@time @safetestset "ABM Convergence Tests" include("abm_convergence_tests.jl")
815
@time @safetestset "Adams Variable Coefficients Tests" include("adams_tests.jl")
916
@time @safetestset "Regression test for threading versions vs non threading versions" include("regression_test_threading.jl")
1017
end
1118

12-
# Run QA tests (JET, Aqua)
13-
if TEST_GROUP != "Core" && isempty(VERSION.prerelease)
19+
# Run QA tests (AllocCheck, JET, Aqua) - skip on pre-release Julia
20+
# Allocation tests must run before JET because JET's static analysis
21+
# invalidates compiled code and causes spurious runtime allocations.
22+
if (TEST_GROUP == "QA" || TEST_GROUP == "ALL") && isempty(VERSION.prerelease)
23+
activate_qa_env()
24+
@time @safetestset "Allocation Tests" include("allocation_tests.jl")
25+
Pkg.activate(ORIGINAL_PROJECT)
1426
@time @safetestset "JET Tests" include("jet.jl")
1527
@time @safetestset "Aqua" include("qa.jl")
1628
end

lib/OrdinaryDiffEqBDF/Project.toml

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,6 @@ Enzyme = "7da242da-08ed-463a-9acd-ee780be4f1d9"
3535
DifferentiationInterface = "a0c0ee7d-e4b9-4e03-894e-1c5f64a51d63"
3636
JET = "c3a54625-cd67-489e-a8e7-0a5a0ff4e31b"
3737
ODEProblemLibrary = "fdc4e326-1af4-4b90-96e7-779fcce2daa5"
38-
AllocCheck = "9b6a8646-10ed-4001-bbdc-1d2f46dfbb1a"
3938
SafeTestsets = "1bc83da4-3b8d-516f-aca4-4fe02f6d838f"
4039
StaticArrays = "90137ffa-7385-5640-81b9-e52037218182"
4140

@@ -71,14 +70,13 @@ ADTypes = "1.16"
7170
RecursiveArrayTools = "3.52"
7271
ODEProblemLibrary = "1"
7372
OrdinaryDiffEqNonlinearSolve = "1.16.0"
74-
AllocCheck = "0.2"
7573
DiffEqBase = "6.194"
7674
Reexport = "1.2"
7775
SafeTestsets = "0.1.0"
7876
StaticArrays = "1.9"
7977

8078
[targets]
81-
test = ["DiffEqDevTools", "DifferentiationInterface", "ForwardDiff", "Random", "SafeTestsets", "StaticArrays", "Test", "ODEProblemLibrary", "NonlinearSolve", "Enzyme", "LinearSolve", "Pkg", "JET", "Aqua", "AllocCheck"]
79+
test = ["DiffEqDevTools", "DifferentiationInterface", "ForwardDiff", "Random", "SafeTestsets", "StaticArrays", "Test", "ODEProblemLibrary", "NonlinearSolve", "Enzyme", "LinearSolve", "Pkg", "JET", "Aqua"]
8280

8381
[sources.OrdinaryDiffEqSDIRK]
8482
path = "../OrdinaryDiffEqSDIRK"

0 commit comments

Comments
 (0)