API Reference

Symbols and Terms

Creating Symbols and Terms

SymbolicUtils.@symsMacro
@syms <lhs_expr>[::T1] <lhs_expr>[::T2]...

For instance:

@syms foo::Real bar baz(x, y::Real)::Complex

Create one or more variables. <lhs_expr> can be just a symbol in which case it will be the name of the variable, or a function call in which case a function-like variable which has the same name as the function being called. The Sym type, or in the case of a function-like Sym, the output type of calling the function can be set using the ::T syntax.

Examples:

  • @syms foo bar::Real baz::Int will create

variable foo of symtype Number (the default), bar of symtype Real and baz of symtype Int

  • @syms f(x) g(y::Real, x)::Int h(a::Int, f(b)) creates 1-arg f 2-arg g

and 2 arg h. The second argument to h must be a one argument function-like variable. So, h(1, g) will fail and h(1, f) will work.

source
SymbolicUtils.termFunction
term(f, args...; type = nothing)

Create a symbolic term with operation f and arguments args.

Arguments

  • f: The operation or function head of the term
  • args...: The arguments to the operation
  • type: Optional type specification for the term. If not provided, the type is inferred using promote_symtype.

Examples

julia> @syms x y
(x, y)

julia> term(+, x, y)
x + y

julia> term(sin, x)
sin(x)

julia> term(^, x, 2)
x^2
source
Missing docstring.

Missing docstring for SymbolicUtils.Sym. Check Documenter's build log for details.

Inspecting Terms

SymbolicUtils.issymFunction
issym(x)

Returns true if x is a Sym. If true, nameof must be defined on x and must return a Symbol.

source
SymbolicUtils.symtypeFunction
symtype(x)

Returns the numeric type of x. By default this is just typeof(x). Define this for your symbolic types if you want SymbolicUtils.simplify to apply rules specific to numbers (such as commutativity of multiplication). Or such rules that may be implemented in the future.

source
Missing docstring.

Missing docstring for SymbolicUtils.iscall. Check Documenter's build log for details.

Missing docstring.

Missing docstring for SymbolicUtils.operation. Check Documenter's build log for details.

Missing docstring.

Missing docstring for SymbolicUtils.arguments. Check Documenter's build log for details.

TermInterface.sorted_argumentsFunction
sorted_arguments(x::BasicSymbolic)

Get the arguments of a symbolic expression in canonical sorted order.

For commutative operations like addition and multiplication, the arguments are sorted according to a canonical ordering. This ensures that equivalent expressions have the same representation.

Arguments

  • x::BasicSymbolic: The symbolic expression

Returns

A vector of the arguments in sorted order. For non-commutative operations, returns the arguments in their original order.

Examples

julia> @syms x y z
(x, y, z)

julia> expr = x + z + y
x + y + z

julia> sorted_arguments(expr)
3-element Vector{Any}:
 x
 y
 z
source
SymbolicUtils.showrawFunction
showraw([io::IO], t)

Display the raw structure of a symbolic expression without simplification.

This function shows the internal structure of symbolic expressions without applying any simplification rules, which is useful for debugging and understanding the exact form of an expression.

Arguments

  • io::IO: Optional IO stream to write to (defaults to stdout)
  • t: The symbolic expression to display

Examples

julia> @syms x
x

julia> expr = x + x + x
3x

julia> showraw(expr)  # Shows the unsimplified structure
x + x + x
source

Term Types

Missing docstring.

Missing docstring for SymbolicUtils.Term. Check Documenter's build log for details.

Missing docstring.

Missing docstring for SymbolicUtils.Add. Check Documenter's build log for details.

Missing docstring.

Missing docstring for SymbolicUtils.Mul. Check Documenter's build log for details.

Missing docstring.

Missing docstring for SymbolicUtils.Pow. Check Documenter's build log for details.

Metadata

SymbolicUtils.hasmetadataFunction
hasmetadata(s::Symbolic, ctx)

Check if a symbolic expression has metadata for a given context.

Arguments

  • s::Symbolic: The symbolic expression to check
  • ctx: The metadata context key (typically a DataType)

