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 Syms (or iscall objects) return an expression that iscall. 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 an iscall 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.iscall 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.
Symbolics supports creating variables that denote an array of some size.
julia> @variables x[1:3]
1-element Vector{Symbolics.Arr{Num, 1}}:
x[1:3]
julia> @variables y[1:3, 1:6] # support for tensors
1-element Vector{Symbolics.Arr{Num, 2}}:
y[1:3,1:6]
julia> @variables t z(t)[1:3] # also works for dependent variables
2-element Vector{Any}:
t
(z(t))[1:3]A symbol or expression that represents an array can be turned into an array of symbols or expressions using the scalarize function.
julia> Symbolics.scalarize(z)
3-element Vector{Num}:
(z(t))[1]
(z(t))[2]
(z(t))[3]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, :value_b, :value_c)
julia> vars = @variables t $a $b(t) $c(t)[1:3]
4-element Vector{Any}:
t
runtime_symbol_value
value_b(t)
(value_c(t))[1:3]
julia> (t, a, b, c)
(t, :runtime_symbol_value, :value_b, :value_c)Symbolics.variable — Functionvariable(name::Symbol, idx::Integer...; T=Real)Create a variable with the given name along with subscripted indices with the symtype=T. When T=FnType, it creates a symbolic function.
julia> Symbolics.variable(:x, 4, 2, 0)
x₄ˏ₂ˏ₀
julia> Symbolics.variable(:x, 4, 2, 0, T=Symbolics.FnType)
x₄ˏ₂ˏ₀⋆Also see variables.
Symbolics.variables — Functionvariables(name::Symbol, indices...)Create a multi-dimensional array of individual variables named with subscript notation. Use @variables instead to create symbolic array variables (as opposed to array of variables). See variable to create one variable with subscripts.
julia> Symbolics.variables(:x, 1:3, 3:6)
3×4 Matrix{Num}:
x₁ˏ₃ x₁ˏ₄ x₁ˏ₅ x₁ˏ₆
x₂ˏ₃ x₂ˏ₄ x₂ˏ₅ x₂ˏ₆
x₃ˏ₃ x₃ˏ₄ x₃ˏ₅ x₃ˏ₆Symbolics.Equation — Typestruct EquationAn 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, rhs) -> Any
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
(broadcast(~, A, B))[1:3,1:3]
julia> A .~ 3x
(broadcast(~, A, 3x))[1:3,1:3]A note about functions restricted to Numbers
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 to allow calling functions which are restricted to Number or Real.
using Symbolics
@variables t x y z(t);
Symbolics.operation(Symbolics.value(x + y))+ (generic function with 1060 methods)Symbolics.operation(Symbolics.value(z))\[ \begin{equation} z \end{equation} \]
Symbolics.arguments(Symbolics.value(x + y))2-element SymbolicUtils.SmallVec{Any, Vector{Any}}:
y
xNote that Julia converts irrationals — like π and ℯ — to Float64 whenever they are involved in arithmetic with other numbers, including integers. An expression like 2π will be converted to a float immediately, so an expression like 2π * x will leave the symbolic x multiplied by a Float64. It may be preferable to have a symbolic representation of π also, which can be achieved with Num(π). For generic programming, it may be helpful to simply redefine the variable π to be of the same type as some other argument, as in
function f(x)
let π=oftype(x, π)
1 + (2//3 + 4π/5) * x
end
end
f(t)\[ \begin{equation} 1 + t \left( \frac{2}{3} + \frac{4}{5} \pi \right) \end{equation} \]
This will work for any floating-point input, as well as symbolic input.
Symbolic Control Flow
Control flow can be expressed in Symbolics.jl in the following way:
Base.ifelse — Methodifelse(cond::Num, x, y)Symbolic conditional expression. Returns x if cond evaluates to true, and y if cond evaluates to false. This allows encoding conditional logic in symbolic expressions.
Examples
@variables a b c
ifelse(a > b, c, 0) # Returns c if a > b, otherwise 0Inspection Functions
TermInterface.iscall — Functioniscall(expr)Check if a symbolic expression expr represents a function call. Returns true if the expression is a composite expression with an operation and arguments, false otherwise.
This function is fundamental for traversing and analyzing symbolic expressions. In SymbolicUtils.jl, an expression is considered a "call" if it represents a function application (including operators like +, -, *, etc.).
Examples
using SymbolicUtils
@variables x y
# Basic variables are not calls
iscall(x) # false
# Function calls are calls
expr = sin(x + y)
iscall(expr) # true
# Arithmetic expressions are calls
iscall(x + y) # true
iscall(x * y) # trueMissing docstring for SymbolicUtils.operation. Check Documenter's build log for details.
TermInterface.arguments — Functionarguments(x, op::Function)Get the arguments of the symbolic expression x with respect to the operation or function op.
arguments(expr)Extract the arguments from a symbolic function call expression. Only valid for expressions where iscall(expr) returns true.
Returns a collection (typically a vector) containing the arguments passed to the operation. For binary operations like + or *, this returns a collection of all operands. For function calls, this returns the function arguments.
Examples
using SymbolicUtils
@variables x y z
# Binary arithmetic operations
expr1 = x + y
arguments(expr1) # returns collection containing x and y
expr2 = x * y * z
arguments(expr2) # returns collection containing x, y, and z
# Function calls
expr3 = sin(x)
arguments(expr3) # returns collection containing x
# Nested expressions
expr4 = sin(x + y)
arguments(expr4) # returns collection containing (x + y)
arguments(arguments(expr4)[1]) # returns collection containing x and yVariable Utilities
Missing docstring for Symbolics.get_variables!. Check Documenter's build log for details.
Symbolics.getparent — Functiongetparent(x) -> Any
getparent(x, val) -> Any
Return the array variable that was indexed to obtain symbolic variable x.
Variable Parsing
For implementing custom variable-creating macros: