Symbolics.Symbolics
— ModuleSymbolics.jl
Symbolics.jl is a fast and modern Computer Algebra System (CAS) for a fast and modern programming language (Julia). The goal is to have a high-performance and parallelized symbolic algebra system that is directly extendable in the same language as the users.
Installation
To install Symbolics.jl, use the Julia package manager:
julia> using Pkg
julia> Pkg.add("Symbolics")
Documentation
For information on using the package, see the stable documentation. Use the in-development documentation for the version of the documentation which contains the unreleased features.
Relationship to Other Packages
- SymbolicUtils.jl: This is a rule-rewriting system that is the core of Symbolics.jl. Symbolics.jl builds off of SymbolicUtils.jl to extend it to a whole symbolic algebra system, complete with support for differentiation, solving symbolic systems of equations, etc. If you're looking for the barebones to build a new CAS for specific algebras, SymbolicUtils.jl is that foundation. Otherwise, Symbolics.jl is for you.
- ModelingToolkit.jl: This is a symbolic-numeric modeling system for the SciML ecosystem. It heavily uses Symbolics.jl for its representation of symbolic equations along with tools like differentiation, and adds the representation of common modeling systems like ODEs, SDEs, and more.
Example
julia> using Symbolics
julia> @variables t x y
julia> D = Differential(t)
julia> z = t + t^2
julia> D(z) # symbolic representation of derivative(t + t^2, t)
Differential(t)(t + t^2)
julia> expand_derivatives(D(z))
1 + 2t
julia> Symbolics.jacobian([x + x*y, x^2 + y],[x, y])
2×2 Matrix{Num}:
1 + y x
2x 1
julia> B = simplify.([t^2 + t + t^2 2t + 4t
x + y + y + 2t x^2 - x^2 + y^2])
2×2 Matrix{Num}:
t + 2(t^2) 6t
x + 2t + 2y y^2
julia> simplify.(substitute.(B, (Dict(x => y^2),)))
2×2 Matrix{Num}:
t + 2(t^2) 6t
2t + y^2 + 2y y^2
julia> substitute.(B, (Dict(x => 2.0, y => 3.0, t => 4.0),))
2×2 Matrix{Num}:
36.0 24.0
16.0 9.0
Citation
If you use Symbolics.jl, please cite this paper (or see the free arxiv version)
@article{10.1145/3511528.3511535,
author = {Gowda, Shashi and Ma, Yingbo and Cheli, Alessandro and Gw\'{o}\'{z}zd\'{z}, Maja and Shah, Viral B. and Edelman, Alan and Rackauckas, Christopher},
title = {High-Performance Symbolic-Numerics via Multiple Dispatch},
year = {2022},
issue_date = {September 2021},
publisher = {Association for Computing Machinery},
address = {New York, NY, USA},
volume = {55},
number = {3},
issn = {1932-2240},
url = {https://doi.org/10.1145/3511528.3511535},
doi = {10.1145/3511528.3511535},
abstract = {As mathematical computing becomes more democratized in high-level languages, high-performance symbolic-numeric systems are necessary for domain scientists and engineers to get the best performance out of their machine without deep knowledge of code optimization. Naturally, users need different term types either to have different algebraic properties for them, or to use efficient data structures. To this end, we developed Symbolics.jl, an extendable symbolic system which uses dynamic multiple dispatch to change behavior depending on the domain needs. In this work we detail an underlying abstract term interface which allows for speed without sacrificing generality. We show that by formalizing a generic API on actions independent of implementation, we can retroactively add optimized data structures to our system without changing the pre-existing term rewriters. We showcase how this can be used to optimize term construction and give a 113x acceleration on general symbolic transformations. Further, we show that such a generic API allows for complementary term-rewriting implementations. Exploiting this feature, we demonstrate the ability to swap between classical term-rewriting simplifiers and e-graph-based term-rewriting simplifiers. We illustrate how this symbolic system improves numerical computing tasks by showcasing an e-graph ruleset which minimizes the number of CPU cycles during expression evaluation, and demonstrate how it simplifies a real-world reaction-network simulation to halve the runtime. Additionally, we show a reaction-diffusion partial differential equation solver which is able to be automatically converted into symbolic expressions via multiple dispatch tracing, which is subsequently accelerated and parallelized to give a 157x simulation speedup. Together, this presents Symbolics.jl as a next-generation symbolic-numeric computing environment geared towards modeling and simulation.},
journal = {ACM Commun. Comput. Algebra},
month = {jan},
pages = {92–96},
numpages = {5}
}
Symbolics.ArrayOp
— TypeArrayOp(output_idx, expr, reduce)
A tensor expression where output_idx
are the output indices expr
, is the tensor expression and reduce
is the function used to reduce over contracted dimensions.
Symbolics.Difference
— Typestruct Difference <: Symbolics.Operator
Represents a difference operator.
Fields
t
: Fixed Differencedt
update
Examples
julia> using Symbolics
julia> @variables t;
julia> Δ = Difference(t; dt=0.01)
(::Difference) (generic function with 2 methods)
Symbolics.Differential
— Typestruct Differential <: Symbolics.Operator
Represents a differential operator.
Fields
x
: The variable or expression to differentiate with respect to.
Examples
julia> using Symbolics
julia> @variables x y;
julia> D = Differential(x)
(D'~x)
julia> D(y) # Differentiate y wrt. x
(D'~x)(y)
julia> Dx = Differential(x) * Differential(y) # d^2/dxy operator
(D'~x(t)) ∘ (D'~y(t))
julia> D3 = Differential(x)^3 # 3rd order differential operator
(D'~x(t)) ∘ (D'~x(t)) ∘ (D'~x(t))
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.
Symbolics.Inequality
— Typestruct Inequality
An inequality relationship between two expressions.
Fields
lhs
: The expression on the left-hand side of the inequality.rhs
: The expression on the right-hand side of the inequality.relational_op
: The relational operator of the inequality.
Symbolics.Num
— TypeNum(val)
Wrap anything in a type that is a subtype of Real
Symbolics.SemiMonomial
— Typestruct SemiMonomial
Attrtibutes
p::Union{S, N} where {S<:SymbolicUtils.Symbolic, N<:Real}
: monomialcoeff::Any
: coefficient
Symbolics.ShardedForm
— TypeShardedForm{multithread}(cutoff, ncalls)
Split a long array construction into nested functions where each function calls ncalls
other functions, and the leaf functions populate at most cutoff
number of items in the array. If multithread
is true, uses threading.
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]
SymbolicUtils.substitute
— Functionsubstitute(expr, s)
Performs the substitution on expr
according to rule(s) s
.
Examples
julia> @variables t x y z(t)
4-element Vector{Num}:
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)
Symbolics.:≲
— Method≲(lhs, rhs) -> Any
Create an Inequality
out of two Num
instances, or an Num
and a Number
.
Examples
julia> using Symbolics
julia> @variables x y;
julia> x ≲ y
x ≲ y
julia> x - y ≲ 0
x - y ≲ 0
Symbolics.:≳
— Method≳(lhs, rhs) -> Any
Create an Inequality
out of two Num
instances, or an Num
and a Number
.
Examples
julia> using Symbolics
julia> @variables x y;
julia> x ≳ y
x ≳ y
julia> x - y ≳ 0
x - y ≳ 0
Symbolics.DiscreteUpdate
— MethodDiscreteUpdate(t; dt)
Represents a discrete update (shift) operator with the semantics
DiscreteUpdate(t; dt=0.01)(y) ~ y(t+dt)
Examples
julia> using Symbolics
julia> @variables t;
julia> U = DiscreteUpdate(t; dt=0.01)
(::Difference) (generic function with 2 methods)
Symbolics._build_function
— MethodBuild function target: CTarget
function _build_function(target::CTarget, ex::AbstractArray, args...;
columnmajor = true,
conv = toexpr,
expression = Val{true},
fname = :diffeqf,
lhsname = :du,
rhsnames = [Symbol("RHS$i") for i in 1:length(args)],
libpath = tempname(),
compiler = :gcc)
This builds an in-place C function. Only works on expressions. If expression == Val{false}
, then this builds a function in C, compiles it, and returns a lambda to that compiled function. These special keyword arguments control the compilation:
- libpath: the path to store the binary. Defaults to a temporary path.
- compiler: which C compiler to use. Defaults to :gcc, which is currently the only available option.
Symbolics._build_function
— MethodBuild function target: CTarget
function _build_function(target::CTarget, eqs::Array{<:Equation}, args...;
conv = toexpr, expression = Val{true},
fname = :diffeqf,
lhsname=:du,rhsnames=[Symbol("RHS$i") for i in 1:length(args)],
libpath=tempname(),compiler=:gcc)
This builds an in-place C function. Only works on arrays of equations. If expression == Val{false}
, then this builds a function in C, compiles it, and returns a lambda to that compiled function. These special keyword arguments control the compilation:
- libpath: the path to store the binary. Defaults to a temporary path.
- compiler: which C compiler to use. Defaults to :gcc, which is currently the only available option.
Symbolics._build_function
— MethodBuild function target: JuliaTarget
function _build_function(target::JuliaTarget, rhss, args...;
conv = toexpr, expression = Val{true},
checkbounds = false,
linenumbers = false,
headerfun = addheader, outputidxs=nothing,
convert_oop = true, force_SA = false,
skipzeros = outputidxs===nothing,
fillzeros = skipzeros && !(typeof(rhss)<:SparseMatrixCSC),
parallel=SerialForm(), kwargs...)
Generates a Julia function which can then be utilized for further evaluations. If expression=Val{false}, the return is a Julia function which utilizes RuntimeGeneratedFunctions.jl to be free of world-age issues.
If the rhss
is a scalar, the generated function is a function with a scalar output. Otherwise, if it's an AbstractArray
, the output is two functions, one for out-of-place AbstractArray output and a second which is a mutating function. The outputted functions match the given argument order, i.e., f(u,p,args...) for the out-of-place and scalar functions and f!(du,u,p,args..)
for the in-place version.
Special Keyword Arguments:
parallel
: The kind of parallelism to use in the generated function. Defaults toSerialForm()
, i.e. no parallelism. Note that the parallel forms are not exported and thus need to be chosen likeSymbolics.SerialForm()
. The choices are:SerialForm()
: Serial execution.ShardedForm(cutoff, ncalls)
: splits the output function into sub-functions which contain at mostcutoff
number of outputrhss
. These sub-functions are called by the top-level function that buildfunction returns.MultithreadedForm()
: Multithreaded execution with a static split, evenly splitting the number of expressions per thread.
conv
: The conversion function of symbolic types to Expr. By default, this uses thetoexpr
function.checkbounds
: For whether to enable bounds checking inside the generated function. Defaults to false, meaning that@inbounds
is applied.linenumbers
: Determines whether the generated function expression retains the line numbers. Defaults to true.convert_oop
: Determines whether the OOP version should try to convert the output to match the type of the first input. This is useful for cases like LabelledArrays or other array types that carry extra information. Defaults to true.force_SA
: Forces the output of the OOP version to be a StaticArray. Defaults tofalse
, and outputs a static array when the first argument is a static array.skipzeros
: Whether to skip filling zeros in the in-place version if the filling function is 0.fillzeros
: Whether to performfill(out,0)
before the calculations to ensure safety withskipzeros
.
Symbolics._build_function
— MethodBuild function target: MATLABTarget
function _build_function(target::MATLABTarget, ex::AbstractArray, args...;
columnmajor = true,
conv = toexpr,
expression = Val{true},
fname = :diffeqf,
lhsname = :internal_var___du,
rhsnames = [:internal_var___u,:internal_var___p,:internal_var___t])
This builds an out of place anonymous function @(t,rhsnames[1]) to be used in MATLAB. Compatible with the MATLAB differential equation solvers. Only allowed on expressions, and arrays of expressions.
Symbolics._build_function
— MethodBuild function target: MATLABTarget
function _build_function(target::MATLABTarget, eqs::Array{<:Equation}, args...;
conv = toexpr, expression = Val{true},
lhsname=:internal_var___du,
rhsnames=[:internal_var___u,:internal_var___p,:internal_var___t])
This builds an out of place anonymous function @(t,rhsnames[1]) to be used in MATLAB. Compatible with the MATLAB differential equation solvers. Only allowed on expressions, and arrays of expressions.
Symbolics._build_function
— MethodBuild function target: StanTarget
function _build_function(target::StanTarget, ex::AbstractArray, vs, ps, iv;
columnmajor = true,
conv = toexpr,
expression = Val{true},
fname = :diffeqf, lhsname=:internal_var___du,
rhsnames = [:internal_var___u,:internal_var___p,:internal_var___t])
This builds an in-place Stan function compatible with the Stan differential equation solvers. Unlike other build targets, this one requires (vs, ps, iv) as the function arguments. Only allowed on expressions, and arrays of expressions.
Symbolics._build_function
— MethodBuild function target: StanTarget
function _build_function(target::StanTarget, eqs::Array{<:Equation}, vs, ps, iv;
conv = toexpr, expression = Val{true},
fname = :diffeqf, lhsname=:internal_var___du,
rhsnames=[:internal_var___u,:internal_var___p,:internal_var___t])
This builds an in-place Stan function compatible with the Stan differential equation solvers. Unlike other build targets, this one requires (vs, ps, iv) as the function arguments. Only allowed on arrays of equations.
Symbolics.arrterm
— Methodarrterm(f, args...; arrayop=nothing)
Create a term of Term{<: AbstractArray}
which is the representation of f(args...)
.
- Calls
propagate_atype(f, args...)
to determine the container type, i.e.Array
orStaticArray
etc. - Calls
propagate_eltype(f, args...)
to determine the output element type. - Calls
propagate_ndims(f, args...)
to determine the output dimension. - Calls
propagate_shape(f, args...)
to determine the output array shape.
propagate_shape
, propagate_atype
, propagate_eltype
may return Unknown()
to say that the output cannot be determined
But propagate_ndims
must work and return a non-negative integer.
Symbolics.build_function
— Methodbuild_function
Generates a numerically-usable function from a Symbolics Num
.
build_function(ex, args...;
expression = Val{true},
target = JuliaTarget(),
parallel=nothing,
kwargs...)
Arguments:
ex
: TheNum
to compileargs
: The arguments of the functionexpression
: Whether to generate code or whether to generate the compiled form. By default,expression = Val{true}
, which means that the code for the function is returned. IfVal{false}
, then the returned value is compiled.
Keyword Arguments:
target
: The output target of the compilation process. Possible options are:JuliaTarget
: Generates a Julia functionCTarget
: Generates a C functionStanTarget
: Generates a function for compiling with the Stan probabilistic programming languageMATLABTarget
: Generates an anonymous function for use in MATLAB and Octave environments
parallel
: The kind of parallelism to use in the generated function. Defaults toSerialForm()
, i.e. no parallelism, ifex
is a single expression or an array containing <= 1500 non-zero expressions. Ifex
is an array of > 1500 non-zero expressions, thenShardedForm(80, 4)
is used. See below for more onShardedForm
. Note that the parallel forms are not exported and thus need to be chosen likeSymbolics.SerialForm()
. The choices are:SerialForm()
: Serial execution.ShardedForm(cutoff, ncalls)
: splits the output function into sub-functions which contain at mostcutoff
number of outputrhss
. These sub-functions are called by the top-level function that buildfunction returns. This helps in reducing the compile time of the generated function.MultithreadedForm()
: Multithreaded execution with a static split, evenly splitting the number of expressions per thread.
fname
: Used by some targets for the name of the function in the target space.
Note that not all build targets support the full compilation interface. Check the individual target documentation for details.
Symbolics.coeff
— Functioncoeff(p, sym=nothing)
Extract the coefficient of p
with respect to sym
. Note that p
might need to be expanded and/or simplified with expand
and/or simplify
.
Examples
julia> @variables a x y;
julia> Symbolics.coeff(2a, x)
0
julia> Symbolics.coeff(3x + 2y, y)
2
julia> Symbolics.coeff(x^2 + y, x^2)
1
Symbolics.degree
— Functiondegree(p, sym=nothing)
Extract the degree of p
with respect to sym
.
Examples
julia> @variables x;
julia> Symbolics.degree(x^0)
0
julia> Symbolics.degree(x)
1
julia> Symbolics.degree(x^2)
2
Symbolics.derivative
— Methodderivative(O, v; simplify)
A helper function for computing the derivative of an expression with respect to var
.
Symbolics.derivative_idx
— Methodderivative_idx(O, _)
Calculate the derivative of the op O
with respect to its argument with index idx
.
Examples
julia> using Symbolics
julia> @variables x y;
julia> Symbolics.derivative_idx(Symbolics.value(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 = Symbolics.value(sin(x) * y^2)
sin(x)*(y^2)
julia> typeof(Symbolics.operation(myop)) # Op is multiplication function
typeof(*)
julia> Symbolics.derivative_idx(myop, 1) # wrt. sin(x)
y^2
julia> Symbolics.derivative_idx(myop, 2) # wrt. y^2
sin(x)
Symbolics.diff2term
— Methoddiff2term(x) -> Symbolic
Convert a differential variable to a Term
. Note that it only takes a Term
not a Num
.
julia> @variables x t u(x, t) z(t)[1:2]; Dt = Differential(t); Dx = Differential(x);
julia> Symbolics.diff2term(Symbolics.value(Dx(Dt(u))))
uˍtx(x, t)
julia> Symbolics.diff2term(Symbolics.value(Dt(z[1])))
var"z(t)[1]ˍt"
Symbolics.expand_derivatives
— Functionexpand_derivatives(O)
expand_derivatives(O, simplify; occurrences)
TODO
Examples
julia> @variables x y z k;
julia> f=k*(abs(x-y)/y-z)^2
k*((abs(x - y) / y - z)^2)
julia> Dx=Differential(x) # Differentiate wrt x
(::Differential) (generic function with 2 methods)
julia> dfx=expand_derivatives(Dx(f))
(k*((2abs(x - y)) / y - 2z)*IfElse.ifelse(signbit(x - y), -1, 1)) / y
Symbolics.exprs_occur_in
— Methodexprs_occur_in(exprs::Vector, expr)
Return an array of booleans finds
where finds[i]
is true if exprs[i]
occurs in expr
false otherwise.
Symbolics.get_variables
— Functionget_variables(O) -> Vector{BasicSymbolic}
Returns the variables in the expression. Note that the returned variables are not wrapped in the Num
type.
Examples
julia> @variables t x y z(t)
4-element Vector{Num}:
t
x
y
z(t)
julia> ex = x + y + sin(z)
(x + y) + sin(z(t))
julia> Symbolics.get_variables(ex)
3-element Vector{Any}:
x
y
z(t)
Symbolics.gradient
— Methodgradient(O, vars; simplify)
A helper function for computing the gradient of an expression with respect to an array of variable expressions.
Symbolics.groebner_basis
— Methodgroebner_basis(polynomials)
Computes a Groebner basis of the ideal generated by the given polynomials
. The basis is reduced, thus guaranteed to be unique.
Example
julia> using Symbolics
julia> @variables x y;
julia> groebner_basis([x*y^2 + x, x^2*y + y])
The coefficients in the resulting basis are in the same domain as for input polynomials. Hence, if the coefficient becomes too large to be represented exactly, DomainError
is thrown.
The algorithm is randomized, so the basis will be correct with high probability.
Symbolics.has_vars
— Methodhas_vars(expr, vars) -> Bool
Return true if expr
contains any variables in vars
.
Symbolics.hasderiv
— Methodhasderiv(O)
Returns true if the expression or equation O
contains Differential
terms.
Symbolics.hasdiff
— Methodhasdiff(O)
Returns true if the expression or equation O
contains Difference
terms (this include DiscreteUpdate
).
Symbolics.hessian
— Methodhessian(O, vars; simplify)
A helper function for computing the Hessian of an expression with respect to an array of variable expressions.
Symbolics.hessian_sparsity
— Methodhessian_sparsity(
expr,
vars::AbstractVector;
full
) -> Union{SparseArrays.SparseMatrixCSC{Bool, Int64}, SparseArrays.SparseMatrixCSC{Int64, Int64}}
Return the sparsity pattern of the Hessian of an expression with respect to an array of variable expressions.
Arguments
expr
: a symbolic expression.vars
: a vector of symbolic variables.
Examples
julia> using Symbolics
julia> vars = @variables x₁ x₂;
julia> expr = 3x₁^2 + 4x₁ * x₂;
julia> Symbolics.hessian_sparsity(expr, vars)
2×2 SparseArrays.SparseMatrixCSC{Bool, Int64} with 3 stored entries:
1 1
1 ⋅
Symbolics.hessian_sparsity
— Methodhessian_sparsity(
f::Function,
input::AbstractVector,
args...;
full,
kwargs...
) -> Union{SparseArrays.SparseMatrixCSC{Bool, Int64}, SparseArrays.SparseMatrixCSC{Int64, Int64}}
Return the sparsity pattern of the Hessian of the given function f
.
Arguments
f
: an out-of-place functionf(input, args...; kwargs...)
.input
: a vector of input values whose eltype can be either symbolic or primitive.
Examples
julia> using Symbolics
julia> f(x) = 4x[1] * x[2] - 5x[2]^2;
julia> input = Vector{Float64}(undef, 2);
julia> Symbolics.hessian_sparsity(f, input)
2×2 SparseArrays.SparseMatrixCSC{Bool, Int64} with 3 stored entries:
⋅ 1
1 1
Symbolics.is_groebner_basis
— MethodDo not document this for now.
isgroebnerbasis(polynomials)
Checks whether the given polynomials
forms a Groebner basis.
Example
julia> using Symbolics
julia> @variables x y;
julia> is_groebner_basis([x^2 - y^2, x*y^2 + x, y^3 + y])
Symbolics.isaffine
— Methodisaffine(ex, u)
Check if an expression is affine with respect to a list of variable expressions.
Symbolics.islinear
— Methodislinear(ex, u)
Check if an expression is linear with respect to a list of variable expressions.
Symbolics.jacobian
— Methodjacobian(ops, vars; simplify, scalarize)
A helper function for computing the Jacobian of an array of expressions with respect to an array of variable expressions.
Symbolics.jacobian_sparsity
— Methodjacobian_sparsity(
exprs::AbstractArray,
vars::AbstractArray
) -> SparseArrays.SparseMatrixCSC{Bool, Int64}
Return the sparsity pattern of the Jacobian of an array of expressions with respect to an array of variable expressions.
Arguments
exprs
: an array of symbolic expressions.vars
: an array of symbolic variables.
Examples
julia> using Symbolics
julia> vars = @variables x₁ x₂;
julia> exprs = [2x₁, 3x₂, 4x₁ * x₂];
julia> Symbolics.jacobian_sparsity(exprs, vars)
3×2 SparseArrays.SparseMatrixCSC{Bool, Int64} with 4 stored entries:
1 ⋅
⋅ 1
1 1
Symbolics.jacobian_sparsity
— Methodjacobian_sparsity(
f!::Function,
output::AbstractArray,
input::AbstractArray,
args...;
kwargs...
) -> SparseArrays.SparseMatrixCSC{Bool, Int64}
Return the sparsity pattern of the Jacobian of the mutating function f!
.
Arguments
f!
: an in-place functionf!(output, input, args...; kwargs...)
.output
: output array.input
: input array.
The eltype of output
and input
can be either symbolic or primitive.
Examples
julia> using Symbolics
julia> f!(y, x) = y .= [x[2], 2x[1], 3x[1] * x[2]];
julia> output = Vector{Float64}(undef, 3);
julia> input = Vector{Float64}(undef, 2);
julia> Symbolics.jacobian_sparsity(f!, output, input)
3×2 SparseArrays.SparseMatrixCSC{Bool, Int64} with 4 stored entries:
⋅ 1
1 ⋅
1 1
Symbolics.linear_expansion
— Method(a, b, islinear) = linear_expansion(t, x)
When islinear
, return a
and b
such that a * x + b == t
.
Symbolics.parse_expr_to_symbolic
— Functionparse_expr_to_symbolic(ex, mod::Module)
Applies the parse_expr_to_symbolic
function in the current module, i.e. parse_expr_to_symbolic(ex, mod)
where mod
is the module of the function caller.
Arguments
ex
: the expression to parsemod
: the module to apply the parsing in. See the limitations section for details.
Example
ex = :(y(t) ~ x(t))
parse_expr_to_symbolic(ex,Main) # gives the symbolic expression `y(t) ~ x(t)` in empty Main
# Now do a whole system
ex = [:(y ~ x)
:(y ~ -2x + 3 / z)
:(z ~ 2)]
eqs = parse_expr_to_symbolic.(ex, (Main,))
@variables x y z
ex = [y ~ x
y ~ -2x + 3 / z
z ~ 2]
all(isequal.(eqs,ex)) # true
Limitations
Symbolic-ness Tied to Environment Definitions
The parsing to a symbolic expression has to be able to recognize the difference between functions, numbers, and globals defined within one's Julia environment and those that are to be made symbolic. The way this functionality handles this problem is that it does not define anything as symbolic that is already defined in the chosen mod
module. For example, f(x,y)
will have f
as non-symbolic if the function f
(named f
) is defined in mod
, i.e. if isdefined(mod,:f)
is true. When the symbol is defined, it will be replaced by its value. Notably, this means that the parsing behavior changes depending on the environment that it is applied.
For example:
parse_expr_to_symbolic(:(x - y),@__MODULE__) # x - y
x = 2.0
parse_expr_to_symbolic(:(x - y),@__MODULE__) # 2.0 - y
This is required to detect that standard functions like -
are functions instead of symbolic symbols. For safety, one should create anonymous modules or other sub-environments to ensure no stray variables are defined.
Metadata is Blank
Because all the variables defined by the expressions are not defined with the standard @variables
, there is no metadata that is or can be associated with any of the generated variables. Instead, they all have blank metadata, but are defined in the Real
domain. Thus, the variables which come out of this parsing may not evaluate as equal to a symbolic variable defined elsewhere.
Symbolics.polynomial_coeffs
— Methodpolynomial_coeffs(expr, vars)
Find coefficients of a polynomial in vars
.
Returns a tuple of two elements:
- A dictionary of coefficients keyed by monomials in
vars
- A residual expression which is the constant term
(Same as semipolynomial_form(expr, vars, Inf)
)
Symbolics.recursive_hasoperator
— MethodSymbolics.semilinear_form
— Methodsemilinear_form(
exprs::AbstractArray,
vars
) -> Tuple{SparseArrays.SparseMatrixCSC{Num, Int64}, Any}
Returns a tuple of a sparse matrix A
, and a residual vector c
such that, A * vars + c
is the same as exprs
.
Symbolics.semipolynomial_form
— Methodsemipolynomial_form(
exprs::AbstractArray,
vars,
degree::Real;
consts
) -> Tuple{Any, Any}
For every expression in exprs
computes the semi-polynomial form and returns a tuple of two objects – a vector of coefficient dictionaries, and a vector of residual terms.
If consts
is set to true
, then the returned dictionary will contain a key 1
and the corresponding value will be the constant term. If false
, the constant term will be part of the residual.
Symbolics.semipolynomial_form
— Methodsemipolynomial_form(
expr,
vars,
degree::Real;
consts
) -> Tuple{Any, Any}
Returns a tuple of two objects:
- A dictionary of coefficients keyed by monomials in
vars
upto the givendegree
, - A residual expression which has all terms not represented as a product of monomial and a coefficient
degree
should be a nonnegative number.
If consts
is set to true
, then the returned dictionary will contain a key 1
and the corresponding value will be the constant term. If false
, the constant term will be part of the residual.
Symbolics.semiquadratic_form
— Methodsemiquadratic_form(
exprs,
vars
) -> Tuple{SparseArrays.SparseMatrixCSC{Num, Int64}, SparseArrays.SparseMatrixCSC{Num, Int64}, Any, Any}
Returns a tuple of 4 objects:
- a matrix
A
of dimensions (m x n) - a matrix
B
of dimensions (m x (n+1)*n/2) - a vector
v2
of length (n+1)*n/2 containing monomials ofvars
upto degree 2 and zero where they are not required. - a residual vector
c
of length m.
where n == length(exprs)
and m == length(vars)
.
The result is arranged such that, A * vars + B * v2 + c
is the same as exprs
.
Symbolics.shape
— Methodshape(s::Any)
Returns axes(s)
or throws.
Symbolics.shape
— Methodshape(s::SymArray)
Extract the shape metadata from a SymArray. If not known, returns Unknown()
Symbolics.solve_for
— Methodsolve_for(eq, var; simplify, check) -> Any
Solve equation(s) eqs
for a set of variables vars
.
Assumes length(eqs) == length(vars)
Currently only works if all equations are linear. check
if the expr is linear w.r.t vars
.
Examples
julia> @variables x y
2-element Vector{Num}:
x
y
julia> Symbolics.solve_for(x + y ~ 0, x)
-y
julia> Symbolics.solve_for([x + y ~ 0, x - y ~ 2], [x, y])
2-element Vector{Float64}:
1.0
-1.0
Symbolics.sparsehessian
— Methodsparsehessian(op, vars; simplify, full)
A helper function for computing the sparse Hessian of an expression with respect to an array of variable expressions.
Symbolics.sparsehessian_vals
— Methodsparsehessian_vals(op, vars, I, J; simplify)
A helper function for computing the values of the sparse Hessian of an expression with respect to an array of variable expressions given the sparsity structure.
Symbolics.sparsejacobian
— Methodsparsejacobian(ops, vars; simplify)
A helper function for computing the sparse Jacobian of an array of expressions with respect to an array of variable expressions.
Symbolics.sparsejacobian_vals
— Methodsparsejacobian_vals(ops, vars, I, J; simplify)
A helper function for computing the values of the sparse Jacobian of an array of expressions with respect to an array of variable expressions given the sparsity structure.
Symbolics.tosymbol
— Methodtosymbol(x::Union{Num,Symbolic}; states=nothing, escape=true) -> Symbol
Convert x
to a symbol. states
are the states of a system, and escape
means if the target has escapes like val"y(t)"
. If escape
is false, then it will only output y
instead of y(t)
.
Examples
julia> @variables t z(t)
2-element Vector{Num}:
t
z(t)
julia> Symbolics.tosymbol(z)
Symbol("z(t)")
julia> Symbolics.tosymbol(z; escape=false)
:z
Symbolics.variable
— Methodvariable(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
— Methodvariables(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.@derivatives
— MacroDefine one or more differentials.
Examples
julia> using Symbolics
julia> @variables x y z;
julia> Dx = Differential(x); Dy = Differential(y); # Create differentials wrt. x and y
julia> Dx(z) # Differentiate z wrt. x
Differential(x)(z)
julia> Dy(z) # Differentiate z wrt. y
Differential(y)(z)
Symbolics.@parse_expr_to_symbolic
— Macro@parse_expr_to_symbolic ex
Applies the parse_expr_to_symbolic
function in the current module, i.e. parse_expr_to_symbolic(ex, mod)
where mod
is the module of the function caller.
Arguments
ex
: the expression to parse
Example
ex = :(y(t) ~ x(t))
@parse_expr_to_symbolic ex # gives the symbolic expression `y(t) ~ x(t)`
Limitations
The same limitations apply as for the function parse_expr_to_symbolic
. See its docstring for more details.
Symbolics.@register_symbolic
— Macro@register_symbolic(expr, define_promotion = true, Ts = [Num, Symbolic, Real])
Overload appropriate methods so that Symbolics can stop tracing into the registered function. If define_promotion
is true, then a promotion method in the form of
SymbolicUtils.promote_symtype(::typeof(f_registered), args...) = Real # or the annotated return type
is defined for the register function. Note that when defining multiple register overloads for one function, all the rest of the registers must set define_promotion
to false
except for the first one, to avoid method overwriting.
Examples
@register_symbolic foo(x, y)
@register_symbolic foo(x, y::Bool) false # do not overload a duplicate promotion rule
@register_symbolic goo(x, y::Int) # `y` is not overloaded to take symbolic objects
@register_symbolic hoo(x, y)::Int # `hoo` returns `Int`
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)