SymbolicUtils is an practical symbolic programming utility in Julia. It lets you create, rewrite and simplify symbolic expressions, and generate Julia code from them.
Features:
Fast expressions
A combinator library for making rewriters.
Efficient representation of numeric expressions
Type promotion:
Set of extendable simplification rules.
Table of contents
First, let's use the @syms
macro to create a few symbols.
using SymbolicUtils
@syms w z α::Real β::Real
(w, z, α, β)
Type annotations are optional when creating symbols. Here α
, β
behave like Real numbers. w
and z
behave like Number
, which is the default. You can use the symtype
function to find the type of a symbol.
using SymbolicUtils: symtype
symtype(w), symtype(z), symtype(α), symtype(β)
(Number, Number, Real, Real)
Note however that they are not subtypes of these types!
@show w isa Number
@show α isa Real
w isa Number = false
α isa Real = false
As their types are different:
@show typeof(w)
@show typeof(α)
typeof(w) = SymbolicUtils.BasicSymbolic{Number}
typeof(α) = SymbolicUtils.BasicSymbolic{Real}
(see this post for why they are all not just subtypes of Number
)
You can do basic arithmetic on symbols to get symbolic expressions:
expr1 = α*sin(w)^2 + β*cos(z)^2
expr2 = α*cos(z)^2 + β*sin(w)^2
expr1 + expr2
α*(sin(w)^2) + β*(sin(w)^2) + α*(cos(z)^2) + β*(cos(z)^2)
SymbolicUtils automatically simplifies
2w + 3w - 3z + α
α + 5w - 3z
and reorders
(z + w)*(α + β)
(w + z)*(α + β)
expressions of type Symbolic{<:Number}
(which includes Sym{Real}
) when they are created. It also does constant elimination (including rational numbers)
5 + 2w - 3z + α - (β + 5//3) + 3w - 2 + 3//2 * β
(4//3) + α + 5w + (1//2)*β - 3z
It's worth remembering that the expression may be transformed with respect to the input when it's created.
Function-like symbols
Symbols can be defined to behave like functions. Both the input and output types for the function can be specified. Any application to that function will only admit either values of those types or symbols of the same symtype
.
using SymbolicUtils
@syms f(x) g(x::Real, y::Real)::Real
f(z) + g(1, α) + sin(w)
sin(w) + f(z) + g(1, α)
This does not work since z
is a Number
, not a Real
.
g(1, z)
Tuple{Int64, Number} is not a subtype of Tuple{Real, Real}.
This works because g
"returns" a Real
.
g(2//5, g(1, β))
g(2//5, g(1, β))
Symbolic expressions are of type Term{T}
, Add{T}
, Mul{T}
, Pow{T}
or Div{T}
and denote some function call where one or more arguments are themselves such expressions or Sym
s. See more about the representation here.
All the expression types support the following:
istree(x)
– always returns true
denoting, x
is not a leaf node like Sym or a literal.
operation(x)
– the function being called
arguments(x)
– a vector of arguments
symtype(x)
– the "inferred" type (T
)
See more on the interface here
SymbolicUtils contains a rule-based rewriting language for easy pattern matching and rewriting of expression. There is also a combinator library to combine rules to chain, branch and loop over rules.
By default +
, *
and ^
operations apply the most basic simplification upon construction of the expression.
The rules with which the canonical form of Symbolic{<:Number}
terms are constructed are the next (where x isa Symbolic
and c isa Number
)
0 + x
, 1 * x
and x^1
always gives x
0 * x
always gives 0
and x ^ 0
gives 1
-x
becomes (-1)*x
commutativity and associativity over +
and *
are assumed. Re-ordering of terms will be done under a total order
p/q * x
or x * p/q
results in (p*x)/q
p/q * x/y
results in (p*x)/(q*y)
x + ... + x
will be fused into n*x
with type Mul
x * ... * x
will be fused into x^n
with type Pow
sum of Add
's are fused
product of Mul
's are fused
c * (c₁x₁ + ... + cₙxₙ)
will be converted into c*c₁*x₁ + ... + c*cₙ*xₙ
(x₁^c₁ * ... * xₙ^cₙ)^c
will be converted into x₁^(c*c₁) * ... * xₙ^(c*cₙ)
there are come other simplifications on construction that you can check here
Here is an example of this
2 * (w+w+α+β + sin(z)^2 + cos(z)^2 - 1)
2(α + β + 2w + cos(z)^2 + sin(z)^2 - 1)
The simplify
function applies a built-in set of rules to rewrite expressions in order to simplify it.
simplify(2 * (w+w+α+β + sin(z)^2 + cos(z)^2 - 1))
2(α + β + 2w)
The rules in the default simplify applies simple constant elimination and trigonometric identities.
If you read the previous section on the rules DSL, you should be able to read and understand the rules that are used by simplify
.
Experimental feature
It is common to want to generate executable code from symbolic expressions and blocks of them. We are working on experimental support for turning Symbolic expressions into executable functions with specific focus on array input and output and performance which is critical to the Differential Equations ecosystem which is making heavy use of this package.
See Code generation for more about this.
If you have a package that you would like to utilize rule-based rewriting in, look at the suggestions in the Interfacing section to find out how you can do that without any fundamental changes to your package. Look at the API documentation for docstrings about specific functions or macros.
Head over to the github repository to ask questions and report problems! Join the Zulip stream to chat!