Symbolics.SymbolicsModule

Symbolics.jl

Github Action CI codecov Build Status Stable Dev

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.ArrayOpType
ArrayOp(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.DifferenceType
struct Difference <: Symbolics.Operator

Represents a difference operator.

Fields

  • t: Fixed Difference

  • dt

  • update

Examples

julia> using Symbolics

julia> @variables t;

julia> Δ = Difference(t; dt=0.01)
(::Difference) (generic function with 2 methods)
Symbolics.DifferentialType
struct 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.EquationType
struct 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.InequalityType
struct 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.NumType
Num(val)

Wrap anything in a type that is a subtype of Real

Symbolics.SemiMonomialType
struct SemiMonomial

Attrtibutes

  • p::Union{S, N} where {S<:SymbolicUtils.Symbolic, N<:Real}: monomial

  • coeff::Any: coefficient

Symbolics.ShardedFormType
ShardedForm{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.substituteFunction
substitute(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.DiscreteUpdateMethod
DiscreteUpdate(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_functionMethod

Build 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_functionMethod

Build 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_functionMethod

Build 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 to SerialForm(), i.e. no parallelism. Note that the parallel forms are not exported and thus need to be chosen like Symbolics.SerialForm(). The choices are:
    • SerialForm(): Serial execution.
    • ShardedForm(cutoff, ncalls): splits the output function into sub-functions which contain at most cutoff number of output rhss. 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 the toexpr 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 to false, 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 perform fill(out,0) before the calculations to ensure safety with skipzeros.
Symbolics._build_functionMethod

Build 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_functionMethod

Build 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_functionMethod

Build 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_functionMethod

Build 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.arrtermMethod
arrterm(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 or StaticArray 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_functionMethod

build_function

Generates a numerically-usable function from a Symbolics Num.

build_function(ex, args...;
               expression = Val{true},
               target = JuliaTarget(),
               parallel=nothing,
               kwargs...)

Arguments:

  • ex: The Num to compile
  • args: The arguments of the function
  • expression: 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. If Val{false}, then the returned value is compiled.

Keyword Arguments:

  • target: The output target of the compilation process. Possible options are:
    • JuliaTarget: Generates a Julia function
    • CTarget: Generates a C function
    • StanTarget: Generates a function for compiling with the Stan probabilistic programming language
    • MATLABTarget: Generates an anonymous function for use in MATLAB and Octave environments
  • parallel: The kind of parallelism to use in the generated function. Defaults to SerialForm(), i.e. no parallelism, if ex is a single expression or an array containing <= 1500 non-zero expressions. If ex is an array of > 1500 non-zero expressions, then ShardedForm(80, 4) is used. See below for more on ShardedForm. Note that the parallel forms are not exported and thus need to be chosen like Symbolics.SerialForm(). The choices are:
    • SerialForm(): Serial execution.
    • ShardedForm(cutoff, ncalls): splits the output function into sub-functions which contain at most cutoff number of output rhss. 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.coeffFunction
coeff(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.degreeFunction
degree(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.derivativeMethod
derivative(O, v; simplify)

A helper function for computing the derivative of an expression with respect to var.

Symbolics.derivative_idxMethod
derivative_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.diff2termMethod
diff2term(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_derivativesFunction
expand_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_inMethod
exprs_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_variablesFunction
get_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.gradientMethod
gradient(O, vars; simplify)

A helper function for computing the gradient of an expression with respect to an array of variable expressions.

Symbolics.groebner_basisMethod

groebner_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_varsMethod
has_vars(expr, vars) -> Bool

Return true if expr contains any variables in vars.

Symbolics.hessianMethod
hessian(O, vars; simplify)

A helper function for computing the Hessian of an expression with respect to an array of variable expressions.

Symbolics.hessian_sparsityMethod
hessian_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_sparsityMethod
hessian_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 function f(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_basisMethod

Do 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.isaffineMethod
isaffine(ex, u)

Check if an expression is affine with respect to a list of variable expressions.

Symbolics.islinearMethod
islinear(ex, u)

Check if an expression is linear with respect to a list of variable expressions.

Symbolics.jacobianMethod
jacobian(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_sparsityMethod
jacobian_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_sparsityMethod
jacobian_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 function f!(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_expansionMethod
(a, b, islinear) = linear_expansion(t, x)

When islinear, return a and b such that a * x + b == t.

Symbolics.parse_expr_to_symbolicFunction
parse_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 parse
  • mod: 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_coeffsMethod
polynomial_coeffs(expr, vars)

Find coefficients of a polynomial in vars.

Returns a tuple of two elements:

  1. A dictionary of coefficients keyed by monomials in vars
  2. A residual expression which is the constant term

(Same as semipolynomial_form(expr, vars, Inf))

Symbolics.semilinear_formMethod
semilinear_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_formMethod
semipolynomial_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_formMethod
semipolynomial_form(
    expr,
    vars,
    degree::Real;
    consts
) -> Tuple{Any, Any}

Returns a tuple of two objects:

  1. A dictionary of coefficients keyed by monomials in vars upto the given degree,
  2. 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_formMethod
semiquadratic_form(
    exprs,
    vars
) -> Tuple{SparseArrays.SparseMatrixCSC{Num, Int64}, SparseArrays.SparseMatrixCSC{Num, Int64}, Any, Any}

Returns a tuple of 4 objects:

  1. a matrix A of dimensions (m x n)
  2. a matrix B of dimensions (m x (n+1)*n/2)
  3. a vector v2 of length (n+1)*n/2 containing monomials of vars upto degree 2 and zero where they are not required.
  4. 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.shapeMethod
shape(s::SymArray)

Extract the shape metadata from a SymArray. If not known, returns Unknown()

Symbolics.solve_forMethod
solve_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.sparsehessianMethod
sparsehessian(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_valsMethod
sparsehessian_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.sparsejacobianMethod
sparsejacobian(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_valsMethod
sparsejacobian_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.tosymbolMethod
tosymbol(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.variableMethod
variable(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.variablesMethod
variables(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.@derivativesMacro

Define 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_symbolicMacro
@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_symbolicMacro
@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.@variablesMacro

Define 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)