Skip to content

Add comprehensive QA testing infrastructure for solver allocations and type stability #2805

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
Merged
Show file tree
Hide file tree
Changes from 54 commits
Commits
Show all changes
55 commits
Select commit Hold shift + click to select a range
24509e8
Add comprehensive QA testing infrastructure for solver allocations an…
claude Aug 1, 2025
8975af6
Update sublibrary test configurations for allocation testing
claude Aug 1, 2025
18832be
Update documentation to reflect full CI integration
claude Aug 1, 2025
d5f1c0e
Move existing JET and Aqua tests into version-gated section
claude Aug 1, 2025
106770d
Document unified QA version gating approach
claude Aug 1, 2025
a5ef7d9
Update allocation tests to use AllocCheck.jl properly
claude Aug 1, 2025
ac40467
Update tests to loop over all exported solvers in each sublibrary
claude Aug 1, 2025
87240c7
Update lib/OrdinaryDiffEqTsit5/test/jet.jl
ChrisRackauckas Aug 1, 2025
a15b8e7
Update lib/OrdinaryDiffEqRosenbrock/test/allocation_tests.jl
ChrisRackauckas Aug 1, 2025
4475ac4
Update lib/OrdinaryDiffEqTsit5/test/allocation_tests.jl
ChrisRackauckas Aug 1, 2025
c34a50d
Clean up duplicate JET tests and remove documentation file
claude Aug 1, 2025
730fb40
Fix CI spell check error for 'extrapolant' parameter
claude Aug 3, 2025
294b732
Fix CI test dependency issues in sublibrary Project.toml files
claude Aug 3, 2025
2cb2015
Fix Project.toml dependency configuration
claude Aug 3, 2025
89a1bd3
Fix JET.jl compat alignment for Julia 1.10 LTS support
ChrisRackauckas Aug 3, 2025
49e8e94
Fix benchmark workflow parameter names
ChrisRackauckas Aug 3, 2025
1651d07
Fix JET test issues and mark failing tests as @test_broken
ChrisRackauckas Aug 3, 2025
94bde3f
Address CI-reported JET test failures across multiple packages
ChrisRackauckas Aug 3, 2025
c7b2c7c
Fix spelling errors identified by typos CI check
ChrisRackauckas Aug 4, 2025
5bb024f
Fix additional spelling errors from typos CI check
ChrisRackauckas Aug 4, 2025
6366cf5
Fix JET test structure to prevent CI failures
ChrisRackauckas Aug 4, 2025
19f4b45
Fix test_package broken parameter usage in JET tests
ChrisRackauckas Aug 4, 2025
acc1f6a
Restore proper JET tests and fix DAE solver problems
ChrisRackauckas Aug 4, 2025
4ba8d42
Fix typo test for BDF - now passing, no longer broken
ChrisRackauckas Aug 5, 2025
13a51b1
Fix typo tests that are now passing - remove broken flags
ChrisRackauckas Aug 5, 2025
ccc4d5f
Fix spelling errors and add typos configuration
ChrisRackauckas Aug 5, 2025
0bbada2
Update .github/workflows/benchmark.yml
ChrisRackauckas Aug 6, 2025
88e804a
Update .github/workflows/benchmark.yml
ChrisRackauckas Aug 6, 2025
601edef
Delete _typos.toml
ChrisRackauckas Aug 6, 2025
f3ed95a
Update .typos.toml
ChrisRackauckas Aug 6, 2025
21835cb
Update JET configuration and fix test structure
ChrisRackauckas Aug 6, 2025
8f6415b
Update algorithms.jl
ChrisRackauckas Aug 8, 2025
2b318d0
Fix: Move AllocCheck, JET, and Aqua to test dependencies only
ChrisRackauckas Aug 8, 2025
a3c68a1
Fix: Change 'project' to 'projects' in DowngradeSublibraries workflow
ChrisRackauckas Aug 8, 2025
b34ff2c
Update .github/workflows/DowngradeSublibraries.yml
ChrisRackauckas Aug 8, 2025
342b9a5
Fix: Remove duplicate entries in all sublibrary Project.toml files
ChrisRackauckas Aug 8, 2025
9e74121
Fix: Use 'projects' parameter in DowngradeSublibraries workflow (again)
ChrisRackauckas Aug 8, 2025
9e661d2
Fix Project.toml: Remove duplicates and add missing compat entries
ChrisRackauckas Aug 8, 2025
199714e
Fix CI errors: Add missing dependencies and fix AllocCheck compat
ChrisRackauckas Aug 9, 2025
47ab7de
Add AllocCheck to [extras] section in all packages that use it
ChrisRackauckas Aug 9, 2025
c2ec115
Fix all sublibrary compat issues
ChrisRackauckas Aug 9, 2025
daec64d
Add AllocCheck to all lib package test dependencies
ChrisRackauckas Aug 9, 2025
3d0064c
Add dt=0.1 to AllocCheck and JET test solve/init calls
ChrisRackauckas Aug 9, 2025
0353741
Mark OrdinaryDiffEqCore JET tests as broken
ChrisRackauckas Aug 9, 2025
902b59e
Fix JET test to use @test with broken=true
ChrisRackauckas Aug 9, 2025
ebcbfcc
Fix missing J extraction from cache in FIRK addsteps\!
ChrisRackauckas Aug 9, 2025
6bd71c0
Fix FIRK addsteps\! to compute Jacobian instead of extracting from cache
ChrisRackauckas Aug 9, 2025
4ccdfaa
Fix multiple test issues in OrdinaryDiffEq
ChrisRackauckas Aug 9, 2025
8621487
Remove recompile_flag and reorganize BDF allocation tests
ChrisRackauckas Aug 9, 2025
1dea2e4
Clean up recompile parameter removal
ChrisRackauckas Aug 9, 2025
508a40b
Standardize BDF test setup and remove SplitEuler from tests
ChrisRackauckas Aug 9, 2025
e80eb70
Fix BDF allocation tests to use correct problem types
ChrisRackauckas Aug 9, 2025
34b1a06
Move SBDF methods to IMEX/Split solver group
ChrisRackauckas Aug 9, 2025
d4cb3a4
Update lib/OrdinaryDiffEqBDF/test/allocation_tests.jl
ChrisRackauckas Aug 9, 2025
ea0c9db
Update lib/OrdinaryDiffEqBDF/test/jet.jl
ChrisRackauckas Aug 9, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/DowngradeSublibraries.yml
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ jobs:
version: ${{ matrix.julia-version }}
- uses: julia-actions/julia-downgrade-compat@v2
with:
project: ${{ matrix.project }}
projects: ${{ matrix.project }}
skip: Pkg,TOML
- uses: julia-actions/julia-buildpkg@v1
with:
Expand Down
10 changes: 10 additions & 0 deletions .typos.toml
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,16 @@ linearization = "linearization"
parameterized = "parameterized"
discretized = "discretized"
vectorized = "vectorized"
extrapolant = "extrapolant"
# Person names in citations - these are correct spellings
Sigal = "Sigal" # Sigal Gottlieb is a real person/author