Returns

  • true if the expression has metadata for the given context, false otherwise

Examples

julia> @syms x
x

julia> hasmetadata(x, Float64)
false
source
SymbolicUtils.getmetadataFunction
getmetadata(s::Symbolic, ctx)

Retrieve metadata associated with a symbolic expression for a given context.

Arguments

  • s::Symbolic: The symbolic expression
  • ctx: The metadata context key (typically a DataType)

Returns

The metadata value associated with the given context

Throws

  • ArgumentError if the expression does not have metadata for the given context

Examples

julia> @syms x::Float64
x

julia> getmetadata(x, symtype)  # Get the type metadata
Float64
source
getmetadata(s::Symbolic, ctx, default)

Retrieve metadata associated with a symbolic expression for a given context, returning a default value if not found.

Arguments

  • s::Symbolic: The symbolic expression
  • ctx: The metadata context key (typically a DataType)
  • default: The default value to return if metadata is not found

Returns

The metadata value associated with the given context, or default if not found

Examples

julia> @syms x
x

julia> getmetadata(x, Float64, "no type")
"no type"
source
SymbolicUtils.setmetadataFunction
setmetadata(s::Symbolic, ctx::DataType, val)

Set metadata for a symbolic expression in a given context.

Arguments

  • s::Symbolic: The symbolic expression
  • ctx::DataType: The metadata context key
  • val: The metadata value to set

Returns

A new symbolic expression with the updated metadata

Examples

julia> @syms x
x

julia> x_with_meta = setmetadata(x, Float64, "custom value")
x

julia> getmetadata(x_with_meta, Float64)
"custom value"
source

Type Promotion

SymbolicUtils.promote_symtypeFunction
promote_symtype(f, Ts...)

The result of applying f to arguments of symtypeTs...

julia> promote_symtype(+, Real, Real)
Real

julia> promote_symtype(+, Complex, Real)
Number

julia> @syms f(x)::Complex
(f(::Number)::Complex,)

julia> promote_symtype(f, Number)
Complex

When constructing Terms without an explicit symtype, promote_symtype is used to figure out the symtype of the Term.

source
promote_symtype(f::FnType{X,Y}, arg_symtypes...)

The output symtype of applying variable f to arguments of symtype arg_symtypes.... if the arguments are of the wrong type then this function will error.

source

Rewriting System

Rule Creation

SymbolicUtils.@ruleMacro
@rule LHS => RHS

Creates a Rule object. A rule object is callable, and takes an expression and rewrites it if it matches the LHS pattern to the RHS pattern, returns nothing otherwise. The rule language is described below.

LHS can be any possibly nested function call expression where any of the arguments can optionally be a Slot (~x) or a Segment (~~x) (described below).

If an expression matches LHS entirely, then it is rewritten to the pattern in the RHS Segment (~x) and slot variables (~~x) on the RHS will substitute the result of the matches found for these variables in the LHS.

Slot:

A Slot variable is written as ~x and matches a single expression. x is the name of the variable. If a slot appears more than once in an LHS expression then expression matched at every such location must be equal (as shown by isequal).

Example:

Simple rule to turn any sin into cos:

julia> @syms a b c
(a, b, c)

julia> r = @rule sin(~x) => cos(~x)
sin(~x) => cos(~x)

julia> r(sin(1+a))
cos((1 + a))

A rule with 2 segment variables

julia> r = @rule sin(~x + ~y) => sin(~x)*cos(~y) + cos(~x)*sin(~y)
sin(~x + ~y) => sin(~x) * cos(~y) + cos(~x) * sin(~y)

julia> r(sin(a + b))
cos(a)*sin(b) + sin(a)*cos(b)

A rule that matches two of the same expressions:

julia> r = @rule sin(~x)^2 + cos(~x)^2 => 1
sin(~x) ^ 2 + cos(~x) ^ 2 => 1

julia> r(sin(2a)^2 + cos(2a)^2)
1

julia> r(sin(2a)^2 + cos(a)^2)
# nothing

Segment:

A Segment variable is written as ~~x and matches zero or more expressions in the function call.

Example:

