Skip to content

Commit 16a3571

Browse files
Merge pull request #740 from SciML/myb/dep
Deprecate `@derivatives` and better `derivatives` function
2 parents ea7207d + f0f4240 commit 16a3571

39 files changed

+180
-103
lines changed

Project.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
name = "ModelingToolkit"
22
uuid = "961ee093-0014-501f-94e3-6117800e7a78"
33
authors = ["Chris Rackauckas <[email protected]>"]
4-
version = "4.5.0"
4+
version = "5.0.0"
55

66
[deps]
77
ArrayInterface = "4fba245c-0d91-5ea0-9b3e-6abc04ee57a9"

README.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ using ModelingToolkit, OrdinaryDiffEq
3030

3131
@parameters t σ ρ β
3232
@variables x(t) y(t) z(t)
33-
@derivatives D'~t
33+
D = Differential(t)
3434

3535
eqs = [D(D(x)) ~ σ*(y-x),
3636
D(y) ~ x*-z)-y,
@@ -67,7 +67,7 @@ using ModelingToolkit, OrdinaryDiffEq
6767

6868
@parameters t σ ρ β
6969
@variables x(t) y(t) z(t)
70-
@derivatives D'~t
70+
D = Differential(t)
7171

7272
eqs = [D(x) ~ σ*(y-x),
7373
D(y) ~ x*-z)-y,

docs/src/IR.md

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -3,17 +3,19 @@
33
ModelingToolkit IR mirrors the Julia AST but allows for easy mathematical
44
manipulation by itself following mathematical semantics. The base of the IR is
55
the `Sym` type, which defines a symbolic variable. Registered (mathematical)
6-
functions on `Sym`s (or `Term`s) return `Term`s. For example, `op1 = x+y` is
7-
one `Term` and `op2 = 2z` is another, and so `op1*op2` is another `Term`. Then,
8-
at the top, an `Equation`, normally written as `op1 ~ op2`, defines the
9-
symbolic equality between two operations.
6+
functions on `Sym`s (or `istree` objects) return an expression that `istree`.
7+
For example, `op1 = x+y` is one symbolic object and `op2 = 2z` is another, and
8+
so `op1*op2` is another tree object. Then, at the top, an `Equation`, normally
9+
written as `op1 ~ op2`, defines the symbolic equality between two operations.
1010

