Skip to content

Commit 2db7f5f

Browse files
Merge pull request #3386 from vyudu/implicit_discrete_system
feat: `ImplicitDiscreteSystem`
2 parents 157966e + 1e5fce8 commit 2db7f5f

File tree

18 files changed

+666
-17
lines changed

18 files changed

+666
-17
lines changed

docs/pages.jl

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,8 @@ pages = [
4343
"systems/NonlinearSystem.md",
4444
"systems/OptimizationSystem.md",
4545
"systems/PDESystem.md",
46-
"systems/DiscreteSystem.md"],
46+
"systems/DiscreteSystem.md",
47+
"systems/ImplicitDiscreteSystem.md"],
4748
"comparison.md",
4849
"internals.md"
4950
]

docs/src/systems/DiscreteSystem.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,3 +26,9 @@ structural_simplify
2626
DiscreteProblem(sys::DiscreteSystem, u0map, tspan)
2727
DiscreteFunction(sys::DiscreteSystem, args...)
2828
```
29+
30+
## Discrete Domain
31+
32+
```@docs; canonical=false
33+
Shift
34+
```
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
# ImplicitDiscreteSystem
2+
3+
## System Constructors
4+
5+
```@docs
6+
ImplicitDiscreteSystem
7+
```
8+
9+
## Composition and Accessor Functions
10+
11+
- `get_eqs(sys)` or `equations(sys)`: The equations that define the implicit discrete system.
12+
- `get_unknowns(sys)` or `unknowns(sys)`: The set of unknowns in the implicit discrete system.
13+
- `get_ps(sys)` or `parameters(sys)`: The parameters of the implicit discrete system.
14+
- `get_iv(sys)`: The independent variable of the implicit discrete system
15+
- `discrete_events(sys)`: The set of discrete events in the implicit discrete system.
16+
17+
## Transformations
18+
19+
```@docs; canonical=false
20+
structural_simplify
21+
```
22+
23+
## Problem Constructors
24+
25+
```@docs; canonical=false
26+
ImplicitDiscreteProblem(sys::ImplicitDiscreteSystem, u0map, tspan)
27+
ImplicitDiscreteFunction(sys::ImplicitDiscreteSystem, args...)
28+
```
29+
30+
## Discrete Domain
31+
32+
```@docs; canonical=false
33+
Shift
34+
```

docs/src/tutorials/SampledData.md

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,8 +25,10 @@ The operators [`Sample`](@ref) and [`Hold`](@ref) are thus providing the interfa
2525
The [`ShiftIndex`](@ref) operator is used to refer to past and future values of discrete-time variables. The example below illustrates its use, implementing the discrete-time system
2626

2727
```math
28-
x(k+1) = 0.5x(k) + u(k)
29-
y(k) = x(k)
28+
\begin{align}
29+
x(k+1) &= 0.5x(k) + u(k) \\
30+
y(k) &= x(k)
31+
\end{align}
3032
```
3133

3234
```@example clocks
@@ -187,3 +189,10 @@ connections = [r ~ sin(t) # reference signal
187189
188190
@named cl = ODESystem(connections, t, systems = [f, c, p])
189191
```
192+
193+
```@docs
194+
Sample
195+
Hold
196+
ShiftIndex
197+
Clock
198+
```

src/ModelingToolkit.jl

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -127,6 +127,7 @@ abstract type AbstractTimeIndependentSystem <: AbstractSystem end
127127
abstract type AbstractODESystem <: AbstractTimeDependentSystem end
128128
abstract type AbstractMultivariateSystem <: AbstractSystem end
129129
abstract type AbstractOptimizationSystem <: AbstractTimeIndependentSystem end
130+
abstract type AbstractDiscreteSystem <: AbstractTimeDependentSystem end
130131

131132
function independent_variable end
132133

@@ -174,6 +175,7 @@ include("systems/diffeqs/modelingtoolkitize.jl")
174175
include("systems/diffeqs/basic_transformations.jl")
175176

176177
include("systems/discrete_system/discrete_system.jl")
178+
include("systems/discrete_system/implicit_discrete_system.jl")
177179

178180
include("systems/jumps/jumpsystem.jl")
179181

@@ -235,6 +237,8 @@ export DAEFunctionExpr, DAEProblemExpr
235237
export SDESystem, SDEFunction, SDEFunctionExpr, SDEProblemExpr
236238
export SystemStructure
237239
export DiscreteSystem, DiscreteProblem, DiscreteFunction, DiscreteFunctionExpr
240+
export ImplicitDiscreteSystem, ImplicitDiscreteProblem, ImplicitDiscreteFunction,
241+
ImplicitDiscreteFunctionExpr
238242
export JumpSystem
239243
export ODEProblem, SDEProblem
240244
export NonlinearFunction, NonlinearFunctionExpr
@@ -298,7 +302,7 @@ export debug_system
298302
#export ContinuousClock, Discrete, sampletime, input_timedomain, output_timedomain
299303
#export has_discrete_domain, has_continuous_domain
300304
#export is_discrete_domain, is_continuous_domain, is_hybrid_domain
301-
export Sample, Hold, Shift, ShiftIndex, sampletime, SampleTime
305+
export Sample, Hold, Shift, ShiftIndex, sampletime, SampleTime, Next, Prev
302306
export Clock, SolverStepClock, TimeDomain
303307

304308
export MTKParameters, reorder_dimension_by_tunables!, reorder_dimension_by_tunables

src/doc

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+

src/structural_transformation/StructuralTransformations.jl

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,7 @@ export torn_system_jacobian_sparsity
6565
export full_equations
6666
export but_ordered_incidence, lowest_order_variable_mask, highest_order_variable_mask
6767
export computed_highest_diff_variables
68-
export shift2term, lower_shift_varname
68+
export shift2term, lower_shift_varname, simplify_shifts, distribute_shift
6969

7070
include("utils.jl")
7171
include("pantelides.jl")

src/structural_transformation/symbolics_tearing.jl

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -558,6 +558,10 @@ function generate_system_equations!(state::TearingState, neweqs, var_eq_matching
558558
end
559559

560560
total_sub[simplify_shifts(neweq.lhs)] = neweq.rhs
561+
# Substitute unshifted variables x(k), y(k) on RHS of implicit equations
562+
if is_only_discrete(structure)
563+
var_to_diff[iv] === nothing && (total_sub[var] = neweq.rhs)
564+
end
561565
push!(diff_eqs, neweq)
562566
push!(diffeq_idxs, ieq)
563567
push!(diff_vars, diff_to_var[iv])

src/structural_transformation/utils.jl

Lines changed: 54 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -457,11 +457,10 @@ Handle renaming variable names for discrete structural simplification. Three cas
457457
"""
458458
function lower_shift_varname(var, iv)
459459
op = operation(var)
460-
op isa Shift || return Shift(iv, 0)(var, true) # hack to prevent simplification of x(t) - x(t)
461-
if op.steps < 0
460+
if op isa Shift
462461
return shift2term(var)
463462
else
464-
return var
463+
return Shift(iv, 0)(var, true)
465464
end
466465
end
467466

@@ -476,10 +475,14 @@ function shift2term(var)
476475

477476
backshift = is_lowered ? op.steps + ModelingToolkit.getshift(arg) : op.steps
478477

479-
num = join(Char(0x2080 + d) for d in reverse!(digits(-backshift))) # subscripted number, e.g. ₁
480-
ds = join([Char(0x209c), Char(0x208b), num])
481-
# Char(0x209c) = ₜ
482478
# Char(0x208b) = ₋ (subscripted minus)
479+
# Char(0x208a) = ₊ (subscripted plus)
480+
pm = backshift > 0 ? Char(0x208a) : Char(0x208b)
481+
# subscripted number, e.g. ₁
482+
num = join(Char(0x2080 + d) for d in reverse!(digits(abs(backshift))))
483+
# Char(0x209c) = ₜ
484+
# ds = ₜ₋₁
485+
ds = join([Char(0x209c), pm, num])
483486

484487
O = is_lowered ? ModelingToolkit.getunshifted(arg) : arg
485488
oldop = operation(O)
@@ -499,6 +502,9 @@ function isdoubleshift(var)
499502
ModelingToolkit.isoperator(arguments(var)[1], ModelingToolkit.Shift)
500503
end
501504

505+
"""
506+
Simplify multiple shifts: Shift(t, k1)(Shift(t, k2)(x)) becomes Shift(t, k1+k2)(x).
507+
"""
502508
function simplify_shifts(var)
503509
ModelingToolkit.hasshift(var) || return var
504510
var isa Equation && return simplify_shifts(var.lhs) ~ simplify_shifts(var.rhs)
@@ -518,3 +524,45 @@ function simplify_shifts(var)
518524
unwrap(var).metadata)
519525
end
520526
end
527+
528+
"""
529+
Distribute a shift applied to a whole expression or equation.
530+
Shift(t, 1)(x + y) will become Shift(t, 1)(x) + Shift(t, 1)(y).
531+
Only shifts variables whose independent variable is the same t that appears in the Shift (i.e. constants, time-independent parameters, etc. do not get shifted).
532+
"""
533+
function distribute_shift(var)
534+
var = unwrap(var)
535+
var isa Equation && return distribute_shift(var.lhs) ~ distribute_shift(var.rhs)
536+
537+
ModelingToolkit.hasshift(var) || return var
538+
shift = operation(var)
539+
shift isa Shift || return var
540+
541+
shift = operation(var)
542+
expr = only(arguments(var))
543+
if expr isa Equation
544+
return distribute_shift(shift(expr.lhs)) ~ distribute_shift(shift(expr.rhs))
545+
end
546+
shiftexpr = _distribute_shift(expr, shift)
547+
return simplify_shifts(shiftexpr)
548+
end
549+
550+
function _distribute_shift(expr, shift)
551+
if iscall(expr)
552+
op = operation(expr)
553+
args = arguments(expr)
554+
555+
if ModelingToolkit.isvariable(expr)
556+
(length(args) == 1 && isequal(shift.t, only(args))) ? (return shift(expr)) :
557+
(return expr)
558+
elseif op isa Shift
559+
return shift(expr)
560+
else
561+
return maketerm(
562+
typeof(expr), operation(expr), Base.Fix2(_distribute_shift, shift).(args),
563+
unwrap(expr).metadata)
564+
end
565+
else
566+
return expr
567+
end
568+
end

src/systems/discrete_system/discrete_system.jl

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ eqs = [x(k+1) ~ σ*(y-x),
1717
@named de = DiscreteSystem(eqs)
1818
```
1919
"""
20-
struct DiscreteSystem <: AbstractTimeDependentSystem
20+
struct DiscreteSystem <: AbstractDiscreteSystem
2121
"""
2222
A tag for the system. If two systems have the same tag, then they are
2323
structurally identical.
@@ -237,6 +237,8 @@ function DiscreteSystem(eqs, iv; kwargs...)
237237
collect(allunknowns), collect(new_ps); kwargs...)
238238
end
239239

240+
DiscreteSystem(eq::Equation, args...; kwargs...) = DiscreteSystem([eq], args...; kwargs...)
241+
240242
function flatten(sys::DiscreteSystem, noeqs = false)
241243
systems = get_systems(sys)
242244
if isempty(systems)

0 commit comments

Comments
 (0)