# Common journal abbreviations
Numer = "Numer" # As in "P. Numer. Math." (Periodica Numerica Mathematica)

# Technical variable names
dorder = "dorder" # Parameter for delta order / order change


# Common variable patterns in Julia/SciML
ists = "ists"
Expand Down
12 changes: 7 additions & 5 deletions lib/ImplicitDiscreteSolve/Project.toml
Original file line number Diff line number Diff line change
Expand Up @@ -4,19 +4,20 @@ authors = ["vyudu <vincent.duyuan@gmail.com>"]
version = "1.0.0"

[deps]
SimpleNonlinearSolve = "727e6d20-b764-4bd8-a329-72de5adea6c7"
SymbolicIndexingInterface = "2efcf032-c050-4f8e-a9bb-153293bab1f5"
OrdinaryDiffEqCore = "bbf590c4-e513-4bbe-9b18-05decba2e5d8"
SciMLBase = "0bca4576-84f4-4d90-8ffe-ffa030f20462"
UnPack = "3a884ed6-31ef-47d7-9d2a-63182c4928ed"
DiffEqBase = "2b5f629d-d688-5b77-993f-72d75c75574e"
SymbolicIndexingInterface = "2efcf032-c050-4f8e-a9bb-153293bab1f5"
SimpleNonlinearSolve = "727e6d20-b764-4bd8-a329-72de5adea6c7"
Reexport = "189a3867-3050-52da-a836-e630ba90ab69"
OrdinaryDiffEqCore = "bbf590c4-e513-4bbe-9b18-05decba2e5d8"
SciMLBase = "0bca4576-84f4-4d90-8ffe-ffa030f20462"

