Introduction to Julia's Symbolic Computing Package Symbolics.jl
Overview
This introduces Symbolics.jl
, a package that supports symbolic algebra systems in Julia1. This package provides an extremely intuitive and powerful interface along with Julia’s basic syntax.
Difference from SymEngine.jl
Symbolics.jl
is implemented natively in Julia and has many better aspects in terms of performance and interface. SymEngine.jl
, introduced in the post on how to do symbolic operations in Julia, is originally a library written in C++ and wrapped in Julia. As a wrapper, it was useful at the time without any separate feature development due to its immediacy, but now, unless one is already familiar with the original SymEngine
, there is no reason to use SymEngine.jl
.
Code
Variable Declaration @variables
julia> using Symbolics
julia> @variables t x y z
4-element Vector{Num}:
t
x
y
z
julia> w = log(y) + cos(x) + 2t^3
log(y) + cos(x) + 2(t^3)
Variables to be used symbolically can be declared by listing them after @variables
. These variables can naturally be used as arguments to transcendental functions, etc.
Expansion expand
julia> (x + y)^3
(x + y)^3
julia> expand((x + y)^3)
x^3 + 3(x^2)*y + 3x*(y^2) + y^3
Expansion can be done through the expand
function. If no function is taken, it does not perform expansion unnecessarily for performance reasons, among others.
Simplification simplify
julia> simplify(2x + (cos(x))^2 + 3x + (sin(x))^2)
1 + 5x
The simplify
function can be used to eliminate terms that become 0 or to make things simpler by adding coefficients together. As seen in the example, it automatically calculates things like the sum of the squares of trigonometric functions becoming 1.
Substitution and Assignment substitute
The substitute
function can be used for substituting or assigning variables. How the substitution is done can be indicated by creating and inserting a dictionary.
$$
w = \log y + \cos x + 2t^3
$$
Let’s try substitution and assignment on this $w$.
julia> substitute(w, Dict(t => x))
log(y) + cos(x) + 2(x^3)
Substitution between symbolic variables is of course possible.
julia> substitute(w, Dict(t => 2, x => π, y => exp(1)))
16.0
The results of assignments with $t = 2$, $x = \pi$, $y = e$ are returned after numerical calculation.
Differentiation Differential
julia> Dt = Differential(t)
(::Differential) (generic function with 3 methods)
julia> Dt(w)
Differential(t)(log(y) + cos(x) + 2(t^3))
julia> expand_derivatives(Dt(w))
6(t^2)
Interestingly, in Symbolics
, differentiation is done by defining a differential operator for a specific variable to differentiate with respect to, instead of passing the variable to integrate over directly to the function that performs the differentiation. At first glance, the code may seem messy and complicated, but from a mathematician’s perspective, it is very intuitive and a valid interface. The result of the differentiation, like expansion, is not calculated immediately but waits lazily and is returned through the expand_derivatives
function.
julia> Dx = Differential(x)
(::Differential) (generic function with 3 methods)
julia> Dy = Differential(y)
(::Differential) (generic function with 3 methods)
julia> Dx(w) |> expand_derivatives
-sin(x)
julia> Dy(w) |> expand_derivatives
1 / y
From the perspective of defining an operator, partial differentiation is also very naturally implemented.
Vectors and Matrices
julia> @variables σ ρ β
3-element Vector{Num}:
σ
ρ
β
julia> Lorenz = [-σ*x + σ*y
-x*y + ρ*x - y
x*y - β*z]
3-element Vector{Num}:
-x*σ + y*σ
-y - x*y + x*ρ
x*y - z*β
Vectors are created just like in Julia syntax. For example, the right side of the Lorenz attractor can also be simply expressed symbolically as above.
julia> M = [x*y y^2
2x 9]
2×2 Matrix{Num}:
x*y y^2
2x 9
Matrices are also very intuitively defined.
Jacobian
julia> Symbolics.jacobian(Lorenz, [x, y, z])
3×3 Matrix{Num}:
-σ σ 0
-y + ρ -1 - x 0
y x -β
The Jacobian matrix can be easily obtained through Symbolics.jacobian
.
Complete Code
using Symbolics
@variables t x y z
w = log(y) + cos(x) + 2t^3
(x + y)^3
expand((x + y)^3)
simplify(2x + (cos(x))^2 + 3x + (sin(x))^2)
substitute(w, Dict(t => x))
substitute(w, Dict(t => 2, x => π, y => exp(1)))
Dt = Differential(t)
Dt(w)
expand_derivatives(Dt(w))
Dx = Differential(x)
Dy = Differential(y)
Dx(w) |> expand_derivatives
Dy(w) |> expand_derivatives
@variables σ ρ β
Lorenz = [-σ*x + σ*y
-x*y + ρ*x - y
x*y - β*z]
M = [x*y y^2
2x 9]
Symbolics.jacobian(Lorenz, [x, y, z])
Environment
- OS: Windows
- julia: v1.10.0
- Symbolics v5.16.1