From 5c7e61f52e64ea390eea9c4e6e7aa67c4aa3b82f Mon Sep 17 00:00:00 2001 From: "Documenter.jl" Date: Mon, 21 Oct 2024 19:17:53 +0000 Subject: [PATCH] build based on eb34c90 --- dev/.documenter-siteinfo.json | 2 +- dev/api/index.html | 14 +++++++------- dev/index.html | 2 +- dev/manual/codegen/index.html | 6 +++--- dev/manual/interface/index.html | 2 +- dev/manual/representation/index.html | 2 +- dev/manual/rewrite/index.html | 2 +- dev/objects.inv | Bin 1076 -> 1061 bytes dev/search_index.js | 2 +- dev/upgrade/index.html | 2 +- 10 files changed, 17 insertions(+), 17 deletions(-) diff --git a/dev/.documenter-siteinfo.json b/dev/.documenter-siteinfo.json index 5df70edc..bdeb3b68 100644 --- a/dev/.documenter-siteinfo.json +++ b/dev/.documenter-siteinfo.json @@ -1 +1 @@ -{"documenter":{"julia_version":"1.11.1","generation_timestamp":"2024-10-17T01:46:17","documenter_version":"1.7.0"}} \ No newline at end of file +{"documenter":{"julia_version":"1.11.1","generation_timestamp":"2024-10-21T19:17:47","documenter_version":"1.7.0"}} \ No newline at end of file diff --git a/dev/api/index.html b/dev/api/index.html index 3a0ff1bd..17bb0c41 100644 --- a/dev/api/index.html +++ b/dev/api/index.html @@ -1,6 +1,6 @@ -API Reference · SymbolicUtils.jl

API Reference

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
Missing docstring.

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

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.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.

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

The result of applying f to arguments of symtype Ts...

julia> promote_symtype(+, Real, Real)
+API Reference · SymbolicUtils.jl

API Reference

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
Missing docstring.

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

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.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.

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

The result of applying f to arguments of symtype Ts...

julia> promote_symtype(+, Real, Real)
 Real
 
 julia> promote_symtype(+, Complex, Real)
@@ -10,7 +10,7 @@
 (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

Rewriters

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
+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

Rewriters

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)
@@ -56,13 +56,13 @@
 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.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

Simplify

SymbolicUtils.simplifyFunction
simplify(x; expand=false,
+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.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

Simplify

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

Utilities

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)
 
@@ -82,4 +82,4 @@
    ...
    ...
  ────────────────────────────────────────────────────────────────────────────────────────────────
-(a ^ 26) * (b ^ 30) * (c ^ 16) * (d ^ 28)
source
+(a ^ 26) * (b ^ 30) * (c ^ 16) * (d ^ 28)
source
diff --git a/dev/index.html b/dev/index.html index 33ab606b..9a5f681b 100644 --- a/dev/index.html +++ b/dev/index.html @@ -14,4 +14,4 @@ expr1 + expr2

SymbolicUtils automatically simplifies

2w + 3w - 3z + α

and reorders

(z + w)*(α + β)

expressions of type Symbolic{<:Number} (which includes Sym{Real}) when they are created. It also does constant elimination (including rational numbers)