[extras]
JET = "c3a54625-cd67-489e-a8e7-0a5a0ff4e31b"
OrdinaryDiffEqSDIRK = "2d112036-d095-4a1e-ab9a-08536f3ecdbf"
Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40"
Aqua = "4c88cf16-eb10-579e-8560-4a9242c79595"
AllocCheck = "9b6a8646-10ed-4001-bbdc-1d2f46dfbb1a"

[compat]
Test = "1.10.0"
Expand All @@ -29,11 +30,12 @@ SymbolicIndexingInterface = "0.3.38"
julia = "1.10"
JET = "0.9.18, 0.10.4"
UnPack = "1.0.2"
AllocCheck = "0.2"
DiffEqBase = "6.164.1"
Reexport = "1.2.2"

[targets]
test = ["OrdinaryDiffEqSDIRK", "Test", "JET", "Aqua"]
test = ["OrdinaryDiffEqSDIRK", "Test", "JET", "Aqua", "AllocCheck"]

[sources.OrdinaryDiffEqCore]
path = "../OrdinaryDiffEqCore"
8 changes: 5 additions & 3 deletions lib/OrdinaryDiffEqAdamsBashforthMoulton/Project.toml
Original file line number Diff line number Diff line change
Expand Up @@ -4,16 +4,16 @@ authors = ["ParamThakkar123 <paramthakkar864@gmail.com>"]
version = "1.3.0"

[deps]
Reexport = "189a3867-3050-52da-a836-e630ba90ab69"
OrdinaryDiffEqCore = "bbf590c4-e513-4bbe-9b18-05decba2e5d8"
Polyester = "f517fe37-dbe3-4b94-8317-1923a5111588"
Static = "aedffcd0-7271-4cad-89d0-dc628f76c6d3"
FastBroadcast = "7034ab61-46d4-4ed7-9d0f-46aef9175898"
OrdinaryDiffEqLowOrderRK = "1344f307-1e59-4825-a18e-ace9aa3fa4c6"
RecursiveArrayTools = "731186ca-8d62-57ce-b412-fbd966d074cd"
SciMLBase = "0bca4576-84f4-4d90-8ffe-ffa030f20462"
Reexport = "189a3867-3050-52da-a836-e630ba90ab69"
DiffEqBase = "2b5f629d-d688-5b77-993f-72d75c75574e"
MuladdMacro = "46d2c3a1-f734-5fdb-9937-b9b9aeba4221"
OrdinaryDiffEqCore = "bbf590c4-e513-4bbe-9b18-05decba2e5d8"

[extras]
JET = "c3a54625-cd67-489e-a8e7-0a5a0ff4e31b"
Expand All @@ -22,6 +22,7 @@ Random = "9a3f8284-a2c9-5f02-9a11-845980a1fd5c"
DiffEqDevTools = "f3b72e0c-5b89-59e1-b016-84e28bfd966d"
ODEProblemLibrary = "fdc4e326-1af4-4b90-96e7-779fcce2daa5"
Aqua = "4c88cf16-eb10-579e-8560-4a9242c79595"
AllocCheck = "9b6a8646-10ed-4001-bbdc-1d2f46dfbb1a"
DiffEqBase = "2b5f629d-d688-5b77-993f-72d75c75574e"
SafeTestsets = "1bc83da4-3b8d-516f-aca4-4fe02f6d838f"

Expand All @@ -41,12 +42,13 @@ julia = "1.10"
JET = "0.9.18, 0.10.4"
RecursiveArrayTools = "3.27.0"
ODEProblemLibrary = "0.1.8"
AllocCheck = "0.2"
DiffEqBase = "6.152.2"
Reexport = "1.2.2"
SafeTestsets = "0.1.0"

