Skip to content

Commit 9709e9e

Browse files
Merge pull request #85 from JuliaDiffEq/hg/refactor/variable
Refactor :DependentVariable into :Unknown
2 parents 69390a9 + 1cd6630 commit 9709e9e

13 files changed

+93
-148
lines changed

README.md

Lines changed: 13 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -21,15 +21,15 @@ to manipulate.
2121
### Example: ODE
2222

2323
Let's build an ODE. First we define some variables. In a differential equation
24-
system, we need to differentiate between our dependent variables, independent
24+
system, we need to differentiate between our unknown (dependent) variables, independent
2525
variables, and parameters. Therefore we label them as follows:
2626

2727
```julia
2828
using ModelingToolkit
2929

3030
# Define some variables
3131
@IVar t
32-
@DVar x(t) y(t) z(t)
32+
@Unknown x(t) y(t) z(t)
3333
@Deriv D'~t
3434
@Param σ ρ β
3535
```
@@ -47,7 +47,7 @@ Each operation builds an `Operation` type, and thus `eqs` is an array of
4747
analyzed by other programs. We can turn this into a `DiffEqSystem` via:
4848

4949
```julia
50-
de = DiffEqSystem(eqs,[t],[x,y,z],Variable[],[σ,ρ,β])
50+
de = DiffEqSystem(eqs,[t],[x,y,z],[σ,ρ,β])
5151
de = DiffEqSystem(eqs)
5252
```
5353

@@ -87,12 +87,10 @@ f = ODEFunction(de)
8787

8888
We can also build nonlinear systems. Let's say we wanted to solve for the steady
8989
state of the previous ODE. This is the nonlinear system defined by where the
90-
derivatives are zero. We could use dependent variables for our nonlinear system
91-
(for direct compatibility with the above ODE code), or we can use non-tagged
92-
variables. Here we will show the latter. We write:
90+
derivatives are zero. We use unknown variables for our nonlinear system.
9391

9492
```julia
95-
@Var x y z
93+
@Unknown x y z
9694
@Param σ ρ β
9795

9896
# Define a nonlinear system
@@ -191,7 +189,7 @@ structure is as follows:
191189
the system of equations.
192190
- Name to subtype mappings: these describe how variable `subtype`s are mapped
193191
to the contexts of the system. For example, for a differential equation,
194-
the dependent variable corresponds to given subtypes and then the `eqs` can
192+
the unknown variable corresponds to given subtypes and then the `eqs` can
195193
be analyzed knowing what the state variables are.
196194
- Variable names which do not fall into one of the system's core subtypes are
197195
treated as intermediates which can be used for holding subcalculations and
@@ -262,7 +260,7 @@ syntactic sugar in some form. For example, the variable construction:
262260

263261
```julia
264262
@IVar t
265-
@DVar x(t) y(t) z(t)
263+
@Unknown x(t) y(t) z(t)
266264
@Deriv D'~t
267265
@Param σ ρ β
268266
```
@@ -271,9 +269,9 @@ is syntactic sugar for:
271269

272270
```julia
273271
t = IndependentVariable(:t)
274-
x = DependentVariable(:x,dependents = [t])
275-
y = DependentVariable(:y,dependents = [t])
276-
z = DependentVariable(:z,dependents = [t])
272+
x = Unknown(:x, dependents = [t])
273+
y = Unknown(:y, dependents = [t])
274+
z = Unknown(:z, dependents = [t])
277275
D = Differential(t) # Default of first derivative, Derivative(t,1)
278276
σ = Parameter()
279277
ρ = Parameter()
@@ -285,10 +283,10 @@ D = Differential(t) # Default of first derivative, Derivative(t,1)
285283
The system building functions can handle intermediate calculations. For example,
286284

