Use ModelingToolkit's generated initialization maps in the kernel - #541
Merged
ChrisRackauckas merged 1 commit intoSep 13, 2026
Conversation
ModelingToolkit now emits the state and parameter initialization maps as isbits `RuntimeGeneratedFunction`s under `FullSpecialize` (#5043), so `EnsembleGPUKernel` can evaluate them on the device as they are. Delete the host-side symbolic tracing that reconstructed them as gather recipes: 232 lines, and it could not express entries computed from the solved initialization system at all. A problem qualifies when both maps are isbits and the state map builds an isbits `u0`. Reaching that needs `FullSpecialize`, static `u0_constructor` / `p_constructor`, and out-of-place. The last is not an independent choice: an `MVector` is a mutable struct and so is not isbits, while an in-place problem cannot write into an immutable `SVector`, which leaves out-of-place plus `SVector` as the only self-consistent pairing. Each missing piece gets its own error naming what to change, rather than failing inside the kernel. `make_parameter_compatible` now also rejects parameters whose contents are not isbits — an interpolation object, a callable closing over an array, a type — naming the offending `MTKParameters` portions. Converting buffer storage to `SArray` cannot rescue those, and previously they produced a silently non-isbits problem that failed deep in the kernel. The extension's `static_parameter_storage` was a duplicate of `make_static_storage` in core, so it is gone. The survivor is documented and `@public` as the conversion hook: a package owning a non-isbits type adds a method giving it an isbits stand-in, and both `u0` and the parameters pick it up without DiffEqGPU knowing the type. Co-Authored-By: Chris Rackauckas <accounts@chrisrackauckas.com> Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Agent-Harness: Claude Code 2.0.14 Agent-Model: claude-opus-5[1m] Agent-Session: https://claude.ai/code/session_01GdSpCLd7NBZuuePJmcDzU7 Claude-Session: https://claude.ai/code/session_01GdSpCLd7NBZuuePJmcDzU7
ChrisRackauckas
marked this pull request as ready for review
September 13, 2026 20:50
ChrisRackauckas
added a commit
that referenced
this pull request
Sep 14, 2026
- Use ModelingToolkit's generated initialization maps in the kernel (#541) - fix: restore Enzyme gradients through kernel ensembles (#532) Agent-Harness: Claude Code Agent-Model: claude-opus-5[1m] Claude-Session: https://claude.ai/code/session_014FEzNTLFutCmTEAZ3zBg5R Co-authored-by: Claude Opus 5 (1M context) <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.
What changed and why
ModelingToolkit now emits the state and parameter initialization maps as isbits
RuntimeGeneratedFunctions underFullSpecialize(#5043,
#5045), so
EnsembleGPUKernelcanevaluate them on the device as they are. This deletes the host-side symbolic tracing that
reconstructed them as gather recipes — the approach in the closed
#516 — and uses the maps directly.
ext/ModelingToolkitBaseExt.jlgoes from 314 to 108 lines. The tracing could not expressmap entries computed from the solved initialization system at all; the generated maps
handle them for free.
A problem qualifies when both maps are isbits and the state map builds an isbits
u0.Reaching that needs three things together, and each missing one now gets its own error
naming what to change:
SciMLBase.FullSpecialize, or the maps are host closures that cannot be uploaded.u0_constructor/p_constructor, because the maps rebuildu0/pinside thekernel, which cannot allocate. ModelingToolkit fixes the container when the problem is
built, so this cannot be corrected afterwards.
MVectoris a mutable struct and so is notisbits, which rules it out of a device array; an in-place problem cannot write into an
immutable
SVector. Out-of-place plusSVectoris the only self-consistent pairing.make_parameter_compatibleadditionally rejects parameters whose contents are not isbits— an interpolation object, a callable closing over an array, a type — naming the offending
MTKParametersportions. Converting buffer storage toSArraycannot rescue those, andthey previously produced a silently non-isbits problem that failed deep in the kernel.
Conversion hook
The extension's
static_parameter_storagewas a duplicate ofmake_static_storageinsrc/utils.jl, so it is gone. The survivor is documented and@publicas the conversionhook, so a package owning a non-isbits type can make it work without a change here:
Both
u0and the parameters route through it. Unconverted values fall through the identitymethod and are then caught by the isbits check, so the failure mode is an error naming the
value rather than a kernel crash.
Verification
Julia 1.12.7,
GROUP=JLArrays. The full ModelingToolkit DAE suite, all 18 testsets:Zero failures, exit 0.
GROUP=QA:Extensions loaded 6/6,Quality Assurance 21/21, exit0 — all six backend extensions load, so ExplicitImports sees the real module set. Runic,
typosover the diff andgit diff --checkare clean.The
Computed initialization mapstestset is the case closed #516 existed to fix:myisan observed variable of the torn initialization system, so the state map computes it rather
than copying it. Host reference and kernel agree exactly (
u0 = [3.0, 1.0]). The oldtracing threw on this shape.
What I did not verify
JLArrays, which runskernels on the CPU. That matters here specifically: a heap allocation inside a kernel
passes under
JLArraysand fails on a real device. That is why the isbits checks areexplicit host-side assertions rather than something inferred from a green suite — but a
CUDA run is still the real confirmation.
ModelingToolkitBasev1.71.3 predates Fix FullSpecialize initialization map buffer types ModelingToolkit.jl#5145, which fixes the
map buffer types this depends on. Everything above was run against ModelingToolkit
master. CI will be red until a
ModelingToolkitBaserelease carrying #5145 exists,at which point the
[compat]lower bound should be raised to it; I have not guessed aversion number.
JLArraysandQAgroups were run.CPU,CPU 32-bit,OpenCLand the docsbuild were not.
Anything a reviewer should push back on
now errors unless it is out-of-place with static constructors. The tutorial already
pre-announced requiring
FullSpecialize, but not the other two. Worth deciding whetherthat warrants a version bump.
u0— not acheck on the specialization level. A small enough system can satisfy it at
AutoDespecializeand is then accepted, which I think is right but is a judgement call.Ignore this draft until reviewed by @ChrisRackauckas.
🤖 Generated with Claude Code (model: claude-opus-5[1m])
https://claude.ai/code/session_01GdSpCLd7NBZuuePJmcDzU7