[targets]
test = ["DiffEqDevTools", "ODEProblemLibrary", "Random", "SafeTestsets", "Test", "DiffEqBase", "JET", "Aqua"]
test = ["DiffEqDevTools", "ODEProblemLibrary", "Random", "SafeTestsets", "Test", "DiffEqBase", "JET", "Aqua", "AllocCheck"]

[sources.OrdinaryDiffEqLowOrderRK]
path = "../OrdinaryDiffEqLowOrderRK"
Expand Down
6 changes: 4 additions & 2 deletions lib/OrdinaryDiffEqBDF/Project.toml
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,8 @@ OrdinaryDiffEqSDIRK = "2d112036-d095-4a1e-ab9a-08536f3ecdbf"
TruncatedStacktraces = "781d530d-4396-4725-bb49-402e4bee1e77"
SciMLBase = "0bca4576-84f4-4d90-8ffe-ffa030f20462"
OrdinaryDiffEqCore = "bbf590c4-e513-4bbe-9b18-05decba2e5d8"
ArrayInterface = "4fba245c-0d91-5ea0-9b3e-6abc04ee57a9"
Preferences = "21216c6a-2e73-6563-6e65-726566657250"
ArrayInterface = "4fba245c-0d91-5ea0-9b3e-6abc04ee57a9"
MacroTools = "1914dd2f-81c6-5fcd-8719-6d5c9610ff09"
StaticArrays = "90137ffa-7385-5640-81b9-e52037218182"
ADTypes = "47edcb42-4c32-4615-8424-f2b9edc5f35b"
Expand All @@ -35,6 +35,7 @@ Enzyme = "7da242da-08ed-463a-9acd-ee780be4f1d9"
JET = "c3a54625-cd67-489e-a8e7-0a5a0ff4e31b"
StaticArrays = "90137ffa-7385-5640-81b9-e52037218182"
ODEProblemLibrary = "fdc4e326-1af4-4b90-96e7-779fcce2daa5"
AllocCheck = "9b6a8646-10ed-4001-bbdc-1d2f46dfbb1a"
SafeTestsets = "1bc83da4-3b8d-516f-aca4-4fe02f6d838f"

[compat]
Expand Down Expand Up @@ -65,12 +66,13 @@ ADTypes = "1.11"
RecursiveArrayTools = "3.27.0"
ODEProblemLibrary = "0.1.8"
OrdinaryDiffEqNonlinearSolve = "1.6"
AllocCheck = "0.2"
DiffEqBase = "6.169.1"
Reexport = "1.2.2"
SafeTestsets = "0.1.0"

[targets]
test = ["DiffEqDevTools", "ForwardDiff", "Random", "SafeTestsets", "Test", "ODEProblemLibrary", "NonlinearSolve", "StaticArrays", "Enzyme", "LinearSolve", "JET", "Aqua"]
test = ["DiffEqDevTools", "ForwardDiff", "Random", "SafeTestsets", "Test", "ODEProblemLibrary", "NonlinearSolve", "StaticArrays", "Enzyme", "LinearSolve", "JET", "Aqua", "AllocCheck"]

[sources.OrdinaryDiffEqSDIRK]
path = "../OrdinaryDiffEqSDIRK"
Expand Down
124 changes: 124 additions & 0 deletions lib/OrdinaryDiffEqBDF/test/allocation_tests.jl
Original file line number Diff line number Diff line change
@@ -0,0 +1,124 @@
using OrdinaryDiffEqBDF
using OrdinaryDiffEqCore
using AllocCheck
using Test

"""
Allocation tests for OrdinaryDiffEqBDF solvers using AllocCheck.jl.
These tests verify that the step! operation should not allocate during stepping.
Currently, many BDF solvers are allocating and marked with @test_broken.
"""

@testset "BDF Allocation Tests" begin
# Test problem for standard ODE BDF methods
function simple_system!(du, u, p, t)
du[1] = -0.5 * u[1]
du[2] = -1.5 * u[2]
end
prob = ODEProblem(simple_system!, [1.0, 1.0], (0.0, 1.0))