1111
### Types
1212
`Sym`, `Term`, and `FnType` are from [SymbolicUtils.jl](https://juliasymbolics.github.io/SymbolicUtils.jl/api/). Note that in
1313
ModelingToolkit, we always use `Sym{Real}`, `Term{Real}`, and
14-
`FnType{Tuple{Any}, Real}`. To get the arguments of a `Term` use
15-
`arguments(t::Term)`, and to get the operation of a `Term` use
16-
`operation(t::Term)`.
14+
`FnType{Tuple{Any}, Real}`. To get the arguments of a `istree` object use
15+
`arguments(t::Term)`, and to get the operation, use `operation(t::Term)`.
16+
However, note that one should never dispatch on `Term` or test `isa Term`.
17+
Instead, one needs to use `SymbolicUtils.istree` to check if `arguments` and
18+
`operation` is defined.
1719

1820
```@docs
1921
Equation

docs/src/highlevel.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,14 +10,15 @@ methods.
1010
```@docs
1111
@parameters
1212
@variables
13-
@derivatives
13+
Differential
1414
Base.:~(::Num, ::Num)
1515
modelingtoolkitize
1616
```
1717

1818
## Differentiation Functions
1919

2020
```@docs
21+
ModelingToolkit.derivative
2122
ModelingToolkit.gradient
2223
ModelingToolkit.jacobian
2324
ModelingToolkit.sparsejacobian

docs/src/tutorials/higher_order.md

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ using ModelingToolkit, OrdinaryDiffEq
1515

1616
@parameters t σ ρ β
1717
@variables x(t) y(t) z(t)
18-
@derivatives D'~t
18+
D = Differential(t)
1919

2020
eqs = [D(D(x)) ~ σ*(y-x),
2121
D(y) ~ x*-z)-y,
@@ -25,10 +25,11 @@ sys = ODESystem(eqs)
2525
```
2626

2727
Note that we could've used an alternative syntax for 2nd order, i.e.
28-
`@derivatives E''~t` and then `E(x)` would be the second derivative,
29-
and this syntax extends to Nth order.
28+
`D = Differential(t)^2` and then `E(x)` would be the second derivative,
29+
and this syntax extends to `N`-th order. Also, we can use `*` or `` to compose
30+
`Differential`s, like `Differential(t) * Differential(x)`.
3031

31-
Now let's transform this into the ODESystem of first order components.
32+
Now let's transform this into the `ODESystem` of first order components.
3233
We do this by simply calling `ode_order_lowering`:
3334

3435
```julia

docs/src/tutorials/ode_modeling.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ using ModelingToolkit, OrdinaryDiffEq
99

1010
@parameters t σ ρ β
1111
@variables x(t) y(t) z(t)
12-
@derivatives D'~t
12+
D = Differential(t)
1313

1414
eqs = [D(x) ~ σ*(y-x),
1515
D(y) ~ x*-z)-y,
@@ -58,7 +58,7 @@ using ModelingToolkit
5858

5959
@parameters t σ ρ β
6060
@variables x(t) y(t) z(t)
61-
@derivatives D'~t
61+
D = Differential(t)
6262
```
6363

6464
Then we build the system:

docs/src/tutorials/symbolic_functions.md

Lines changed: 13 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -7,17 +7,15 @@ The way to define symbolic variables is via the `@variables` macro:
77
@variables x y
88
```
99

10-
After defining variables as symbolic, symbolic expressions, which we
11-
call a `Term`, can be generated by utilizing Julia expressions.
12-
For example:
10+
After defining variables as symbolic, symbolic expressions, which we call a
11+
`istree` object, can be generated by utilizing Julia expressions. For example:
1312

1413
```julia
1514
z = x^2 + y
1615
```
1716

18-
Here, `z` is the `Term` for "square `x` and add `y`". To
19-
make an array of symbolic expressions, simply make an array of
20-
symbolic expressions:
17+
Here, `z` is an expression tree for "square `x` and add `y`". To make an array
18+
of symbolic expressions, simply make an array of symbolic expressions:
2119

2220
```julia
2321
A = [x^2+y 0 2x
@@ -30,7 +28,10 @@ A = [x^2+y 0 2x
3028
y ^ 2 + x 0 0
3129
```
3230

33-
Note that by default, `@variables` returns `Sym` or `Term` objects wrapped in `Num` in order to make them behave like subtypes of `Real`. Any operation on these `Num` objects will return a new `Num` object, wrapping the result of computing symbolically on the underlying values.
31+
Note that by default, `@variables` returns `Sym` or `istree` objects wrapped in
32+
`Num` in order to make them behave like subtypes of `Real`. Any operation on
33+
these `Num` objects will return a new `Num` object, wrapping the result of
34+
computing symbolically on the underlying values.
3435

3536
To better view the results, we can use [Latexify.jl](https://github.com/korsbo/Latexify.jl).
3637
ModelingToolkit.jl comes with Latexify recipes so it works automatically:
@@ -282,11 +283,11 @@ sufficiently large!)
282283
One common thing to compute in a symbolic system is derivatives. In
283284
ModelingToolkit.jl, derivatives are represented lazily via operations,
284285
just like any other function. To build a differential operator, use
285-
`@derivatives` like:
286+
`Differential` like:
286287

287288
```julia
288289
@variables t
289-
@derivatives D'~t
290+
Differential(t)
290291
```
291292

292293
This is the differential operator ``D = \frac{\partial}{\partial t}``: the number of
@@ -450,7 +451,7 @@ z = g(x) + g(y)
450451

451452
One of the benefits of a one-language Julia symbolic stack is that the
452453
primitives are all written in Julia, and therefore it's trivially
453-
extendable from Julia itself. By default, new functions are traced
454+
extendible from Julia itself. By default, new functions are traced
454455
to the primitives and the symbolic expressions are written on the
455456
primitives. However, we can expand the allowed primitives by registering
456457
new functions. For example, let's register a new function `h`:
@@ -479,7 +480,8 @@ ModelingToolkit.derivative(::typeof(h), args::NTuple{2,Any}, ::Val{2}) = 1
479480
and now it works with the rest of the system:
480481

481482
```julia
482-
@derivatives Dx'~x Dy'~y
483+
Dx = Differential(x)
484+
Dy = Differential(y)
483485
expand_derivatives(Dx(h(x,y) + y^2)) # 2x
484486
expand_derivatives(Dy(h(x,y) + y^2)) # 1 + 2y
485487
```

src/ModelingToolkit.jl

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,9 @@ RuntimeGeneratedFunctions.init(@__MODULE__)
2323
using RecursiveArrayTools
2424

2525
import SymbolicUtils
26-
import SymbolicUtils: Term, Add, Mul, Pow, Sym, to_symbolic, FnType, @rule, Rewriters, substitute, similarterm
26+
import SymbolicUtils: Term, Add, Mul, Pow, Sym, to_symbolic, FnType,
27+
@rule, Rewriters, substitute, similarterm,
28+
promote_symtype
2729

2830
import SymbolicUtils.Rewriters: Chain, Postwalk, Prewalk, Fixpoint
2931

src/differentials.jl

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -16,10 +16,16 @@ julia> using ModelingToolkit
1616
julia> @variables x y;
1717
1818
julia> D = Differential(x)
19-
(D'~x())
19+
(D'~x)
2020
21-
julia> D(y) # Differentiate y wrt. x
22-
(D'~x())(y())
21+
julia> D(y) # Differentiate y wrt. x
22+
(D'~x)(y)
23+
24+
julia> Dx = Differential(x) * Differential(y) # d^2/dxy operator
25+
(D'~x(t)) ∘ (D'~y(t))
26+
27+
julia> D3 = Differential(x)^3 # 3rd order differential operator
28+
(D'~x(t)) ∘ (D'~x(t)) ∘ (D'~x(t))
2329
```
2430
"""
2531
struct Differential <: Function
@@ -31,6 +37,11 @@ end
3137
(D::Differential)(x::Num) = Num(D(value(x)))
3238
SymbolicUtils.promote_symtype(::Differential, x) = x
3339

40+
Base.:*(D1, D2::Differential) = D1 D2
41+
Base.:*(D1::Differential, D2) = D1 D2
42+
Base.:*(D1::Differential, D2::Differential) = D1 D2
43+
Base.:^(D::Differential, n::Integer) = _repeat_apply(D, n)
44+
3445
Base.show(io::IO, D::Differential) = print(io, "(D'~", D.x, ")")
3546

3647
Base.:(==)(D1::Differential, D2::Differential) = isequal(D1.x, D2.x)
@@ -229,6 +240,7 @@ end
229240
_repeat_apply(f, n) = n == 1 ? f : f _repeat_apply(f, n-1)
230241
function _differential_macro(x)
231242
ex = Expr(:block)
243+
push!(ex.args, :(Base.depwarn("`@derivatives D'''~x` is deprecated. Use `Differential(x)^3` instead.", Symbol("@derivatives"), force=true)))
232244
lhss = Symbol[]
233245
x = x isa Tuple && first(x).head == :tuple ? first(x).args : x # tuple handling
234246
x = flatten_expr!(x)

src/direct.jl

Lines changed: 22 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,19 @@
1+
"""
2+
```julia
3+
derivative(O, v; simplify = true)
4+
```
5+
6+
A helper function for computing the derivative of an expression with respect to
7+
`var`.
8+
"""
9+
function derivative(O, v; simplify = true)
10+
if O isa AbstractArray
11+
Num[Num(expand_derivatives(Differential(v)(value(o)), simplify)) for o in O]
12+
else
13+
Num(expand_derivatives(Differential(v)(value(O)), simplify))
14+
end
15+
end
16+
117
"""
218
```julia
319
gradient(O, vars::AbstractVector; simplify = true)
@@ -7,7 +23,7 @@ A helper function for computing the gradient of an expression with respect to
723
an array of variable expressions.
824
"""
925
function gradient(O, vars::AbstractVector; simplify = true)
10-
Num[expand_derivatives(Differential(v)(value(O)),simplify) for v in vars]
26+
Num[Num(expand_derivatives(Differential(v)(value(O)),simplify)) for v in vars]
1127
end
1228

1329
"""
@@ -19,7 +35,7 @@ A helper function for computing the Jacobian of an array of expressions with res
1935
an array of variable expressions.
2036
"""
2137
function jacobian(ops::AbstractVector, vars::AbstractVector; simplify = true)
22-
Num[expand_derivatives(Differential(value(v))(value(O)),simplify) for O in ops, v in vars]
38+
Num[Num(expand_derivatives(Differential(value(v))(value(O)),simplify)) for O in ops, v in vars]
2339
end
2440

2541
"""
@@ -41,7 +57,7 @@ function sparsejacobian(ops::AbstractVector, vars::AbstractVector; simplify = tr
4157
exprs = Num[]
4258

4359
for (i,j) in zip(I, J)
44-
push!(exprs, expand_derivatives(Differential(vars[j])(ops[i]), simplify))
60+
push!(exprs, Num(expand_derivatives(Differential(vars[j])(ops[i]), simplify)))
4561
end
4662
sparse(I,J, exprs, length(ops), length(vars))
4763
end
@@ -232,7 +248,9 @@ function toexpr(O; canonicalize=true)
232248
op = operation(O)
233249
args = arguments(O)
234250
if op isa Differential
235-
return :(derivative($(toexpr(args[1]; canonicalize=canonicalize)),$(toexpr(op.x; canonicalize=canonicalize))))
251+
ex = toexpr(args[1]; canonicalize=canonicalize)
252+
wrt = toexpr(op.x; canonicalize=canonicalize)
253+
return :(_derivative($ex, $wrt))
236254
elseif op isa Sym
237255
isempty(args) && return nameof(op)
238256
return Expr(:call, toexpr(op; canonicalize=canonicalize), toexpr(args; canonicalize=canonicalize)...)

0 commit comments

Comments
 (0)