ModelingToolkit IR

ModelingToolkit IR mirrors the Julia AST but allows for easy mathematical manipulation by itself following mathematical semantics. The base of the IR is the Sym type, which defines a symbolic variable. Registered (mathematical) functions on Syms (or Terms) return Terms. For example, op1 = x+y is one Term and op2 = 2z is another, and so op1*op2 is another Term. Then, at the top, an Equation, normally written as op1 ~ op2, defines the symbolic equality between two operations.

Types

Sym, Term, and FnType are from SymbolicUtils.jl. Note that in ModelingToolkit, we always use Sym{Real}, Term{Real}, and FnType{Tuple{Any}, Real}. To get the arguments of a Term use arguments(t::Term), and to get the operation of a Term use operation(t::Term).

ModelingToolkit.EquationType
struct Equation

An equality relationship between two expressions.

Fields

  • lhs

    The expression on the left-hand side of the equation.

  • rhs

    The expression on the right-hand side of the equation.

source

A note about functions restricted to Numbers

Sym and Term objects are NOT subtypes of Number. ModelingToolkit provides a simple wrapper type called Num which is a subtype of Real. Num wraps either a Sym or a Term or any other object, defines the same set of operations as symbolic expressions and forwards those to the values it wraps. You can use ModelingToolkit.value function to unwrap a Num.

By default, the @variables and @parameters functions return Num-wrapped objects so as to allow calling functions which are restricted to Number or Real.

julia> @parameters t; @variables x y z(t);

julia> ModelingToolkit.operation(ModelingToolkit.value(x + y))
+ (generic function with 377 methods)

julia> ModelingToolkit.operation(ModelingToolkit.value(z))
z(::Any)::Real

julia> ModelingToolkit.arguments(ModelingToolkit.value(x + y))
2-element Vector{Sym{Real}}:
 x
 y

Function Registration

The ModelingToolkit graph only allowed for registered Julia functions for the operations. All other functions are automatically traced down to registered functions. By default, ModelingToolkit.jl pre-registers the common functions utilized in SymbolicUtils.jl and pre-defines their derivatives. However, the user can utilize the @register macro to add their function to allowed functions of the computation graph.

ModelingToolkit.@registerMacro
@register(expr, Ts = [Num, Symbolic, Real])

Overload approperate methods such that ModelingToolkit can stop tracing into the registered function.

Examples

@register foo(x, y)
@register goo(x, y::Int) # `y` is not overloaded to take symbolic objects
source

Derivatives and Differentials

A Differential(op) is a partial derivative with respect to op, which can then be applied to some other operations. For example, D=Differential(t) is what would commonly be referred to as d/dt, which can then be applied to other operations using its function call, so D(x+y) is d(x+y)/dt.

By default, the derivatives are left unexpanded to capture the symbolic representation of the differential equation. If the user would like to expand out all of the differentials, the expand_derivatives function eliminates all of the differentials down to basic one-variable expressions.

ModelingToolkit.derivativeFunction
derivative(O, idx)

Calculate the derivative of the op O with respect to its argument with index idx.

Examples

julia> using ModelingToolkit

julia> @variables x y;

julia> ModelingToolkit.derivative(sin(x), 1)
cos(x())

Note that the function does not recurse into the operation's arguments, i.e., the chain rule is not applied:

julia> myop = sin(x) * y^2
sin(x()) * y() ^ 2

julia> typeof(myop.op)  # Op is multiplication function
typeof(*)

julia> ModelingToolkit.derivative(myop, 1)  # wrt. sin(x)
y() ^ 2

julia> ModelingToolkit.derivative(myop, 2)  # wrt. y^2
sin(x())
source
ModelingToolkit.DifferentialType
struct Differential <: Function

Represents a differential operator.

Fields

  • x

    The variable or expression to differentiate with respect to.

Examples

julia> using ModelingToolkit

julia> @variables x y;

julia> D = Differential(x)
(D'~x())

julia> D(y)  # Differentiate y wrt. x
(D'~x())(y())
source
ModelingToolkit.jacobianFunction
jacobian(ops::AbstractVector, vars::AbstractVector; simplify = true)

A helper function for computing the Jacobian of an array of expressions with respect to an array of variable expressions.

source
ModelingToolkit.gradientFunction
gradient(O, vars::AbstractVector; simplify = true)

A helper function for computing the gradient of an expression with respect to an array of variable expressions.

source
ModelingToolkit.hessianFunction
hessian(O, vars::AbstractVector; simplify = true)

A helper function for computing the Hessian of an expression with respect to an array of variable expressions.

source

For jacobians which are sparse, use the sparsejacobian function. For hessians which are sparse, use the sparsehessian function.

Adding Derivatives

There is a large amount of derivatives pre-defined by DiffRules.jl.

f(x,y,z) = x^2 + sin(x+y) - z

automatically has the derivatives defined via the tracing mechanism. It will do this by directly building the operation the internals of your function and differentiating that.

However, in many cases you may want to define your own derivatives so that way automatic Jacobian etc. calculations can utilize this information. This can allow for more succinct versions of the derivatives to be calculated in order to better scale to larger systems. You can define derivatives for your own function via the dispatch:

# `N` arguments are accepted by the relevant method of `my_function`
ModelingToolkit.derivative(::typeof(my_function), args::NTuple{N,Any}, ::Val{i})

where i means that it's the derivative with respect to the ith argument. args is the array of arguments, so, for example, if your function is f(x,t), then args = [x,t]. You should return an Term for the derivative of your function.

For example, sin(t)'s derivative (by t) is given by the following:

ModelingToolkit.derivative(::typeof(sin), args::NTuple{1,Any}, ::Val{1}) = cos(args[1])

IR Manipulation

ModelingToolkit.jl provides functionality for easily manipulating expressions. Most of the functionality comes by the expression objects obeying the standard mathematical semantics. For example, if one has A a matrix of symbolic expressions wrapped in Num, then A^2 calculates the expressions for the squared matrix. In that sense, it is encouraged that one uses standard Julia for performing a lot of the manipulation on the IR, as, for example, calculating the sparse form of the matrix via sparse(A) is valid, legible, and easily understandable to all Julia programmers.

Other additional manipulation functions are given below.

ModelingToolkit.get_variablesFunction
get_variables(O) -> Vector{Union{Sym, Term}}

Returns the variables in the expression. Note that the returned variables are not wrapped in the Num type.

Examples

julia> @parameters t
(t,)

julia> @variables x y z(t)
(x, y, z(t))

julia> ex = x + y + sin(z)
(x + y) + sin(z(t))

julia> ModelingToolkit.get_variables(ex)
3-element Vector{Any}:
 x
 y
 z(t)
source
SymbolicUtils.substituteFunction
substitute(expr, s::Pair)
substitute(expr, s::Dict)
substitute(expr, s::Vector)

Performs the substitution on expr according to rule(s) s.

Examples

julia> @parameters t
(t,)

julia> @variables x y z(t)
(x, y, z(t))

julia> ex = x + y + sin(z)
(x + y) + sin(z(t))

julia> substitute(ex, Dict([x => z, sin(z) => z^2]))
(z(t) + y) + (z(t) ^ 2)
source
Missing docstring.

Missing docstring for tovar. Check Documenter's build log for details.

Missing docstring.

Missing docstring for toparam. Check Documenter's build log for details.

Missing docstring.

Missing docstring for tosymbol. Check Documenter's build log for details.

Missing docstring.

Missing docstring for makesym. Check Documenter's build log for details.

Missing docstring.

Missing docstring for diff2term. Check Documenter's build log for details.