# Split problem for IMEX methods
function f1!(du, u, p, t)
du[1] = -0.5 * u[1]
du[2] = 0.0
end
function f2!(du, u, p, t)
du[1] = 0.0
du[2] = -1.5 * u[2]
end
split_prob = SplitODEProblem(f1!, f2!, [1.0, 1.0], (0.0, 1.0))

# DAE problem for DAE solvers
function dae_f!(resid, du, u, p, t)
resid[1] = -0.5 * u[1] + u[2] - du[1]
resid[2] = u[1] - u[2] - du[2]
end
du0 = zeros(2)
differential_vars = [true, false]
dae_prob = DAEProblem(dae_f!, du0, [1.0, 1.0], (0.0, 1.0), differential_vars=differential_vars)

# Test all exported BDF solvers for allocation-free behavior
# Standard ODE BDF methods
bdf_solvers = [ABDF2(), QNDF1(), QBDF1(), QNDF2(), QBDF2(), QNDF(), QBDF(), FBDF(), MEBDF2()]

# IMEX/Split methods need SplitODEProblem
imex_solvers = [SBDF(order=2), SBDF2(), SBDF3(), SBDF4(), IMEXEuler(), IMEXEulerARK()]

# DAE methods need DAEProblem
dae_solvers = [DABDF2(), DImplicitEuler(), DFBDF()]

@testset "BDF Solver Allocation Analysis" begin
for solver in bdf_solvers
@testset "$(typeof(solver)) allocation check" begin
integrator = init(prob, solver, dt=0.1, save_everystep=false, abstol=1e-6, reltol=1e-6)
step!(integrator) # Setup step may allocate

# Use AllocCheck to verify step! is allocation-free
allocs = check_allocs(step!, (typeof(integrator),))

# These solvers should be allocation-free, but mark as broken for now
# to verify with AllocCheck (more accurate than @allocated)
@test length(allocs) == 0 broken=true

if length(allocs) > 0
println("AllocCheck found $(length(allocs)) allocation sites in $(typeof(solver)) step!:")
for (i, alloc) in enumerate(allocs[1:min(3, end)]) # Show first 3
println(" $i. $alloc")
end
else
println("✓ $(typeof(solver)) appears allocation-free with AllocCheck")
end
end
end
end

@testset "IMEX Solver Allocation Analysis" begin
for solver in imex_solvers
@testset "$(typeof(solver)) allocation check" begin
integrator = init(split_prob, solver, dt=0.1, save_everystep=false, abstol=1e-6, reltol=1e-6)
step!(integrator) # Setup step may allocate

# Use AllocCheck to verify step! is allocation-free
allocs = check_allocs(step!, (typeof(integrator),))

# These solvers should be allocation-free, but mark as broken for now
# to verify with AllocCheck (more accurate than @allocated)
@test length(allocs) == 0 broken=true

if length(allocs) > 0
println("AllocCheck found $(length(allocs)) allocation sites in $(typeof(solver)) step!:")
for (i, alloc) in enumerate(allocs[1:min(3, end)]) # Show first 3
println(" $i. $alloc")
end
else
println("✓ $(typeof(solver)) appears allocation-free with AllocCheck")
end
end
end
end

@testset "DAE Solver Allocation Analysis" begin
for solver in dae_solvers
@testset "$(typeof(solver)) allocation check" begin
integrator = init(dae_prob, solver, dt=0.1, save_everystep=false, abstol=1e-6, reltol=1e-6)
step!(integrator) # Setup step may allocate

# Use AllocCheck to verify step! is allocation-free
allocs = check_allocs(step!, (typeof(integrator),))

# These solvers should be allocation-free, but mark as broken for now
# to verify with AllocCheck (more accurate than @allocated)
@test length(allocs) == 0 broken=true

if length(allocs) > 0
println("AllocCheck found $(length(allocs)) allocation sites in $(typeof(solver)) step!:")
for (i, alloc) in enumerate(allocs[1:min(3, end)]) # Show first 3
println(" $i. $alloc")
end
else
println("✓ $(typeof(solver)) appears allocation-free with AllocCheck")
end
end
end
end
end
71 changes: 71 additions & 0 deletions lib/OrdinaryDiffEqBDF/test/jet.jl
Original file line number Diff line number Diff line change
@@ -1,7 +1,78 @@
import OrdinaryDiffEqBDF
using OrdinaryDiffEqBDF
using OrdinaryDiffEqCore
using DiffEqBase: SplitODEProblem, DAEProblem
using JET
using Test

