Skip to content

shift_discrete_system: substitution rebuilds a + with mismatched shapes (Unknown(2) vs scalar) on MTK 11.37 #146

Description

@baggepinnen

Goal

Upgrade the MTK stack under a purely-discrete (clocked) model that we compile with SynchToolkit's stkcompile. We currently pin ModelingToolkit = "= 11.34.0" / ModelingToolkitBase = "= 1.54.1" / ModelingToolkitTearing = "= 1.19.3", and we'd like to move to current versions.

What fails

On the newer stack, split_system throws while shifting the discrete system:

ArgumentError: Cannot add arguments of different sizes -
  encountered shapes UnitRange{Int64}[] and SymbolicUtils.Unknown(2).

 [1] throw_unequal_shape_error(x::SymbolicUtils.Unknown, y::SymbolicUtils.SmallVec{UnitRange{Int64}, …})
     @ SymbolicUtils src/symbolic_ops/addsub.jl:2
 [2] promote_shape(::typeof(+), …)
     @ SymbolicUtils src/symbolic_ops/addsub.jl:10
 [3] maketerm(::Type{…BasicSymbolicImpl{SymReal}}, f, args, metadata; type)
     @ SymbolicUtils src/terminterface.jl:354
 [5] DefaultSubstituter{…}(ex)
     @ SymbolicUtils src/substitute.jl:198
 [9] shift_discrete_system(ts::TearingState)
     @ ModelingToolkitTearing src/tearingstate.jl:1092
[10] mark_discrete
     @ ModelingToolkitTearing src/clock_inference/interface.jl:127

Line 1092 is the equation-substitution loop in shift_discrete_system:

for k in discvars
    k in fullvars_set || continue
    MTKBase.isoperator(k, Union{Sample, Hold, Pre}) && continue
    discmap[k] = MTKBase.simplify_shifts(Shift(iv, 1)(k))
end
...
for i in eachindex(eqs)
    eqs[i] = MTKBase.simplify_shifts(substitute(          # <-- throws here
        eqs[i], discmap; filterer = Symbolics.FPSubFilterer{Union{Sample, Hold, Pre}}()))
end

So substituting k → Shift(iv, 1)(k) into an equation rebuilds a + whose operands no longer agree on shape: one side is scalar (UnitRange{Int64}[]), the other reports SymbolicUtils.Unknown(2). Every variable involved is scalar in our models — we have no array variables in these components.

infer_clocks! succeeds; it is split_system that throws:

state = MTKTearing.TearingState(expand_connections(sys))
ci    = MTKTearing.infer_clocks!(MTKTearing.ClockInference(state))
MTKTearing.split_system(ci)     # ArgumentError here

Versions

Fails with ModelingToolkit 11.37.1 (master), ModelingToolkitBase 1.57.1 (master), ModelingToolkitTearing 1.20.0 (main), SymbolicUtils 4.42.0, Symbolics 7.34.0, SciMLBase 3.39.1, on Julia 1.12.6.

Works with ModelingToolkit 11.34.0 / ModelingToolkitBase 1.54.1 / ModelingToolkitTearing 1.19.3 — our full test suite (5453 tests) passes there and exercises exactly this path.

Reproduction

Any of several clocked components in QuanserComponents reproduce it through the three lines above — GoHome, SwingupCatch, RuntimeController, SwingupWithHoming. They are ordinary controller blocks assembled from DiscreteComponents delay and filter primitives (UnitDelay, DiscreteDerivative, ExponentialFilter), i.e. the equations contain u@(clk-1)-style shifts, all on scalar signals. The simplest failing one is GoHome.

I have not reduced this to a dependency-free example, and I have not yet checked whether released 11.37.0 is also affected — in that configuration an unrelated SampleTime error (SynchToolkit#172, fixed on MTK master) aborted compilation before split_system was reached, so the two were masking each other. Happy to bisect further or produce a minimal case if that would help.

Question

Is there something our models should be doing differently for the shift pass on the newer stack — some shape or metadata expectation on scalar discrete variables that changed — or is shift_discrete_system reconstructing terms in a way that no longer satisfies the newer promote_shape? Also happy to move this issue if it belongs in ModelingToolkitBase or SymbolicUtils rather than here; I filed it where the throwing frame lives.

🤖 Generated with Claude Code

https://claude.ai/code/session_01GyRqJUikfxyEUKMcQaxBF9

Activity

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

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions