Variable and Equation Types
Symbolics 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 Sym
s (or istree
objects) return an expression that istree
. For example, op1 = x+y
is one symbolic object and op2 = 2z
is another, and so op1*op2
is another tree object. 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 Symbolics, we always use Sym{Real}
, Term{Real}
, and FnType{Tuple{Any}, Real}
. To get the arguments of a istree
object use arguments(t::Term)
, and to get the operation, use operation(t::Term)
. However, note that one should never dispatch on Term
or test isa Term
. Instead, one needs to use SymbolicUtils.istree
to check if arguments
and operation
is defined.
Symbolics.@variables
— MacroDefine one or more unknown variables.
@variables t α σ(..) β[1:2]
@variables w(..) x(t) y z(t, α, x)
expr = β[1]* x + y^α + σ(3) * (z - t) - β[2] * w(t - 1)
(..)
signifies that the value should be left uncalled.
Sometimes it is convenient to define arrays of variables to model things like x₁,…,x₃
. The @variables
macro supports this with the following syntax:
julia> @variables x[1:3]
1-element Vector{Vector{Num}}:
[x₁, x₂, x₃]
julia> @variables y[2:3, 1:5:6] # support for arbitrary ranges and tensors
1-element Vector{Matrix{Num}}:
[y₂ˏ₁ y₂ˏ₆; y₃ˏ₁ y₃ˏ₆]
julia> @variables t z[1:3](t) # also works for dependent variables
2-element Vector{Any}:
t
Num[z₁(t), z₂(t), z₃(t)]
Note that @variables
returns a vector of all the defined variables.
@variables
can also take runtime symbol values by the $
interpolation operator, and in this case, @variables
doesn't automatically assign the value, instead, it only returns a vector of symbolic variables. All the rest of the syntax also applies here.
julia> a, b, c = :runtime_symbol_value, :value_b, :value_c
:runtime_symbol_value
julia> vars = @variables t $a $b(t) $c[1:3](t)
3-element Vector{Num}:
t
runtime_symbol_value
value_b(t)
Num[value_c₁(t), value_c₂(t), value_c₃(t)]
julia> (t, a, b, c)
(t, :runtime_symbol_value, :value_b, :value_c)
Symbolics.Equation
— Typestruct 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.
Base.:~
— Method~(lhs::Num, rhs::Num) -> Equation
Create an Equation
out of two Num
instances, or an Num
and a Number
.
Examples
julia> using Symbolics
julia> @variables x y;
julia> @variables A[1:3, 1:3] B[1:3, 1:3];
julia> x ~ y
x ~ y
julia> x - y ~ 0
x - y ~ 0
julia> A .~ B
3×3 Array{Equation,2}:
A₁ˏ₁ ~ B₁ˏ₁ A₁ˏ₂ ~ B₁ˏ₂ A₁ˏ₃ ~ B₁ˏ₃
A₂ˏ₁ ~ B₂ˏ₁ A₂ˏ₂ ~ B₂ˏ₂ A₂ˏ₃ ~ B₂ˏ₃
A₃ˏ₁ ~ B₃ˏ₁ A₃ˏ₂ ~ B₃ˏ₂ A₃ˏ₃ ~ B₃ˏ₃
julia> A .~ 3x
3×3 Array{Equation,2}:
A₁ˏ₁ ~ 3x A₁ˏ₂ ~ 3x A₁ˏ₃ ~ 3x
A₂ˏ₁ ~ 3x A₂ˏ₂ ~ 3x A₂ˏ₃ ~ 3x
A₃ˏ₁ ~ 3x A₃ˏ₂ ~ 3x A₃ˏ₃ ~ 3x
A note about functions restricted to Number
s
Sym
and Term
objects are NOT subtypes of Number
. Symbolics 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 Symbolics.value
function to unwrap a Num
.
By default, the @variables
macros return Num-wrapped objects so as to allow calling functions which are restricted to Number
or Real
.
julia> @variables t x y z(t);
julia> Symbolics.operation(Symbolics.value(x + y))
+ (generic function with 377 methods)
julia> Symbolics.operation(Symbolics.value(z))
z(::Any)::Real
julia> Symbolics.arguments(Symbolics.value(x + y))
2-element Vector{Sym{Real}}:
x
y
Symbolic Control Flow
Control flow can be expressed in Symbolics.jl in the following ways:
IfElse.ifelse(cond,x,y)
: this is a dispatch-able version of theifelse
function provided byIfElse.jl
which allows for encoding conditionals in the symbolic branches.
Inspection Functions
SymbolicUtils.istree
— Functionistree(x::T)
Check if x
represents an expression tree. If returns true, it will be assumed that operation(::T)
and arguments(::T)
methods are defined. Definining these three should allow use of simplify
on custom types. Optionally symtype(x)
can be defined to return the expected type of the symbolic expression.
SymbolicUtils.operation
— Functionoperation(x::T)
Returns the operation (a function object) performed by an expression tree. Called only if istree(::T)
is true. Part of the API required for simplify
to work. Other required methods are arguments
and istree
SymbolicUtils.arguments
— Functionarguments(x::T)
Returns the arguments (a Vector
) for an expression tree. Called only if istree(x)
is true
. Part of the API required for simplify
to work. Other required methods are operation
and istree