This implements the distributive property of multiplication: +(~~ys) matches expressions like a + b, a+b+c and so on. On the RHS ~~ys presents as any old julia array.

julia> r = @rule ~x * +((~~ys)) => sum(map(y-> ~x * y, ~~ys));

julia> r(2 * (a+b+c))
((2 * a) + (2 * b) + (2 * c))

Predicates:

There are two kinds of predicates, namely over slot variables and over the whole rule. For the former, predicates can be used on both ~x and ~~x by using the ~x::f or ~~x::f. Here f can be any julia function. In the case of a slot the function gets a single matched subexpression, in the case of segment, it gets an array of matched expressions.

The predicate should return true if the current match is acceptable, and false otherwise.

julia> two_πs(x::Number) = abs(round(x/(2π)) - x/(2π)) < 10^-9
two_πs (generic function with 1 method)

julia> two_πs(x) = false
two_πs (generic function with 2 methods)

julia> r = @rule sin(~~x + ~y::two_πs + ~~z) => sin(+(~~x..., ~~z...))
sin(~(~x) + ~(y::two_πs) + ~(~z)) => sin(+(~(~x)..., ~(~z)...))

julia> r(sin(a+3π))

julia> r(sin(a+6π))
sin(a)

julia> r(sin(a+6π+c))
sin((a + c))

Predicate function gets an array of values if attached to a segment variable (~~x).

For the predicate over the whole rule, use @rule <LHS> => <RHS> where <predicate>:

julia> @syms a b;

julia> predicate(x) = x === a;

julia> r = @rule ~x => ~x where predicate(~x);

julia> r(a)
a

julia> r(b) === nothing
true

Note that this is syntactic sugar and that it is the same as something like @rule ~x => f(~x) ? ~x : nothing.

Context:

In predicates: Contextual predicates are functions wrapped in the Contextual type. The function is called with 2 arguments: the expression and a context object passed during a call to the Rule object (maybe done by passing a context to simplify or a RuleSet object).

The function can use the inputs however it wants, and must return a boolean indicating whether the predicate holds or not.

In the consequent pattern: Use (@ctx) to access the context object on the right hand side of an expression.

source
SymbolicUtils.@acruleMacro
@acrule(lhs => rhs)

Create an associative-commutative rule that matches all permutations of the arguments.

This macro creates a rule that can match patterns regardless of the order of arguments in associative and commutative operations like addition and multiplication.

Arguments

  • lhs: The pattern to match (left-hand side)
  • rhs: The replacement expression (right-hand side)

Examples

julia> @syms x y z
(x, y, z)

julia> r = @acrule x + y => 2x  # Matches both x + y and y + x
ACRule(x + y => 2x)

julia> r(x + y)
2x

julia> r(y + x)
2x

See also: @rule, @ordered_acrule

source

Rewriters

SymbolicUtils.RewritersModule

A rewriter is any function which takes an expression and returns an expression or nothing. If nothing is returned that means there was no changes applicable to the input expression.

The Rewriters module contains some types which create and transform rewriters.

  • Empty() is a rewriter which always returns nothing
  • Chain(itr) chain an iterator of rewriters into a single rewriter which applies each chained rewriter in the given order. If a rewriter returns nothing this is treated as a no-change.
  • RestartedChain(itr) like Chain(itr) but restarts from the first rewriter once on the first successful application of one of the chained rewriters.
  • IfElse(cond, rw1, rw2) runs the cond function on the input, applies rw1 if cond returns true, rw2 if it returns false
  • If(cond, rw) is the same as IfElse(cond, rw, Empty())
  • Prewalk(rw; threaded=false, thread_cutoff=100) returns a rewriter which does a pre-order traversal of a given expression and applies the rewriter rw. Note that if rw returns nothing when a match is not found, then Prewalk(rw) will also return nothing unless a match is found at every level of the walk. threaded=true will use multi threading for traversal. thread_cutoff is the minimum number of nodes in a subtree which should be walked in a threaded spawn.
  • Postwalk(rw; threaded=false, thread_cutoff=100) similarly does post-order traversal.
  • Fixpoint(rw) returns a rewriter which applies rw repeatedly until there are no changes to be made.
  • FixpointNoCycle behaves like Fixpoint but instead it applies rw repeatedly only while it is returning new results.
  • PassThrough(rw) returns a rewriter which if rw(x) returns nothing will instead return x otherwise will return rw(x).
