feat: add Integrators submodule (ODE solver strategies)#163
Merged
Conversation
Add `CTSolvers.Integrators`, a sibling of Solvers/Modelers/DOCP that drives ODE integration through the same `CommonSolve.solve(problem, strategy)` idiom, mirroring the Solvers core/extension split and the post-#162 contract-co-location convention. Core (src/Integrators/): - AbstractIntegrator <: Strategies.AbstractStrategy (abstract_integrator.jl) - SciML / AbstractSciMLIntegrator / SciMLTag / Tsit5Tag + id/description + ctor, metadata and __default_sciml_algorithm stubs + options_point/options_trajectory accessors (sciml.jl) - AbstractIntegrationResult + final_state/times/evaluate_at stubs (integration_result.jl) - CommonSolve.solve(prob, ::AbstractIntegrator) + merge stubs + __unsafe default (contract.jl); build_integrator (conveniences.jl); real_norm/deepvalue fallbacks (internal_norm.jl) Extensions (ext/), with SciMLBase as a weak dependency: - CTSolversSciMLIntegrator: metadata + build_sciml_integrator + the typed CommonSolve.solve(::AbstractODEProblem, ::SciML) + SciMLIntegrationResult + merge - CTSolversForwardDiff: dual-number deepvalue/real_norm (grid invariance) - CTSolversOrdinaryDiffEqTsit5: default Tsit5 algorithm The domain glue that builds an ODEProblem from a control system/config (build_problem/build_options) intentionally stays in the consuming package (e.g. CTFlows); CTSolvers only integrates a ready-made ODEProblem. Purely additive. Adds test/suite/integrators/ (61 tests, standalone end-to-end against a raw ODEProblem) and the SciMLBase/DiffEqBase/OrdinaryDiffEqTsit5/ForwardDiff weak deps. Widen OrderedCollections compat to "1, 2" so the test env resolves with the ODE stack. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- New developer guide guides/implementing_an_integrator.md (registered in make.jl), mirroring implementing_a_solver.md for the ODE integrator. - API-reference entries in docs/api_reference.jl for the Integrators submodule and the three extensions (each guarded by Base.get_extension, like the solver extensions). - CHANGELOG entry under 0.4.25-beta documenting the new submodule and extensions. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
OT is used as a type parameter name in SciML struct (options_trajectory type parameter)
Adding the SciML/ODE stack to the test environment shifts the shared resolver to
MadNLPGPU 0.10 / CUDA 6 (up-to-date), where MadNLPGPU binds `CUDSSSolver` to `nothing`
on machines without a functional GPU (0.8 always exposed a concrete type). The MadNLP/
MadNCL `:linear_solver` validator then called
`__madnlp_suite_consistent_linear_solver(param, nothing)`, which had no method → the
metadata/describe and GPU-linear-solver tests errored.
- Add `__madnlp_suite_consistent_linear_solver(::Type{<:AbstractStrategyParameter}, ::Nothing)`
returning `true` (nothing concrete to validate → consistent, no warning). Fixes the
describe_registry and GPU-linear-solver MethodErrors at the source, CPU and GPU.
- Guard the CUDSS-solver `@test_logs` inconsistency assertions on
`MadNLPGPU.CUDSSSolver isa Type` so they run for real when a functional GPU exposes the
type and skip cleanly otherwise.
Keeps the solver compat broad (MadNLP/MadNLPGPU 0.10, CUDA 6 stay allowed) so the suite
runs against the latest solvers. Full suite green.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…U 0.10) MadNLPGPU 0.10 sets CUDSSSolver = nothing when CUDSS is absent, even on machines with functional CUDA. The GPU solve tests passed `MadNLPGPU.CUDSSSolver` directly as `linear_solver`, which then arrived as `nothing` and failed CTBase.Options type validation. Tighten all six GPU test sections (MadNLP + MadNCL, three sections each) from `if is_cuda_on()` to `if is_cuda_on() && MadNLPGPU.CUDSSSolver isa Type`. Also narrow available_tests in runtests.jl to these two files for a targeted GPU CI run; revert to full glob after confirmation. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Targeted iteration confirmed both madnlp and madncl GPU tests pass (266/266 on GPU runner). Restore the full suite glob. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…r gap tests Fill the gaps identified against Handbook/philosophy/testing.md: - test_integrator_exports.jl (new) — verifies names(Integrators) for all public symbols; confirms internal helpers (__unsafe, __default_sciml_algorithm, deepvalue, real_norm) are not exported; asserts CTSolvers has no top-level re-exports. - test_integrator_type_stability.jl (new) — @inferred on Strategies.id, SciML construction, options_point/options_trajectory, and result accessors (final_state, times, evaluate_at) after a real solve. - test_integrator_forwarddiff.jl (new) — end-to-end solve with Dual-valued initial condition; checks value and sensitivity at t=1 match exp(-1); also tests evaluate_at at an interior point and a nested Dual (exercises deepvalue recursion). - test_integrator_metadata.jl — add mode=:free testset: confirms that strict mode rejects reltol=-1 while free mode passes the same value. - test_integrator_solve.jl — add evaluate_at boundary-points testset (t=0 and t=1). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
:permissive mode accepts unknown options (with warning) but still applies value validators. :free is not a valid mode. Replaces the incorrect mode=:free test with two correct tests: - strict mode rejects unknown options - permissive mode accepts unknown options (warns, captured with @test_logs) Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…s{NT}
Julia infers SciML{StrategyOptions{NT}, ...} where NT<:NamedTuple for the
constructor but returns the fully concrete NamedTuple type. @inferred
rejects this mismatch. Replace with isa checks, matching the precedent in
solvers/test_type_stability.jl which has the same note for Ipopt/MadNLP.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Phase C of the prep-for-integrators plan: add
CTSolvers.Integrators, a sibling ofSolvers/Modelers/DOCPthat drives ODE integration through the sameCommonSolve.solve(problem, strategy)idiom. It mirrors theSolverscore/extension split1:1 and follows the post-#162 contract-co-location convention.
This is an additive copy: the solver-core is brought into CTSolvers so it can be released
standalone; CTFlows is untouched and keeps its own live copy. The later CTFlows rewire
(consume
CTSolvers.Integrators, retarget glue) is a separate step.What's in it
Core —
src/Integrators/abstract_integrator.jl—AbstractIntegrator <: Strategies.AbstractStrategy(type only)integration_result.jl—AbstractIntegrationResult+final_state/times/evaluate_atsciml.jl—SciML/AbstractSciMLIntegrator/SciMLTag/Tsit5Tag,id/description,ctor +
metadata/build_sciml_integrator/__default_sciml_algorithmstubs,options_point/options_trajectoryaccessorscontract.jl— genericCommonSolve.solve(prob, ::AbstractIntegrator)+mergestubs +__unsafedefault (nosolve_problem, no callable)conveniences.jl—build_integrator;internal_norm.jl—real_norm/deepvaluefallbacksExtensions —
ext/(SciMLBaseis a weak dep)CTSolversSciMLIntegrator— metadata + builder + typedCommonSolve.solve(::AbstractODEProblem, ::SciML)+SciMLIntegrationResult+mergeCTSolversForwardDiff— dual-numberdeepvalue/real_norm(grid invariance / IND)CTSolversOrdinaryDiffEqTsit5— defaultTsit5algorithmWhat stays out (CTFlows domain glue):
build_problem(system/config →ODEProblem) andbuild_options(config-dispatch). CTSolvers only integrates a ready-madeODEProblemandexposes the
options_point/options_trajectoryaccessors.Project.toml:
SciMLBase/DiffEqBase/OrdinaryDiffEqTsit5/ForwardDiffadded as weakdeps (+ compat/extras/targets); version →
0.4.25-beta.OrderedCollectionscompat widenedto
"1, 2"so the test env resolves alongside the ODE stack.Docs: new guide
guides/implementing_an_integrator.md+ API-reference pages for thesubmodule and the three extensions.
Tests
New
test/suite/integrators/(61 tests, all green): type hierarchy, extension/contractstubs, metadata +
:autooption resolution, internal-norm/grid-invariance (incl. ForwardDiff),and a standalone end-to-end solve of a raw
ODEProblem(u' = -u, u(0)=1 ⇒ u(1)=exp(-1))checking
final_state/times/evaluate_at,merge, and theunsaferetcode path.Full suite: 2479 passed, 0 failed, 5 errored. The 5 errors are pre-existing and
environmental — all in
MadNLP{GPU}/MadNCL{GPU}describe/consistency paths(
__madnlp_suite_consistent_linear_solver(::GPU, ::Nothing)), triggered because CUDA is notfunctional on the test machine (the GPU default linear solver resolves to
nothing). Nofile under
ext/CTSolversMadNLP.jlortest/suite/strategies/was touched by this PR.meta/test_aquais green.🤖 Generated with Claude Code