Skip to content

Preserve contiguous array derivative views in implicit residual codegen - #5242

Draft
ChrisRackauckas-Claude wants to merge 4 commits into
SciML:masterfrom
ChrisRackauckas-Claude:agent/5097-adopt-5121
Draft

ChrisRackauckas-Claude wants to merge 4 commits into
SciML:masterfrom
ChrisRackauckas-Claude:agent/5097-adopt-5121

Conversation

@ChrisRackauckas-Claude

@ChrisRackauckas-Claude ChrisRackauckas-Claude commented Oct 1, 2026 •

Copy link
Copy Markdown
Member

Please ignore until reviewed by @ChrisRackauckas.

Summary

Adopts stalled #5121 onto current master. Implicit DAE residuals previously expanded each array derivative into one scalar du read per element via SymbolicUtils.array_literal, so residual expression size grew with state dimension (D(x) ~ -x AST nodes 623→1631 for n=24→96). Contiguous derivative blocks are now bound to reshaped views of the du argument, keeping generated residual code size independent of n when the state layout is contiguous.

Cherry-picked from #5121 with authorship preserved (7bb83a3, e19beae). The DiffEqBase 7.18.1 bump commit was skipped: master already requires it. Conflict resolution kept master's existing array_equation_dae.jl coverage and master's element-wise dervars handling in build_explicit_observed_function (from the #5148 array-equation work); #5121's regression tests for code size / observations / interleaved layouts / sentinel collision were retained.

Review follow-up (AayushSabharwal)

Addressed the requested changes in array_derivative_arguments! (96afb6e):

  • Collect terms via IRStructureSearchBuffer + Symbolics.get_variables! (as suggested).
  • Also applied the companion suggestions: Symbolics.STerm/SArgsT for the reshape binding, and SU.IRSubstituter{false} for residual substitution.
  • Added short intent comments for contiguous du views, the element-wise fallback, and the __mtk_dae_du sentinel collision check.
  • Later comment wording fix (isempty(terms) || array unknowns) with no behaviour change.

Relation to #5101 / #5121

Fixes #5097.

Verification

Negative control (src fix reverted to master codegen.jl, new tests kept)

Unchanged residual-size assertion; still fails on master:

array derivative code size: Test Failed
  Expression: sizes[1] == sizes[2]
   Evaluated: 623 == 1631
Test Summary:              | Pass  Fail  Total  Time
array derivative code size |    6     1      7  12.9s
ERROR: LoadError: Some tests did not pass: 6 passed, 1 failed, 0 errored, 0 broken.

With fix after review follow-up (include("lib/ModelingToolkitBase/test/array_equation_dae.jl"))

Test Summary:              | Pass  Total  Time
array derivative code size |    7      7   6.6s
...
Multidimensional derivative code size |   10     10  15.0s

All 14 testsets in that file passed.

Owning group (after review follow-up)

cd lib/ModelingToolkitBase && GROUP=InterfaceI julia +1.12 --project=. -e 'using Pkg; Pkg.test()'
Test Summary: | Pass  Broken  Total      Time
InterfaceI    | 1794       5   1799  34m11.9s
     Testing ModelingToolkitBase tests passed
IFACE_EXIT=0

Local InterfaceI was run against Symbolics v7.42.0. Current CI reds in InterfaceI (variable_parsing.jl:66 — "Variable x must have default of matching size") and Extended (ccompile.jl:11 CTarget) come from Symbolics v7.44.1 (released 2026-10-06); they reproduce on master and are unrelated to this PR.

Runic --check --diff and typos clean on lib/ModelingToolkitBase/src/systems/codegen.jl.

Not verified

  • Full Core / InterfaceII / QA / Extended suites
  • Large-array solver/factorization wall-clock scaling (this PR bounds generated expression size, not numeric storage or solve cost)
  • aarch64 (ran on x86_64 Linux, Julia 1.12.7)
  • Re-running InterfaceI under Symbolics v7.44.1 (CI reds attributed to Symbolics, not this diff)

What a reviewer should push back on

  • Reserving the fixed sentinel __mtk_dae_du vs generating a different stable name when it collides with a user symbol (collision currently throws ArgumentError).
  • Whether noncontiguous/interleaved layouts should get a stronger contiguous-view path later, or whether the scalar fallback is enough forever.

