Skip to content

Commit 2c11936

Browse files
Merge pull request #3528 from AayushSabharwal/as/fix-delayscope
fix: fix incorrect namespacing of DelayParentScope variables in `collect_scoped_vars!`
2 parents d5f3dc1 + 5c494c2 commit 2c11936

File tree

5 files changed

+89
-16
lines changed

5 files changed

+89
-16
lines changed

Project.toml

+1-1
Original file line numberDiff line numberDiff line change
@@ -151,7 +151,7 @@ StochasticDelayDiffEq = "1.8.1"
151151
StochasticDiffEq = "6.72.1"
152152
SymbolicIndexingInterface = "0.3.37"
153153
SymbolicUtils = "3.25.1"
154-
Symbolics = "6.36"
154+
Symbolics = "6.37"
155155
URIs = "1"
156156
UnPack = "0.1, 1.0"
157157
Unitful = "1.1"

src/systems/abstractsystem.jl

+76
Original file line numberDiff line numberDiff line change
@@ -1085,6 +1085,7 @@ function getvar(sys::AbstractSystem, name::Symbol; namespace = does_namespacing(
10851085

10861086
if has_eqs(sys)
10871087
for eq in get_eqs(sys)
1088+
eq isa Equation || continue
10881089
if eq.lhs isa AnalysisPoint && nameof(eq.rhs) == name
10891090
return namespace ? renamespace(sys, eq.rhs) : eq.rhs
10901091
end
@@ -1122,9 +1123,25 @@ function _apply_to_variables(f::F, ex) where {F}
11221123
metadata(ex))
11231124
end
11241125

1126+
"""
1127+
Variable metadata key which contains information about scoping/namespacing of the
1128+
variable in a hierarchical system.
1129+
"""
11251130
abstract type SymScope end
11261131

1132+
"""
1133+
$(TYPEDEF)
1134+
1135+
The default scope of a variable. It belongs to the system whose equations it is involved
1136+
in and is namespaced by every level of the hierarchy.
1137+
"""
11271138
struct LocalScope <: SymScope end
1139+
1140+
"""
1141+
$(TYPEDSIGNATURES)
1142+
1143+
Apply `LocalScope` to `sym`.
1144+
"""
11281145
function LocalScope(sym::Union{Num, Symbolic, Symbolics.Arr{Num}})
11291146
apply_to_variables(sym) do sym
11301147
if iscall(sym) && operation(sym) === getindex
@@ -1138,9 +1155,25 @@ function LocalScope(sym::Union{Num, Symbolic, Symbolics.Arr{Num}})
11381155
end
11391156
end
11401157

1158+
"""
1159+
$(TYPEDEF)
1160+
1161+
Denotes that the variable does not belong to the system whose equations it is involved
1162+
in. It is not namespaced by this system. In the immediate parent of this system, the
1163+
scope of this variable is given by `parent`.
1164+
1165+
# Fields
1166+
1167+
$(TYPEDFIELDS)
1168+
"""
11411169
struct ParentScope <: SymScope
11421170
parent::SymScope
11431171
end
1172+
"""
1173+
$(TYPEDSIGNATURES)
1174+
1175+
Apply `ParentScope` to `sym`, with `parent` being `LocalScope`.
1176+
"""
11441177
function ParentScope(sym::Union{Num, Symbolic, Symbolics.Arr{Num}})
11451178
apply_to_variables(sym) do sym
11461179
if iscall(sym) && operation(sym) === getindex
@@ -1156,11 +1189,34 @@ function ParentScope(sym::Union{Num, Symbolic, Symbolics.Arr{Num}})
11561189
end
11571190
end
11581191

1192+
"""
1193+
$(TYPEDEF)
1194+
1195+
Denotes that a variable belongs to a system that is at least `N + 1` levels up in the
1196+
hierarchy from the system whose equations it is involved in. It is namespaced by the
1197+
first `N` parents and not namespaced by the `N+1`th parent in the hierarchy. The scope
1198+
of the variable after this point is given by `parent`.
1199+
1200+
In other words, this scope delays applying `ParentScope` by `N` levels, and applies
1201+
`LocalScope` in the meantime.
1202+
1203+
# Fields
1204+
1205+
$(TYPEDFIELDS)
1206+
"""
11591207
struct DelayParentScope <: SymScope
11601208
parent::SymScope
11611209
N::Int
11621210
end
1211+
1212+
"""
1213+
$(TYPEDSIGNATURES)
1214+
1215+
Apply `DelayParentScope` to `sym`, with a delay of `N` and `parent` being `LocalScope`.
1216+
"""
11631217
function DelayParentScope(sym::Union{Num, Symbolic, Symbolics.Arr{Num}}, N)
1218+
Base.depwarn(
1219+
"`DelayParentScope` is deprecated and will be removed soon", :DelayParentScope)
11641220
apply_to_variables(sym) do sym
11651221
if iscall(sym) && operation(sym) == getindex
11661222
args = arguments(sym)
@@ -1174,9 +1230,29 @@ function DelayParentScope(sym::Union{Num, Symbolic, Symbolics.Arr{Num}}, N)
11741230
end
11751231
end
11761232
end
1233+
1234+
"""
1235+
$(TYPEDSIGNATURES)
1236+
1237+
Apply `DelayParentScope` to `sym`, with a delay of `1` and `parent` being `LocalScope`.
1238+
"""
11771239
DelayParentScope(sym::Union{Num, Symbolic, Symbolics.Arr{Num}}) = DelayParentScope(sym, 1)
11781240

1241+
"""
1242+
$(TYPEDEF)
1243+
1244+
Denotes that a variable belongs to the root system in the hierarchy, regardless of which
1245+
equations of subsystems in the hierarchy it is involved in. Variables with this scope
1246+
are never namespaced and only added to the unknowns/parameters of a system when calling
1247+
`complete` or `structural_simplify`.
1248+
"""
11791249
struct GlobalScope <: SymScope end
1250+
1251+
"""
1252+
$(TYPEDSIGNATURES)
1253+
1254+
Apply `GlobalScope` to `sym`.
1255+
"""
11801256
function GlobalScope(sym::Union{Num, Symbolic, Symbolics.Arr{Num}})
11811257
apply_to_variables(sym) do sym
11821258
if iscall(sym) && operation(sym) == getindex

src/utils.jl

+6-9
Original file line numberDiff line numberDiff line change
@@ -535,7 +535,7 @@ recursively searches through all subsystems of `sys`, increasing the depth if it
535535
"""
536536
function collect_scoped_vars!(unknowns, parameters, sys, iv; depth = 1, op = Differential)
537537
if has_eqs(sys)
538-
for eq in get_eqs(sys)
538+
for eq in equations(sys)
539539
eqtype_supports_collect_vars(eq) || continue
540540
if eq isa Equation
541541
eq.lhs isa Union{Symbolic, Number} || continue
@@ -544,7 +544,7 @@ function collect_scoped_vars!(unknowns, parameters, sys, iv; depth = 1, op = Dif
544544
end
545545
end
546546
if has_parameter_dependencies(sys)
547-
for eq in get_parameter_dependencies(sys)
547+
for eq in parameter_dependencies(sys)
548548
if eq isa Pair
549549
collect_vars!(unknowns, parameters, eq, iv; depth, op)
550550
else
@@ -553,17 +553,13 @@ function collect_scoped_vars!(unknowns, parameters, sys, iv; depth = 1, op = Dif
553553
end
554554
end
555555
if has_constraints(sys)
556-
for eq in get_constraints(sys)
556+
for eq in constraints(sys)
557557
eqtype_supports_collect_vars(eq) || continue
558558
collect_vars!(unknowns, parameters, eq, iv; depth, op)
559559
end
560560
end
561561
if has_op(sys)
562-
collect_vars!(unknowns, parameters, get_op(sys), iv; depth, op)
563-
end
564-
newdepth = depth == -1 ? depth : depth + 1
565-
for ssys in get_systems(sys)
566-
collect_scoped_vars!(unknowns, parameters, ssys, iv; depth = newdepth, op)
562+
collect_vars!(unknowns, parameters, objective(sys), iv; depth, op)
567563
end
568564
end
569565

@@ -608,6 +604,7 @@ end
608604
function collect_var!(unknowns, parameters, var, iv; depth = 0)
609605
isequal(var, iv) && return nothing
610606
check_scope_depth(getmetadata(var, SymScope, LocalScope()), depth) || return nothing
607+
var = setmetadata(var, SymScope, LocalScope())
611608
if iscalledparameter(var)
612609
callable = getcalledparameter(var)
613610
push!(parameters, callable)
@@ -636,7 +633,7 @@ function check_scope_depth(scope, depth)
636633
elseif scope isa ParentScope
637634
return depth > 0 && check_scope_depth(scope.parent, depth - 1)
638635
elseif scope isa DelayParentScope
639-
return depth >= scope.N && check_scope_depth(scope.parent, depth - scope.N)
636+
return depth >= scope.N && check_scope_depth(scope.parent, depth - scope.N - 1)
640637
elseif scope isa GlobalScope
641638
return depth == -1
642639
end

test/jumpsystem.jl

+2-2
Original file line numberDiff line numberDiff line change
@@ -381,12 +381,12 @@ let
381381
@variables x1(t) x2(t) x3(t) x4(t) x5(t)
382382
x2 = ParentScope(x2)
383383
x3 = ParentScope(ParentScope(x3))
384-
x4 = DelayParentScope(x4, 2)
384+
x4 = DelayParentScope(x4)
385385
x5 = GlobalScope(x5)
386386
@parameters p1 p2 p3 p4 p5
387387
p2 = ParentScope(p2)
388388
p3 = ParentScope(ParentScope(p3))
389-
p4 = DelayParentScope(p4, 2)
389+
p4 = DelayParentScope(p4)
390390
p5 = GlobalScope(p5)
391391

392392
j1 = ConstantRateJump(p1, [x1 ~ x1 + 1])

test/variable_scope.jl

+4-4
Original file line numberDiff line numberDiff line change
@@ -106,12 +106,12 @@ defs = ModelingToolkit.defaults(bar)
106106
@variables x1(t) x2(t) x3(t) x4(t) x5(t)
107107
x2 = ParentScope(x2)
108108
x3 = ParentScope(ParentScope(x3))
109-
x4 = DelayParentScope(x4, 2)
109+
x4 = DelayParentScope(x4)
110110
x5 = GlobalScope(x5)
111111
@parameters p1 p2 p3 p4 p5
112112
p2 = ParentScope(p2)
113113
p3 = ParentScope(ParentScope(p3))
114-
p4 = DelayParentScope(p4, 2)
114+
p4 = DelayParentScope(p4)
115115
p5 = GlobalScope(p5)
116116

117117
@named sys1 = ODESystem([D(x1) ~ p1, D(x2) ~ p2, D(x3) ~ p3, D(x4) ~ p4, D(x5) ~ p5], t)
@@ -126,10 +126,10 @@ p5 = GlobalScope(p5)
126126
sys3 = sys3 sys2
127127
@test length(unknowns(sys3)) == 4
128128
@test any(isequal(x3), unknowns(sys3))
129-
@test any(isequal(x4), unknowns(sys3))
129+
@test any(isequal(ModelingToolkit.renamespace(sys1, x4)), unknowns(sys3))
130130
@test length(parameters(sys3)) == 4
131131
@test any(isequal(p3), parameters(sys3))
132-
@test any(isequal(p4), parameters(sys3))
132+
@test any(isequal(ModelingToolkit.renamespace(sys1, p4)), parameters(sys3))
133133
sys4 = complete(sys3)
134134
@test length(unknowns(sys3)) == 4
135135
@test length(parameters(sys4)) == 5

0 commit comments

Comments
 (0)