Skip to content

Build shifted variables in var_derivative! for discrete systems - #163

Merged
baggepinnen merged 1 commit into
mainfrom
discrete-var-derivative-shift
Sep 21, 2026
Merged

baggepinnen merged 1 commit into
mainfrom
discrete-var-derivative-shift

Conversation

@baggepinnen

Copy link
Copy Markdown
Contributor

Summary

StateSelection.var_derivative!(::TearingState, ::Int) always builds the new variable as Differential(iv)(v). In a discrete system the variables are related by shifts, so the differential form is not recognized by the discrete reassembly. The method now builds simplify_shifts(Shift(iv, 1)(v)) when the structure is discrete.

Reproduction

eliminate_perfect_aliases! calls var_derivative! when a variable that is shifted in the system is eliminated in favour of an alias target that has no shifted form yet:

using ModelingToolkit
using ModelingToolkit: t_nounits as t
k = ShiftIndex(t)
@parameters p
@variables u(t) y(t) z(t)
@named sys = System([u ~ p, y ~ u(k - 1), z ~ y], t)
ss = mtkcompile(sys)
unknowns(ss)   # [y(t), yₜ₊₁(t)]
equations(ss)  # [Shift(t, 1)(y(t)) ~ yₜ₊₁(t)]
observed(ss)   # [Differential(t, 1)(y(t)) ~ p, z(t) ~ y(t), uₜ₋₁(t) ~ y(t)]

The alias z ~ y puts u (whose shifted form u(k + 1) is a variable of the system after shift_discrete_system) in the same alias group as y(k + 1) and z(k + 1). With y(k + 1) as the target, its shifted form is requested from var_derivative!, which returns Differential(t)(y). The compiled system then has two unknowns and one equation, and DiscreteProblem fails with "Equations (1), unknowns (2), and initial conditions (2) are of different lengths". With z ~ 2y in place of the alias the compile is correct, since no alias elimination takes place.

With this change the system compiles to the single unknown y with Shift(t, 1)(y) ~ yₜ₊₁, yₜ₊₁ ~ p, z ~ y and uₜ₋₁ ~ y.

This is the structure of every model that uses a one-sample delay whose output is connected to another block, such as DiscreteComponents.UnitDelay; it was found while running linearize_hybrid of SciML/ModelingToolkit.jl#5173 over the DiscreteComponents examples (finding 3 of this comment).

Tests

A regression test in lib/ModelingToolkitTearing/test/runtests.jl compiles the model above, checks that a single unknown remains and that no Differential appears in the equations or observed equations, and checks the values of y and z after the first tick of a DiscreteProblem. The discrete_system, implicit_discrete_system and clock tests of ModelingToolkit pass with this change.

🤖 Generated with Claude Code

https://claude.ai/code/session_01QBgBrXDidWG1G2KF64gAte

`StateSelection.var_derivative!(::TearingState, ::Int)` built the new variable as
`Differential(iv)(v)` for every system. The variables of a discrete system are related by
shifts, and the differential form is not recognized by the discrete reassembly: the alias
elimination of `[u ~ p, y ~ u(k - 1), z ~ y]` requests the shifted form of the alias target
`y(k + 1)` and left `Differential(t)(y)` in the system, so that `mtkcompile` returned two
unknowns and one equation. The method now builds `simplify_shifts(Shift(iv, 1)(v))` when the
structure is discrete.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QBgBrXDidWG1G2KF64gAte
@baggepinnen
baggepinnen merged commit 285667f into main Sep 21, 2026
9 checks passed
@baggepinnen
baggepinnen deleted the discrete-var-derivative-shift branch September 21, 2026 23:50
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.

2 participants