287285
```julia
288-
@Var a x y z
286+
@Unknown x y z
289287
@Param σ ρ β
290-
eqs = [a ~ y-x,
291-
0 ~ σ*a,
288+
a = y - x
289+
eqs = [0 ~ σ*a,
292290
0 ~ x*-z)-y,
293291
0 ~ x*y - β*z]
294292
ns = NonlinearSystem(eqs,[x,y,z],[σ,ρ,β])

src/equations.jl

Lines changed: 25 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -12,40 +12,36 @@ Base.:~(lhs::Expression, rhs::Number ) = Equation(lhs, rhs)
1212
Base.:~(lhs::Number , rhs::Expression) = Equation(lhs, rhs)
1313

1414

15-
function extract_elements(eqs, targetmap, default = nothing)
16-
elems = Dict{Symbol, Vector{Variable}}()
17-
names = Dict{Symbol, Set{Symbol}}()
18-
if default == nothing
19-
targets = unique(collect(values(targetmap)))
20-
else
21-
targets = [unique(collect(values(targetmap))), default]
22-
end
23-
for target in targets
24-
elems[target] = Vector{Variable}()
25-
names[target] = Set{Symbol}()
26-
end
27-
for eq in eqs
28-
extract_elements!(eq, elems, names, targetmap, default)
15+
_is_derivative(x::Variable) = x.diff !== nothing
16+
_is_dependent(x::Variable) = x.subtype === :Unknown && !isempty(x.dependents)
17+
_subtype(subtype::Symbol) = x -> x.subtype === subtype
18+
19+
function extract_elements(eqs, predicates)
20+
result = [Variable[] for p predicates]
21+
vars = foldl(vars!, eqs; init=Set{Variable}())
22+
23+
for var vars
24+
for (i, p) enumerate(predicates)
25+
p(var) && (push!(result[i], var); break)
26+
end
2927
end
30-
Tuple(elems[target] for target in targets)
28+
29+
return result
3130
end
32-
# Walk the tree recursively and push variables into the right set
33-
function extract_elements!(op, elems, names, targetmap, default)
31+
32+
function vars!(vars, op)
3433
args = isa(op, Equation) ? Expression[op.lhs, op.rhs] : op.args
3534

36-
for arg in args
37-
if arg isa Operation
38-
extract_elements!(arg, elems, names, targetmap, default)
39-
elseif arg isa Variable
40-
if default == nothing
41-
target = haskey(targetmap, arg.subtype) ? targetmap[arg.subtype] : continue
42-
else
43-
target = haskey(targetmap, arg.subtype) ? targetmap[arg.subtype] : default
44-
end
45-
if !in(arg.name, names[target])
46-
push!(names[target], arg.name)
47-
push!(elems[target], arg)
35+
for arg args
36+
if isa(arg, Operation)
37+
vars!(vars, arg)
38+
elseif isa(arg, Variable)
39+
push!(vars, arg)
40+
for dep arg.dependents
41+
push!(vars, dep)
4842
end
4943
end
5044
end
45+
46+
return vars
5147
end

src/systems/diffeqs/diffeqsystem.jl

Lines changed: 18 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -2,51 +2,35 @@ mutable struct DiffEqSystem <: AbstractSystem
22
eqs::Vector{Equation}
33
ivs::Vector{Variable}
44
dvs::Vector{Variable}
5-
vs::Vector{Variable}
65
ps::Vector{Variable}
7-
iv_name::Symbol
8-
dv_name::Symbol
9-
p_name::Symbol
106
jac::Matrix{Expression}
117
end
128

13-
function DiffEqSystem(eqs, ivs, dvs, vs, ps)
14-
iv_name = ivs[1].subtype
15-
dv_name = dvs[1].subtype
16-
p_name = isempty(ps) ? :Parameter : ps[1].subtype
17-
DiffEqSystem(eqs, ivs, dvs, vs, ps, iv_name, dv_name, p_name, Matrix{Expression}(undef,0,0))
18-
end
9+
DiffEqSystem(eqs, ivs, dvs, ps) = DiffEqSystem(eqs, ivs, dvs, ps, Matrix{Expression}(undef,0,0))
1910

20-
function DiffEqSystem(eqs; iv_name = :IndependentVariable,
21-
dv_name = :DependentVariable,
22-
v_name = :Variable,
23-
p_name = :Parameter)
24-
targetmap = Dict(iv_name => iv_name, dv_name => dv_name, v_name => v_name,
25-
p_name => p_name)
26-
ivs, dvs, vs, ps = extract_elements(eqs, targetmap)
27-
DiffEqSystem(eqs, ivs, dvs, vs, ps, iv_name, dv_name, p_name, Matrix{Expression}(0,0))
11+
function DiffEqSystem(eqs)
12+
predicates = [_is_derivative, _subtype(:IndependentVariable), _is_dependent, _subtype(:Parameter)]
13+
_, ivs, dvs, ps = extract_elements(eqs, predicates)
14+
DiffEqSystem(eqs, ivs, dvs, ps, Matrix{Expression}(undef,0,0))
2815
end
2916

30-
function DiffEqSystem(eqs, ivs;
31-
dv_name = :DependentVariable,
32-
v_name = :Variable,
33-
p_name = :Parameter)
34-
targetmap = Dict(dv_name => dv_name, v_name => v_name, p_name => p_name)
35-
dvs, vs, ps = extract_elements(eqs, targetmap)
36-
DiffEqSystem(eqs, ivs, dvs, vs, ps, ivs[1].subtype, dv_name, p_name, Matrix{Expression}(undef,0,0))
17+
function DiffEqSystem(eqs, ivs)
18+
predicates = [_is_derivative, _is_dependent, _subtype(:Parameter)]
19+
_, dvs, ps = extract_elements(eqs, predicates)
20+
DiffEqSystem(eqs, ivs, dvs, ps, Matrix{Expression}(undef,0,0))
3721
end
3822

3923
function generate_ode_function(sys::DiffEqSystem;version = ArrayFunction)
40-
var_exprs = [:($(sys.dvs[i].name) = u[$i]) for i in 1:length(sys.dvs)]
41-
param_exprs = [:($(sys.ps[i].name) = p[$i]) for i in 1:length(sys.ps)]
24+
var_exprs = [:($(sys.dvs[i].name) = u[$i]) for i in eachindex(sys.dvs)]
25+
param_exprs = [:($(sys.ps[i].name) = p[$i]) for i in eachindex(sys.ps)]
4226
sys_exprs = build_equals_expr.(sys.eqs)
4327
if version == ArrayFunction
44-
dvar_exprs = [:(du[$i] = $(Symbol("$(sys.dvs[i].name)_$(sys.ivs[1].name)"))) for i in 1:length(sys.dvs)]
28+
dvar_exprs = [:(du[$i] = $(Symbol("$(sys.dvs[i].name)_$(sys.ivs[1].name)"))) for i in eachindex(sys.dvs)]
4529
exprs = vcat(var_exprs,param_exprs,sys_exprs,dvar_exprs)
4630
block = expr_arr_to_block(exprs)
4731
:((du,u,p,t)->$(toexpr(block)))
4832
elseif version == SArrayFunction
49-
dvar_exprs = [:($(Symbol("$(sys.dvs[i].name)_$(sys.ivs[1].name)"))) for i in 1:length(sys.dvs)]
33+
dvar_exprs = [:($(Symbol("$(sys.dvs[i].name)_$(sys.ivs[1].name)"))) for i in eachindex(sys.dvs)]
5034
svector_expr = quote
5135
E = eltype(tuple($(dvar_exprs...)))
5236
T = StaticArrays.similar_type(typeof(u), E)
@@ -84,8 +68,8 @@ function calculate_jacobian(sys::DiffEqSystem, simplify=true)
8468
end
8569

8670
function generate_ode_jacobian(sys::DiffEqSystem, simplify=true)
87-
var_exprs = [:($(sys.dvs[i].name) = u[$i]) for i in 1:length(sys.dvs)]
88-
param_exprs = [:($(sys.ps[i].name) = p[$i]) for i in 1:length(sys.ps)]
71+
var_exprs = [:($(sys.dvs[i].name) = u[$i]) for i in eachindex(sys.dvs)]
72+
param_exprs = [:($(sys.ps[i].name) = p[$i]) for i in eachindex(sys.ps)]
8973
diff_exprs = filter(!isintermediate, sys.eqs)
9074
jac = calculate_jacobian(sys, simplify)
9175
sys.jac = jac
@@ -96,12 +80,12 @@ function generate_ode_jacobian(sys::DiffEqSystem, simplify=true)
9680
end
9781

9882
function generate_ode_iW(sys::DiffEqSystem, simplify=true)
99-
var_exprs = [:($(sys.dvs[i].name) = u[$i]) for i in 1:length(sys.dvs)]
100-
param_exprs = [:($(sys.ps[i].name) = p[$i]) for i in 1:length(sys.ps)]
83+
var_exprs = [:($(sys.dvs[i].name) = u[$i]) for i in eachindex(sys.dvs)]
84+
param_exprs = [:($(sys.ps[i].name) = p[$i]) for i in eachindex(sys.ps)]
10185
diff_exprs = filter(!isintermediate, sys.eqs)
10286
jac = sys.jac
10387

104-
gam = Variable(:gam)
88+
gam = Unknown(:gam)
10589

10690
W = LinearAlgebra.I - gam*jac
10791
W = SMatrix{size(W,1),size(W,2)}(W)

src/systems/nonlinear/nonlinear_system.jl

Lines changed: 3 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -2,25 +2,11 @@ struct NonlinearSystem <: AbstractSystem
22
eqs::Vector{Equation}
33
vs::Vector{Variable}
44
ps::Vector{Variable}
5-
v_name::Vector{Symbol}
6-
p_name::Symbol
75
end
86

9-
function NonlinearSystem(eqs, vs, ps;
10-
v_name = :Variable,
11-
dv_name = :DependentVariable,
12-
p_name = :Parameter)
13-
NonlinearSystem(eqs, vs, ps, [v_name,dv_name], p_name)
14-
end
15-
16-
function NonlinearSystem(eqs;
17-
v_name = :Variable,
18-
dv_name = :DependentVariable,
19-
p_name = :Parameter)
20-
# Allow the use of :DependentVariable to make it seamless with DE use
21-
targetmap = Dict(v_name => v_name, dv_name => v_name, p_name => p_name)
22-
vs, ps = extract_elements(eqs, targetmap)
23-
NonlinearSystem(eqs, vs, ps, [v_name,dv_name], p_name)
7+
function NonlinearSystem(eqs)
8+
vs, ps = extract_elements(eqs, [_subtype(:Unknown), _subtype(:Parameter)])
9+
NonlinearSystem(eqs, vs, ps)
2410
end
2511

2612
function generate_nlsys_function(sys::NonlinearSystem)

src/variables.jl

Lines changed: 6 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -5,20 +5,15 @@ mutable struct Variable <: Expression
55
dependents::Vector{Variable}
66
end
77

8-
Variable(name; subtype::Symbol=:Variable, dependents::Vector{Variable} = Variable[]) =
8+
Variable(name; subtype::Symbol, dependents::Vector{Variable} = Variable[]) =
99
Variable(name, subtype, nothing, dependents)
10-
Variable(name, args...; kwargs...) = Variable(name, args...; subtype=:Variable, kwargs...)
1110

1211
Parameter(name,args...;kwargs...) = Variable(name,args...;subtype=:Parameter,kwargs...)
1312
IndependentVariable(name,args...;kwargs...) = Variable(name,args...;subtype=:IndependentVariable,kwargs...)
13+
Unknown(name,args...;kwargs...) = Variable(name,args...;subtype=:Unknown,kwargs...)
1414

15-
function DependentVariable(name,args...;dependents = [],kwargs...)
16-
@assert !isempty(dependents)
17-
Variable(name,args...;subtype=:DependentVariable,dependents=dependents,kwargs...)
18-
end
19-
20-
export Variable,Parameter,Constant,DependentVariable,IndependentVariable,
21-
@Var, @Param, @Const, @DVar, @IVar
15+
export Variable,Parameter,Constant,Unknown,IndependentVariable,
16+
@Param, @Const, @Unknown, @IVar
2217

2318

2419
Base.copy(x::Variable) = Variable(x.name, x.subtype, x.diff, x.dependents)
@@ -72,7 +67,7 @@ function _parse_vars(macroname, fun, x)
7267
@assert iscall || issym "@$macroname expects a tuple of expressions!\nE.g. `@$macroname x y z`"
7368

7469
if iscall
75-
dependents = :([$(_var.args[2:end]...)])
70+
dependents = :(Variable[$(_var.args[2:end]...)])
7671
lhs = _var.args[1]
7772
else
7873
dependents = Variable[]
@@ -88,9 +83,7 @@ function _parse_vars(macroname, fun, x)
8883
return ex
8984
end
9085

91-
for funs in ((:DVar, :DependentVariable), (:IVar, :IndependentVariable),
92-
(:Var, :Variable),
93-
(:Param, :Parameter))
86+
for funs in [(:Unknown, :Unknown), (:IVar, :IndependentVariable), (:Param, :Parameter)]
9487
@eval begin
9588
macro ($(funs[1]))(x...)
9689
esc(_parse_vars(String($funs[1]), $funs[2], x))

test/ambiguity.jl

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ using ModelingToolkit
22
using Test
33

44
@IVar t
5-
@DVar x(t) y(t) z(t)
5+
@Unknown x(t) y(t) z(t)
66

77
struct __MyType__ end
88
Base.:~(::__MyType__,::Number) = 2

test/basic_variables_and_operations.jl

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,16 +2,15 @@ using ModelingToolkit
22
using Test
33

44
@IVar t
5-
@DVar x(t) y(t) z(t)
6-
@test_throws AssertionError @DVar w
5+
@Unknown x(t) y(t) z(t)
76

87
@Deriv D'~t # Default of first derivative, Derivative(t,1)
98
@Param σ ρ β
109
@Const c=0
1110

1211
# Default values
1312
p = Parameter(:p)
14-
u = DependentVariable(:u, dependents = [t])
13+
u = Unknown(:u, dependents = [t])
1514

1615
σ*(y-x)
1716
D(x)

test/components.jl

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ function generate_lorenz_eqs(t,x,y,z,σ,ρ,β)
1717
D(z) ~ x*y - β*z]
1818
end
1919
function Lorenz(t)
20-
@DVar x(t) y(t) z(t)
20+
@Unknown x(t) y(t) z(t)
2121
@Param σ ρ β
2222
Lorenz(x, y, z, σ, ρ, β, generate_lorenz_eqs(t, x, y, z, σ, ρ, β))
2323
end

test/derivatives.jl

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ using Test
33

44
# Derivatives
55
@IVar t
6-
@Var x(t) y(t) z(t)
6+
@Unknown x(t) y(t) z(t)
77
@Param σ ρ β
88
@Deriv D'~t
99
dsin = D(sin(t))
@@ -43,5 +43,5 @@ jac = ModelingToolkit.calculate_jacobian(sys)
4343
@test jac[3,3] == -1*β
4444

4545
# Variable dependence checking in differentiation
46-
@Var a(t) b(a)
46+
@Unknown a(t) b(a)
4747
@test D(b) Constant(0)

test/internal.jl

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,7 @@ using Test
44
# `Expr`, `Number` -> `Operation`
55
@IVar a
66
@Param b
7-
@DVar x(t)
8-
@Var y
7+
@Unknown x(t) y()
98
@test convert(Expression, 2) == 2
109
expr = :(-inv(2sqrt(+($a, $b))))
1110
op = Operation(-, [Operation(inv,

0 commit comments

Comments
 (0)