Developer API
The names on this page are public extension points for packages that build on SymbolicUtils, including Symbolics.jl. They are intentionally not generally exported. User code should prefer the higher-level symbolic constructors, rewriters, and substitution APIs documented on the main API page.
Expression and cache interfaces
SymbolicUtils.:<ₑ — Function
a <ₑ bCompare symbolic expressions and their degree representations using the canonical expression ordering. The operator is intended for deterministic ordering of terms, not for mathematical less-than comparisons.
SymbolicUtils.@cache — Macro
@cache [options...] function foo(arg1::Type, arg2::Type; kwargs...)::ReturnType
# ...
endCreate a cached version of the function foo. This is typically useful for recursive functions that descend through an expression tree.
The return type of the function should be annotated to avoid warnings. If any of the argument types is a BasicSymbolic, uses a special caching for efficiency. If an argument has Any type or a Union containing BasicSymbolic, a runtime check is performed to handle it. This can be avoided if the type is annotated with BasicSymbolic. The maximum number of entries in the cache can be set using the limit option by providing an integer size. This defaults to 100_000. When this limit is hit, a fraction of the entries in the cache will be cleared at random. The fraction of entries retained is given by the retain_fraction option, which defaults to 0.5.
Multiple methods of the same function cannot be cached and will lead to an error. This should be avoided by creating a wrapper function which calls the one with ,multiple methods, and caching the wrapper. The function with multiple methods should recursively call the wrapper. Caching a single method is valid.
The cache is thread-safe and uses TaskLocalValues.jl to maintain a task-specific cache.
The caching behavior for this function is enabled by default. Use the enabled option to toggle this.
See also: SymbolicUtils.get_limit, SymbolicUtils.set_limit!, SymbolicUtils.get_retain_fraction, SymbolicUtils.set_retain_fraction!, SymbolicUtils.toggle_caching!, SymbolicUtils.is_caching_enabled, SymbolicUtils.get_stats, SymbolicUtils.clear_cache!, SymbolicUtils.reset_stats!.
SymbolicUtils.ACDict — Type
The type of the dictionary stored in BSImpl.AddMul. Passing this to the SymbolicUtils.Add or SymbolicUtils.Mul constructors will avoid allocating a new dictionary.
SymbolicUtils.AddMulVariant — Module
An EnumX.jl enum used to distinguish between addition and multiplication in SymbolicUtils.BSImpl.AddMul.
SymbolicUtils.ArgsT — Type
The type of a mutable buffer containing symbolic arguments. Passing this to the SymbolicUtils.Term constructor will avoid allocating a new array.
SymbolicUtils.BSImpl — Module
Alias for SymbolicUtils.BasicSymbolicImpl.
SymbolicUtils.BasicSymbolicImpl — Module
Core ADT for symbolic expressions.
SymbolicUtils.Const — Type
Const{T}(val) where {T}Alias for BSImpl.Const{T}.
SymbolicUtils.Div — Type
Div{T}(n, d, simplified; type = promote_symtype(/, symtype(n), symtype(d)), kw...) where {T}High-level constructor for division expressions with simplification.
Arguments
n: The numeratord: The denominatorsimplified::Bool: Whether simplification has been attemptedtype: The result type (default: inferred usingpromote_symtype)kw...: Additional keyword arguments (e.g.,shape,metadata,unsafe)
Returns
BasicSymbolic{T}: An optimized representation ofn / d
Details
This constructor creates symbolic division expressions with extensive simplification:
- Zero numerator returns zero
- Unit denominator returns the numerator
- Zero denominator returns
Const{T}(1 // 0)(infinity). Any infinity may be returned. - Nested divisions are flattened
- Constant divisions are evaluated
- Rational coefficients are simplified
- Multiplications in numerator/denominator are handled specially
For non-SafeReal variants, automatic cancellation is attempted using quick_cancel. The simplified flag prevents infinite simplification loops.
SymbolicUtils.MetadataT — Type
Type of metadata field for symbolics.
SymbolicUtils.Operator — Type
OperatorAbstract supertype for symbolic callable operators. An operator is treated as an atomic function-like object by expression traversal and supplies its own symbolic type and shape promotion through promote_symtype and promote_shape.
Developer implementations should subtype Operator and define those promotion methods before constructing operator terms.
SymbolicUtils.ROArgsT — Type
The type of a read-only buffer containing symbolic arguments. Passing this to the SymbolicUtils.Term constructor will avoid allocating a new array. This is the type returned from TermInterface.arguments.
SymbolicUtils.Substituter — Type
Substituter{Fold}An abstract supertype for functors that perform substitution operations on symbolic expressions. This can also be used as a constructor for the functor used by substitute. Fold corresponds to the fold keyword of substitute. Passing fold = Val(true) corresponds to Substituter{true} (and similarly for Val(false)). To define substitution rules for custom types that wrap/contain BasicSymbolic, define methods for this abstract type. For example,
struct Equation
lhs::BasicSymbolic{SymReal}
rhs::BasicSymbolic{SymReal}
end
function (subst::Substituter)(eq::Equation)
return Equation(subst(eq.lhs), subst(eq.rhs))
endCustom substitution algorithms should define functors that subtype Substituter. For example, a functor may be defined for substituting until the expression reaches a fixpoint. These functors should then implement:
(s::Substituter{Fold})(ex::BasicSymbolic{T}) where {T}to perform the appropriate substitution on the given symbolic expression.get_substitution_dict(::Substituter)returning anAbstractDictof the substitution rules.
Instead of repeatedly calling substitute with the same rules, it is usually more efficient to build a Substituter and reuse it.
Substituter is also allowed to cache intermediate results as necessary. When constructing Substituter with an AbstractDict, it will alias the provided mapping. Mutating the map such that the identity of the substitution rules changes invalidates the substituter. It can be reused by clearing the cache using SymbolicUtils.clear_cache!.
The caching is only available when the SymbolicUtils.vartype of the expressions is inferable from the substitution rules, or explicitly specified. As long as either the keys or values of the substitution rules are all BasicSymbolic{T} (for some T) the automatic inference will work. To allow the inference to work for your custom wrapper type, implement SymbolicUtils.infer_vartype. For example:
struct Num <: Real
inner::BasicSymbolic{SymReal}
end
SymbolicUtils.infer_vartype(::Type{Num}) = SymRealAlternatively, the vartype can be provided as the third positional argument to the Substituter constructor.
Extended help
The following is internal details of Substituter and should not be relied on as public API.
Fields
SymbolicUtils.Term — Type
Term{T}(f, args; type = _promote_symtype(f, args), kw...) where {T}Alias for BSImpl.Term{T} except it also unwraps args.
SymbolicUtils.TypeT — Type
mutable struct DataType <: Type{T}Allowed types for the SymbolicUtils.symtype of symbolics.
SymbolicUtils._isone — Function
_isone(x) -> Bool
Check if a value is one, with caching for performance.
Arguments
x: The value to check (can be a number, array, or symbolic expression)
Returns
trueif the unwrapped value is one,falseotherwise
Details
This cached function efficiently checks if a value is one by first unwrapping any constant wrappers. Handles both numeric values and arrays. Returns false for symbolic expressions that are not constant one. The @cache macro improves performance by memoizing results for previously seen values.
SymbolicUtils._iszero — Function
_iszero(x) -> Bool
Check if a value is zero, with caching for performance.
Arguments
x: The value to check (can be a number, array, or symbolic expression)
Returns
trueif the unwrapped value is zero,falseotherwise
Details
This cached function efficiently checks if a value is zero by first unwrapping any constant wrappers. Handles both numeric values and arrays. Returns false for symbolic expressions that are not constant zero. The @cache macro improves performance by memoizing results for previously seen values.
SymbolicUtils.clear_cache! — Function
clear_cache!(fn) -> Any
Clear the cache for SymbolicUtils.@cached function fn. This is task specific. Also resets the stats.
clear_cache!(subst::SymbolicUtils.DefaultSubstituter) -> Any
Clear the cached values associated with subst. See the documentation of SymbolicUtils.Substituter for more details.
SymbolicUtils.default_is_atomic — Function
default_is_atomic(
ex::SymbolicUtils.BasicSymbolicImpl.var"typeof(BasicSymbolicImpl)"{T}
) -> Any
The default is_atomic predicate for search_variables!. ex is considered atomic if one of the following conditions is true:
- It is a
Symand not an internal index variable for an arrayop - It is a
Term, the operation is aBasicSymbolicand the operation represents a dependent variable according tois_function_symbolic. - It is a
Term, the operation isgetindexand the variable being indexed is atomic.
SymbolicUtils.default_substitute_filter — Function
default_substitute_filter(
ex::SymbolicUtils.BasicSymbolicImpl.var"typeof(BasicSymbolicImpl)"{T}
) -> Any
The default filter function used by substitute to determine whether to substitute within an expression. Returns false for expressions that are Terms with an Operator as the operation (preventing substitution within operator calls), and true otherwise.
Arguments
ex::BasicSymbolic{T}: The expression to check.
Returns
Bool:falseif the expression should not be substituted into,trueotherwise.
SymbolicUtils.evaluate — Function
evaluate(expr; filterer = default_substitute_filter)Evaluate a symbolic expression using the standard substitution and folding machinery without replacing any variables. filterer controls which symbolic nodes may be folded. The result is the folded expression or value returned by substitute with fold = Val(true).
SymbolicUtils.get_substitution_dict — Function
get_substitution_dict(
s::SymbolicUtils.DefaultSubstituter
) -> AbstractDict
Get an AbstractDict of the substitution rules for the given SymbolicUtils.Substituter.
SymbolicUtils.hashcons — Function
hashcons(expr::BasicSymbolic; reregister = false)Intern expr in the vartype-specific weak hash-cons table and return the canonical object. If reregister is false, an expression with an existing identifier is returned unchanged; setting it to true permits registration in the current table. Hash-consing is a developer optimization and preserves isequal semantics, including symbolic metadata.
SymbolicUtils.is_array_shape — Function
is_array_shape(shape)Return true when a symbolic shape represents an array rather than a scalar. Unknown is considered an array shape because its rank may be nonzero; an empty ShapeVecT is the scalar shape.
SymbolicUtils.is_function_symbolic — Function
is_function_symbolic(
x::SymbolicUtils.BasicSymbolicImpl.var"typeof(BasicSymbolicImpl)"{T} where T
) -> Any
Check if x is a symbolic representing a function (as opposed to a dependent variable). A symbolic function either has a defined signature or the function type defined. For example, all of the below are considered symbolic functions:
@syms f(::Real, ::Real) g(::Real)::Integer h(::Real)[1:2]::Integer (ff::MyCallableT)(..)However, the following is considered a dependent variable with unspecified independent variable:
@syms x(..)See also: SymbolicUtils.is_function_symtype.
SymbolicUtils.is_called_function_symbolic — Function
is_called_function_symbolic(
x::SymbolicUtils.BasicSymbolicImpl.var"typeof(BasicSymbolicImpl)"{T}
) -> Any
Check if the given symbolic x is the result of calling a symbolic function (as opposed to a dependent variable).
See also: SymbolicUtils.is_function_symbolic.
SymbolicUtils.isarrayop — Function
isarrayop(
x::SymbolicUtils.BasicSymbolicImpl.var"typeof(BasicSymbolicImpl)"{T} where T
) -> Bool
Check if a value is an ArrayOp variant of BasicSymbolic.
Arguments
x: Value to check (forBasicSymbolicinput returns true ifArrayOp, for others returns false).
Returns
trueifxis aBasicSymbolicwithArrayOpvariant,falseotherwise.
Details
Array operations represent vectorized computations created by the @arrayop macro.
SymbolicUtils.isbinop — Function
isbinop(x)Return whether x is recognized as a binary symbolic operator. This extension point defaults to false for non-symbolic values and can be specialized by symbolic operator implementations.
SymbolicUtils.numerators — Function
numerators(x) -> Any
Return the numerator of expression x as an array of multiplied terms.
SymbolicUtils.denominators — Function
denominators(x) -> Any
Return the denominator of expression x as an array of multiplied terms.
SymbolicUtils.one_of_vartype — Function
one_of_vartype(
_::Type{SymReal}
) -> SymbolicUtils.BasicSymbolicImpl.var"typeof(BasicSymbolicImpl)"{SymReal}
Return a Const representing 1 with the provided vartype.
SymbolicUtils.operator_to_term — Function
operator_to_term(operator::Operator, ex::BasicSymbolic)Return the symbolic term representing an operator application. The default implementation returns ex; custom Operator subtypes may override it when their printed or canonical term differs from the original expression.
SymbolicUtils.promote_symtype — Function
promote_symtype(
f,
Ts...
) -> Type{SymbolicUtils.FnType{_A, _B, Nothing}} where {_A, _B}
The result of applying f to arguments of SymbolicUtils.symtype Ts...
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)
ComplexWhen constructing expressions without an explicit symtype, promote_symtype is used to figure out the symtype of the Term.
It is recommended that all type arguments be annotated with SymbolicUtils.TypeT and one method be implemented for any combination of f and the number of arguments. For example, one method is implemented for unary - and one method for binary -. Each method has an if..elseif chain to handle possible types. Any call to promote_type should be typeasserted with ::TypeT.
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.
promotesymtype(f::ComposedFunction, argsymtypes::TypeT...)
Compute the symbolic type of applying a composed function to arguments.
For a composed function f ∘ g ∘ h, this computes the result type by propagating type information from the innermost function outward:
- Apply
hto the input argument types - Use that result type as input to
g - Use that result type as input to
f
This implementation assumes each function returns a single value that becomes the argument to the next function. Multi-argument returns (tuples) are not currently supported but could be added if needed.
SymbolicUtils.query — Function
query(
predicate,
expr::SymbolicUtils.BasicSymbolicImpl.var"typeof(BasicSymbolicImpl)"{T};
recurse,
default
) -> Any
Recursively search an expression tree to determine if any subexpression satisfies a given predicate. This function traverses the expression tree and returns true if the predicate returns true for any node in the tree.
Arguments
predicate::F: A function that takes an expression and returns aBool.expr::BasicSymbolic: The expression to search.
Keyword Arguments
recurse::G=iscall: A function determining whether to recurse into a subexpression.default::Bool=false: The default value to return if the expression is not a call or recursion is prevented.
Returns
Bool:trueif any subexpression satisfies the predicate,falseotherwise.
query(
predicate,
ir::IRStructure{T},
expr::SymbolicUtils.BasicSymbolicImpl.var"typeof(BasicSymbolicImpl)"{T};
recurse
) -> Bool
Optimized version of SymbolicUtils.query that leverages the provided SymbolicUtils.IRStructure. Requires that expr is present in ir.
SymbolicUtils.scalarize — Function
scalarize(
x::SymbolicUtils.BasicSymbolicImpl.var"typeof(BasicSymbolicImpl)"{T}
) -> Any
scalarize(
x::SymbolicUtils.BasicSymbolicImpl.var"typeof(BasicSymbolicImpl)"{T},
v::Val{toplevel}
) -> Any
Convert a symbolic expression with array operations into a fully scalarized form. This function expands array operations into element-wise operations, converting symbolic array expressions into arrays of scalar symbolic expressions.
For ArrayOp expressions, this function reduces eliminated indices and substitutes concrete values for output indices to generate scalar expressions for each array element.
Arguments
x::BasicSymbolic{T}: The symbolic expression to scalarize.::Val{toplevel}=Val{false}(): Whether to evaluate constant expressions at the top level. Whentrue, constant subexpressions are evaluated; whenfalse, they are recursively scalarized.
Returns
- The scalarized expression. For array-shaped expressions, returns an array of scalar expressions. For scalar expressions, returns the expression unchanged or with recursively scalarized subexpressions.
SymbolicUtils.search_variables — Function
search_variables(expr; is_atomic = default_is_atomic, recurse = iscall)Return a set-like collection containing the atomic symbolic variables found in expr. The keyword arguments are forwarded to search_variables!, so custom wrappers can define both the atomic predicate and recursion policy.
SymbolicUtils.search_variables! — Function
search_variables!(
buffer,
expr::SymbolicUtils.BasicSymbolicImpl.var"typeof(BasicSymbolicImpl)"{T};
is_atomic,
recurse,
_seen
)
Find all variables used in expr and add them to buffer. A variable is identified by the predicate is_atomic. The predicate recurse determines whether to search further inside expr if it is not a variable. Note that recurse must at least return false if iscall returns false.
Wrappers for BasicSymbolic should implement this function by unwrapping.
See also: default_is_atomic.
search_variables!(
buffer::AbstractSet{SymbolicUtils.BasicSymbolicImpl.var"typeof(BasicSymbolicImpl)"{T}},
ir::IRStructure{T},
expr::SymbolicUtils.BasicSymbolicImpl.var"typeof(BasicSymbolicImpl)"{T};
is_atomic,
recurse
)
Optimized version of SymbolicUtils.search_variables! that leverages the provided SymbolicUtils.IRStructure. May also add expr to ir in the process.
SymbolicUtils.show_call — Function
show_call(io, f, expr; kwargs...)Render a symbolic call expr to io using operation f. This is the developer printing hook used by the symbolic display machinery. Keyword arguments are accepted for specialized printers and are otherwise ignored.
SymbolicUtils.stable_eachindex — Function
stable_eachindex(
x::SymbolicUtils.BasicSymbolicImpl.var"typeof(BasicSymbolicImpl)"{T} where T
) -> SymbolicUtils.StableIndices
Returns a type-stable iterator over all indices of a symbolic array x.
This function provides an efficient, allocation-friendly way to iterate over multi-dimensional symbolic arrays. Unlike Base.eachindex, which returns CartesianIndices with type parameters that may be uninferrable for symbolic arrays, stable_eachindex returns a StableIndices iterator that produces StableIndex values in a fully type-stable manner.
Note that the returned iterator does not match the shape of x. In other words, collect(stable_eachindex(x)) will be a vector regardless of the shape of x.
Arguments
x::BasicSymbolic: A symbolic array expression with a known concrete shape.
Returns
StableIndices: An iterator that yieldsStableIndexvalues for each position in the array.
Throws
- This function assumes
xhas a concrete shape (i.e.,shape(x)is aShapeVecT, notUnknown). If the shape is unknown, it will error.
Examples
using SymbolicUtils
# Create a symbolic 2×3 matrix
@variables x[1:2, 1:3]
# Iterate over all indices in a type-stable manner
for idx in stable_eachindex(x)
println("Index: ", idx, " -> Value: ", x[idx])
end
# Compare with regular eachindex
for idx in eachindex(x) # Returns CartesianIndices
println("Index: ", idx, " -> Value: ", x[idx])
endSee also
StableIndices: The iterator type returned by this functionStableIndex: The index type produced byStableIndicesBase.eachindex: The standard Julia function for iterating over array indices
SymbolicUtils.toggle_caching! — Function
toggle_caching!(fn, state::Bool) -> Bool
Enable or disable the caching of fn according to state.
SymbolicUtils.zero_of_vartype — Function
zero_of_vartype(
_::Type{SymReal}
) -> SymbolicUtils.BasicSymbolicImpl.var"typeof(BasicSymbolicImpl)"{SymReal}
Return a Const representing 0 with the provided vartype.
SymbolicUtils.zeropoly — Function
zeropoly(
) -> DynamicPolynomials.Polynomial{DynamicPolynomials.Commutative{DynamicPolynomials.CreationOrder}, MultivariatePolynomials.Graded{MultivariatePolynomials.Reverse{MultivariatePolynomials.InverseLexOrder}}, Number}
Create a zero polynomial with empty monomial vector.
Returns
- A
PolynomialTrepresenting the zero polynomial
Parsing and polynomial conversion
SymbolicUtils.MonomialOrder — Type
MonomialOrderThe canonical monomial ordering used by SymbolicUtils polynomial conversion. It is graded reverse lexicographic ordering with the reverse tie-breaker used by DynamicPolynomials.
SymbolicUtils.MonomialT — Type
MonomialTThe concrete monomial type used inside PolynomialT values.
SymbolicUtils.PolyCoeffT — Type
PolyCoeffTThe coefficient type accepted by the developer polynomial interface. SymbolicUtils uses Number so integer, rational, and floating-point coefficients can share the same conversion routines.
SymbolicUtils.PolyVarOrder — Type
PolyVarOrderThe DynamicPolynomials variable-order type used by PolyVarT and PolynomialT. Variables are ordered by their creation order.
SymbolicUtils.PolyVarT — Type
PolyVarTThe concrete polynomial-variable type used for symbolic expressions converted to polynomial form. Values are created with DynamicPolynomials.@polyvar-compatible constructors and are not themselves symbolic terms.
SymbolicUtils.PolynomialT — Type
PolynomialTThe concrete sparse polynomial type returned by to_poly!. Its variables use PolyVarOrder, its monomials use MonomialOrder, and its coefficients are PolyCoeffT.
SymbolicUtils._indexed_ndims — Function
_indexed_ndims(index_types...)Return the number of array dimensions selected by a tuple of index types. Integer and scalar indices consume no dimensions, while Colon and integer vectors consume one. Invalid index types throw ArgumentError.
This helper is part of the developer indexing interface used by promote_symtype(getindex, ...).
SymbolicUtils.basicsymbolic_to_polyvar — Function
basicsymbolic_to_polyvar(
bs_to_poly::AbstractDict,
x::SymbolicUtils.BasicSymbolicImpl.var"typeof(BasicSymbolicImpl)"{T} where T
) -> DynamicPolynomials.Variable{DynamicPolynomials.Commutative{DynamicPolynomials.CreationOrder}, MultivariatePolynomials.Graded{MultivariatePolynomials.Reverse{MultivariatePolynomials.InverseLexOrder}}}
Convert a BasicSymbolic expression to a polynomial variable, caching the result.
Arguments
bs_to_poly::AbstractDict: Dictionary cache mappingBasicSymbolictoPolyVarTx::BasicSymbolic: The symbolic expression to convert
Returns
- A
PolyVarTpolynomial variable representingx, created or retrieved from cache
SymbolicUtils.from_poly — Function
from_poly(poly_to_bs, poly)Reconstruct a BasicSymbolic expression from a polynomial produced by to_poly!. poly_to_bs must contain an entry for every polynomial variable in poly.
Returns
A BasicSymbolic expression with the same polynomial value.
SymbolicUtils.parse_variable — Function
parse_variable(x; default_type) -> Dict{Symbol, Any}
Parse an Expr or Symbol representing a variable in the syntax of the @syms macro. Returns a Dict{Symbol, Any} with the following keys guaranteed to exist:
:name: The name of the variable.nothingif not specified.:type: The type of the variable.default_typeif not specified.:shape: The shape of the variable.:isruntime: Whether the name is a runtime value (comes from a$nameinterpolation syntax).
This does not attempt to eval to interpret types. Values in the above keys are concrete values when possible and Exprs when not.
If the variable is a function, it contains additional keys:
:head: ADict{Symbol, Any}containing the name and type of the function.:args: A list ofDict{Symbol, Any}corresponding to each argument of the function. If there is a single argument.., the onlyDict{Symbol, Any}in:argswill only contain:name => :... For arguments of the form::T(type annotation without a name) the name will benothing.
Refer to the docstring for @syms for a description of the grammar accepted by this function.
SymbolicUtils.sym_from_parse_result — Function
sym_from_parse_result(
result::Dict{Symbol, Any},
vartype;
do_esc
) -> Expr
Return an Expr which constructs a Sym for the given parsed variable result. vartype is the vartype of the variable to be constructed. do_esc controls whether this function is responsible for escing necessary values, or the caller will manually sanitize and esc the expression.
SymbolicUtils.to_poly! — Function
to_poly!(poly_to_bs, bs_to_poly, expr, recurse = true)Convert a BasicSymbolic expression into a sparse polynomial representation. poly_to_bs maps generated polynomial variables back to symbolic expressions, while bs_to_poly caches the reverse mapping. With recurse = false, non-polynomial subexpressions become single polynomial variables.
Returns
A PolyVarT or PolynomialT representing expr.
Rewriting and broadcasting helpers
SymbolicUtils.@map_methods — Macro
@map_methods T argument_transform result_transformGenerate Base.map methods for a symbolic array-like type. argument_transform converts the symbolic container to the arguments passed to map, and result_transform wraps the mapped result. This is a developer interface and must be used while defining the corresponding symbolic type.
SymbolicUtils.@mapreduce_methods — Macro
@mapreduce_methods T argument_transform result_transformGenerate Base.mapreduce methods for a symbolic array-like type. The transform expressions receive the mapped input and the result transform reconstructs the symbolic representation.
SymbolicUtils.@number_methods — Macro
@number_methods T unary_body binary_body [options]Emit arithmetic methods for T using the supplied unary and binary expression bodies. See number_methods for the generated dispatch combinations and supported options values.
SymbolicUtils.number_methods — Function
@number_methods T unary_body binary_body [options]Generate the standard arithmetic methods needed for a symbolic type T. unary_body and binary_body are expressions evaluated with the generated function and binary-operation variables in scope. options may be nothing, :skipbasics, :onlybasics, or a vector expression naming operations to skip.
This is a developer macro for symbolic-type implementations. It performs no runtime dispatch by itself; it emits methods for the numeric and symbolic promotion combinations supported by T.
SymbolicUtils.Rule — Type
Rule{L, M, R}Compiled representation of a rewrite rule created by @rule or @acrule.
Fields
expr: the original rule expression used for display.lhs: the pattern matched by the rule.matcher: the compiled matcher forlhs.rhs: the replacement expression or function.depth: the maximum expression depth inspected by the rule.
SymbolicUtils.SymBroadcast — Type
SymBroadcast{T}Broadcast style for BasicSymbolic{T} expressions. It keeps symbolic arrays in the symbolic broadcast path and rejects broadcasts that combine incompatible symbolic variants.
Code generation
SymbolicUtils.Code — Module
SymbolicUtils.jl
Tutorials and 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.
SymbolicUtils.jl provides various utilities for symbolic computing. SymbolicUtils.jl is what one would use to build a Computer Algebra System (CAS). If you're looking for a complete CAS, similar to SymPy or Mathematica, see Symbolics.jl. If you want to build a crazy CAS for your weird Octonian algebras, you've come to the right place.
Symbols in SymbolicUtils carry type information. Operations on them propagate this information. A rule-based rewriting language can be used to find subexpressions that satisfy arbitrary conditions and apply arbitrary transformations on the matches. The library also contains a set of useful simplification rules for expressions of numeric symbols and numbers. These can be remixed and extended for special purposes.
If you are a Julia package developer in need of a rule rewriting system for your own types, have a look at the interfacing guide.
"I don't want to read your manual, just show me some cool code"
julia> using SymbolicUtils
julia> SymbolicUtils.show_simplified[] = true
julia> @syms x::Real y::Real z::Complex{Real} f(::Number)::Real
(x, y, z, f(::Number)::Real)
julia> 2x^2 - y + x^2
(3 * (x ^ 2)) + (-1 * y)
julia> f(sin(x)^2 + cos(x)^2) + z
f(1) + z
julia> r = @rule sinh(im * ~x) => sin(~x)
sinh(im * ~x) => sin(~x)
julia> r(sinh(im * y))
sin(y)
julia> simplify(cos(y)^2 + sinh(im*y)^2, RuleSet([r]))
1Citations
- The pattern matcher is an adaption of the one by Gerald Jay Sussman (as seen in 6.945 at MIT), his use of symbolic programming in the book SICM inspired this package.
- Rewrite.jl and Simplify.jl by Harrison Grodin also inspired this package.
Developer-facing intermediate-representation and code-generation helpers for SymbolicUtils. The public names in this module are stable extension points for packages that generate code from symbolic expressions; ordinary callers should prefer SymbolicUtils.build_function-style high-level APIs.
SymbolicUtils.Code.LazyState — Type
LazyState()Lazily initialized rewrite state used while generating code from a symbolic expression. The state exposes the same rewrite dictionary interface as NameState after its first access.
SymbolicUtils.Code.cse_inside_expr — Function
cse_inside_expr(sym, f) -> Bool
Return true if CSE should descend inside sym, which has operation f.
SymbolicUtils.Code.fast_toexpr — Function
fast_toexpr(
sym::SymbolicUtils.BasicSymbolicImpl.var"typeof(BasicSymbolicImpl)"{T},
rewrites::Dict{Any, Any}
) -> Expr
Replacement of SymbolicUtils.Code.toexpr using IR-based codegen.
fast_toexpr(
sym::SymbolicUtils.Code.CodegenPrimitive,
ir::IRStructure{T},
rewrites::Dict{Any, Any}
) -> Any
SymbolicUtils.Code.function_to_expr — Function
function_to_expr(op, expr, state)
Lower one symbolic operation to a Julia expression during code generation.
This is a developer extension point used by `SymbolicUtils.Code.toexpr` and
the IR-based code generator. `state` must be the active code-generation
state; custom operation handlers should preserve the expression and state
invariants expected by the surrounding generator.SymbolicUtils.Code.get_rewrites — Function
get_rewrites(expr)
Collect symbolic subexpressions that need rewrite bindings during code
generation. This developer hook returns an empty collection for literals
and recursively visits arrays, tuples, and destructured arguments.SymbolicUtils.Code.supports_with_allocator — Function
supports_with_allocator(ex::BasicSymbolic)
Return `true` when `ex` is an allocating symbolic expression accepted by
[`with_allocator`](@ref). The supported forms are `array_literal`,
`SymbolicUtils.@arrayop`, and `SymbolicUtils.@makearray`, including array
operations backed by `Fill`.SymbolicUtils.Code.with_allocator — Function
with_allocator(
alloc,
ex::SymbolicUtils.BasicSymbolicImpl.var"typeof(BasicSymbolicImpl)"{T}
) -> SymbolicUtils.BasicSymbolicImpl.var"typeof(BasicSymbolicImpl)"{T} where T
Associate an allocating expression with an allocator. Expressions such as SymbolicUtils.@arrayop, SymbolicUtils.@makearray, and SymbolicUtils.array_literal need to allocate an array for their results. By default, the generated code will allocate an array using any method it deems appropriate (such as zeros). To use a specific allocation method, or perhaps provide a preallocated array of the appropriate size and eltype, with_allocator can be used. The first argument is an allocator (API described below), and the second argument is an expression which allocates an array. The array allocation will use the provided allocator. Note that this only applies to immediate next allocation in ex. For example, an array_literal containing an @arrayop has two allocations. with_allocator on the outer array_literal will only control the allocation used for array_literal, and not @arrayop. Each allocating expression needs its own with_allocator.
Allocator API
The API for the allocator is relatively simple. It should be a callable accepting an NTuple{N, Int} indicating the size of the required array, and returning an array of the appropriate eltype and indicated size. The array should be mutable, allowing setindex!.
Note that the eltype of the array is not provided, and is the responsibility of the allocator to identify. This is intentional, since figuring this out ahead of time can be prohibitively expensive. For example, to obtain a value representing the eltype of an @arrayop can require running the entire set of reduction loops. The symtype of the expression is also typically very wide - it is usually a type like Real or Number. The provider of the allocator typically has more information about the required eltype, since they know how the expression is involved in the larger code, where the arguments come from, and the concrete types of the buffers.