source
SymbolicUtils.Rewriters.EmptyType
Empty()

A rewriter that always returns nothing, indicating no rewrite occurred.

This is useful as a placeholder or for conditional rewriting patterns.

Examples

julia> Empty()(x)
nothing
source
SymbolicUtils.Rewriters.IfElseType
IfElse(cond, yes, no)

A conditional rewriter that applies yes if cond(x) is true, otherwise applies no.

Arguments

  • cond: A function that returns true or false for the input
  • yes: The rewriter to apply if the condition is true
  • no: The rewriter to apply if the condition is false

Examples

julia> r = IfElse(x -> x > 0, x -> -x, x -> x)
julia> r(5)  # Returns -5
julia> r(-3) # Returns -3

See also: If

source
SymbolicUtils.Rewriters.IfFunction
If(cond, yes)

A conditional rewriter that applies yes if cond(x) is true, otherwise returns the input unchanged.

This is equivalent to IfElse(cond, yes, Empty()).

Arguments

  • cond: A function that returns true or false for the input
  • yes: The rewriter to apply if the condition is true

Examples

julia> r = If(x -> x > 0, x -> -x)
julia> r(5)  # Returns -5
julia> r(-3) # Returns -3 (unchanged)
source
SymbolicUtils.Rewriters.ChainType
Chain(rws; stop_on_match=false)

Apply a sequence of rewriters to an expression, chaining the results.

Each rewriter in the chain receives the result of the previous rewriter. If a rewriter returns nothing, the input is passed unchanged to the next rewriter.

Arguments

  • rws: A collection of rewriters to apply in sequence
  • stop_on_match: If true, stop at the first rewriter that produces a change

Examples

julia> r1 = @rule sin(~x)^2 + cos(~x)^2 => 1
julia> r2 = @rule sin(2*(~x)) => 2*sin(~x)*cos(~x)
julia> chain = Chain([r1, r2])
julia> chain(sin(x)^2 + cos(x)^2)  # Returns 1
source
SymbolicUtils.Rewriters.RestartedChainType
RestartedChain(rws)

Apply rewriters in sequence, restarting the chain when any rewriter produces a change.

When any rewriter in the chain produces a non-nothing result, the entire chain is restarted with that result as the new input.

Arguments

  • rws: A collection of rewriters to apply

Examples

julia> r1 = @rule ~x + ~x => 2 * ~x
julia> r2 = @rule 2 * ~x => ~x * 2
julia> chain = RestartedChain([r1, r2])
julia> chain(x + x)  # Applies r1, then restarts and applies r2
source
SymbolicUtils.Rewriters.FixpointType
Fixpoint(rw)

Apply a rewriter repeatedly until a fixed point is reached.

The rewriter is applied repeatedly until the output equals the input (either by identity or by isequal), indicating a fixed point has been reached.

Arguments

  • rw: The rewriter to apply repeatedly

Examples

julia> r = @rule ~x + ~x => 2 * ~x
julia> fp = Fixpoint(r)
julia> fp(x + x + x + x)  # Keeps applying until no more changes

See also: FixpointNoCycle

source
SymbolicUtils.Rewriters.FixpointNoCycleType
FixpointNoCycle(rw)

FixpointNoCycle behaves like Fixpoint, but returns a rewriter which applies rw repeatedly until it produces a result that was already produced before, for example, if the repeated application of rw produces results a, b, c, d, b in order, FixpointNoCycle stops because b has been already produced.

source
SymbolicUtils.Rewriters.PostwalkFunction
Postwalk(rw; threaded=false, thread_cutoff=100, maketerm=maketerm)

Apply a rewriter to a symbolic expression tree in post-order (bottom-up).

Post-order traversal visits child nodes before their parents, allowing for simplification of subexpressions before the containing expression.

