Add Implementation for AMF-type ODE schemes#3334
Conversation
There was a problem hiding this comment.
Pull request overview
Note
Copilot was unable to run its full agentic suite in this review.
Adds an OrdinaryDiffEqAMF subpackage to support AMF-style Rosenbrock-W solves using SciMLOperator-based Jacobians/W_prototype, along with regression tests validating correctness on standard model problems.
Changes:
- Introduces
AMFalgorithm wrapper andSciMLOpFactorizationLinearSolve backend. - Adds
AMFOperatorandbuild_amf_functionutilities for constructingjac_prototype/W_prototypeoperator pipelines. - Adds POLLU and 2D finite-difference regression tests plus a
runtests.jlentrypoint.
Reviewed changes
Copilot reviewed 8 out of 8 changed files in this pull request and generated 4 comments.
Show a summary per file
| File | Description |
|---|---|
| lib/OrdinaryDiffEqAMF/src/OrdinaryDiffEqAMF.jl | New module wiring/exports for AMF functionality. |
| lib/OrdinaryDiffEqAMF/src/amf.jl | Adds AMF wrapper that injects an AMF linear solver into Rosenbrock-W algorithms. |
| lib/OrdinaryDiffEqAMF/src/linsolve.jl | Implements a LinearSolve algorithm intended to factorize operator-based W. |
| lib/OrdinaryDiffEqAMF/src/operators.jl | Adds operator constructors and build_amf_function helper to set jac_prototype/W_prototype. |
| lib/OrdinaryDiffEqAMF/test/runtests.jl | Test entrypoint including new regressions. |
| lib/OrdinaryDiffEqAMF/test/test_pollu.jl | Regression test on POLLU stiff chemical kinetics model. |
| lib/OrdinaryDiffEqAMF/test/test_fd2d.jl | Regression + validation tests for AMF split operators and custom factors. |
| lib/OrdinaryDiffEqAMF/Project.toml | New package manifest for OrdinaryDiffEqAMF. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| return SciMLBase.build_linear_solution(alg, y, nothing, cache) | ||
| end | ||
|
|
||
| LinearSolve.needs_concrete_A(::SciMLOpFactorization) = true |
There was a problem hiding this comment.
needs_concrete_A(::SciMLOpFactorization) = true will cause LinearSolve to concretize operator A into a matrix before solving. That contradicts the stated goal in AMF docs (avoid densifying W_prototype) and can lead to very large allocations/OOM for big Kronecker-style operators. Prefer setting this to false (and ensuring factorize/ldiv! operate on the SciMLOperator form), or if densification is actually required then update the AMF docstring/implementation to match that behavior.
| actual_sparsity = isnothing(sparsity) ? convert(AbstractMatrix, J_op) : sparsity | ||
| return SciMLBase.ODEFunction(f!; jac_prototype = J_op, W_prototype = W_op, sparsity = actual_sparsity) |
There was a problem hiding this comment.
When sparsity is not provided, computing it via convert(AbstractMatrix, J_op) forces full materialization of the Jacobian operator into an N×N matrix. This can be prohibitively expensive and undermines the operator-based approach. Consider only passing the sparsity keyword when the caller supplies it, or using a cheap sparsity-pattern representation that doesn’t require concretizing J_op.
| isnothing(jac_cache) && (jac_cache = zeros(N)) | ||
| isnothing(w_cache) && (w_cache = zeros(N)) | ||
|
|
||
| J_op = cache_operator(jac, jac_cache) |
There was a problem hiding this comment.
The default caches zeros(N) appear inconsistent with how cache_operator is used elsewhere in this PR (tests allocate zeros(N^2) for cached N×N matrix-like operators). If cache_operator expects storage for the concretized operator (common for MatrixOperator/operator sums), zeros(N) will be undersized and can trigger bounds errors or incorrect caching during matrix materialization (e.g., for factorization/sparsity). Recommend sizing these defaults to zeros(N^2) (or zeros(N, N) depending on SciMLOperators’ expected cache shape), or using a SciMLOperators API that allocates an appropriately-sized cache automatically.
| [compat] | ||
| julia = "1" |
There was a problem hiding this comment.
The [compat] section pins only julia. For a registered/consumable Julia package, it’s important to add compat bounds for direct deps (e.g., SciMLBase, SciMLOperators, LinearSolve, Reexport, OrdinaryDiffEqCore) to avoid resolver breakage and unintended upgrades. Please add appropriate version bounds based on the APIs you rely on.
Checklist
contributor guidelines, in particular the SciML Style Guide and
COLPRAC.
Additional context
Implementing AMF-type schemes for solving ODEs.
build_amf_function(f!; jac, split = nothing, amf_factors = nothing)is the single construction entry point.jacprovides the full Jacobian operator and becomes thejac_prototypeon the returnedODEFunction.splitis omitted, the builder constructs the exact RosenbrockWoperator fromjac.split = (J₁, J₂, ...)is provided, the builder forms the default AMF operator as the ordered product∏ᵢ (I - γJᵢ).splitis operator-based: eachJᵢis anAbstractSciMLOperatorcarrying its own structure and update rules.I - γJᵢproduct,amf_factors = (W₁, W₂, ...)can be passed directly.W_prototypeproduct while still usingjacas the full Jacobian prototype.AMF(alg)remains a thin solver wrapper that plugs the operator-aware linear solve into an otherwise standard SciML solve workflow.