5 + 2w - 3z + α - (β + 5//3) + 3w - 2 + 3//2 * β

It's worth remembering that the expression may be transformed with respect to the input when it's created.

Function-like symbols

Symbols can be defined to behave like functions. Both the input and output types for the function can be specified. Any application to that function will only admit either values of those types or symbols of the same symtype.

using SymbolicUtils
 @syms f(x) g(x::Real, y::Real)::Real
 
-f(z) + g(1, α) + sin(w)

This does not work since z is a Number, not a Real.

g(1, z)

This works because g "returns" a Real.

g(2//5, g(1, β))

Expression interface

Symbolic expressions are of type Term{T}, Add{T}, Mul{T}, Pow{T} or Div{T} and denote some function call where one or more arguments are themselves such expressions or Syms. See more about the representation here.

All the expression types support the TermInterface.jl interface. Please refer to the package for the complete reference of the interface.

Term rewriting

SymbolicUtils contains a rule-based rewriting language for easy pattern matching and rewriting of expression. There is also a combinator library to combine rules to chain, branch and loop over rules.

Simplification

By default +, * and ^ operations apply the most basic simplification upon construction of the expression.

The rules with which the canonical form of Symbolic{<:Number} terms are constructed are the next (where x isa Symbolic and c isa Number)

Here is an example of this

2 * (w+w+α+β + sin(z)^2 + cos(z)^2 - 1)

The simplify function applies a built-in set of rules to rewrite expressions in order to simplify it.

simplify(2 * (w+w+α+β + sin(z)^2 + cos(z)^2 - 1))

The rules in the default simplify applies simple constant elimination and trigonometric identities.

If you read the previous section on the rules DSL, you should be able to read and understand the rules that are used by simplify.

Code generation

Experimental feature

It is common to want to generate executable code from symbolic expressions and blocks of them. We are working on experimental support for turning Symbolic expressions into executable functions with specific focus on array input and output and performance which is critical to the Differential Equations ecosystem which is making heavy use of this package.

See Code generation for more about this.

Learn more

If you have a package that you would like to utilize rule-based rewriting in, look at the suggestions in the Interfacing section to find out how you can do that without any fundamental changes to your package. Look at the API documentation for docstrings about specific functions or macros.

Head over to the github repository to ask questions and report problems! Join the Zulip stream to chat!

+f(z) + g(1, α) + sin(w)

This does not work since z is a Number, not a Real.

g(1, z)

This works because g "returns" a Real.

g(2//5, g(1, β))

Expression interface

Symbolic expressions are of type Term{T}, Add{T}, Mul{T}, Pow{T} or Div{T} and denote some function call where one or more arguments are themselves such expressions or Syms. See more about the representation here.

All the expression types support the TermInterface.jl interface. Please refer to the package for the complete reference of the interface.

Term rewriting

SymbolicUtils contains a rule-based rewriting language for easy pattern matching and rewriting of expression. There is also a combinator library to combine rules to chain, branch and loop over rules.

Simplification

By default +, * and ^ operations apply the most basic simplification upon construction of the expression.

The rules with which the canonical form of Symbolic{<:Number} terms are constructed are the next (where x isa Symbolic and c isa Number)

Here is an example of this

2 * (w+w+α+β + sin(z)^2 + cos(z)^2 - 1)

The simplify function applies a built-in set of rules to rewrite expressions in order to simplify it.

simplify(2 * (w+w+α+β + sin(z)^2 + cos(z)^2 - 1))

The rules in the default simplify applies simple constant elimination and trigonometric identities.

If you read the previous section on the rules DSL, you should be able to read and understand the rules that are used by simplify.

Code generation

Experimental feature

It is common to want to generate executable code from symbolic expressions and blocks of them. We are working on experimental support for turning Symbolic expressions into executable functions with specific focus on array input and output and performance which is critical to the Differential Equations ecosystem which is making heavy use of this package.

See Code generation for more about this.

Learn more

If you have a package that you would like to utilize rule-based rewriting in, look at the suggestions in the Interfacing section to find out how you can do that without any fundamental changes to your package. Look at the API documentation for docstrings about specific functions or macros.

Head over to the github repository to ask questions and report problems! Join the Zulip stream to chat!

diff --git a/dev/manual/codegen/index.html b/dev/manual/codegen/index.html index 026ecd80..dda67b81 100644 --- a/dev/manual/codegen/index.html +++ b/dev/manual/codegen/index.html @@ -11,7 +11,7 @@ args: Array{Any}((3,)) 1: + (function of type typeof(+)) 2: Symbol a - 3: Symbol b

Note that the function is an actual function object.

For more complex expressions, see other code-related combinators,

Namely Assignment, Let, Func, SetArray, MakeArray, MakeSparseArray and MakeTuple.

To make your own type convertible to Expr using toexpr define toexpr(x, st) and forward the state st in internal calls to toexpr. st is state used to know when to leave something like y(t) as it is or when to make it var"y(t)". E.g. when y(t) is itself the argument of a function rather than y.

source

Code Combinators

These are all exported when you do using SymbolicUtils.Code

SymbolicUtils.Code.AssignmentType
Assignment(lhs, rhs)

An assignment expression. Shorthand lhs ← rhs (\leftarrow)

source
SymbolicUtils.Code.LetType
Let(assignments, body[, let_block])

A Let block.

  • assignments is a vector of Assignments
  • body is the body of the let block
  • let_block boolean (default=true) – do not create a let block if false.
source
SymbolicUtils.Code.FuncType
Func(args, kwargs, body[, pre])

A function.

  • args is a vector of expressions
  • kwargs is a vector of Assignments
  • body is the body of the function
  • pre a vector of expressions to be prepended to the function body, for example, it could be [Expr(:meta, :inline), Expr(:meta, :propagate_inbounds)] to create an @inline @propagate_inbounds function definition.

Special features in args:

  • args can contain DestructuredArgs
  • call expressions

For example,


+    3: Symbol b

Note that the function is an actual function object.

For more complex expressions, see other code-related combinators,

Namely Assignment, Let, Func, SetArray, MakeArray, MakeSparseArray and MakeTuple.

To make your own type convertible to Expr using toexpr define toexpr(x, st) and forward the state st in internal calls to toexpr. st is state used to know when to leave something like y(t) as it is or when to make it var"y(t)". E.g. when y(t) is itself the argument of a function rather than y.

source

Code Combinators

These are all exported when you do using SymbolicUtils.Code

SymbolicUtils.Code.AssignmentType
Assignment(lhs, rhs)

An assignment expression. Shorthand lhs ← rhs (\leftarrow)

source
SymbolicUtils.Code.LetType
Let(assignments, body[, let_block])

A Let block.

  • assignments is a vector of Assignments
  • body is the body of the let block
  • let_block boolean (default=true) – do not create a let block if false.
source
SymbolicUtils.Code.FuncType
Func(args, kwargs, body[, pre])

A function.

  • args is a vector of expressions
  • kwargs is a vector of Assignments
  • body is the body of the function
  • pre a vector of expressions to be prepended to the function body, for example, it could be [Expr(:meta, :inline), Expr(:meta, :propagate_inbounds)] to create an @inline @propagate_inbounds function definition.

Special features in args:

  • args can contain DestructuredArgs
  • call expressions

For example,


 julia> @syms a b c t f(d) x(t) y(t) z(t)
 (a, b, c, t, f(::Number)::Number, x(::Number)::Number, y(::Number)::Number, z(::Number)::Number)
 
@@ -28,9 +28,9 @@
 #10 (generic function with 1 method)
 
 julia> executable(1, 2.0, [2,3.0], x->string(x); var"z(t)" = sqrt(42))
-"11.98074069840786"
source
SymbolicUtils.Code.SpawnFetchType
SpawnFetch{ParallelType}(funcs [, args], reduce)

Run every expression in funcs in its own task, the expression should be a Func object and is passed to Threads.Task(f). If Func takes arguments, then the arguments must be passed in as args–a vector of vector of arguments to each function in funcs. We don't use @spawn in order to support RuntimeGeneratedFunctions which disallow closures, instead we interpolate these functions or closures as smaller RuntimeGeneratedFunctions.

reduce function is used to combine the results of executing exprs. A SpawnFetch expression returns the reduced result.

Use Symbolics.MultithreadedForm ParallelType from the Symbolics.jl package to get the RuntimeGeneratedFunction version SpawnFetch.

ParallelType can be used to define more parallelism types SymbolicUtils supports Multithreaded type. Which spawns threaded tasks.

source
SymbolicUtils.Code.SetArrayType
SetArray(inbounds, arr, elems)

An expression representing setting of elements of arr.

By default, every element of elems is copied over to arr,

but if elems contains AtIndex(i, val) objects, then arr[i] = val is performed in its place.

inbounds is a boolean flag, true surrounds the resulting expression in an @inbounds.

source
SymbolicUtils.Code.MakeArrayType
MakeArray(elems, similarto, [output_eltype=nothing])

An expression which constructs an array.

  • elems is the output array
  • similarto can either be a type, or some symbol that is an array whose type needs to be emulated. If similarto is a StaticArrays.SArray, then the output array is also created as an SArray, similarly, an Array will result in an Array, and a LabelledArrays.SLArray will result in a labelled static array.
  • output_eltype: if set, then forces the element type of the output array to be this. by default, the output type is inferred automatically.

You can define:

@inline function create_array(A::Type{<:MyArray},a
+"11.98074069840786"
source
SymbolicUtils.Code.SpawnFetchType
SpawnFetch{ParallelType}(funcs [, args], reduce)

Run every expression in funcs in its own task, the expression should be a Func object and is passed to Threads.Task(f). If Func takes arguments, then the arguments must be passed in as args–a vector of vector of arguments to each function in funcs. We don't use @spawn in order to support RuntimeGeneratedFunctions which disallow closures, instead we interpolate these functions or closures as smaller RuntimeGeneratedFunctions.

reduce function is used to combine the results of executing exprs. A SpawnFetch expression returns the reduced result.

Use Symbolics.MultithreadedForm ParallelType from the Symbolics.jl package to get the RuntimeGeneratedFunction version SpawnFetch.

ParallelType can be used to define more parallelism types SymbolicUtils supports Multithreaded type. Which spawns threaded tasks.

source
SymbolicUtils.Code.SetArrayType
SetArray(inbounds, arr, elems)

An expression representing setting of elements of arr.

By default, every element of elems is copied over to arr,

but if elems contains AtIndex(i, val) objects, then arr[i] = val is performed in its place.

inbounds is a boolean flag, true surrounds the resulting expression in an @inbounds.

source
SymbolicUtils.Code.MakeArrayType
MakeArray(elems, similarto, [output_eltype=nothing])

An expression which constructs an array.

  • elems is the output array
  • similarto can either be a type, or some symbol that is an array whose type needs to be emulated. If similarto is a StaticArrays.SArray, then the output array is also created as an SArray, similarly, an Array will result in an Array, and a LabelledArrays.SLArray will result in a labelled static array.
  • output_eltype: if set, then forces the element type of the output array to be this. by default, the output type is inferred automatically.

You can define:

@inline function create_array(A::Type{<:MyArray},a
                               ::Nothing, d::Val{dims}, elems...) where dims
 
 # and
 
-@inline function create_array(::Type{<:MyArray}, T, ::Val{dims}, elems...) where dims

which creates an array of size dims using the elements elems and eltype T, to allow MakeArray to create arrays similarto MyArrays.

source
SymbolicUtils.Code.MakeSparseArrayType
MakeSpaseArray(array)

An expression which creates a SparseMatrixCSC or a SparseVector.

The generated expression contains the sparsity information of array,

it only creates the nzval field at run time.

source
SymbolicUtils.Code.MakeTupleType
MakeTuple(tup)

Make a Tuple from a tuple of expressions.

source
SymbolicUtils.Code.LiteralExprType
LiteralExpr(ex)

Literally ex, an Expr. toexpr on LiteralExpr recursively calls toexpr on any interpolated symbolic expressions.

source
+@inline function create_array(::Type{<:MyArray}, T, ::Val{dims}, elems...) where dims

which creates an array of size dims using the elements elems and eltype T, to allow MakeArray to create arrays similarto MyArrays.

source
SymbolicUtils.Code.MakeSparseArrayType
MakeSpaseArray(array)

An expression which creates a SparseMatrixCSC or a SparseVector.

The generated expression contains the sparsity information of array,

it only creates the nzval field at run time.

source
SymbolicUtils.Code.MakeTupleType
MakeTuple(tup)

Make a Tuple from a tuple of expressions.

source
SymbolicUtils.Code.LiteralExprType
LiteralExpr(ex)

Literally ex, an Expr. toexpr on LiteralExpr recursively calls toexpr on any interpolated symbolic expressions.

source
diff --git a/dev/manual/interface/index.html b/dev/manual/interface/index.html index d085e53f..66a8ce73 100644 --- a/dev/manual/interface/index.html +++ b/dev/manual/interface/index.html @@ -1,2 +1,2 @@ -Interfacing with SymbolicUtils.jl · SymbolicUtils.jl

Interfacing with SymbolicUtils.jl

This section is for Julia package developers who may want to use the simplify and rule rewriting system on their own expression types.

Defining the interface

SymbolicUtils matchers can match any Julia object that implements an interface to traverse it as a tree. The interface in question, is defined in the TermInterface.jl package. Its purpose is to provide a shared interface between various symbolic programming Julia packages.

In particular, you should define methods from TermInterface.jl for an expression tree type T with symbol types S to work with SymbolicUtils.jl

You can read the documentation of TermInterface.jl on the Github repository.

SymbolicUtils.jl only methods

Missing docstring.

Missing docstring for symtype. Check Documenter's build log for details.

Missing docstring.

Missing docstring for issym. Check Documenter's build log for details.

Missing docstring.

Missing docstring for promote_symtype. Check Documenter's build log for details.

+Interfacing with SymbolicUtils.jl · SymbolicUtils.jl

Interfacing with SymbolicUtils.jl

This section is for Julia package developers who may want to use the simplify and rule rewriting system on their own expression types.

Defining the interface

SymbolicUtils matchers can match any Julia object that implements an interface to traverse it as a tree. The interface in question, is defined in the TermInterface.jl package. Its purpose is to provide a shared interface between various symbolic programming Julia packages.

In particular, you should define methods from TermInterface.jl for an expression tree type T with symbol types S to work with SymbolicUtils.jl

You can read the documentation of TermInterface.jl on the Github repository.

diff --git a/dev/manual/representation/index.html b/dev/manual/representation/index.html index 3d3586ce..6fa60192 100644 --- a/dev/manual/representation/index.html +++ b/dev/manual/representation/index.html @@ -23,4 +23,4 @@ (cos(x)^2 + sin(x)^2) / (cos(x)*sin(x)) julia> simplify(ans) -1 / (cos(x)*sin(x)) +1 / (cos(x)*sin(x)) diff --git a/dev/manual/rewrite/index.html b/dev/manual/rewrite/index.html index ac8d9d0c..a71b7856 100644 --- a/dev/manual/rewrite/index.html +++ b/dev/manual/rewrite/index.html @@ -111,4 +111,4 @@ 1 + 2sin(x)*cos(x)

It restarts the chain after each successful application of a rule, so after sqexpand is hit it (re)starts again and successfully applies acpyid to resulting expression.

You can also use Fixpoint to apply the rules until there are no changes.

Fixpoint(cas)((cos(x) + sin(x))^2)
 
 # output
-1 + 2sin(x)*cos(x)
+1 + 2sin(x)*cos(x) diff --git a/dev/objects.inv b/dev/objects.inv index c7080bc72208b85dc23f4ef01b54f640549fa9c6..ca2639bbefdc6d50061757edc93fc10be80a3a4f 100644 GIT binary patch delta 940 zcmV;d15^C82&D*+hkw*C5WeRrMuI)M+aeCYsilAfDpIve&&Zohx-PLD*-p#ecnF@5 zCt>V3zng7#dvoHMZ|0j{3zgw-urB+M=@i%nzR6~4&9Mo}n-CgJ0C3ieS81!ZjU$PH1WhycB(L z`dq@*?N{u>3H#~Lw?jJ<$>3>42n_O#IYU}Z0h|O#QS`}zj zTE`}9j3r+vo)!5s5vUl`N3k28>pY4hZ$mdV|XP~7PQ%$iXUm{A?~ zpI0I5Ggt?dL($me2Dwp8YJam|?coaiRZ@r1hArlOn{MGK71mRb zYBu2{>u$F-G<}ljG^W%Jh;cF*J%idJVZCRN-!$PFG_tq{u=xCD@$P)_{_PCR3m#zC zTQ1aT(>7}KhV3WJYY^YK2jvoi^K>Ld$y1{tCVD8kczfO#p7df}@+b8LX?~v0V2$p|7`{76vs+}| zd7V)s!ND@M)y6vEs+wvZx7RcBI~g1~N;q(c^L40jB>x*cF?HmgozGd2nl$*G|H9KF ze(4v?yxAbN7tm%U9Tibw$@de2@A*&x8YTMzRxQAfs;uc9tx6g9Q}!r7X*@~o@aO53 O<=0hkx8Q5Wedv255X_HZF2#bBgOEKnfQy*2%fxN+T~5{s<&xN4Gvi zpRiBTAw~Ubt=-%xalUW80Hab0t{vshVls6F?>=rQT4zz)8kRWL(wU4MWRs*<7 z#p!bgH}{{())Fo19J9KjxE2!=)2|(TO?Tc*ZGxxkEV#_3F=P}&HjQDVmHdG6bUJ={ zb#6k)a@JcurGE~d5z*AQWIywUP3&h+*Hd)oF)_U4^B>%a+%Z!!TT5gI?yz9K4MfU4 z6MCxaLn)t5;_i!5-I`tXdRUyDn;}Vbn!D5wh{Y(UH&$OHZ1lzoYb?I8 zW*Qd+S08`9`t8lt?{7C~e&Tg^d*ni2Hfki#Mi#+!l~2~L)&s}If#U$?H7X?wjO->7KFS;EnWG+(EH zGyT8A3sXntx$T@qsa1u4@$Yzf#NUSnvox(XN-YA~t)%lK8jPf>vhF8L?FqirRcrIv dP)cJMooCreGe;SZe_viM;Q_T_*8jX*OGpe{-t_[::T1] [::T2]...\n\nFor instance:\n\n@syms foo::Real bar baz(x, y::Real)::Complex\n\nCreate one or more variables. 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.\n\nExamples:\n\n@syms foo bar::Real baz::Int will create\n\nvariable foo of symtype Number (the default), bar of symtype Real and baz of symtype Int\n\n@syms f(x) g(y::Real, x)::Int h(a::Int, f(b)) creates 1-arg f 2-arg g\n\nand 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.\n\n\n\n\n\n","category":"macro"},{"location":"api/#SymbolicUtils.symtype","page":"API Reference","title":"SymbolicUtils.symtype","text":"symtype(x)\n\n\nReturns 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.\n\n\n\n\n\n","category":"function"},{"location":"api/#SymbolicUtils.promote_symtype","page":"API Reference","title":"SymbolicUtils.promote_symtype","text":"promote_symtype(f, Ts...)\n\nThe result of applying f to arguments of symtype Ts...\n\njulia> promote_symtype(+, Real, Real)\nReal\n\njulia> promote_symtype(+, Complex, Real)\nNumber\n\njulia> @syms f(x)::Complex\n(f(::Number)::Complex,)\n\njulia> promote_symtype(f, Number)\nComplex\n\nWhen constructing Terms without an explicit symtype, promote_symtype is used to figure out the symtype of the Term.\n\n\n\n\n\npromote_symtype(f::FnType{X,Y}, arg_symtypes...)\n\nThe 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.\n\n\n\n\n\n","category":"function"},{"location":"api/#Rewriters","page":"API Reference","title":"Rewriters","text":"","category":"section"},{"location":"api/","page":"API Reference","title":"API Reference","text":"@rule\nSymbolicUtils.Rewriters","category":"page"},{"location":"api/#SymbolicUtils.@rule","page":"API Reference","title":"SymbolicUtils.@rule","text":"@rule LHS => RHS\n\nCreates 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.\n\nLHS 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).\n\nIf 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.\n\nSlot:\n\nA 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).\n\nExample:\n\nSimple rule to turn any sin into cos:\n\njulia> @syms a b c\n(a, b, c)\n\njulia> r = @rule sin(~x) => cos(~x)\nsin(~x) => cos(~x)\n\njulia> r(sin(1+a))\ncos((1 + a))\n\nA rule with 2 segment variables\n\njulia> r = @rule sin(~x + ~y) => sin(~x)*cos(~y) + cos(~x)*sin(~y)\nsin(~x + ~y) => sin(~x) * cos(~y) + cos(~x) * sin(~y)\n\njulia> r(sin(a + b))\ncos(a)*sin(b) + sin(a)*cos(b)\n\nA rule that matches two of the same expressions:\n\njulia> r = @rule sin(~x)^2 + cos(~x)^2 => 1\nsin(~x) ^ 2 + cos(~x) ^ 2 => 1\n\njulia> r(sin(2a)^2 + cos(2a)^2)\n1\n\njulia> r(sin(2a)^2 + cos(a)^2)\n# nothing\n\nSegment:\n\nA Segment variable is written as ~~x and matches zero or more expressions in the function call.\n\nExample:\n\nThis 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.\n\njulia> r = @rule ~x * +((~~ys)) => sum(map(y-> ~x * y, ~~ys));\n\njulia> r(2 * (a+b+c))\n((2 * a) + (2 * b) + (2 * c))\n\nPredicates:\n\nThere 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.\n\nThe predicate should return true if the current match is acceptable, and false otherwise.\n\njulia> two_πs(x::Number) = abs(round(x/(2π)) - x/(2π)) < 10^-9\ntwo_πs (generic function with 1 method)\n\njulia> two_πs(x) = false\ntwo_πs (generic function with 2 methods)\n\njulia> r = @rule sin(~~x + ~y::two_πs + ~~z) => sin(+(~~x..., ~~z...))\nsin(~(~x) + ~(y::two_πs) + ~(~z)) => sin(+(~(~x)..., ~(~z)...))\n\njulia> r(sin(a+3π))\n\njulia> r(sin(a+6π))\nsin(a)\n\njulia> r(sin(a+6π+c))\nsin((a + c))\n\nPredicate function gets an array of values if attached to a segment variable (~~x).\n\nFor the predicate over the whole rule, use @rule => where :\n\njulia> @syms a b;\n\njulia> predicate(x) = x === a;\n\njulia> r = @rule ~x => ~x where predicate(~x);\n\njulia> r(a)\na\n\njulia> r(b) === nothing\ntrue\n\nNote that this is syntactic sugar and that it is the same as something like @rule ~x => f(~x) ? ~x : nothing.\n\nContext:\n\nIn 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).\n\nThe function can use the inputs however it wants, and must return a boolean indicating whether the predicate holds or not.\n\nIn the consequent pattern: Use (@ctx) to access the context object on the right hand side of an expression.\n\n\n\n\n\n","category":"macro"},{"location":"api/#SymbolicUtils.Rewriters","page":"API Reference","title":"SymbolicUtils.Rewriters","text":"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.\n\nThe Rewriters module contains some types which create and transform rewriters.\n\nEmpty() is a rewriter which always returns nothing\nChain(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.\nRestartedChain(itr) like Chain(itr) but restarts from the first rewriter once on the first successful application of one of the chained rewriters.\nIfElse(cond, rw1, rw2) runs the cond function on the input, applies rw1 if cond returns true, rw2 if it returns false\nIf(cond, rw) is the same as IfElse(cond, rw, Empty())\nPrewalk(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.\nPostwalk(rw; threaded=false, thread_cutoff=100) similarly does post-order traversal.\nFixpoint(rw) returns a rewriter which applies rw repeatedly until there are no changes to be made.\nFixpointNoCycle behaves like Fixpoint but instead it applies rw repeatedly only while it is returning new results.\nPassThrough(rw) returns a rewriter which if rw(x) returns nothing will instead return x otherwise will return rw(x).\n\n\n\n\n\n","category":"module"},{"location":"api/#Simplify","page":"API Reference","title":"Simplify","text":"","category":"section"},{"location":"api/","page":"API Reference","title":"API Reference","text":"SymbolicUtils.simplify\nSymbolicUtils.expand\nSymbolicUtils.substitute","category":"page"},{"location":"api/#SymbolicUtils.simplify","page":"API Reference","title":"SymbolicUtils.simplify","text":"simplify(x; expand=false,\n threaded=false,\n thread_subtree_cutoff=100,\n rewriter=nothing)\n\nSimplify an expression (x) by applying rewriter until there are no changes. expand=true applies expand in the beginning of each fixpoint iteration.\n\nBy default, simplify will assume denominators are not zero and allow cancellation in fractions. Pass simplify_fractions=false to prevent this.\n\n\n\n\n\n","category":"function"},{"location":"api/#SymbolicUtils.expand","page":"API Reference","title":"SymbolicUtils.expand","text":"expand(expr)\n\nExpand expressions by distributing multiplication over addition, e.g., a*(b+c) becomes ab+ac.\n\nexpand 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.\n\n\n\n\n\n","category":"function"},{"location":"api/#SymbolicUtils.substitute","page":"API Reference","title":"SymbolicUtils.substitute","text":"substitute(expr, dict; fold=true)\n\nsubstitute any subexpression that matches a key in dict with the corresponding value. If fold=false, expressions which can be evaluated won't be evaluated.\n\njulia> substitute(1+sqrt(y), Dict(y => 2), fold=true)\n2.414213562373095\njulia> substitute(1+sqrt(y), Dict(y => 2), fold=false)\n1 + sqrt(2)\n\n\n\n\n\n","category":"function"},{"location":"api/#Utilities","page":"API Reference","title":"Utilities","text":"","category":"section"},{"location":"api/","page":"API Reference","title":"API Reference","text":"SymbolicUtils.@timerewrite","category":"page"},{"location":"api/#SymbolicUtils.@timerewrite","page":"API Reference","title":"SymbolicUtils.@timerewrite","text":"@timerewrite expr\n\nIf expr calls simplify or a RuleSet object, track the amount of time it spent on applying each rule and pretty print the timing.\n\nThis uses TimerOutputs.jl.\n\nExample:\n\n\njulia> expr = foldr(*, rand([a,b,c,d], 100))\n(a ^ 26) * (b ^ 30) * (c ^ 16) * (d ^ 28)\n\njulia> @timerewrite simplify(expr)\n ────────────────────────────────────────────────────────────────────────────────────────────────\n Time Allocations\n ────────────────────── ───────────────────────\n Tot / % measured: 340ms / 15.3% 92.2MiB / 10.8%\n\n Section ncalls time %tot avg alloc %tot avg\n ────────────────────────────────────────────────────────────────────────────────────────────────\n ACRule((~y) ^ ~n * ~y => (~y) ^ (~n ... 667 11.1ms 21.3% 16.7μs 2.66MiB 26.8% 4.08KiB\n RHS 92 277μs 0.53% 3.01μs 14.4KiB 0.14% 160B\n ACRule((~x) ^ ~n * (~x) ^ ~m => (~x)... 575 7.63ms 14.6% 13.3μs 1.83MiB 18.4% 3.26KiB\n (*)(~(~(x::!issortedₑ))) => sort_arg... 831 6.31ms 12.1% 7.59μs 738KiB 7.26% 910B\n RHS 164 3.03ms 5.81% 18.5μs 250KiB 2.46% 1.52KiB\n ...\n ...\n ────────────────────────────────────────────────────────────────────────────────────────────────\n(a ^ 26) * (b ^ 30) * (c ^ 16) * (d ^ 28)\n\n\n\n\n\n","category":"macro"},{"location":"manual/representation/#Term-representation-and-simplification","page":"Term representation and simplification","title":"Term representation and simplification","text":"","category":"section"},{"location":"manual/representation/","page":"Term representation and simplification","title":"Term representation and simplification","text":"Performance of symbolic simplification depends on the datastructures used to represent terms. Efficient datastructures often have the advantage of automatic simplification, and of efficient storage.","category":"page"},{"location":"manual/representation/","page":"Term representation and simplification","title":"Term representation and simplification","text":"The most basic term representation simply holds a function call and stores the function and the arguments it is called with. This is done by the Term type in SymbolicUtils. Functions that aren't commutative or associative, such as sin or hypot are stored as Terms. Commutative and associative operations like +, *, and their supporting operations like -, / and ^, when used on terms of type <:Number, stand to gain from the use of more efficient datastrucutres.","category":"page"},{"location":"manual/representation/","page":"Term representation and simplification","title":"Term representation and simplification","text":"All term representations must support operation and arguments functions. And they must define iscall and isexpr to return true when called with an instance of the type. Generic term-manipulation programs such as the rule-based rewriter make use of this interface to inspect expressions. In this way, the interface wins back the generality lost by having a zoo of term representations instead of one. (see interface section for more on this.)","category":"page"},{"location":"manual/representation/#Preliminary-representation-of-arithmetic","page":"Term representation and simplification","title":"Preliminary representation of arithmetic","text":"","category":"section"},{"location":"manual/representation/","page":"Term representation and simplification","title":"Term representation and simplification","text":"Linear combinations such as alpha_1 x_1 + alpha_2 x_2 ++ alpha_n x_n are represented by Add(Dict(x₁ => α₁, x₂ => α₂, ..., xₙ => αₙ)). Here, any x_i may itself be other types mentioned here, except for Add. When an Add is added to an Add, we merge their dictionaries and add up matching coefficients to create a single \"flattened\" Add.","category":"page"},{"location":"manual/representation/","page":"Term representation and simplification","title":"Term representation and simplification","text":"Similarly, x_1^m_1x_2^m_2x_m_n is represented by Mul(Dict(x₁ => m₁, x₂ => m₂, ..., xₙ => mₙ)). x_i may not themselves be Mul, multiplying a Mul with another Mul returns a \"flattened\" Mul.","category":"page"},{"location":"manual/representation/","page":"Term representation and simplification","title":"Term representation and simplification","text":"Note that Add and Mul types perform a preliminary simplification which suffices to simplify numeric expressions to a large extent during construction.","category":"page"},{"location":"manual/representation/","page":"Term representation and simplification","title":"Term representation and simplification","text":"p q","category":"page"},{"location":"manual/representation/","page":"Term representation and simplification","title":"Term representation and simplification","text":"is represented by Div(p, q). The result of * on Div is maintained as a Div. For example, Div(p_1, q_1) * Div(p_2, q_2) results in Div(p_1 * p_2, q_1 * q_2) and so on. The effect is, in Div(p, q), p or q or, if they are Mul, any of their multiplicands is not a Div. So Muls must always be nested inside a Div and can never show up immediately wrapping it. This rule sets up an expression so that it helps the simplify_fractions procedure described two sections below.","category":"page"},{"location":"manual/representation/#Polynomial-representation","page":"Term representation and simplification","title":"Polynomial representation","text":"","category":"section"},{"location":"manual/representation/","page":"Term representation and simplification","title":"Term representation and simplification","text":"Packages like DynamicPolynomials.jl provide representations that are even more efficient than the Add and Mul types mentioned above. They are designed specifically for multi-variate polynomials. They provide common algorithms such as multi-variate polynomial GCD. The restrictions that make it fast also mean some things are not possible: Firstly, DynamicPolynomials can only represent flat polynomials. For example, (x-3)*(x+5) can only be represented as (x^2) + 15 - 8x. Secondly, DynamicPolynomials does not have ways to represent generic Terms such as sin(x-y) in the tree.","category":"page"},{"location":"manual/representation/","page":"Term representation and simplification","title":"Term representation and simplification","text":"To reconcile these differences while being able to use the efficient algorithms of DynamicPolynomials we have the PolyForm type. This type holds a polynomial and the mappings necessary to present the polynomial as a SymbolicUtils expression (i.e. by defining operation and arguments). The mappings constructed for the conversion are 1) a bijection from DynamicPolynomials Variable type to a Symbolics Sym, and 2) a mapping from Syms to non-polynomial terms that the Syms stand-in for. These terms may themselves contain PolyForm if there are polynomials inside them. The mappings are transiently global, that is, when all references to the mappings go out of scope, they are released and re-created.","category":"page"},{"location":"manual/representation/","page":"Term representation and simplification","title":"Term representation and simplification","text":"julia> @syms x y\n(x, y)\n\njulia> PolyForm((x-3)*(y-5))\nx*y + 15 - 5x - 3y","category":"page"},{"location":"manual/representation/","page":"Term representation and simplification","title":"Term representation and simplification","text":"Terms for which the operation is not +, *, or ^ are replaced with a generated symbol when representing the polynomial, and a mapping from this new symbol to the original expression it stands-in for is maintained as stated above.","category":"page"},{"location":"manual/representation/","page":"Term representation and simplification","title":"Term representation and simplification","text":"julia> p = PolyForm((sin(x) + cos(x))^2)\n(cos(x)^2) + 2cos(x)*sin(x) + (sin(x)^2)\n\njulia> p.p # this is the actual DynamicPolynomial stored\ncos_3658410937268741549² + 2cos_3658410937268741549sin_10720964503106793468 + sin_10720964503106793468²","category":"page"},{"location":"manual/representation/","page":"Term representation and simplification","title":"Term representation and simplification","text":"By default, polynomials inside non-polynomial terms are not also converted to PolyForm. For example,","category":"page"},{"location":"manual/representation/","page":"Term representation and simplification","title":"Term representation and simplification","text":"julia> PolyForm(sin((x-3)*(y-5)))\nsin((x - 3)*(y - 5))","category":"page"},{"location":"manual/representation/","page":"Term representation and simplification","title":"Term representation and simplification","text":"But you can pass in the recurse=true keyword argument to do this.","category":"page"},{"location":"manual/representation/","page":"Term representation and simplification","title":"Term representation and simplification","text":"julia> PolyForm(sin((x-3)*(y-5)), recurse=true)\nsin(x*y + 15 - 5x - 3y)","category":"page"},{"location":"manual/representation/","page":"Term representation and simplification","title":"Term representation and simplification","text":"Polynomials are constructed by first turning symbols and non-polynomial terms into DynamicPolynomials-style variables and then applying the +, *, ^ operations on these variables. You can control the list of the polynomial operations with the Fs keyword argument. It is a Union type of the functions to apply. For example, let's say you want to turn terms into polynomials by only applying the + and ^ operations, and want to preserve * operations as-is, you could pass in Fs=Union{typeof(+), typeof(^)}","category":"page"},{"location":"manual/representation/","page":"Term representation and simplification","title":"Term representation and simplification","text":"julia> PolyForm((x+y)^2*(x-y), Fs=Union{typeof(+), typeof(^)}, recurse=true)\n((x - (y))*((x^2) + 2x*y + (y^2)))","category":"page"},{"location":"manual/representation/","page":"Term representation and simplification","title":"Term representation and simplification","text":"Note that in this case recurse=true was necessary as otherwise the polynomialization would not descend into the * operation as it is now considered a generic operation.","category":"page"},{"location":"manual/representation/#Simplifying-fractions","page":"Term representation and simplification","title":"Simplifying fractions","text":"","category":"section"},{"location":"manual/representation/","page":"Term representation and simplification","title":"Term representation and simplification","text":"simplify_fractions(expr) recurses through expr finding Divs and simplifying them using polynomial division.","category":"page"},{"location":"manual/representation/","page":"Term representation and simplification","title":"Term representation and simplification","text":"First the factors of the numerators and the denominators are converted into PolyForm objects, then numerators and denominators are divided by their respective pairwise GCDs. The conversion of the numerator and denominator into PolyForm is set up so that simplify_fractions does not result in increase in the expression size due to polynomial expansion. Specifically, the factors are individually converted into PolyForm objects, and any powers of polynomial is not expanded, but the division process repeatedly divides them as many times as the power.","category":"page"},{"location":"manual/representation/","page":"Term representation and simplification","title":"Term representation and simplification","text":"julia> simplify_fractions((x*y+5x+3y+15)/((x+3)*(x-4)))\n(5.0 + y) / (x - 4)\n\njulia> simplify_fractions((x*y+5x+3y+15)^2/((x+3)*(x-4)))\n((5.0 + y)*(15 + 5x + x*y + 3y)) / (x - 4)\n\njulia> simplify_fractions(3/(x+3) + x/(x+3))\n1\n\njulia> simplify_fractions(sin(x)/cos(x) + cos(x)/sin(x))\n(cos(x)^2 + sin(x)^2) / (cos(x)*sin(x))\n\njulia> simplify(ans)\n1 / (cos(x)*sin(x))","category":"page"},{"location":"manual/rewrite/#Term-Rewriting","page":"Term Rewriting","title":"Term Rewriting","text":"","category":"section"},{"location":"manual/rewrite/#Rule-based-rewriting","page":"Term Rewriting","title":"Rule-based rewriting","text":"","category":"section"},{"location":"manual/rewrite/","page":"Term Rewriting","title":"Term Rewriting","text":"Rewrite rules match and transform an expression. A rule is written using either the @rule macro or the @acrule macro. It creates a callable Rule object.","category":"page"},{"location":"manual/rewrite/#Basics-of-rule-based-term-rewriting-in-SymbolicUtils","page":"Term Rewriting","title":"Basics of rule-based term rewriting in SymbolicUtils","text":"","category":"section"},{"location":"manual/rewrite/","page":"Term Rewriting","title":"Term Rewriting","text":"Here is a simple rewrite rule, that uses formula for the double angle of the sine function:","category":"page"},{"location":"manual/rewrite/","page":"Term Rewriting","title":"Term Rewriting","text":"using SymbolicUtils\n\n@syms w z α::Real β::Real\n\n(w, z, α, β) # hide\n\nr1 = @rule sin(2(~x)) => 2sin(~x)*cos(~x)\n\nr1(sin(2z))\n\n# output\n2sin(z)*cos(z)","category":"page"},{"location":"manual/rewrite/","page":"Term Rewriting","title":"Term Rewriting","text":"The @rule macro takes a pair of patterns – the matcher and the consequent (@rule matcher => consequent). If an expression matches the matcher pattern, it is rewritten to the consequent pattern. @rule returns a callable object that applies the rule to an expression.","category":"page"},{"location":"manual/rewrite/","page":"Term Rewriting","title":"Term Rewriting","text":"~x in the example is what is a slot variable named x. In a matcher pattern, slot variables are placeholders that match exactly one expression. When used on the consequent side, they stand in for the matched expression. If a slot variable appears twice in a matcher pattern, all corresponding matches must be equal (as tested by Base.isequal function). Hence this rule says: if you see something added to itself, make it twice of that thing, and works as such.","category":"page"},{"location":"manual/rewrite/","page":"Term Rewriting","title":"Term Rewriting","text":"If you try to apply this rule to an expression with triple angle, it will return nothing – this is the way a rule signifies failure to match.","category":"page"},{"location":"manual/rewrite/","page":"Term Rewriting","title":"Term Rewriting","text":"r1(sin(3z)) === nothing\n\n# output\ntrue","category":"page"},{"location":"manual/rewrite/","page":"Term Rewriting","title":"Term Rewriting","text":"Slot variable (matcher) is not necessary a single variable","category":"page"},{"location":"manual/rewrite/","page":"Term Rewriting","title":"Term Rewriting","text":"r1(sin(2*(w-z)))\n\n# output\n2cos(w - z)*sin(w - z)","category":"page"},{"location":"manual/rewrite/","page":"Term Rewriting","title":"Term Rewriting","text":"but it must be a single expression","category":"page"},{"location":"manual/rewrite/","page":"Term Rewriting","title":"Term Rewriting","text":"r1(sin(2*(w+z)*(α+β))) === nothing\n\n# output\ntrue","category":"page"},{"location":"manual/rewrite/","page":"Term Rewriting","title":"Term Rewriting","text":"Rules are of course not limited to single slot variable","category":"page"},{"location":"manual/rewrite/","page":"Term Rewriting","title":"Term Rewriting","text":"r2 = @rule sin(~x + ~y) => sin(~x)*cos(~y) + cos(~x)*sin(~y);\n\nr2(sin(α+β))\n\n# output\nsin(β)*cos(α) + cos(β)*sin(α)","category":"page"},{"location":"manual/rewrite/","page":"Term Rewriting","title":"Term Rewriting","text":"If you want to match a variable number of subexpressions at once, you will need a segment variable. ~~xs in the following example is a segment variable:","category":"page"},{"location":"manual/rewrite/","page":"Term Rewriting","title":"Term Rewriting","text":"@syms x y z\n@rule(+(~~xs) => ~~xs)(x + y + z)\n\n# output\n3-element view(::Vector{Any}, 1:3) with eltype Any:\n z\n y\n x","category":"page"},{"location":"manual/rewrite/","page":"Term Rewriting","title":"Term Rewriting","text":"~~xs is a vector of subexpressions matched. You can use it to construct something more useful:","category":"page"},{"location":"manual/rewrite/","page":"Term Rewriting","title":"Term Rewriting","text":"r3 = @rule ~x * +(~~ys) => sum(map(y-> ~x * y, ~~ys));\n\nr3(2 * (w+w+α+β))\n\n# output\n4w + 2α + 2β","category":"page"},{"location":"manual/rewrite/","page":"Term Rewriting","title":"Term Rewriting","text":"Notice that the expression was autosimplified before application of the rule.","category":"page"},{"location":"manual/rewrite/","page":"Term Rewriting","title":"Term Rewriting","text":"2 * (w+w+α+β)\n\n# output\n2(2w + α + β)","category":"page"},{"location":"manual/rewrite/#Predicates-for-matching","page":"Term Rewriting","title":"Predicates for matching","text":"","category":"section"},{"location":"manual/rewrite/","page":"Term Rewriting","title":"Term Rewriting","text":"Matcher pattern may contain slot variables with attached predicates, written as ~x::f where f is a function that takes a matched expression and returns a boolean value. Such a slot will be considered a match only if f returns true.","category":"page"},{"location":"manual/rewrite/","page":"Term Rewriting","title":"Term Rewriting","text":"Similarly ~~x::g is a way of attaching a predicate g to a segment variable. In the case of segment variables g gets a vector of 0 or more expressions and must return a boolean value. If the same slot or segment variable appears twice in the matcher pattern, then at most one of the occurrence should have a predicate.","category":"page"},{"location":"manual/rewrite/","page":"Term Rewriting","title":"Term Rewriting","text":"For example,","category":"page"},{"location":"manual/rewrite/","page":"Term Rewriting","title":"Term Rewriting","text":"using SymbolicUtils\n@syms a b c d\n\nr = @rule ~x + ~~y::(ys->iseven(length(ys))) => \"odd terms\";\n\n@show r(a + b + c + d)\n@show r(b + c + d)\n@show r(b + c + b)\n@show r(a + b)\n\n# output\nr(a + b + c + d) = nothing\nr(b + c + d) = \"odd terms\"\nr(b + c + b) = nothing\nr(a + b) = nothing","category":"page"},{"location":"manual/rewrite/#Associative-Commutative-Rules","page":"Term Rewriting","title":"Associative-Commutative Rules","text":"","category":"section"},{"location":"manual/rewrite/","page":"Term Rewriting","title":"Term Rewriting","text":"Given an expression f(x, f(y, z, u), v, w), a f is said to be associative if the expression is equivalent to f(x, y, z, u, v, w) and commutative if the order of arguments does not matter. SymbolicUtils has a special @acrule macro meant for rules on functions which are associate and commutative such as addition and multiplication of real and complex numbers.","category":"page"},{"location":"manual/rewrite/","page":"Term Rewriting","title":"Term Rewriting","text":"using SymbolicUtils\n@syms x y z\n\nacr = @acrule((~a)^(~x) * (~a)^(~y) => (~a)^(~x + ~y))\n\nacr(x^y * x^z)\n\n# output\nx^(y + z)","category":"page"},{"location":"manual/rewrite/","page":"Term Rewriting","title":"Term Rewriting","text":"although in case of Number it also works the same way with regular @rule since autosimplification orders and applies associativity and commutativity to the expression.","category":"page"},{"location":"manual/rewrite/#Example-of-applying-the-rules-to-simplify-expression","page":"Term Rewriting","title":"Example of applying the rules to simplify expression","text":"","category":"section"},{"location":"manual/rewrite/","page":"Term Rewriting","title":"Term Rewriting","text":"Consider expression (cos(x) + sin(x))^2 that we would like simplify by applying some trigonometric rules. First, we need rule to expand square of cos(x) + sin(x). First we try the simplest rule to expand square of the sum and try it on simple expression","category":"page"},{"location":"manual/rewrite/","page":"Term Rewriting","title":"Term Rewriting","text":"using SymbolicUtils\n\n@syms x::Real y::Real\n\nsqexpand = @rule (~x + ~y)^2 => (~x)^2 + (~y)^2 + 2 * ~x * ~y\n\nsqexpand((cos(x) + sin(x))^2)\n\n# output\nsin(x)^2 + 2sin(x)*cos(x) + cos(x)^2","category":"page"},{"location":"manual/rewrite/","page":"Term Rewriting","title":"Term Rewriting","text":"It works. This can be further simplified using Pythagorean identity and check it","category":"page"},{"location":"manual/rewrite/","page":"Term Rewriting","title":"Term Rewriting","text":"pyid = @rule sin(~x)^2 + cos(~x)^2 => 1\n\npyid(cos(x)^2 + sin(x)^2) === nothing\n\n# output\ntrue","category":"page"},{"location":"manual/rewrite/","page":"Term Rewriting","title":"Term Rewriting","text":"Why does it return nothing? If we look at the rule, we see that the order of sin(x) and cos(x) is different. Therefore, in order to work, the rule needs to be associative-commutative.","category":"page"},{"location":"manual/rewrite/","page":"Term Rewriting","title":"Term Rewriting","text":"acpyid = @acrule sin(~x)^2 + cos(~x)^2 => 1\n\nacpyid(cos(x)^2 + sin(x)^2 + 2cos(x)*sin(x))\n\n# output\n1 + 2sin(x)*cos(x)","category":"page"},{"location":"manual/rewrite/","page":"Term Rewriting","title":"Term Rewriting","text":"It has been some work. Fortunately rules may be chained together into more sophisticated rewriters to avoid manual application of the rules.","category":"page"},{"location":"manual/rewrite/#Composing-rewriters","page":"Term Rewriting","title":"Composing rewriters","text":"","category":"section"},{"location":"manual/rewrite/","page":"Term Rewriting","title":"Term Rewriting","text":"A rewriter is any callable object 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 Rules we created above are rewriters.","category":"page"},{"location":"manual/rewrite/","page":"Term Rewriting","title":"Term Rewriting","text":"The SymbolicUtils.Rewriters module contains some types which create and transform rewriters.","category":"page"},{"location":"manual/rewrite/","page":"Term Rewriting","title":"Term Rewriting","text":"Empty() is a rewriter which always returns nothing\nChain(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.\nRestartedChain(itr) like Chain(itr) but restarts from the first rewriter once on the first successful application of one of the chained rewriters.\nIfElse(cond, rw1, rw2) runs the cond function on the input, applies rw1 if cond returns true, rw2 if it returns false\nIf(cond, rw) is the same as IfElse(cond, rw, Empty())\nPrewalk(rw; threaded=false, thread_cutoff=100) returns a rewriter which does a pre-order (from top to bottom and from left to right) traversal of a given expression and applies the rewriter rw. threaded=true will use multi threading for traversal. 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. If you are applying multiple rules, then Chain already has the appropriate passthrough behavior. If you only want to apply one rule, then consider using PassThrough. thread_cutoff is the minimum number of nodes in a subtree which should be walked in a threaded spawn.\nPostwalk(rw; threaded=false, thread_cutoff=100) similarly does post-order (from left to right and from bottom to top) traversal.\nFixpoint(rw) returns a rewriter which applies rw repeatedly until there are no changes to be made.\nPassThrough(rw) returns a rewriter which if rw(x) returns nothing will instead return x otherwise will return rw(x).","category":"page"},{"location":"manual/rewrite/#Chaining-rewriters","page":"Term Rewriting","title":"Chaining rewriters","text":"","category":"section"},{"location":"manual/rewrite/","page":"Term Rewriting","title":"Term Rewriting","text":"Several rules may be chained to give chain of rules. Chain is an array of rules which are subsequently applied to the expression.","category":"page"},{"location":"manual/rewrite/","page":"Term Rewriting","title":"Term Rewriting","text":"To check that, we will combine rules from previous example into a chain","category":"page"},{"location":"manual/rewrite/","page":"Term Rewriting","title":"Term Rewriting","text":"using SymbolicUtils\nusing SymbolicUtils.Rewriters\n\n@syms x\n\nsqexpand = @rule (~x + ~y)^2 => (~x)^2 + (~y)^2 + 2 * ~x * ~y\nacpyid = @acrule sin(~x)^2 + cos(~x)^2 => 1\n\ncsa = Chain([sqexpand, acpyid])\n\ncsa((cos(x) + sin(x))^2)\n\n# output\n1 + 2sin(x)*cos(x)","category":"page"},{"location":"manual/rewrite/","page":"Term Rewriting","title":"Term Rewriting","text":"Important feature of Chain is that it returns the expression instead of nothing if it doesn't change the expression","category":"page"},{"location":"manual/rewrite/","page":"Term Rewriting","title":"Term Rewriting","text":"Chain([@acrule sin(~x)^2 + cos(~x)^2 => 1])((cos(x) + sin(x))^2)\n\n# output\n(sin(x) + cos(x))^2","category":"page"},{"location":"manual/rewrite/","page":"Term Rewriting","title":"Term Rewriting","text":"it's important to notice, that chain is ordered, so if rules are in different order it wouldn't work the same as in earlier example","category":"page"},{"location":"manual/rewrite/","page":"Term Rewriting","title":"Term Rewriting","text":"cas = Chain([acpyid, sqexpand])\n\ncas((cos(x) + sin(x))^2)\n\n# output\nsin(x)^2 + 2sin(x)*cos(x) + cos(x)^2","category":"page"},{"location":"manual/rewrite/","page":"Term Rewriting","title":"Term Rewriting","text":"since Pythagorean identity is applied before square expansion, so it is unable to match squares of sine and cosine.","category":"page"},{"location":"manual/rewrite/","page":"Term Rewriting","title":"Term Rewriting","text":"One way to circumvent the problem of order of applying rules in chain is to use RestartedChain","category":"page"},{"location":"manual/rewrite/","page":"Term Rewriting","title":"Term Rewriting","text":"using SymbolicUtils.Rewriters: RestartedChain\n\nrcas = RestartedChain([acpyid, sqexpand])\n\nrcas((cos(x) + sin(x))^2)\n\n# output\n1 + 2sin(x)*cos(x)","category":"page"},{"location":"manual/rewrite/","page":"Term Rewriting","title":"Term Rewriting","text":"It restarts the chain after each successful application of a rule, so after sqexpand is hit it (re)starts again and successfully applies acpyid to resulting expression.","category":"page"},{"location":"manual/rewrite/","page":"Term Rewriting","title":"Term Rewriting","text":"You can also use Fixpoint to apply the rules until there are no changes.","category":"page"},{"location":"manual/rewrite/","page":"Term Rewriting","title":"Term Rewriting","text":"Fixpoint(cas)((cos(x) + sin(x))^2)\n\n# output\n1 + 2sin(x)*cos(x)","category":"page"},{"location":"upgrade/#Upgrade-to-SymbolicUtils-v1","page":"Upgrade to SymbolicUtils v1","title":"Upgrade to SymbolicUtils v1","text":"","category":"section"},{"location":"upgrade/","page":"Upgrade to SymbolicUtils v1","title":"Upgrade to SymbolicUtils v1","text":"The version 1 of SymbolicUtils utilizes Unityper to speed up the compilation time. We introduce a new type BasicSymbolic <: Symbolic to represent all the previous types Sym, Term, Add, Mul, Pow and Div. Since BasicSymbolic is a concrete type, checking x isa Sym or x isa Pow no longer works. Instead, one should use issym(x) or ispow(x) to check the \"type\" of the expression. Also, note that if one does not need to work on a specific symbolic representation, issym(x) and isterm(x) should be replaced by x isa Symbolic && !istree(x) and istree(x) to be more generic. Furthermore, dispatching on Sym, Term, etc no longer works. Instead, a function that takes BasicSymbolic should check the type of the expression using a if statement to select the right code path.","category":"page"},{"location":"upgrade/","page":"Upgrade to SymbolicUtils v1","title":"Upgrade to SymbolicUtils v1","text":"Although we don't have Sym, Term, etc in the Julia type system anymore, we still provide convenient constructors for them. For example, Sym{Real}(:x) would still work. For constructor that takes a collection like Terms, we recommend directly construct Any element type constructors like Term(f, Any[x1, x2]) to reduce extra memory allocation and compile time.","category":"page"},{"location":"manual/codegen/#Code-generation","page":"Code generation","title":"Code generation","text":"","category":"section"},{"location":"manual/codegen/","page":"Code generation","title":"Code generation","text":"Note: this feature is experimental and the API might change frequently","category":"page"},{"location":"manual/codegen/","page":"Code generation","title":"Code generation","text":"toexpr(ex) turns any expression (ex) into the equivalent Expr which is suitable for eval. The SymbolicUtils.Code module provides some combinators which provides the ability to construct more complex expressions than just function calls. These include:","category":"page"},{"location":"manual/codegen/","page":"Code generation","title":"Code generation","text":"Let blocks\nFunctions with arguments and keyword arguments\nFunctions with arguments which are to be de-structured\nExpressions that set array elements in-place\nExpressions that create an array similar in type to a reference array (currently supports Array, StaticArrays.SArray, and LabelledArrays.SLArray)\nExpressions that create sparse arrays","category":"page"},{"location":"manual/codegen/","page":"Code generation","title":"Code generation","text":"Do using SymbolicUtils.Code to get the following bindings","category":"page"},{"location":"manual/codegen/#toexpr","page":"Code generation","title":"toexpr","text":"","category":"section"},{"location":"manual/codegen/","page":"Code generation","title":"Code generation","text":"SymbolicUtils.Code.toexpr","category":"page"},{"location":"manual/codegen/#SymbolicUtils.Code.toexpr","page":"Code generation","title":"SymbolicUtils.Code.toexpr","text":"toexpr(ex, [st,])\n\nConvert a symbolic expression into an Expr, suitable to be passed into eval.\n\nFor example,\n\njulia> @syms a b\n(a, b)\n\njulia> toexpr(a+b)\n:((+)(a, b))\n\njulia> toexpr(a+b) |> dump\nExpr\n head: Symbol call\n args: Array{Any}((3,))\n 1: + (function of type typeof(+))\n 2: Symbol a\n 3: Symbol b\n\nNote that the function is an actual function object.\n\nFor more complex expressions, see other code-related combinators,\n\nNamely Assignment, Let, Func, SetArray, MakeArray, MakeSparseArray and MakeTuple.\n\nTo make your own type convertible to Expr using toexpr define toexpr(x, st) and forward the state st in internal calls to toexpr. st is state used to know when to leave something like y(t) as it is or when to make it var\"y(t)\". E.g. when y(t) is itself the argument of a function rather than y.\n\n\n\n\n\n","category":"function"},{"location":"manual/codegen/#Code-Combinators","page":"Code generation","title":"Code Combinators","text":"","category":"section"},{"location":"manual/codegen/","page":"Code generation","title":"Code generation","text":"These are all exported when you do using SymbolicUtils.Code","category":"page"},{"location":"manual/codegen/","page":"Code generation","title":"Code generation","text":"SymbolicUtils.Code.Assignment\nSymbolicUtils.Code.Let\nSymbolicUtils.Code.Func\nSymbolicUtils.Code.SpawnFetch\nSymbolicUtils.Code.SetArray\nSymbolicUtils.Code.MakeArray\nSymbolicUtils.Code.MakeSparseArray\nSymbolicUtils.Code.MakeTuple\nSymbolicUtils.Code.LiteralExpr","category":"page"},{"location":"manual/codegen/#SymbolicUtils.Code.Assignment","page":"Code generation","title":"SymbolicUtils.Code.Assignment","text":"Assignment(lhs, rhs)\n\nAn assignment expression. Shorthand lhs ← rhs (\\leftarrow)\n\n\n\n\n\n","category":"type"},{"location":"manual/codegen/#SymbolicUtils.Code.Let","page":"Code generation","title":"SymbolicUtils.Code.Let","text":"Let(assignments, body[, let_block])\n\nA Let block.\n\nassignments is a vector of Assignments\nbody is the body of the let block\nlet_block boolean (default=true) – do not create a let block if false.\n\n\n\n\n\n","category":"type"},{"location":"manual/codegen/#SymbolicUtils.Code.Func","page":"Code generation","title":"SymbolicUtils.Code.Func","text":"Func(args, kwargs, body[, pre])\n\nA function.\n\nargs is a vector of expressions\nkwargs is a vector of Assignments\nbody is the body of the function\npre a vector of expressions to be prepended to the function body, for example, it could be [Expr(:meta, :inline), Expr(:meta, :propagate_inbounds)] to create an @inline @propagate_inbounds function definition.\n\nSpecial features in args:\n\nargs can contain DestructuredArgs\ncall expressions\n\nFor example,\n\n\njulia> @syms a b c t f(d) x(t) y(t) z(t)\n(a, b, c, t, f(::Number)::Number, x(::Number)::Number, y(::Number)::Number, z(::Number)::Number)\n\njulia> func = Func([a,x(t), DestructuredArgs([b, y(t)]), f], # args\n [c ← 2, z(t) ← 42], # kwargs\n f((a + b + c) / x(t) + y(t) + z(t)));\n\njulia> toexpr(func)\n:(function (a, var\"x(t)\", var\"##arg#255\", f; c = 2, var\"z(t)\" = 42)\n let b = var\"##arg#255\"[1], var\"y(t)\" = var\"##arg#255\"[2]\n f((+)(var\"y(t)\", var\"z(t)\", (*)((+)(a, b, c), (inv)(var\"x(t)\"))))\n end\n end)\n\nthe second argument is a DestructuredArgs, in the Expr form, it is given a random name, and is expected to receive a vector or tuple of size 2 containing the values of b and y(t). The let block that is automatically generated \"destructures\" these arguments.\nx(t) and y(t) have been replaced with var\"x(t)\" and var\"y(t)\" symbols throughout\n\nthe generated Expr. This makes sure that we are not actually calling the expressions x(t) or y(t) but instead passing the right values in place of the whole expression.\n\nf is also a function-like symbol, same as x and y, but since the args array contains f as itself rather than as say, f(t), it does not become a var\"f(t)\". The generated function expects a function of one argument to be passed in the position of f.\n\nAn example invocation of this function is:\n\njulia> executable = eval(toexpr(func))\n#10 (generic function with 1 method)\n\njulia> executable(1, 2.0, [2,3.0], x->string(x); var\"z(t)\" = sqrt(42))\n\"11.98074069840786\"\n\n\n\n\n\n","category":"type"},{"location":"manual/codegen/#SymbolicUtils.Code.SpawnFetch","page":"Code generation","title":"SymbolicUtils.Code.SpawnFetch","text":"SpawnFetch{ParallelType}(funcs [, args], reduce)\n\nRun every expression in funcs in its own task, the expression should be a Func object and is passed to Threads.Task(f). If Func takes arguments, then the arguments must be passed in as args–a vector of vector of arguments to each function in funcs. We don't use @spawn in order to support RuntimeGeneratedFunctions which disallow closures, instead we interpolate these functions or closures as smaller RuntimeGeneratedFunctions.\n\nreduce function is used to combine the results of executing exprs. A SpawnFetch expression returns the reduced result.\n\nUse Symbolics.MultithreadedForm ParallelType from the Symbolics.jl package to get the RuntimeGeneratedFunction version SpawnFetch.\n\nParallelType can be used to define more parallelism types SymbolicUtils supports Multithreaded type. Which spawns threaded tasks.\n\n\n\n\n\n","category":"type"},{"location":"manual/codegen/#SymbolicUtils.Code.SetArray","page":"Code generation","title":"SymbolicUtils.Code.SetArray","text":"SetArray(inbounds, arr, elems)\n\nAn expression representing setting of elements of arr.\n\nBy default, every element of elems is copied over to arr,\n\nbut if elems contains AtIndex(i, val) objects, then arr[i] = val is performed in its place.\n\ninbounds is a boolean flag, true surrounds the resulting expression in an @inbounds.\n\n\n\n\n\n","category":"type"},{"location":"manual/codegen/#SymbolicUtils.Code.MakeArray","page":"Code generation","title":"SymbolicUtils.Code.MakeArray","text":"MakeArray(elems, similarto, [output_eltype=nothing])\n\nAn expression which constructs an array.\n\nelems is the output array\nsimilarto can either be a type, or some symbol that is an array whose type needs to be emulated. If similarto is a StaticArrays.SArray, then the output array is also created as an SArray, similarly, an Array will result in an Array, and a LabelledArrays.SLArray will result in a labelled static array.\noutput_eltype: if set, then forces the element type of the output array to be this. by default, the output type is inferred automatically.\n\nYou can define:\n\n@inline function create_array(A::Type{<:MyArray},a\n ::Nothing, d::Val{dims}, elems...) where dims\n\n# and\n\n@inline function create_array(::Type{<:MyArray}, T, ::Val{dims}, elems...) where dims\n\nwhich creates an array of size dims using the elements elems and eltype T, to allow MakeArray to create arrays similarto MyArrays.\n\n\n\n\n\n","category":"type"},{"location":"manual/codegen/#SymbolicUtils.Code.MakeSparseArray","page":"Code generation","title":"SymbolicUtils.Code.MakeSparseArray","text":"MakeSpaseArray(array)\n\nAn expression which creates a SparseMatrixCSC or a SparseVector.\n\nThe generated expression contains the sparsity information of array,\n\nit only creates the nzval field at run time.\n\n\n\n\n\n","category":"type"},{"location":"manual/codegen/#SymbolicUtils.Code.MakeTuple","page":"Code generation","title":"SymbolicUtils.Code.MakeTuple","text":"MakeTuple(tup)\n\nMake a Tuple from a tuple of expressions.\n\n\n\n\n\n","category":"type"},{"location":"manual/codegen/#SymbolicUtils.Code.LiteralExpr","page":"Code generation","title":"SymbolicUtils.Code.LiteralExpr","text":"LiteralExpr(ex)\n\nLiterally ex, an Expr. toexpr on LiteralExpr recursively calls toexpr on any interpolated symbolic expressions.\n\n\n\n\n\n","category":"type"},{"location":"#SymbolicUtils.jl-—-Symbolic-programming-in-Julia","page":"SymbolicUtils.jl — Symbolic programming in Julia","title":"SymbolicUtils.jl — Symbolic programming in Julia","text":"","category":"section"},{"location":"#Features","page":"SymbolicUtils.jl — Symbolic programming in Julia","title":"Features","text":"","category":"section"},{"location":"","page":"SymbolicUtils.jl — Symbolic programming in Julia","title":"SymbolicUtils.jl — Symbolic programming in Julia","text":"Fast expressions\nA rule-based rewriting language.\nA combinator library for making rewriters.\nEfficient representation of numeric expressions\nType promotion:\nSymbols (Syms) carry type information. (read more)\nCompound expressions composed of Syms propagate type information. (read more)\nSet of extendable simplification rules.","category":"page"},{"location":"#Creating-symbolic-expressions","page":"SymbolicUtils.jl — Symbolic programming in Julia","title":"Creating symbolic expressions","text":"","category":"section"},{"location":"","page":"SymbolicUtils.jl — Symbolic programming in Julia","title":"SymbolicUtils.jl — Symbolic programming in Julia","text":"First, let's use the @syms macro to create a few symbols.","category":"page"},{"location":"","page":"SymbolicUtils.jl — Symbolic programming in Julia","title":"SymbolicUtils.jl — Symbolic programming in Julia","text":"using SymbolicUtils\n\n@syms w z α::Real β::Real\n\n(w, z, α, β) # hide\n","category":"page"},{"location":"","page":"SymbolicUtils.jl — Symbolic programming in Julia","title":"SymbolicUtils.jl — Symbolic programming in Julia","text":"Type annotations are optional when creating symbols. Here α, β behave like Real numbers. w and z behave like Number, which is the default. You can use the symtype function to find the type of a symbol.","category":"page"},{"location":"","page":"SymbolicUtils.jl — Symbolic programming in Julia","title":"SymbolicUtils.jl — Symbolic programming in Julia","text":"using SymbolicUtils: symtype\n\nsymtype(w), symtype(z), symtype(α), symtype(β)","category":"page"},{"location":"","page":"SymbolicUtils.jl — Symbolic programming in Julia","title":"SymbolicUtils.jl — Symbolic programming in Julia","text":"Note however that they are not subtypes of these types!","category":"page"},{"location":"","page":"SymbolicUtils.jl — Symbolic programming in Julia","title":"SymbolicUtils.jl — Symbolic programming in Julia","text":"@show w isa Number\n@show α isa Real","category":"page"},{"location":"","page":"SymbolicUtils.jl — Symbolic programming in Julia","title":"SymbolicUtils.jl — Symbolic programming in Julia","text":"As their types are different:","category":"page"},{"location":"","page":"SymbolicUtils.jl — Symbolic programming in Julia","title":"SymbolicUtils.jl — Symbolic programming in Julia","text":"@show typeof(w)\n@show typeof(α)","category":"page"},{"location":"","page":"SymbolicUtils.jl — Symbolic programming in Julia","title":"SymbolicUtils.jl — Symbolic programming in Julia","text":"(see this post for why they are all not just subtypes of Number)","category":"page"},{"location":"","page":"SymbolicUtils.jl — Symbolic programming in Julia","title":"SymbolicUtils.jl — Symbolic programming in Julia","text":"You can do basic arithmetic on symbols to get symbolic expressions:","category":"page"},{"location":"","page":"SymbolicUtils.jl — Symbolic programming in Julia","title":"SymbolicUtils.jl — Symbolic programming in Julia","text":"expr1 = α*sin(w)^2 + β*cos(z)^2\nexpr2 = α*cos(z)^2 + β*sin(w)^2\n\nexpr1 + expr2","category":"page"},{"location":"","page":"SymbolicUtils.jl — Symbolic programming in Julia","title":"SymbolicUtils.jl — Symbolic programming in Julia","text":"SymbolicUtils automatically simplifies","category":"page"},{"location":"","page":"SymbolicUtils.jl — Symbolic programming in Julia","title":"SymbolicUtils.jl — Symbolic programming in Julia","text":"2w + 3w - 3z + α","category":"page"},{"location":"","page":"SymbolicUtils.jl — Symbolic programming in Julia","title":"SymbolicUtils.jl — Symbolic programming in Julia","text":"and reorders","category":"page"},{"location":"","page":"SymbolicUtils.jl — Symbolic programming in Julia","title":"SymbolicUtils.jl — Symbolic programming in Julia","text":"(z + w)*(α + β)","category":"page"},{"location":"","page":"SymbolicUtils.jl — Symbolic programming in Julia","title":"SymbolicUtils.jl — Symbolic programming in Julia","text":"expressions of type Symbolic{<:Number} (which includes Sym{Real}) when they are created. It also does constant elimination (including rational numbers)","category":"page"},{"location":"","page":"SymbolicUtils.jl — Symbolic programming in Julia","title":"SymbolicUtils.jl — Symbolic programming in Julia","text":"5 + 2w - 3z + α - (β + 5//3) + 3w - 2 + 3//2 * β","category":"page"},{"location":"","page":"SymbolicUtils.jl — Symbolic programming in Julia","title":"SymbolicUtils.jl — Symbolic programming in Julia","text":"It's worth remembering that the expression may be transformed with respect to the input when it's created.","category":"page"},{"location":"#Function-like-symbols","page":"SymbolicUtils.jl — Symbolic programming in Julia","title":"Function-like symbols","text":"","category":"section"},{"location":"","page":"SymbolicUtils.jl — Symbolic programming in Julia","title":"SymbolicUtils.jl — Symbolic programming in Julia","text":"Symbols can be defined to behave like functions. Both the input and output types for the function can be specified. Any application to that function will only admit either values of those types or symbols of the same symtype.","category":"page"},{"location":"","page":"SymbolicUtils.jl — Symbolic programming in Julia","title":"SymbolicUtils.jl — Symbolic programming in Julia","text":"using SymbolicUtils\n@syms f(x) g(x::Real, y::Real)::Real\n\nf(z) + g(1, α) + sin(w)","category":"page"},{"location":"","page":"SymbolicUtils.jl — Symbolic programming in Julia","title":"SymbolicUtils.jl — Symbolic programming in Julia","text":"This does not work since z is a Number, not a Real.","category":"page"},{"location":"","page":"SymbolicUtils.jl — Symbolic programming in Julia","title":"SymbolicUtils.jl — Symbolic programming in Julia","text":"g(1, z)","category":"page"},{"location":"","page":"SymbolicUtils.jl — Symbolic programming in Julia","title":"SymbolicUtils.jl — Symbolic programming in Julia","text":"This works because g \"returns\" a Real.","category":"page"},{"location":"","page":"SymbolicUtils.jl — Symbolic programming in Julia","title":"SymbolicUtils.jl — Symbolic programming in Julia","text":"g(2//5, g(1, β))","category":"page"},{"location":"#Expression-interface","page":"SymbolicUtils.jl — Symbolic programming in Julia","title":"Expression interface","text":"","category":"section"},{"location":"","page":"SymbolicUtils.jl — Symbolic programming in Julia","title":"SymbolicUtils.jl — Symbolic programming in Julia","text":"Symbolic expressions are of type Term{T}, Add{T}, Mul{T}, Pow{T} or Div{T} and denote some function call where one or more arguments are themselves such expressions or Syms. See more about the representation here.","category":"page"},{"location":"","page":"SymbolicUtils.jl — Symbolic programming in Julia","title":"SymbolicUtils.jl — Symbolic programming in Julia","text":"All the expression types support the TermInterface.jl interface. Please refer to the package for the complete reference of the interface.","category":"page"},{"location":"#Term-rewriting","page":"SymbolicUtils.jl — Symbolic programming in Julia","title":"Term rewriting","text":"","category":"section"},{"location":"","page":"SymbolicUtils.jl — Symbolic programming in Julia","title":"SymbolicUtils.jl — Symbolic programming in Julia","text":"SymbolicUtils contains a rule-based rewriting language for easy pattern matching and rewriting of expression. There is also a combinator library to combine rules to chain, branch and loop over rules.","category":"page"},{"location":"#Simplification","page":"SymbolicUtils.jl — Symbolic programming in Julia","title":"Simplification","text":"","category":"section"},{"location":"","page":"SymbolicUtils.jl — Symbolic programming in Julia","title":"SymbolicUtils.jl — Symbolic programming in Julia","text":"By default +, * and ^ operations apply the most basic simplification upon construction of the expression.","category":"page"},{"location":"","page":"SymbolicUtils.jl — Symbolic programming in Julia","title":"SymbolicUtils.jl — Symbolic programming in Julia","text":"The rules with which the canonical form of Symbolic{<:Number} terms are constructed are the next (where x isa Symbolic and c isa Number)","category":"page"},{"location":"","page":"SymbolicUtils.jl — Symbolic programming in Julia","title":"SymbolicUtils.jl — Symbolic programming in Julia","text":"0 + x, 1 * x and x^1 always gives x\n0 * x always gives 0 and x ^ 0 gives 1\n-x becomes (-1)*x\ncommutativity and associativity over + and * are assumed. Re-ordering of terms will be done under a total order\np/q * x or x * p/q results in (p*x)/q\np/q * x/y results in (p*x)/(q*y)\nx + ... + x will be fused into n*x with type Mul\nx * ... * x will be fused into x^n with type Pow\nsum of Add's are fused\nproduct of Mul's are fused\nc * (c₁x₁ + ... + cₙxₙ) will be converted into c*c₁*x₁ + ... + c*cₙ*xₙ\n(x₁^c₁ * ... * xₙ^cₙ)^c will be converted into x₁^(c*c₁) * ... * xₙ^(c*cₙ)\nthere are come other simplifications on construction that you can check here","category":"page"},{"location":"","page":"SymbolicUtils.jl — Symbolic programming in Julia","title":"SymbolicUtils.jl — Symbolic programming in Julia","text":"Here is an example of this","category":"page"},{"location":"","page":"SymbolicUtils.jl — Symbolic programming in Julia","title":"SymbolicUtils.jl — Symbolic programming in Julia","text":"2 * (w+w+α+β + sin(z)^2 + cos(z)^2 - 1)","category":"page"},{"location":"","page":"SymbolicUtils.jl — Symbolic programming in Julia","title":"SymbolicUtils.jl — Symbolic programming in Julia","text":"The simplify function applies a built-in set of rules to rewrite expressions in order to simplify it.","category":"page"},{"location":"","page":"SymbolicUtils.jl — Symbolic programming in Julia","title":"SymbolicUtils.jl — Symbolic programming in Julia","text":"simplify(2 * (w+w+α+β + sin(z)^2 + cos(z)^2 - 1))","category":"page"},{"location":"","page":"SymbolicUtils.jl — Symbolic programming in Julia","title":"SymbolicUtils.jl — Symbolic programming in Julia","text":"The rules in the default simplify applies simple constant elimination and trigonometric identities.","category":"page"},{"location":"","page":"SymbolicUtils.jl — Symbolic programming in Julia","title":"SymbolicUtils.jl — Symbolic programming in Julia","text":"If you read the previous section on the rules DSL, you should be able to read and understand the rules that are used by simplify.","category":"page"},{"location":"#Code-generation","page":"SymbolicUtils.jl — Symbolic programming in Julia","title":"Code generation","text":"","category":"section"},{"location":"","page":"SymbolicUtils.jl — Symbolic programming in Julia","title":"SymbolicUtils.jl — Symbolic programming in Julia","text":"Experimental feature","category":"page"},{"location":"","page":"SymbolicUtils.jl — Symbolic programming in Julia","title":"SymbolicUtils.jl — Symbolic programming in Julia","text":"It is common to want to generate executable code from symbolic expressions and blocks of them. We are working on experimental support for turning Symbolic expressions into executable functions with specific focus on array input and output and performance which is critical to the Differential Equations ecosystem which is making heavy use of this package.","category":"page"},{"location":"","page":"SymbolicUtils.jl — Symbolic programming in Julia","title":"SymbolicUtils.jl — Symbolic programming in Julia","text":"See Code generation for more about this.","category":"page"},{"location":"#Learn-more","page":"SymbolicUtils.jl — Symbolic programming in Julia","title":"Learn more","text":"","category":"section"},{"location":"","page":"SymbolicUtils.jl — Symbolic programming in Julia","title":"SymbolicUtils.jl — Symbolic programming in Julia","text":"If you have a package that you would like to utilize rule-based rewriting in, look at the suggestions in the Interfacing section to find out how you can do that without any fundamental changes to your package. Look at the API documentation for docstrings about specific functions or macros.","category":"page"},{"location":"","page":"SymbolicUtils.jl — Symbolic programming in Julia","title":"SymbolicUtils.jl — Symbolic programming in Julia","text":"Head over to the github repository to ask questions and report problems! Join the Zulip stream to chat!","category":"page"},{"location":"manual/interface/#Interfacing-with-SymbolicUtils.jl","page":"Interfacing with SymbolicUtils.jl","title":"Interfacing with SymbolicUtils.jl","text":"","category":"section"},{"location":"manual/interface/","page":"Interfacing with SymbolicUtils.jl","title":"Interfacing with SymbolicUtils.jl","text":"This section is for Julia package developers who may want to use the simplify and rule rewriting system on their own expression types.","category":"page"},{"location":"manual/interface/#Defining-the-interface","page":"Interfacing with SymbolicUtils.jl","title":"Defining the interface","text":"","category":"section"},{"location":"manual/interface/","page":"Interfacing with SymbolicUtils.jl","title":"Interfacing with SymbolicUtils.jl","text":"SymbolicUtils matchers can match any Julia object that implements an interface to traverse it as a tree. The interface in question, is defined in the TermInterface.jl package. Its purpose is to provide a shared interface between various symbolic programming Julia packages. ","category":"page"},{"location":"manual/interface/","page":"Interfacing with SymbolicUtils.jl","title":"Interfacing with SymbolicUtils.jl","text":"In particular, you should define methods from TermInterface.jl for an expression tree type T with symbol types S to work with SymbolicUtils.jl","category":"page"},{"location":"manual/interface/","page":"Interfacing with SymbolicUtils.jl","title":"Interfacing with SymbolicUtils.jl","text":"You can read the documentation of TermInterface.jl on the Github repository.","category":"page"},{"location":"manual/interface/#SymbolicUtils.jl-only-methods","page":"Interfacing with SymbolicUtils.jl","title":"SymbolicUtils.jl only methods","text":"","category":"section"},{"location":"manual/interface/","page":"Interfacing with SymbolicUtils.jl","title":"Interfacing with SymbolicUtils.jl","text":"symtype\nissym\npromote_symtype","category":"page"}] +[{"location":"api/#API-Reference","page":"API Reference","title":"API Reference","text":"","category":"section"},{"location":"api/#Symbols-and-Terms","page":"API Reference","title":"Symbols and Terms","text":"","category":"section"},{"location":"api/","page":"API Reference","title":"API Reference","text":"SymbolicUtils.@syms\nSymbolicUtils.Sym\nSymbolicUtils.issym\nSymbolicUtils.symtype\nSymbolicUtils.Term\nSymbolicUtils.Add\nSymbolicUtils.Mul\nSymbolicUtils.Pow\nSymbolicUtils.promote_symtype","category":"page"},{"location":"api/#SymbolicUtils.@syms","page":"API Reference","title":"SymbolicUtils.@syms","text":"@syms [::T1] [::T2]...\n\nFor instance:\n\n@syms foo::Real bar baz(x, y::Real)::Complex\n\nCreate one or more variables. 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.\n\nExamples:\n\n@syms foo bar::Real baz::Int will create\n\nvariable foo of symtype Number (the default), bar of symtype Real and baz of symtype Int\n\n@syms f(x) g(y::Real, x)::Int h(a::Int, f(b)) creates 1-arg f 2-arg g\n\nand 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.\n\n\n\n\n\n","category":"macro"},{"location":"api/#SymbolicUtils.issym","page":"API Reference","title":"SymbolicUtils.issym","text":"issym(x)\n\nReturns true if x is a Sym. If true, nameof must be defined on x and must return a Symbol.\n\n\n\n\n\n","category":"function"},{"location":"api/#SymbolicUtils.symtype","page":"API Reference","title":"SymbolicUtils.symtype","text":"symtype(x)\n\n\nReturns 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.\n\n\n\n\n\n","category":"function"},{"location":"api/#SymbolicUtils.promote_symtype","page":"API Reference","title":"SymbolicUtils.promote_symtype","text":"promote_symtype(f, Ts...)\n\nThe result of applying f to arguments of symtype Ts...\n\njulia> promote_symtype(+, Real, Real)\nReal\n\njulia> promote_symtype(+, Complex, Real)\nNumber\n\njulia> @syms f(x)::Complex\n(f(::Number)::Complex,)\n\njulia> promote_symtype(f, Number)\nComplex\n\nWhen constructing Terms without an explicit symtype, promote_symtype is used to figure out the symtype of the Term.\n\n\n\n\n\npromote_symtype(f::FnType{X,Y}, arg_symtypes...)\n\nThe 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.\n\n\n\n\n\n","category":"function"},{"location":"api/#Rewriters","page":"API Reference","title":"Rewriters","text":"","category":"section"},{"location":"api/","page":"API Reference","title":"API Reference","text":"@rule\nSymbolicUtils.Rewriters","category":"page"},{"location":"api/#SymbolicUtils.@rule","page":"API Reference","title":"SymbolicUtils.@rule","text":"@rule LHS => RHS\n\nCreates 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.\n\nLHS 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).\n\nIf 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.\n\nSlot:\n\nA 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).\n\nExample:\n\nSimple rule to turn any sin into cos:\n\njulia> @syms a b c\n(a, b, c)\n\njulia> r = @rule sin(~x) => cos(~x)\nsin(~x) => cos(~x)\n\njulia> r(sin(1+a))\ncos((1 + a))\n\nA rule with 2 segment variables\n\njulia> r = @rule sin(~x + ~y) => sin(~x)*cos(~y) + cos(~x)*sin(~y)\nsin(~x + ~y) => sin(~x) * cos(~y) + cos(~x) * sin(~y)\n\njulia> r(sin(a + b))\ncos(a)*sin(b) + sin(a)*cos(b)\n\nA rule that matches two of the same expressions:\n\njulia> r = @rule sin(~x)^2 + cos(~x)^2 => 1\nsin(~x) ^ 2 + cos(~x) ^ 2 => 1\n\njulia> r(sin(2a)^2 + cos(2a)^2)\n1\n\njulia> r(sin(2a)^2 + cos(a)^2)\n# nothing\n\nSegment:\n\nA Segment variable is written as ~~x and matches zero or more expressions in the function call.\n\nExample:\n\nThis 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.\n\njulia> r = @rule ~x * +((~~ys)) => sum(map(y-> ~x * y, ~~ys));\n\njulia> r(2 * (a+b+c))\n((2 * a) + (2 * b) + (2 * c))\n\nPredicates:\n\nThere 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.\n\nThe predicate should return true if the current match is acceptable, and false otherwise.\n\njulia> two_πs(x::Number) = abs(round(x/(2π)) - x/(2π)) < 10^-9\ntwo_πs (generic function with 1 method)\n\njulia> two_πs(x) = false\ntwo_πs (generic function with 2 methods)\n\njulia> r = @rule sin(~~x + ~y::two_πs + ~~z) => sin(+(~~x..., ~~z...))\nsin(~(~x) + ~(y::two_πs) + ~(~z)) => sin(+(~(~x)..., ~(~z)...))\n\njulia> r(sin(a+3π))\n\njulia> r(sin(a+6π))\nsin(a)\n\njulia> r(sin(a+6π+c))\nsin((a + c))\n\nPredicate function gets an array of values if attached to a segment variable (~~x).\n\nFor the predicate over the whole rule, use @rule => where :\n\njulia> @syms a b;\n\njulia> predicate(x) = x === a;\n\njulia> r = @rule ~x => ~x where predicate(~x);\n\njulia> r(a)\na\n\njulia> r(b) === nothing\ntrue\n\nNote that this is syntactic sugar and that it is the same as something like @rule ~x => f(~x) ? ~x : nothing.\n\nContext:\n\nIn 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).\n\nThe function can use the inputs however it wants, and must return a boolean indicating whether the predicate holds or not.\n\nIn the consequent pattern: Use (@ctx) to access the context object on the right hand side of an expression.\n\n\n\n\n\n","category":"macro"},{"location":"api/#SymbolicUtils.Rewriters","page":"API Reference","title":"SymbolicUtils.Rewriters","text":"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.\n\nThe Rewriters module contains some types which create and transform rewriters.\n\nEmpty() is a rewriter which always returns nothing\nChain(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.\nRestartedChain(itr) like Chain(itr) but restarts from the first rewriter once on the first successful application of one of the chained rewriters.\nIfElse(cond, rw1, rw2) runs the cond function on the input, applies rw1 if cond returns true, rw2 if it returns false\nIf(cond, rw) is the same as IfElse(cond, rw, Empty())\nPrewalk(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.\nPostwalk(rw; threaded=false, thread_cutoff=100) similarly does post-order traversal.\nFixpoint(rw) returns a rewriter which applies rw repeatedly until there are no changes to be made.\nFixpointNoCycle behaves like Fixpoint but instead it applies rw repeatedly only while it is returning new results.\nPassThrough(rw) returns a rewriter which if rw(x) returns nothing will instead return x otherwise will return rw(x).\n\n\n\n\n\n","category":"module"},{"location":"api/#Simplify","page":"API Reference","title":"Simplify","text":"","category":"section"},{"location":"api/","page":"API Reference","title":"API Reference","text":"SymbolicUtils.simplify\nSymbolicUtils.expand\nSymbolicUtils.substitute","category":"page"},{"location":"api/#SymbolicUtils.simplify","page":"API Reference","title":"SymbolicUtils.simplify","text":"simplify(x; expand=false,\n threaded=false,\n thread_subtree_cutoff=100,\n rewriter=nothing)\n\nSimplify an expression (x) by applying rewriter until there are no changes. expand=true applies expand in the beginning of each fixpoint iteration.\n\nBy default, simplify will assume denominators are not zero and allow cancellation in fractions. Pass simplify_fractions=false to prevent this.\n\n\n\n\n\n","category":"function"},{"location":"api/#SymbolicUtils.expand","page":"API Reference","title":"SymbolicUtils.expand","text":"expand(expr)\n\nExpand expressions by distributing multiplication over addition, e.g., a*(b+c) becomes ab+ac.\n\nexpand 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.\n\n\n\n\n\n","category":"function"},{"location":"api/#SymbolicUtils.substitute","page":"API Reference","title":"SymbolicUtils.substitute","text":"substitute(expr, dict; fold=true)\n\nsubstitute any subexpression that matches a key in dict with the corresponding value. If fold=false, expressions which can be evaluated won't be evaluated.\n\njulia> substitute(1+sqrt(y), Dict(y => 2), fold=true)\n2.414213562373095\njulia> substitute(1+sqrt(y), Dict(y => 2), fold=false)\n1 + sqrt(2)\n\n\n\n\n\n","category":"function"},{"location":"api/#Utilities","page":"API Reference","title":"Utilities","text":"","category":"section"},{"location":"api/","page":"API Reference","title":"API Reference","text":"SymbolicUtils.@timerewrite","category":"page"},{"location":"api/#SymbolicUtils.@timerewrite","page":"API Reference","title":"SymbolicUtils.@timerewrite","text":"@timerewrite expr\n\nIf expr calls simplify or a RuleSet object, track the amount of time it spent on applying each rule and pretty print the timing.\n\nThis uses TimerOutputs.jl.\n\nExample:\n\n\njulia> expr = foldr(*, rand([a,b,c,d], 100))\n(a ^ 26) * (b ^ 30) * (c ^ 16) * (d ^ 28)\n\njulia> @timerewrite simplify(expr)\n ────────────────────────────────────────────────────────────────────────────────────────────────\n Time Allocations\n ────────────────────── ───────────────────────\n Tot / % measured: 340ms / 15.3% 92.2MiB / 10.8%\n\n Section ncalls time %tot avg alloc %tot avg\n ────────────────────────────────────────────────────────────────────────────────────────────────\n ACRule((~y) ^ ~n * ~y => (~y) ^ (~n ... 667 11.1ms 21.3% 16.7μs 2.66MiB 26.8% 4.08KiB\n RHS 92 277μs 0.53% 3.01μs 14.4KiB 0.14% 160B\n ACRule((~x) ^ ~n * (~x) ^ ~m => (~x)... 575 7.63ms 14.6% 13.3μs 1.83MiB 18.4% 3.26KiB\n (*)(~(~(x::!issortedₑ))) => sort_arg... 831 6.31ms 12.1% 7.59μs 738KiB 7.26% 910B\n RHS 164 3.03ms 5.81% 18.5μs 250KiB 2.46% 1.52KiB\n ...\n ...\n ────────────────────────────────────────────────────────────────────────────────────────────────\n(a ^ 26) * (b ^ 30) * (c ^ 16) * (d ^ 28)\n\n\n\n\n\n","category":"macro"},{"location":"manual/representation/#Term-representation-and-simplification","page":"Term representation and simplification","title":"Term representation and simplification","text":"","category":"section"},{"location":"manual/representation/","page":"Term representation and simplification","title":"Term representation and simplification","text":"Performance of symbolic simplification depends on the datastructures used to represent terms. Efficient datastructures often have the advantage of automatic simplification, and of efficient storage.","category":"page"},{"location":"manual/representation/","page":"Term representation and simplification","title":"Term representation and simplification","text":"The most basic term representation simply holds a function call and stores the function and the arguments it is called with. This is done by the Term type in SymbolicUtils. Functions that aren't commutative or associative, such as sin or hypot are stored as Terms. Commutative and associative operations like +, *, and their supporting operations like -, / and ^, when used on terms of type <:Number, stand to gain from the use of more efficient datastrucutres.","category":"page"},{"location":"manual/representation/","page":"Term representation and simplification","title":"Term representation and simplification","text":"All term representations must support operation and arguments functions. And they must define iscall and isexpr to return true when called with an instance of the type. Generic term-manipulation programs such as the rule-based rewriter make use of this interface to inspect expressions. In this way, the interface wins back the generality lost by having a zoo of term representations instead of one. (see interface section for more on this.)","category":"page"},{"location":"manual/representation/#Preliminary-representation-of-arithmetic","page":"Term representation and simplification","title":"Preliminary representation of arithmetic","text":"","category":"section"},{"location":"manual/representation/","page":"Term representation and simplification","title":"Term representation and simplification","text":"Linear combinations such as alpha_1 x_1 + alpha_2 x_2 ++ alpha_n x_n are represented by Add(Dict(x₁ => α₁, x₂ => α₂, ..., xₙ => αₙ)). Here, any x_i may itself be other types mentioned here, except for Add. When an Add is added to an Add, we merge their dictionaries and add up matching coefficients to create a single \"flattened\" Add.","category":"page"},{"location":"manual/representation/","page":"Term representation and simplification","title":"Term representation and simplification","text":"Similarly, x_1^m_1x_2^m_2x_m_n is represented by Mul(Dict(x₁ => m₁, x₂ => m₂, ..., xₙ => mₙ)). x_i may not themselves be Mul, multiplying a Mul with another Mul returns a \"flattened\" Mul.","category":"page"},{"location":"manual/representation/","page":"Term representation and simplification","title":"Term representation and simplification","text":"Note that Add and Mul types perform a preliminary simplification which suffices to simplify numeric expressions to a large extent during construction.","category":"page"},{"location":"manual/representation/","page":"Term representation and simplification","title":"Term representation and simplification","text":"p q","category":"page"},{"location":"manual/representation/","page":"Term representation and simplification","title":"Term representation and simplification","text":"is represented by Div(p, q). The result of * on Div is maintained as a Div. For example, Div(p_1, q_1) * Div(p_2, q_2) results in Div(p_1 * p_2, q_1 * q_2) and so on. The effect is, in Div(p, q), p or q or, if they are Mul, any of their multiplicands is not a Div. So Muls must always be nested inside a Div and can never show up immediately wrapping it. This rule sets up an expression so that it helps the simplify_fractions procedure described two sections below.","category":"page"},{"location":"manual/representation/#Polynomial-representation","page":"Term representation and simplification","title":"Polynomial representation","text":"","category":"section"},{"location":"manual/representation/","page":"Term representation and simplification","title":"Term representation and simplification","text":"Packages like DynamicPolynomials.jl provide representations that are even more efficient than the Add and Mul types mentioned above. They are designed specifically for multi-variate polynomials. They provide common algorithms such as multi-variate polynomial GCD. The restrictions that make it fast also mean some things are not possible: Firstly, DynamicPolynomials can only represent flat polynomials. For example, (x-3)*(x+5) can only be represented as (x^2) + 15 - 8x. Secondly, DynamicPolynomials does not have ways to represent generic Terms such as sin(x-y) in the tree.","category":"page"},{"location":"manual/representation/","page":"Term representation and simplification","title":"Term representation and simplification","text":"To reconcile these differences while being able to use the efficient algorithms of DynamicPolynomials we have the PolyForm type. This type holds a polynomial and the mappings necessary to present the polynomial as a SymbolicUtils expression (i.e. by defining operation and arguments). The mappings constructed for the conversion are 1) a bijection from DynamicPolynomials Variable type to a Symbolics Sym, and 2) a mapping from Syms to non-polynomial terms that the Syms stand-in for. These terms may themselves contain PolyForm if there are polynomials inside them. The mappings are transiently global, that is, when all references to the mappings go out of scope, they are released and re-created.","category":"page"},{"location":"manual/representation/","page":"Term representation and simplification","title":"Term representation and simplification","text":"julia> @syms x y\n(x, y)\n\njulia> PolyForm((x-3)*(y-5))\nx*y + 15 - 5x - 3y","category":"page"},{"location":"manual/representation/","page":"Term representation and simplification","title":"Term representation and simplification","text":"Terms for which the operation is not +, *, or ^ are replaced with a generated symbol when representing the polynomial, and a mapping from this new symbol to the original expression it stands-in for is maintained as stated above.","category":"page"},{"location":"manual/representation/","page":"Term representation and simplification","title":"Term representation and simplification","text":"julia> p = PolyForm((sin(x) + cos(x))^2)\n(cos(x)^2) + 2cos(x)*sin(x) + (sin(x)^2)\n\njulia> p.p # this is the actual DynamicPolynomial stored\ncos_3658410937268741549² + 2cos_3658410937268741549sin_10720964503106793468 + sin_10720964503106793468²","category":"page"},{"location":"manual/representation/","page":"Term representation and simplification","title":"Term representation and simplification","text":"By default, polynomials inside non-polynomial terms are not also converted to PolyForm. For example,","category":"page"},{"location":"manual/representation/","page":"Term representation and simplification","title":"Term representation and simplification","text":"julia> PolyForm(sin((x-3)*(y-5)))\nsin((x - 3)*(y - 5))","category":"page"},{"location":"manual/representation/","page":"Term representation and simplification","title":"Term representation and simplification","text":"But you can pass in the recurse=true keyword argument to do this.","category":"page"},{"location":"manual/representation/","page":"Term representation and simplification","title":"Term representation and simplification","text":"julia> PolyForm(sin((x-3)*(y-5)), recurse=true)\nsin(x*y + 15 - 5x - 3y)","category":"page"},{"location":"manual/representation/","page":"Term representation and simplification","title":"Term representation and simplification","text":"Polynomials are constructed by first turning symbols and non-polynomial terms into DynamicPolynomials-style variables and then applying the +, *, ^ operations on these variables. You can control the list of the polynomial operations with the Fs keyword argument. It is a Union type of the functions to apply. For example, let's say you want to turn terms into polynomials by only applying the + and ^ operations, and want to preserve * operations as-is, you could pass in Fs=Union{typeof(+), typeof(^)}","category":"page"},{"location":"manual/representation/","page":"Term representation and simplification","title":"Term representation and simplification","text":"julia> PolyForm((x+y)^2*(x-y), Fs=Union{typeof(+), typeof(^)}, recurse=true)\n((x - (y))*((x^2) + 2x*y + (y^2)))","category":"page"},{"location":"manual/representation/","page":"Term representation and simplification","title":"Term representation and simplification","text":"Note that in this case recurse=true was necessary as otherwise the polynomialization would not descend into the * operation as it is now considered a generic operation.","category":"page"},{"location":"manual/representation/#Simplifying-fractions","page":"Term representation and simplification","title":"Simplifying fractions","text":"","category":"section"},{"location":"manual/representation/","page":"Term representation and simplification","title":"Term representation and simplification","text":"simplify_fractions(expr) recurses through expr finding Divs and simplifying them using polynomial division.","category":"page"},{"location":"manual/representation/","page":"Term representation and simplification","title":"Term representation and simplification","text":"First the factors of the numerators and the denominators are converted into PolyForm objects, then numerators and denominators are divided by their respective pairwise GCDs. The conversion of the numerator and denominator into PolyForm is set up so that simplify_fractions does not result in increase in the expression size due to polynomial expansion. Specifically, the factors are individually converted into PolyForm objects, and any powers of polynomial is not expanded, but the division process repeatedly divides them as many times as the power.","category":"page"},{"location":"manual/representation/","page":"Term representation and simplification","title":"Term representation and simplification","text":"julia> simplify_fractions((x*y+5x+3y+15)/((x+3)*(x-4)))\n(5.0 + y) / (x - 4)\n\njulia> simplify_fractions((x*y+5x+3y+15)^2/((x+3)*(x-4)))\n((5.0 + y)*(15 + 5x + x*y + 3y)) / (x - 4)\n\njulia> simplify_fractions(3/(x+3) + x/(x+3))\n1\n\njulia> simplify_fractions(sin(x)/cos(x) + cos(x)/sin(x))\n(cos(x)^2 + sin(x)^2) / (cos(x)*sin(x))\n\njulia> simplify(ans)\n1 / (cos(x)*sin(x))","category":"page"},{"location":"manual/rewrite/#Term-Rewriting","page":"Term Rewriting","title":"Term Rewriting","text":"","category":"section"},{"location":"manual/rewrite/#Rule-based-rewriting","page":"Term Rewriting","title":"Rule-based rewriting","text":"","category":"section"},{"location":"manual/rewrite/","page":"Term Rewriting","title":"Term Rewriting","text":"Rewrite rules match and transform an expression. A rule is written using either the @rule macro or the @acrule macro. It creates a callable Rule object.","category":"page"},{"location":"manual/rewrite/#Basics-of-rule-based-term-rewriting-in-SymbolicUtils","page":"Term Rewriting","title":"Basics of rule-based term rewriting in SymbolicUtils","text":"","category":"section"},{"location":"manual/rewrite/","page":"Term Rewriting","title":"Term Rewriting","text":"Here is a simple rewrite rule, that uses formula for the double angle of the sine function:","category":"page"},{"location":"manual/rewrite/","page":"Term Rewriting","title":"Term Rewriting","text":"using SymbolicUtils\n\n@syms w z α::Real β::Real\n\n(w, z, α, β) # hide\n\nr1 = @rule sin(2(~x)) => 2sin(~x)*cos(~x)\n\nr1(sin(2z))\n\n# output\n2sin(z)*cos(z)","category":"page"},{"location":"manual/rewrite/","page":"Term Rewriting","title":"Term Rewriting","text":"The @rule macro takes a pair of patterns – the matcher and the consequent (@rule matcher => consequent). If an expression matches the matcher pattern, it is rewritten to the consequent pattern. @rule returns a callable object that applies the rule to an expression.","category":"page"},{"location":"manual/rewrite/","page":"Term Rewriting","title":"Term Rewriting","text":"~x in the example is what is a slot variable named x. In a matcher pattern, slot variables are placeholders that match exactly one expression. When used on the consequent side, they stand in for the matched expression. If a slot variable appears twice in a matcher pattern, all corresponding matches must be equal (as tested by Base.isequal function). Hence this rule says: if you see something added to itself, make it twice of that thing, and works as such.","category":"page"},{"location":"manual/rewrite/","page":"Term Rewriting","title":"Term Rewriting","text":"If you try to apply this rule to an expression with triple angle, it will return nothing – this is the way a rule signifies failure to match.","category":"page"},{"location":"manual/rewrite/","page":"Term Rewriting","title":"Term Rewriting","text":"r1(sin(3z)) === nothing\n\n# output\ntrue","category":"page"},{"location":"manual/rewrite/","page":"Term Rewriting","title":"Term Rewriting","text":"Slot variable (matcher) is not necessary a single variable","category":"page"},{"location":"manual/rewrite/","page":"Term Rewriting","title":"Term Rewriting","text":"r1(sin(2*(w-z)))\n\n# output\n2cos(w - z)*sin(w - z)","category":"page"},{"location":"manual/rewrite/","page":"Term Rewriting","title":"Term Rewriting","text":"but it must be a single expression","category":"page"},{"location":"manual/rewrite/","page":"Term Rewriting","title":"Term Rewriting","text":"r1(sin(2*(w+z)*(α+β))) === nothing\n\n# output\ntrue","category":"page"},{"location":"manual/rewrite/","page":"Term Rewriting","title":"Term Rewriting","text":"Rules are of course not limited to single slot variable","category":"page"},{"location":"manual/rewrite/","page":"Term Rewriting","title":"Term Rewriting","text":"r2 = @rule sin(~x + ~y) => sin(~x)*cos(~y) + cos(~x)*sin(~y);\n\nr2(sin(α+β))\n\n# output\nsin(β)*cos(α) + cos(β)*sin(α)","category":"page"},{"location":"manual/rewrite/","page":"Term Rewriting","title":"Term Rewriting","text":"If you want to match a variable number of subexpressions at once, you will need a segment variable. ~~xs in the following example is a segment variable:","category":"page"},{"location":"manual/rewrite/","page":"Term Rewriting","title":"Term Rewriting","text":"@syms x y z\n@rule(+(~~xs) => ~~xs)(x + y + z)\n\n# output\n3-element view(::Vector{Any}, 1:3) with eltype Any:\n z\n y\n x","category":"page"},{"location":"manual/rewrite/","page":"Term Rewriting","title":"Term Rewriting","text":"~~xs is a vector of subexpressions matched. You can use it to construct something more useful:","category":"page"},{"location":"manual/rewrite/","page":"Term Rewriting","title":"Term Rewriting","text":"r3 = @rule ~x * +(~~ys) => sum(map(y-> ~x * y, ~~ys));\n\nr3(2 * (w+w+α+β))\n\n# output\n4w + 2α + 2β","category":"page"},{"location":"manual/rewrite/","page":"Term Rewriting","title":"Term Rewriting","text":"Notice that the expression was autosimplified before application of the rule.","category":"page"},{"location":"manual/rewrite/","page":"Term Rewriting","title":"Term Rewriting","text":"2 * (w+w+α+β)\n\n# output\n2(2w + α + β)","category":"page"},{"location":"manual/rewrite/#Predicates-for-matching","page":"Term Rewriting","title":"Predicates for matching","text":"","category":"section"},{"location":"manual/rewrite/","page":"Term Rewriting","title":"Term Rewriting","text":"Matcher pattern may contain slot variables with attached predicates, written as ~x::f where f is a function that takes a matched expression and returns a boolean value. Such a slot will be considered a match only if f returns true.","category":"page"},{"location":"manual/rewrite/","page":"Term Rewriting","title":"Term Rewriting","text":"Similarly ~~x::g is a way of attaching a predicate g to a segment variable. In the case of segment variables g gets a vector of 0 or more expressions and must return a boolean value. If the same slot or segment variable appears twice in the matcher pattern, then at most one of the occurrence should have a predicate.","category":"page"},{"location":"manual/rewrite/","page":"Term Rewriting","title":"Term Rewriting","text":"For example,","category":"page"},{"location":"manual/rewrite/","page":"Term Rewriting","title":"Term Rewriting","text":"using SymbolicUtils\n@syms a b c d\n\nr = @rule ~x + ~~y::(ys->iseven(length(ys))) => \"odd terms\";\n\n@show r(a + b + c + d)\n@show r(b + c + d)\n@show r(b + c + b)\n@show r(a + b)\n\n# output\nr(a + b + c + d) = nothing\nr(b + c + d) = \"odd terms\"\nr(b + c + b) = nothing\nr(a + b) = nothing","category":"page"},{"location":"manual/rewrite/#Associative-Commutative-Rules","page":"Term Rewriting","title":"Associative-Commutative Rules","text":"","category":"section"},{"location":"manual/rewrite/","page":"Term Rewriting","title":"Term Rewriting","text":"Given an expression f(x, f(y, z, u), v, w), a f is said to be associative if the expression is equivalent to f(x, y, z, u, v, w) and commutative if the order of arguments does not matter. SymbolicUtils has a special @acrule macro meant for rules on functions which are associate and commutative such as addition and multiplication of real and complex numbers.","category":"page"},{"location":"manual/rewrite/","page":"Term Rewriting","title":"Term Rewriting","text":"using SymbolicUtils\n@syms x y z\n\nacr = @acrule((~a)^(~x) * (~a)^(~y) => (~a)^(~x + ~y))\n\nacr(x^y * x^z)\n\n# output\nx^(y + z)","category":"page"},{"location":"manual/rewrite/","page":"Term Rewriting","title":"Term Rewriting","text":"although in case of Number it also works the same way with regular @rule since autosimplification orders and applies associativity and commutativity to the expression.","category":"page"},{"location":"manual/rewrite/#Example-of-applying-the-rules-to-simplify-expression","page":"Term Rewriting","title":"Example of applying the rules to simplify expression","text":"","category":"section"},{"location":"manual/rewrite/","page":"Term Rewriting","title":"Term Rewriting","text":"Consider expression (cos(x) + sin(x))^2 that we would like simplify by applying some trigonometric rules. First, we need rule to expand square of cos(x) + sin(x). First we try the simplest rule to expand square of the sum and try it on simple expression","category":"page"},{"location":"manual/rewrite/","page":"Term Rewriting","title":"Term Rewriting","text":"using SymbolicUtils\n\n@syms x::Real y::Real\n\nsqexpand = @rule (~x + ~y)^2 => (~x)^2 + (~y)^2 + 2 * ~x * ~y\n\nsqexpand((cos(x) + sin(x))^2)\n\n# output\nsin(x)^2 + 2sin(x)*cos(x) + cos(x)^2","category":"page"},{"location":"manual/rewrite/","page":"Term Rewriting","title":"Term Rewriting","text":"It works. This can be further simplified using Pythagorean identity and check it","category":"page"},{"location":"manual/rewrite/","page":"Term Rewriting","title":"Term Rewriting","text":"pyid = @rule sin(~x)^2 + cos(~x)^2 => 1\n\npyid(cos(x)^2 + sin(x)^2) === nothing\n\n# output\ntrue","category":"page"},{"location":"manual/rewrite/","page":"Term Rewriting","title":"Term Rewriting","text":"Why does it return nothing? If we look at the rule, we see that the order of sin(x) and cos(x) is different. Therefore, in order to work, the rule needs to be associative-commutative.","category":"page"},{"location":"manual/rewrite/","page":"Term Rewriting","title":"Term Rewriting","text":"acpyid = @acrule sin(~x)^2 + cos(~x)^2 => 1\n\nacpyid(cos(x)^2 + sin(x)^2 + 2cos(x)*sin(x))\n\n# output\n1 + 2sin(x)*cos(x)","category":"page"},{"location":"manual/rewrite/","page":"Term Rewriting","title":"Term Rewriting","text":"It has been some work. Fortunately rules may be chained together into more sophisticated rewriters to avoid manual application of the rules.","category":"page"},{"location":"manual/rewrite/#Composing-rewriters","page":"Term Rewriting","title":"Composing rewriters","text":"","category":"section"},{"location":"manual/rewrite/","page":"Term Rewriting","title":"Term Rewriting","text":"A rewriter is any callable object 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 Rules we created above are rewriters.","category":"page"},{"location":"manual/rewrite/","page":"Term Rewriting","title":"Term Rewriting","text":"The SymbolicUtils.Rewriters module contains some types which create and transform rewriters.","category":"page"},{"location":"manual/rewrite/","page":"Term Rewriting","title":"Term Rewriting","text":"Empty() is a rewriter which always returns nothing\nChain(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.\nRestartedChain(itr) like Chain(itr) but restarts from the first rewriter once on the first successful application of one of the chained rewriters.\nIfElse(cond, rw1, rw2) runs the cond function on the input, applies rw1 if cond returns true, rw2 if it returns false\nIf(cond, rw) is the same as IfElse(cond, rw, Empty())\nPrewalk(rw; threaded=false, thread_cutoff=100) returns a rewriter which does a pre-order (from top to bottom and from left to right) traversal of a given expression and applies the rewriter rw. threaded=true will use multi threading for traversal. 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. If you are applying multiple rules, then Chain already has the appropriate passthrough behavior. If you only want to apply one rule, then consider using PassThrough. thread_cutoff is the minimum number of nodes in a subtree which should be walked in a threaded spawn.\nPostwalk(rw; threaded=false, thread_cutoff=100) similarly does post-order (from left to right and from bottom to top) traversal.\nFixpoint(rw) returns a rewriter which applies rw repeatedly until there are no changes to be made.\nPassThrough(rw) returns a rewriter which if rw(x) returns nothing will instead return x otherwise will return rw(x).","category":"page"},{"location":"manual/rewrite/#Chaining-rewriters","page":"Term Rewriting","title":"Chaining rewriters","text":"","category":"section"},{"location":"manual/rewrite/","page":"Term Rewriting","title":"Term Rewriting","text":"Several rules may be chained to give chain of rules. Chain is an array of rules which are subsequently applied to the expression.","category":"page"},{"location":"manual/rewrite/","page":"Term Rewriting","title":"Term Rewriting","text":"To check that, we will combine rules from previous example into a chain","category":"page"},{"location":"manual/rewrite/","page":"Term Rewriting","title":"Term Rewriting","text":"using SymbolicUtils\nusing SymbolicUtils.Rewriters\n\n@syms x\n\nsqexpand = @rule (~x + ~y)^2 => (~x)^2 + (~y)^2 + 2 * ~x * ~y\nacpyid = @acrule sin(~x)^2 + cos(~x)^2 => 1\n\ncsa = Chain([sqexpand, acpyid])\n\ncsa((cos(x) + sin(x))^2)\n\n# output\n1 + 2sin(x)*cos(x)","category":"page"},{"location":"manual/rewrite/","page":"Term Rewriting","title":"Term Rewriting","text":"Important feature of Chain is that it returns the expression instead of nothing if it doesn't change the expression","category":"page"},{"location":"manual/rewrite/","page":"Term Rewriting","title":"Term Rewriting","text":"Chain([@acrule sin(~x)^2 + cos(~x)^2 => 1])((cos(x) + sin(x))^2)\n\n# output\n(sin(x) + cos(x))^2","category":"page"},{"location":"manual/rewrite/","page":"Term Rewriting","title":"Term Rewriting","text":"it's important to notice, that chain is ordered, so if rules are in different order it wouldn't work the same as in earlier example","category":"page"},{"location":"manual/rewrite/","page":"Term Rewriting","title":"Term Rewriting","text":"cas = Chain([acpyid, sqexpand])\n\ncas((cos(x) + sin(x))^2)\n\n# output\nsin(x)^2 + 2sin(x)*cos(x) + cos(x)^2","category":"page"},{"location":"manual/rewrite/","page":"Term Rewriting","title":"Term Rewriting","text":"since Pythagorean identity is applied before square expansion, so it is unable to match squares of sine and cosine.","category":"page"},{"location":"manual/rewrite/","page":"Term Rewriting","title":"Term Rewriting","text":"One way to circumvent the problem of order of applying rules in chain is to use RestartedChain","category":"page"},{"location":"manual/rewrite/","page":"Term Rewriting","title":"Term Rewriting","text":"using SymbolicUtils.Rewriters: RestartedChain\n\nrcas = RestartedChain([acpyid, sqexpand])\n\nrcas((cos(x) + sin(x))^2)\n\n# output\n1 + 2sin(x)*cos(x)","category":"page"},{"location":"manual/rewrite/","page":"Term Rewriting","title":"Term Rewriting","text":"It restarts the chain after each successful application of a rule, so after sqexpand is hit it (re)starts again and successfully applies acpyid to resulting expression.","category":"page"},{"location":"manual/rewrite/","page":"Term Rewriting","title":"Term Rewriting","text":"You can also use Fixpoint to apply the rules until there are no changes.","category":"page"},{"location":"manual/rewrite/","page":"Term Rewriting","title":"Term Rewriting","text":"Fixpoint(cas)((cos(x) + sin(x))^2)\n\n# output\n1 + 2sin(x)*cos(x)","category":"page"},{"location":"upgrade/#Upgrade-to-SymbolicUtils-v1","page":"Upgrade to SymbolicUtils v1","title":"Upgrade to SymbolicUtils v1","text":"","category":"section"},{"location":"upgrade/","page":"Upgrade to SymbolicUtils v1","title":"Upgrade to SymbolicUtils v1","text":"The version 1 of SymbolicUtils utilizes Unityper to speed up the compilation time. We introduce a new type BasicSymbolic <: Symbolic to represent all the previous types Sym, Term, Add, Mul, Pow and Div. Since BasicSymbolic is a concrete type, checking x isa Sym or x isa Pow no longer works. Instead, one should use issym(x) or ispow(x) to check the \"type\" of the expression. Also, note that if one does not need to work on a specific symbolic representation, issym(x) and isterm(x) should be replaced by x isa Symbolic && !istree(x) and istree(x) to be more generic. Furthermore, dispatching on Sym, Term, etc no longer works. Instead, a function that takes BasicSymbolic should check the type of the expression using a if statement to select the right code path.","category":"page"},{"location":"upgrade/","page":"Upgrade to SymbolicUtils v1","title":"Upgrade to SymbolicUtils v1","text":"Although we don't have Sym, Term, etc in the Julia type system anymore, we still provide convenient constructors for them. For example, Sym{Real}(:x) would still work. For constructor that takes a collection like Terms, we recommend directly construct Any element type constructors like Term(f, Any[x1, x2]) to reduce extra memory allocation and compile time.","category":"page"},{"location":"manual/codegen/#Code-generation","page":"Code generation","title":"Code generation","text":"","category":"section"},{"location":"manual/codegen/","page":"Code generation","title":"Code generation","text":"Note: this feature is experimental and the API might change frequently","category":"page"},{"location":"manual/codegen/","page":"Code generation","title":"Code generation","text":"toexpr(ex) turns any expression (ex) into the equivalent Expr which is suitable for eval. The SymbolicUtils.Code module provides some combinators which provides the ability to construct more complex expressions than just function calls. These include:","category":"page"},{"location":"manual/codegen/","page":"Code generation","title":"Code generation","text":"Let blocks\nFunctions with arguments and keyword arguments\nFunctions with arguments which are to be de-structured\nExpressions that set array elements in-place\nExpressions that create an array similar in type to a reference array (currently supports Array, StaticArrays.SArray, and LabelledArrays.SLArray)\nExpressions that create sparse arrays","category":"page"},{"location":"manual/codegen/","page":"Code generation","title":"Code generation","text":"Do using SymbolicUtils.Code to get the following bindings","category":"page"},{"location":"manual/codegen/#toexpr","page":"Code generation","title":"toexpr","text":"","category":"section"},{"location":"manual/codegen/","page":"Code generation","title":"Code generation","text":"SymbolicUtils.Code.toexpr","category":"page"},{"location":"manual/codegen/#SymbolicUtils.Code.toexpr","page":"Code generation","title":"SymbolicUtils.Code.toexpr","text":"toexpr(ex, [st,])\n\nConvert a symbolic expression into an Expr, suitable to be passed into eval.\n\nFor example,\n\njulia> @syms a b\n(a, b)\n\njulia> toexpr(a+b)\n:((+)(a, b))\n\njulia> toexpr(a+b) |> dump\nExpr\n head: Symbol call\n args: Array{Any}((3,))\n 1: + (function of type typeof(+))\n 2: Symbol a\n 3: Symbol b\n\nNote that the function is an actual function object.\n\nFor more complex expressions, see other code-related combinators,\n\nNamely Assignment, Let, Func, SetArray, MakeArray, MakeSparseArray and MakeTuple.\n\nTo make your own type convertible to Expr using toexpr define toexpr(x, st) and forward the state st in internal calls to toexpr. st is state used to know when to leave something like y(t) as it is or when to make it var\"y(t)\". E.g. when y(t) is itself the argument of a function rather than y.\n\n\n\n\n\n","category":"function"},{"location":"manual/codegen/#Code-Combinators","page":"Code generation","title":"Code Combinators","text":"","category":"section"},{"location":"manual/codegen/","page":"Code generation","title":"Code generation","text":"These are all exported when you do using SymbolicUtils.Code","category":"page"},{"location":"manual/codegen/","page":"Code generation","title":"Code generation","text":"SymbolicUtils.Code.Assignment\nSymbolicUtils.Code.Let\nSymbolicUtils.Code.Func\nSymbolicUtils.Code.SpawnFetch\nSymbolicUtils.Code.SetArray\nSymbolicUtils.Code.MakeArray\nSymbolicUtils.Code.MakeSparseArray\nSymbolicUtils.Code.MakeTuple\nSymbolicUtils.Code.LiteralExpr","category":"page"},{"location":"manual/codegen/#SymbolicUtils.Code.Assignment","page":"Code generation","title":"SymbolicUtils.Code.Assignment","text":"Assignment(lhs, rhs)\n\nAn assignment expression. Shorthand lhs ← rhs (\\leftarrow)\n\n\n\n\n\n","category":"type"},{"location":"manual/codegen/#SymbolicUtils.Code.Let","page":"Code generation","title":"SymbolicUtils.Code.Let","text":"Let(assignments, body[, let_block])\n\nA Let block.\n\nassignments is a vector of Assignments\nbody is the body of the let block\nlet_block boolean (default=true) – do not create a let block if false.\n\n\n\n\n\n","category":"type"},{"location":"manual/codegen/#SymbolicUtils.Code.Func","page":"Code generation","title":"SymbolicUtils.Code.Func","text":"Func(args, kwargs, body[, pre])\n\nA function.\n\nargs is a vector of expressions\nkwargs is a vector of Assignments\nbody is the body of the function\npre a vector of expressions to be prepended to the function body, for example, it could be [Expr(:meta, :inline), Expr(:meta, :propagate_inbounds)] to create an @inline @propagate_inbounds function definition.\n\nSpecial features in args:\n\nargs can contain DestructuredArgs\ncall expressions\n\nFor example,\n\n\njulia> @syms a b c t f(d) x(t) y(t) z(t)\n(a, b, c, t, f(::Number)::Number, x(::Number)::Number, y(::Number)::Number, z(::Number)::Number)\n\njulia> func = Func([a,x(t), DestructuredArgs([b, y(t)]), f], # args\n [c ← 2, z(t) ← 42], # kwargs\n f((a + b + c) / x(t) + y(t) + z(t)));\n\njulia> toexpr(func)\n:(function (a, var\"x(t)\", var\"##arg#255\", f; c = 2, var\"z(t)\" = 42)\n let b = var\"##arg#255\"[1], var\"y(t)\" = var\"##arg#255\"[2]\n f((+)(var\"y(t)\", var\"z(t)\", (*)((+)(a, b, c), (inv)(var\"x(t)\"))))\n end\n end)\n\nthe second argument is a DestructuredArgs, in the Expr form, it is given a random name, and is expected to receive a vector or tuple of size 2 containing the values of b and y(t). The let block that is automatically generated \"destructures\" these arguments.\nx(t) and y(t) have been replaced with var\"x(t)\" and var\"y(t)\" symbols throughout\n\nthe generated Expr. This makes sure that we are not actually calling the expressions x(t) or y(t) but instead passing the right values in place of the whole expression.\n\nf is also a function-like symbol, same as x and y, but since the args array contains f as itself rather than as say, f(t), it does not become a var\"f(t)\". The generated function expects a function of one argument to be passed in the position of f.\n\nAn example invocation of this function is:\n\njulia> executable = eval(toexpr(func))\n#10 (generic function with 1 method)\n\njulia> executable(1, 2.0, [2,3.0], x->string(x); var\"z(t)\" = sqrt(42))\n\"11.98074069840786\"\n\n\n\n\n\n","category":"type"},{"location":"manual/codegen/#SymbolicUtils.Code.SpawnFetch","page":"Code generation","title":"SymbolicUtils.Code.SpawnFetch","text":"SpawnFetch{ParallelType}(funcs [, args], reduce)\n\nRun every expression in funcs in its own task, the expression should be a Func object and is passed to Threads.Task(f). If Func takes arguments, then the arguments must be passed in as args–a vector of vector of arguments to each function in funcs. We don't use @spawn in order to support RuntimeGeneratedFunctions which disallow closures, instead we interpolate these functions or closures as smaller RuntimeGeneratedFunctions.\n\nreduce function is used to combine the results of executing exprs. A SpawnFetch expression returns the reduced result.\n\nUse Symbolics.MultithreadedForm ParallelType from the Symbolics.jl package to get the RuntimeGeneratedFunction version SpawnFetch.\n\nParallelType can be used to define more parallelism types SymbolicUtils supports Multithreaded type. Which spawns threaded tasks.\n\n\n\n\n\n","category":"type"},{"location":"manual/codegen/#SymbolicUtils.Code.SetArray","page":"Code generation","title":"SymbolicUtils.Code.SetArray","text":"SetArray(inbounds, arr, elems)\n\nAn expression representing setting of elements of arr.\n\nBy default, every element of elems is copied over to arr,\n\nbut if elems contains AtIndex(i, val) objects, then arr[i] = val is performed in its place.\n\ninbounds is a boolean flag, true surrounds the resulting expression in an @inbounds.\n\n\n\n\n\n","category":"type"},{"location":"manual/codegen/#SymbolicUtils.Code.MakeArray","page":"Code generation","title":"SymbolicUtils.Code.MakeArray","text":"MakeArray(elems, similarto, [output_eltype=nothing])\n\nAn expression which constructs an array.\n\nelems is the output array\nsimilarto can either be a type, or some symbol that is an array whose type needs to be emulated. If similarto is a StaticArrays.SArray, then the output array is also created as an SArray, similarly, an Array will result in an Array, and a LabelledArrays.SLArray will result in a labelled static array.\noutput_eltype: if set, then forces the element type of the output array to be this. by default, the output type is inferred automatically.\n\nYou can define:\n\n@inline function create_array(A::Type{<:MyArray},a\n ::Nothing, d::Val{dims}, elems...) where dims\n\n# and\n\n@inline function create_array(::Type{<:MyArray}, T, ::Val{dims}, elems...) where dims\n\nwhich creates an array of size dims using the elements elems and eltype T, to allow MakeArray to create arrays similarto MyArrays.\n\n\n\n\n\n","category":"type"},{"location":"manual/codegen/#SymbolicUtils.Code.MakeSparseArray","page":"Code generation","title":"SymbolicUtils.Code.MakeSparseArray","text":"MakeSpaseArray(array)\n\nAn expression which creates a SparseMatrixCSC or a SparseVector.\n\nThe generated expression contains the sparsity information of array,\n\nit only creates the nzval field at run time.\n\n\n\n\n\n","category":"type"},{"location":"manual/codegen/#SymbolicUtils.Code.MakeTuple","page":"Code generation","title":"SymbolicUtils.Code.MakeTuple","text":"MakeTuple(tup)\n\nMake a Tuple from a tuple of expressions.\n\n\n\n\n\n","category":"type"},{"location":"manual/codegen/#SymbolicUtils.Code.LiteralExpr","page":"Code generation","title":"SymbolicUtils.Code.LiteralExpr","text":"LiteralExpr(ex)\n\nLiterally ex, an Expr. toexpr on LiteralExpr recursively calls toexpr on any interpolated symbolic expressions.\n\n\n\n\n\n","category":"type"},{"location":"#SymbolicUtils.jl-—-Symbolic-programming-in-Julia","page":"SymbolicUtils.jl — Symbolic programming in Julia","title":"SymbolicUtils.jl — Symbolic programming in Julia","text":"","category":"section"},{"location":"#Features","page":"SymbolicUtils.jl — Symbolic programming in Julia","title":"Features","text":"","category":"section"},{"location":"","page":"SymbolicUtils.jl — Symbolic programming in Julia","title":"SymbolicUtils.jl — Symbolic programming in Julia","text":"Fast expressions\nA rule-based rewriting language.\nA combinator library for making rewriters.\nEfficient representation of numeric expressions\nType promotion:\nSymbols (Syms) carry type information. (read more)\nCompound expressions composed of Syms propagate type information. (read more)\nSet of extendable simplification rules.","category":"page"},{"location":"#Creating-symbolic-expressions","page":"SymbolicUtils.jl — Symbolic programming in Julia","title":"Creating symbolic expressions","text":"","category":"section"},{"location":"","page":"SymbolicUtils.jl — Symbolic programming in Julia","title":"SymbolicUtils.jl — Symbolic programming in Julia","text":"First, let's use the @syms macro to create a few symbols.","category":"page"},{"location":"","page":"SymbolicUtils.jl — Symbolic programming in Julia","title":"SymbolicUtils.jl — Symbolic programming in Julia","text":"using SymbolicUtils\n\n@syms w z α::Real β::Real\n\n(w, z, α, β) # hide\n","category":"page"},{"location":"","page":"SymbolicUtils.jl — Symbolic programming in Julia","title":"SymbolicUtils.jl — Symbolic programming in Julia","text":"Type annotations are optional when creating symbols. Here α, β behave like Real numbers. w and z behave like Number, which is the default. You can use the symtype function to find the type of a symbol.","category":"page"},{"location":"","page":"SymbolicUtils.jl — Symbolic programming in Julia","title":"SymbolicUtils.jl — Symbolic programming in Julia","text":"using SymbolicUtils: symtype\n\nsymtype(w), symtype(z), symtype(α), symtype(β)","category":"page"},{"location":"","page":"SymbolicUtils.jl — Symbolic programming in Julia","title":"SymbolicUtils.jl — Symbolic programming in Julia","text":"Note however that they are not subtypes of these types!","category":"page"},{"location":"","page":"SymbolicUtils.jl — Symbolic programming in Julia","title":"SymbolicUtils.jl — Symbolic programming in Julia","text":"@show w isa Number\n@show α isa Real","category":"page"},{"location":"","page":"SymbolicUtils.jl — Symbolic programming in Julia","title":"SymbolicUtils.jl — Symbolic programming in Julia","text":"As their types are different:","category":"page"},{"location":"","page":"SymbolicUtils.jl — Symbolic programming in Julia","title":"SymbolicUtils.jl — Symbolic programming in Julia","text":"@show typeof(w)\n@show typeof(α)","category":"page"},{"location":"","page":"SymbolicUtils.jl — Symbolic programming in Julia","title":"SymbolicUtils.jl — Symbolic programming in Julia","text":"(see this post for why they are all not just subtypes of Number)","category":"page"},{"location":"","page":"SymbolicUtils.jl — Symbolic programming in Julia","title":"SymbolicUtils.jl — Symbolic programming in Julia","text":"You can do basic arithmetic on symbols to get symbolic expressions:","category":"page"},{"location":"","page":"SymbolicUtils.jl — Symbolic programming in Julia","title":"SymbolicUtils.jl — Symbolic programming in Julia","text":"expr1 = α*sin(w)^2 + β*cos(z)^2\nexpr2 = α*cos(z)^2 + β*sin(w)^2\n\nexpr1 + expr2","category":"page"},{"location":"","page":"SymbolicUtils.jl — Symbolic programming in Julia","title":"SymbolicUtils.jl — Symbolic programming in Julia","text":"SymbolicUtils automatically simplifies","category":"page"},{"location":"","page":"SymbolicUtils.jl — Symbolic programming in Julia","title":"SymbolicUtils.jl — Symbolic programming in Julia","text":"2w + 3w - 3z + α","category":"page"},{"location":"","page":"SymbolicUtils.jl — Symbolic programming in Julia","title":"SymbolicUtils.jl — Symbolic programming in Julia","text":"and reorders","category":"page"},{"location":"","page":"SymbolicUtils.jl — Symbolic programming in Julia","title":"SymbolicUtils.jl — Symbolic programming in Julia","text":"(z + w)*(α + β)","category":"page"},{"location":"","page":"SymbolicUtils.jl — Symbolic programming in Julia","title":"SymbolicUtils.jl — Symbolic programming in Julia","text":"expressions of type Symbolic{<:Number} (which includes Sym{Real}) when they are created. It also does constant elimination (including rational numbers)","category":"page"},{"location":"","page":"SymbolicUtils.jl — Symbolic programming in Julia","title":"SymbolicUtils.jl — Symbolic programming in Julia","text":"5 + 2w - 3z + α - (β + 5//3) + 3w - 2 + 3//2 * β","category":"page"},{"location":"","page":"SymbolicUtils.jl — Symbolic programming in Julia","title":"SymbolicUtils.jl — Symbolic programming in Julia","text":"It's worth remembering that the expression may be transformed with respect to the input when it's created.","category":"page"},{"location":"#Function-like-symbols","page":"SymbolicUtils.jl — Symbolic programming in Julia","title":"Function-like symbols","text":"","category":"section"},{"location":"","page":"SymbolicUtils.jl — Symbolic programming in Julia","title":"SymbolicUtils.jl — Symbolic programming in Julia","text":"Symbols can be defined to behave like functions. Both the input and output types for the function can be specified. Any application to that function will only admit either values of those types or symbols of the same symtype.","category":"page"},{"location":"","page":"SymbolicUtils.jl — Symbolic programming in Julia","title":"SymbolicUtils.jl — Symbolic programming in Julia","text":"using SymbolicUtils\n@syms f(x) g(x::Real, y::Real)::Real\n\nf(z) + g(1, α) + sin(w)","category":"page"},{"location":"","page":"SymbolicUtils.jl — Symbolic programming in Julia","title":"SymbolicUtils.jl — Symbolic programming in Julia","text":"This does not work since z is a Number, not a Real.","category":"page"},{"location":"","page":"SymbolicUtils.jl — Symbolic programming in Julia","title":"SymbolicUtils.jl — Symbolic programming in Julia","text":"g(1, z)","category":"page"},{"location":"","page":"SymbolicUtils.jl — Symbolic programming in Julia","title":"SymbolicUtils.jl — Symbolic programming in Julia","text":"This works because g \"returns\" a Real.","category":"page"},{"location":"","page":"SymbolicUtils.jl — Symbolic programming in Julia","title":"SymbolicUtils.jl — Symbolic programming in Julia","text":"g(2//5, g(1, β))","category":"page"},{"location":"#Expression-interface","page":"SymbolicUtils.jl — Symbolic programming in Julia","title":"Expression interface","text":"","category":"section"},{"location":"","page":"SymbolicUtils.jl — Symbolic programming in Julia","title":"SymbolicUtils.jl — Symbolic programming in Julia","text":"Symbolic expressions are of type Term{T}, Add{T}, Mul{T}, Pow{T} or Div{T} and denote some function call where one or more arguments are themselves such expressions or Syms. See more about the representation here.","category":"page"},{"location":"","page":"SymbolicUtils.jl — Symbolic programming in Julia","title":"SymbolicUtils.jl — Symbolic programming in Julia","text":"All the expression types support the TermInterface.jl interface. Please refer to the package for the complete reference of the interface.","category":"page"},{"location":"#Term-rewriting","page":"SymbolicUtils.jl — Symbolic programming in Julia","title":"Term rewriting","text":"","category":"section"},{"location":"","page":"SymbolicUtils.jl — Symbolic programming in Julia","title":"SymbolicUtils.jl — Symbolic programming in Julia","text":"SymbolicUtils contains a rule-based rewriting language for easy pattern matching and rewriting of expression. There is also a combinator library to combine rules to chain, branch and loop over rules.","category":"page"},{"location":"#Simplification","page":"SymbolicUtils.jl — Symbolic programming in Julia","title":"Simplification","text":"","category":"section"},{"location":"","page":"SymbolicUtils.jl — Symbolic programming in Julia","title":"SymbolicUtils.jl — Symbolic programming in Julia","text":"By default +, * and ^ operations apply the most basic simplification upon construction of the expression.","category":"page"},{"location":"","page":"SymbolicUtils.jl — Symbolic programming in Julia","title":"SymbolicUtils.jl — Symbolic programming in Julia","text":"The rules with which the canonical form of Symbolic{<:Number} terms are constructed are the next (where x isa Symbolic and c isa Number)","category":"page"},{"location":"","page":"SymbolicUtils.jl — Symbolic programming in Julia","title":"SymbolicUtils.jl — Symbolic programming in Julia","text":"0 + x, 1 * x and x^1 always gives x\n0 * x always gives 0 and x ^ 0 gives 1\n-x becomes (-1)*x\ncommutativity and associativity over + and * are assumed. Re-ordering of terms will be done under a total order\np/q * x or x * p/q results in (p*x)/q\np/q * x/y results in (p*x)/(q*y)\nx + ... + x will be fused into n*x with type Mul\nx * ... * x will be fused into x^n with type Pow\nsum of Add's are fused\nproduct of Mul's are fused\nc * (c₁x₁ + ... + cₙxₙ) will be converted into c*c₁*x₁ + ... + c*cₙ*xₙ\n(x₁^c₁ * ... * xₙ^cₙ)^c will be converted into x₁^(c*c₁) * ... * xₙ^(c*cₙ)\nthere are come other simplifications on construction that you can check here","category":"page"},{"location":"","page":"SymbolicUtils.jl — Symbolic programming in Julia","title":"SymbolicUtils.jl — Symbolic programming in Julia","text":"Here is an example of this","category":"page"},{"location":"","page":"SymbolicUtils.jl — Symbolic programming in Julia","title":"SymbolicUtils.jl — Symbolic programming in Julia","text":"2 * (w+w+α+β + sin(z)^2 + cos(z)^2 - 1)","category":"page"},{"location":"","page":"SymbolicUtils.jl — Symbolic programming in Julia","title":"SymbolicUtils.jl — Symbolic programming in Julia","text":"The simplify function applies a built-in set of rules to rewrite expressions in order to simplify it.","category":"page"},{"location":"","page":"SymbolicUtils.jl — Symbolic programming in Julia","title":"SymbolicUtils.jl — Symbolic programming in Julia","text":"simplify(2 * (w+w+α+β + sin(z)^2 + cos(z)^2 - 1))","category":"page"},{"location":"","page":"SymbolicUtils.jl — Symbolic programming in Julia","title":"SymbolicUtils.jl — Symbolic programming in Julia","text":"The rules in the default simplify applies simple constant elimination and trigonometric identities.","category":"page"},{"location":"","page":"SymbolicUtils.jl — Symbolic programming in Julia","title":"SymbolicUtils.jl — Symbolic programming in Julia","text":"If you read the previous section on the rules DSL, you should be able to read and understand the rules that are used by simplify.","category":"page"},{"location":"#Code-generation","page":"SymbolicUtils.jl — Symbolic programming in Julia","title":"Code generation","text":"","category":"section"},{"location":"","page":"SymbolicUtils.jl — Symbolic programming in Julia","title":"SymbolicUtils.jl — Symbolic programming in Julia","text":"Experimental feature","category":"page"},{"location":"","page":"SymbolicUtils.jl — Symbolic programming in Julia","title":"SymbolicUtils.jl — Symbolic programming in Julia","text":"It is common to want to generate executable code from symbolic expressions and blocks of them. We are working on experimental support for turning Symbolic expressions into executable functions with specific focus on array input and output and performance which is critical to the Differential Equations ecosystem which is making heavy use of this package.","category":"page"},{"location":"","page":"SymbolicUtils.jl — Symbolic programming in Julia","title":"SymbolicUtils.jl — Symbolic programming in Julia","text":"See Code generation for more about this.","category":"page"},{"location":"#Learn-more","page":"SymbolicUtils.jl — Symbolic programming in Julia","title":"Learn more","text":"","category":"section"},{"location":"","page":"SymbolicUtils.jl — Symbolic programming in Julia","title":"SymbolicUtils.jl — Symbolic programming in Julia","text":"If you have a package that you would like to utilize rule-based rewriting in, look at the suggestions in the Interfacing section to find out how you can do that without any fundamental changes to your package. Look at the API documentation for docstrings about specific functions or macros.","category":"page"},{"location":"","page":"SymbolicUtils.jl — Symbolic programming in Julia","title":"SymbolicUtils.jl — Symbolic programming in Julia","text":"Head over to the github repository to ask questions and report problems! Join the Zulip stream to chat!","category":"page"},{"location":"manual/interface/#Interfacing-with-SymbolicUtils.jl","page":"Interfacing with SymbolicUtils.jl","title":"Interfacing with SymbolicUtils.jl","text":"","category":"section"},{"location":"manual/interface/","page":"Interfacing with SymbolicUtils.jl","title":"Interfacing with SymbolicUtils.jl","text":"This section is for Julia package developers who may want to use the simplify and rule rewriting system on their own expression types.","category":"page"},{"location":"manual/interface/#Defining-the-interface","page":"Interfacing with SymbolicUtils.jl","title":"Defining the interface","text":"","category":"section"},{"location":"manual/interface/","page":"Interfacing with SymbolicUtils.jl","title":"Interfacing with SymbolicUtils.jl","text":"SymbolicUtils matchers can match any Julia object that implements an interface to traverse it as a tree. The interface in question, is defined in the TermInterface.jl package. Its purpose is to provide a shared interface between various symbolic programming Julia packages. ","category":"page"},{"location":"manual/interface/","page":"Interfacing with SymbolicUtils.jl","title":"Interfacing with SymbolicUtils.jl","text":"In particular, you should define methods from TermInterface.jl for an expression tree type T with symbol types S to work with SymbolicUtils.jl","category":"page"},{"location":"manual/interface/","page":"Interfacing with SymbolicUtils.jl","title":"Interfacing with SymbolicUtils.jl","text":"You can read the documentation of TermInterface.jl on the Github repository.","category":"page"}] } diff --git a/dev/upgrade/index.html b/dev/upgrade/index.html index 51e8a031..7139ee2b 100644 --- a/dev/upgrade/index.html +++ b/dev/upgrade/index.html @@ -1,2 +1,2 @@ -Upgrade to SymbolicUtils v1 · SymbolicUtils.jl

Upgrade to SymbolicUtils v1

The version 1 of SymbolicUtils utilizes Unityper to speed up the compilation time. We introduce a new type BasicSymbolic <: Symbolic to represent all the previous types Sym, Term, Add, Mul, Pow and Div. Since BasicSymbolic is a concrete type, checking x isa Sym or x isa Pow no longer works. Instead, one should use issym(x) or ispow(x) to check the "type" of the expression. Also, note that if one does not need to work on a specific symbolic representation, issym(x) and isterm(x) should be replaced by x isa Symbolic && !istree(x) and istree(x) to be more generic. Furthermore, dispatching on Sym, Term, etc no longer works. Instead, a function that takes BasicSymbolic should check the type of the expression using a if statement to select the right code path.

Although we don't have Sym, Term, etc in the Julia type system anymore, we still provide convenient constructors for them. For example, Sym{Real}(:x) would still work. For constructor that takes a collection like Terms, we recommend directly construct Any element type constructors like Term(f, Any[x1, x2]) to reduce extra memory allocation and compile time.

+Upgrade to SymbolicUtils v1 · SymbolicUtils.jl

Upgrade to SymbolicUtils v1

The version 1 of SymbolicUtils utilizes Unityper to speed up the compilation time. We introduce a new type BasicSymbolic <: Symbolic to represent all the previous types Sym, Term, Add, Mul, Pow and Div. Since BasicSymbolic is a concrete type, checking x isa Sym or x isa Pow no longer works. Instead, one should use issym(x) or ispow(x) to check the "type" of the expression. Also, note that if one does not need to work on a specific symbolic representation, issym(x) and isterm(x) should be replaced by x isa Symbolic && !istree(x) and istree(x) to be more generic. Furthermore, dispatching on Sym, Term, etc no longer works. Instead, a function that takes BasicSymbolic should check the type of the expression using a if statement to select the right code path.

Although we don't have Sym, Term, etc in the Julia type system anymore, we still provide convenient constructors for them. For example, Sym{Real}(:x) would still work. For constructor that takes a collection like Terms, we recommend directly construct Any element type constructors like Term(f, Any[x1, x2]) to reduce extra memory allocation and compile time.