Arguments

  • rw: The rewriter to apply at each node
  • threaded: If true, use multi-threading for large expressions
  • thread_cutoff: Minimum node count to trigger threading
  • maketerm: Function to construct terms (defaults to maketerm)

Examples

julia> r = @rule ~x + ~x => 2 * ~x
julia> pw = Postwalk(r)
julia> pw((x + x) * (y + y))  # Simplifies both additions
2x * 2y

See also: Prewalk

source
SymbolicUtils.Rewriters.PrewalkFunction
Prewalk(rw; threaded=false, thread_cutoff=100, maketerm=maketerm)

Apply a rewriter to a symbolic expression tree in pre-order (top-down).

Pre-order traversal visits parent nodes before their children, allowing for transformation of the overall structure before processing subexpressions.

Arguments

  • rw: The rewriter to apply at each node
  • threaded: If true, use multi-threading for large expressions
  • thread_cutoff: Minimum node count to trigger threading
  • maketerm: Function to construct terms (defaults to maketerm)

Examples

julia> r = @rule sin(~x) => cos(~x)
julia> pw = Prewalk(r)
julia> pw(sin(sin(x)))  # Transforms outer sin first
cos(cos(x))

See also: Postwalk

source
SymbolicUtils.Rewriters.PassThroughType
PassThrough(rw)

A rewriter that returns the input unchanged if the wrapped rewriter returns nothing.

This is useful for making rewriters that preserve the input when no rule applies.

Arguments

  • rw: The rewriter to wrap

Examples

julia> r = @rule sin(~x) => cos(~x)
julia> pt = PassThrough(r)
julia> pt(sin(x))  # Returns cos(x)
julia> pt(tan(x))  # Returns tan(x) unchanged
source

Simplification and Transformation

SymbolicUtils.simplifyFunction
simplify(x; expand=false,
            threaded=false,
            thread_subtree_cutoff=100,
            rewriter=nothing)

Simplify an expression (x) by applying rewriter until there are no changes. expand=true applies expand in the beginning of each fixpoint iteration.

By default, simplify will assume denominators are not zero and allow cancellation in fractions. Pass simplify_fractions=false to prevent this.

source
SymbolicUtils.expandFunction
expand(expr)

Expand expressions by distributing multiplication over addition, e.g., a*(b+c) becomes ab+ac.

expand uses replace symbols and non-algebraic expressions by variables of type variable_type to compute the distribution using a specialized sparse multivariate polynomials implementation. variable_type can be any subtype of MultivariatePolynomials.AbstractVariable.

source
SymbolicUtils.substituteFunction
substitute(expr, dict; fold=true)

substitute any subexpression that matches a key in dict with the corresponding value. If fold=false, expressions which can be evaluated won't be evaluated.

julia> substitute(1+sqrt(y), Dict(y => 2), fold=true)
2.414213562373095
julia> substitute(1+sqrt(y), Dict(y => 2), fold=false)
1 + sqrt(2)
source

Polynomial Forms

SymbolicUtils.PolyFormType
PolyForm{T} <: Symbolic

Abstracts a MultivariatePolynomials.jl as a SymbolicUtils expression and vice-versa.