@testset "JET Tests" begin
# Test package for typos - now passing
test_package(
OrdinaryDiffEqBDF, target_defined_modules = true, mode = :typo)

# Test individual solver type stability
@testset "Solver Type Stability Tests" begin
# Test problem - use a simple linear problem for stiff solvers
linear_prob = ODEProblem((u, p, t) -> -u, 1.0, (0.0, 1.0))

# Split problem for SBDF solvers (which require SplitODEProblem)
split_prob = SplitODEProblem((u, p, t) -> -u, (u, p, t) -> 0.0, 1.0, (0.0, 1.0))

# DAE problem for DAE solvers
function simple_dae!(du, u, p, t)
du[1] = -u[1]
end
u0 = [1.0]
du0 = [-1.0]
dae_prob = DAEProblem(simple_dae!, du0, u0, (0.0, 1.0))

# Regular BDF solvers (ODEProblem)
regular_bdf_solvers = [ABDF2(), QNDF1(), QBDF1(), QNDF2(), QBDF2(), QNDF(), QBDF(), FBDF(),
MEBDF2(), IMEXEuler(), IMEXEulerARK()]

# DAE solvers (DAEProblem)
dae_solvers = [DABDF2(), DImplicitEuler(), DFBDF()]

# Test SBDF solvers separately with required order parameter and SplitODEProblem
sbdf_solvers = [SBDF(order=2), SBDF(order=3), SBDF(order=4), SBDF2(), SBDF3(), SBDF4()]

for solver in regular_bdf_solvers
@testset "$(typeof(solver)) type stability" begin
try
@test_opt broken=true init(linear_prob, solver, dt=0.1, save_everystep=false, abstol=1e-6, reltol=1e-6)
integrator = init(linear_prob, solver, dt=0.1, save_everystep=false, abstol=1e-6, reltol=1e-6)
@test_opt broken=true step!(integrator)
catch e
@test_broken false # Mark as broken if solver fails to initialize
println("$(typeof(solver)) failed with: $e")
end
end
end

for solver in dae_solvers
@testset "$(typeof(solver)) DAE type stability" begin
try
@test_opt broken=true init(dae_prob, solver, dt=0.1, save_everystep=false, abstol=1e-6, reltol=1e-6)
integrator = init(dae_prob, solver, dt=0.1, save_everystep=false, abstol=1e-6, reltol=1e-6)
@test_opt broken=true step!(integrator)
catch e
@test_broken false # Mark as broken if solver fails to initialize
println("$(typeof(solver)) failed with: $e")
end
end
end

for solver in sbdf_solvers
@testset "$(typeof(solver)) type stability" begin
try
@test_opt broken=true init(split_prob, solver, dt=0.1, save_everystep=false, abstol=1e-6, reltol=1e-6)
integrator = init(split_prob, solver, dt=0.1, save_everystep=false, abstol=1e-6, reltol=1e-6)
@test_opt broken=true step!(integrator)
catch e
@test_broken false # Mark as broken if solver fails to initialize
println("$(typeof(solver)) failed with: $e")
end
end
end
end
end
8 changes: 6 additions & 2 deletions lib/OrdinaryDiffEqBDF/test/runtests.jl
Original file line number Diff line number Diff line change
Expand Up @@ -9,5 +9,9 @@ using SafeTestsets
@time @safetestset "BDF Convergence Tests" include("bdf_convergence_tests.jl")
@time @safetestset "BDF Regression Tests" include("bdf_regression_tests.jl")

@time @safetestset "JET Tests" include("jet.jl")
@time @safetestset "Aqua" include("qa.jl")
# Only run QA and allocation tests on stable Julia versions
if isempty(VERSION.prerelease)
@time @safetestset "JET Tests" include("jet.jl")
@time @safetestset "Aqua" include("qa.jl")
@time @safetestset "Allocation Tests" include("allocation_tests.jl")
end
Loading
Loading