Risk assessment

  • Risk: medium — changes implicit-DAE residual codegen for every system with array-shaped derivatives.
  • Blast radius: generate_rhs(...; implicit_dae=true) / DAEFunction / DAEProblem; observed equations that mention derivatives; assertion expressions in that path.
  • Evidence: fail-before / pass-after on the code-size regression; full array_equation_dae.jl; InterfaceI 1794/5 broken green locally on Symbolics 7.42.0 after the review follow-up.
  • Independent review: Opus 5.5 MERGE (medium); AayushSabharwal requested changes addressed
  • Merge: needs review

Please ignore this draft until reviewed by @ChrisRackauckas.

Harness: Cursor Agent CLI 2026.10.01-14929f9 · Model: auto · Transcript: /home/crackauc/sandbox/goals/issue-backlog/orch-ibmtk/jobs/5097-cursor/log.txt on amdci2.julia.csail.mit.edu

ChrisRackauckas and others added 2 commits October 1, 2026 16:54
Co-Authored-By: Chris Rackauckas <accounts@chrisrackauckas.com>
Co-Authored-By: Codex <noreply@openai.com>
Agent-Harness: Codex CLI 0.153.4
Agent-Model: gpt-6-astra
Agent-Session: local session 01a08018-78ff-7e42-a964-f5174f3f763b
Co-Authored-By: Chris Rackauckas <accounts@chrisrackauckas.com>
Co-Authored-By: Codex <noreply@openai.com>
Agent-Harness: Codex CLI 0.153.4
Agent-Model: gpt-6-astra
Agent-Session: local session 01a08018-78ff-7e42-a964-f5174f3f763b

@AayushSabharwal AayushSabharwal left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Some more comments inside array_derivative_arguments! would be nice too.

Comment on lines +44 to +47
terms = Set{SymbolicT}()
for expressions in (rhss, extra_expressions), rhs in expressions
Symbolics.get_variables!(terms, rhs; is_atomic = array_derivative_is_atomic)
end

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
terms = Set{SymbolicT}()
for expressions in (rhss, extra_expressions), rhs in expressions
Symbolics.get_variables!(terms, rhs; is_atomic = array_derivative_is_atomic)
end
terms = Set{SymbolicT}()
buffer = SU.IRStructureSearchBuffer(ir, terms)
for expressions in (rhss, extra_expressions), rhs in expressions
Symbolics.get_variables!(buffer, rhs; is_atomic = array_derivative_is_atomic)
end

if length(positions) == length(indices) &&
positions == collect(first(positions):last(positions)) &&
all(isequal(dvs[i], parent[j]) for (i, j) in zip(positions, indices))
arrays[parent] = Symbolics.term(reshape, du[first(positions):last(positions)], size(parent); type = symtype(parent), shape = SU.shape(parent))

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
arrays[parent] = Symbolics.term(reshape, du[first(positions):last(positions)], size(parent); type = symtype(parent), shape = SU.shape(parent))
arrays[parent] = Symbolics.STerm(reshape, Symbolics.SArgsT((du[first(positions):last(positions)], size(parent))); type = symtype(parent), shape = SU.shape(parent))

Less compilation

subs[term] = unwrap(Symbolics.substitute(wrap(only(expanded)), subs))
end
end
subber = ex -> unwrap(Symbolics.substitute(wrap(ex), subs))

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
subber = ex -> unwrap(Symbolics.substitute(wrap(ex), subs))
subber = SU.IRSubstituter{false}(ir, subs)

ChrisRackauckas and others added 2 commits October 7, 2026 09:23
Use IRStructureSearchBuffer for term collection, construct reshape via
STerm/SArgsT, and apply substitutions with IRSubstituter. Document why
contiguous du views are safe, when the scalar fallback runs, and what
the sentinel collision check guards.

Co-Authored-By: Chris Rackauckas <accounts@chrisrackauckas.com>
Co-Authored-By: Cursor Agent <noreply@cursor.com>
Agent-Harness: Cursor Agent CLI 2026.10.01-14929f9
Agent-Model: auto
Agent-Session: local session, transcript at /home/crackauc/sandbox/goals/issue-backlog/orch-ibmtk/jobs/5097-cursor/log.txt on amdci2.julia.csail.mit.edu
Co-Authored-By: Chris Rackauckas <accounts@chrisrackauckas.com>
Co-Authored-By: Cursor Agent <noreply@cursor.com>
Agent-Harness: Cursor Agent CLI 2026.10.01-14929f9
Agent-Model: auto
Agent-Session: local session, transcript at /home/crackauc/sandbox/goals/issue-backlog/orch-ibmtk/jobs/5097-cursor/log.txt on amdci2.julia.csail.mit.edu

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Preserve array derivatives in implicit DAE residual code generation

3 participants