The SymbolicUtils term interface (isexpr/iscall, operation, andarguments) works on PolyForm lazily: theoperationandargumentsare created by converting one level of arguments into SymbolicUtils expressions. They may further contain PolyForm within them. We use this to hold polynomials in memory while doingsimplify_fractions`.

PolyForm{T}(x; Fs=Union{typeof(*),typeof(+),typeof(^)}, recurse=false)

Turn a Symbolic expression x into a polynomial and return a PolyForm that abstracts it.

Fs are the types of functions which should be applied if arguments are themselves polynomialized. For example, if you only want to polynomialize the base of power expressions, you would leave out typeof(^) from the union. In this case ^ is not called, but maintained as a Pow term.

recurse is a flag which calls PolyForm recursively on subexpressions. For example:

PolyForm(sin((x+y)^2))               #=> sin((x+y)^2)
PolyForm(sin((x+y)^2), recurse=true) #=> sin((x^2 + (2x)y + y^2))
source
SymbolicUtils.simplify_fractionsFunction
simplify_fractions(x; polyform=false)

Find Div nodes and simplify them by cancelling a set of factors of numerators and denominators. If polyform=true the factors which were converted into PolyForm for the purpose of finding polynomial GCDs will be left as they are. Note that since PolyForms have different hashes than SymbolicUtils expressions, substitute may not work if polyform=true

source
SymbolicUtils.quick_cancelFunction
quick_cancel(d)

Cancel out matching factors from numerator and denominator. This is not as effective as simplify_fractions, for example, it wouldn't simplify (x^2 + 15 - 8x) / (x - 5) to (x - 3). But it will simplify (x - 5)^2*(x - 3) / (x - 5) to (x - 5)*(x - 3). Has optimized processes for Mul and Pow terms.

source

Code Generation

Core Functions

Missing docstring.

Missing docstring for SymbolicUtils.toexpr. Check Documenter's build log for details.

Missing docstring.

Missing docstring for SymbolicUtils.cse. Check Documenter's build log for details.

Code Generation Types

Missing docstring.

Missing docstring for SymbolicUtils.Assignment. Check Documenter's build log for details.

Missing docstring.

Missing docstring for SymbolicUtils.Let. Check Documenter's build log for details.

Missing docstring.

Missing docstring for SymbolicUtils.Func. Check Documenter's build log for details.

Missing docstring.

Missing docstring for SymbolicUtils.DestructuredArgs. Check Documenter's build log for details.

Missing docstring.

Missing docstring for SymbolicUtils.LiteralExpr. Check Documenter's build log for details.

Missing docstring.

Missing docstring for SymbolicUtils.ForLoop. Check Documenter's build log for details.

Array Operations

Missing docstring.

Missing docstring for SymbolicUtils.SetArray. Check Documenter's build log for details.

Missing docstring.

Missing docstring for SymbolicUtils.MakeArray. Check Documenter's build log for details.

Missing docstring.

Missing docstring for SymbolicUtils.MakeSparseArray. Check Documenter's build log for details.

Missing docstring.

Missing docstring for SymbolicUtils.MakeTuple. Check Documenter's build log for details.

Parallelism

Missing docstring.

Missing docstring for SymbolicUtils.SpawnFetch. Check Documenter's build log for details.

Missing docstring.

Missing docstring for SymbolicUtils.Multithreaded. Check Documenter's build log for details.

Utilities

SymbolicUtils.@timerewriteMacro
@timerewrite expr

If expr calls simplify or a RuleSet object, track the amount of time it spent on applying each rule and pretty print the timing.

This uses TimerOutputs.jl.

Example:


julia> expr = foldr(*, rand([a,b,c,d], 100))
(a ^ 26) * (b ^ 30) * (c ^ 16) * (d ^ 28)

julia> @timerewrite simplify(expr)
 ────────────────────────────────────────────────────────────────────────────────────────────────
                                                         Time                   Allocations
                                                 ──────────────────────   ───────────────────────
                Tot / % measured:                     340ms / 15.3%           92.2MiB / 10.8%

 Section                                 ncalls     time   %tot     avg     alloc   %tot      avg
 ────────────────────────────────────────────────────────────────────────────────────────────────
 ACRule((~y) ^ ~n * ~y => (~y) ^ (~n ...    667   11.1ms  21.3%  16.7μs   2.66MiB  26.8%  4.08KiB
   RHS                                       92    277μs  0.53%  3.01μs   14.4KiB  0.14%     160B
 ACRule((~x) ^ ~n * (~x) ^ ~m => (~x)...    575   7.63ms  14.6%  13.3μs   1.83MiB  18.4%  3.26KiB
 (*)(~(~(x::!issortedₑ))) => sort_arg...    831   6.31ms  12.1%  7.59μs    738KiB  7.26%     910B
   RHS                                      164   3.03ms  5.81%  18.5μs    250KiB  2.46%  1.52KiB
   ...
   ...
 ────────────────────────────────────────────────────────────────────────────────────────────────
(a ^ 26) * (b ^ 30) * (c ^ 16) * (d ^ 28)
source