diff --git a/dev/.documenter-siteinfo.json b/dev/.documenter-siteinfo.json index 667d18741..b90bd9df7 100644 --- a/dev/.documenter-siteinfo.json +++ b/dev/.documenter-siteinfo.json @@ -1 +1 @@ -{"documenter":{"julia_version":"1.11.0","generation_timestamp":"2024-10-11T22:09:50","documenter_version":"1.7.0"}} \ No newline at end of file +{"documenter":{"julia_version":"1.11.0","generation_timestamp":"2024-10-11T22:46:43","documenter_version":"1.7.0"}} \ No newline at end of file diff --git a/dev/comparison/index.html b/dev/comparison/index.html index d07b89698..8ff8483b6 100644 --- a/dev/comparison/index.html +++ b/dev/comparison/index.html @@ -1,2 +1,2 @@ -Comparison Against SymPy · Symbolics.jl

Comparison of Julia's Symbolics.jl vs SymPy for Symbolic Computation

Symbolics.jl is a symbolic modeling language for Julia, built in Julia. Its goal is very different from Sympy: it was made to support symbolic-numerics, the combination of symbolic computing with numerical methods to allow for extreme performance computing that would not be possible without modifying the model. Because of this, Symbolics.jl excels in many areas due to purposeful design decisions:

  • Performance: Symbolics.jl is built in Julia, whereas SymPy was built in Python. Thus, the performance bar for Symbolics.jl is much higher. Symbolics.jl started because SymPy was far too slow and SymEngine was far too inflexible for the projects they were doing. Performance is key to Symbolics.jl. If you find any performance issues, please file an issue.
  • build_function: lambdify is “fine” for some people, but if you're building a super fast MPI-enabled Julia/C/Fortran simulation code, having a function that hits the Python interpreter is less than optimal. By default, build_function builds fast JIT-compiled functions due to being in Julia. However, it has support for things like static arrays, non-allocating functions via mutation, fast functions on sparse matrices and arrays of arrays, etc.: all core details of doing high performance computing.
  • Parallelism: Symbolics.jl has pervasive parallelism. The symbolic simplification via SymbolicUtils.jl has built-in parallelism, Symbolics.jl builds functions that parallelize across threads. Symbolics.jl is compatible with GPU libraries like CUDA.jl.
  • Extensible: Symbolics.jl and its underlying tools are written in pure Julia. Want to add new or better simplification rules? Add some Julia code! Need to add new derivatives? Add some Julia code! You get the picture. Breaking down these barriers makes it easier for the user to tailor the program to their needs and accelerates the development of the library.
  • Deep integration with the Julia ecosystem: Symbolics.jl's integration with neural networks is not the only thing that's deep. Symbolics.jl is built with the same philosophy as other SciML packages, eschewing “monorepos” for a distributed development approach that ties together the work of many developers. The differentiation parts utilize tools from automatic differentiation libraries, all linear algebra functionality comes from tracing Julia Base itself, symbolic rewriting (simplification and substitution) comes from SymbolicUtils.jl, parallelism comes from Julia Base libraries, Dagger.jl, etc. SciML Tools like DataDrivenDiffEq.jl can reconstruct symbolic expressions from neural networks and data, while NeuralPDE.jl can automatically solve partial differential equations from symbolic descriptions using physics-informed neural networks. The list keeps going. All told, by design Symbolics.jl's development moves fast because it's effectively using the work of hundreds of Julia developers, allowing it to grow fast.
  • While Symbolics.jl has many features missing from SymPy, it does not superset SymPy's functionality. For a list of missing features, see this issue.
+Comparison Against SymPy · Symbolics.jl

Comparison of Julia's Symbolics.jl vs SymPy for Symbolic Computation

Symbolics.jl is a symbolic modeling language for Julia, built in Julia. Its goal is very different from Sympy: it was made to support symbolic-numerics, the combination of symbolic computing with numerical methods to allow for extreme performance computing that would not be possible without modifying the model. Because of this, Symbolics.jl excels in many areas due to purposeful design decisions:

  • Performance: Symbolics.jl is built in Julia, whereas SymPy was built in Python. Thus, the performance bar for Symbolics.jl is much higher. Symbolics.jl started because SymPy was far too slow and SymEngine was far too inflexible for the projects they were doing. Performance is key to Symbolics.jl. If you find any performance issues, please file an issue.
  • build_function: lambdify is “fine” for some people, but if you're building a super fast MPI-enabled Julia/C/Fortran simulation code, having a function that hits the Python interpreter is less than optimal. By default, build_function builds fast JIT-compiled functions due to being in Julia. However, it has support for things like static arrays, non-allocating functions via mutation, fast functions on sparse matrices and arrays of arrays, etc.: all core details of doing high performance computing.
  • Parallelism: Symbolics.jl has pervasive parallelism. The symbolic simplification via SymbolicUtils.jl has built-in parallelism, Symbolics.jl builds functions that parallelize across threads. Symbolics.jl is compatible with GPU libraries like CUDA.jl.
  • Extensible: Symbolics.jl and its underlying tools are written in pure Julia. Want to add new or better simplification rules? Add some Julia code! Need to add new derivatives? Add some Julia code! You get the picture. Breaking down these barriers makes it easier for the user to tailor the program to their needs and accelerates the development of the library.
  • Deep integration with the Julia ecosystem: Symbolics.jl's integration with neural networks is not the only thing that's deep. Symbolics.jl is built with the same philosophy as other SciML packages, eschewing “monorepos” for a distributed development approach that ties together the work of many developers. The differentiation parts utilize tools from automatic differentiation libraries, all linear algebra functionality comes from tracing Julia Base itself, symbolic rewriting (simplification and substitution) comes from SymbolicUtils.jl, parallelism comes from Julia Base libraries, Dagger.jl, etc. SciML Tools like DataDrivenDiffEq.jl can reconstruct symbolic expressions from neural networks and data, while NeuralPDE.jl can automatically solve partial differential equations from symbolic descriptions using physics-informed neural networks. The list keeps going. All told, by design Symbolics.jl's development moves fast because it's effectively using the work of hundreds of Julia developers, allowing it to grow fast.
  • While Symbolics.jl has many features missing from SymPy, it does not superset SymPy's functionality. For a list of missing features, see this issue.
diff --git a/dev/examples/perturbation/index.html b/dev/examples/perturbation/index.html index ee324b230..97b602267 100644 --- a/dev/examples/perturbation/index.html +++ b/dev/examples/perturbation/index.html @@ -65,4 +65,4 @@ - (𝜀 + 4*𝜀^2 + 10*𝜀^3)*𝑀^3/6 + (𝜀 + 16*𝜀^2 + 91*𝜀^3)*𝑀^5/120

Comparing the formula to the one for 𝐸 in the Wikipedia article on the Kepler's equation:

\[ E = \frac{1}{1-\epsilon}M -\frac{\epsilon}{(1-\epsilon)^4} \frac{M^3}{3!} + \frac{(9\epsilon^2 - + \epsilon)}{(1-\epsilon)^7}\frac{M^5}{5!}\cdots\]

The first deviation is in the coefficient of $\epsilon^3 M^5$.

+ + \epsilon)}{(1-\epsilon)^7}\frac{M^5}{5!}\cdots\]

The first deviation is in the coefficient of $\epsilon^3 M^5$.

diff --git a/dev/getting_started/index.html b/dev/getting_started/index.html index aaad34f17..fa114d378 100644 --- a/dev/getting_started/index.html +++ b/dev/getting_started/index.html @@ -47,9 +47,9 @@ f(u)

\[ \begin{equation} \left[ \begin{array}{c} -u_1 - u_3 \\ - - u_2 + u_1^{2} \\ -u_2 + u_3 \\ +u{_1} - u{_3} \\ + - u{_2} + u{_1}^{2} \\ +u{_2} + u{_3} \\ \end{array} \right] \end{equation} @@ -233,9 +233,9 @@ out = sparse(rows, cols, zeros(length(cols)), size(sj)...) # pre-allocate, and correct structure myf = eval(last(f_expr)) myf(out, rand(N)) # note that out matches the sparsity structure of sj -out

20×10 SparseArrays.SparseMatrixCSC{Float64, Int64} with 20 stored entries:
-⎡⠀⠀⠢⠀⢀⎤
-⎢⠀⠈⠄⡀⠂⎥
-⎢⠀⠁⠀⠲⠆⎥
-⎢⠂⢀⠅⠠⠀⎥
-⎣⠀⠈⠐⠀⠀⎦
+out
20×10 SparseArrays.SparseMatrixCSC{Float64, Int64} with 19 stored entries:
+⎡⠰⠄⠀⠂⠀⎤
+⎢⠑⠠⠀⠔⠐⎥
+⎢⠀⠀⠀⡀⠀⎥
+⎢⠂⢀⠀⠀⠠⎥
+⎣⠠⠀⠂⠠⠒⎦
diff --git a/dev/index.html b/dev/index.html index 1f81f1106..e7033dc24 100644 --- a/dev/index.html +++ b/dev/index.html @@ -399,4 +399,4 @@ [8e850b90] libblastrampoline_jll v5.11.0+0 [8e850ede] nghttp2_jll v1.59.0+0 [3f19e933] p7zip_jll v17.4.0+2 -Info Packages marked with ⌅ have new versions available but compatibility constraints restrict them from upgrading. To see why use `status --outdated -m`

You can also download the manifest file and the project file.

+Info Packages marked with ⌅ have new versions available but compatibility constraints restrict them from upgrading. To see why use `status --outdated -m`

You can also download the manifest file and the project file.

diff --git a/dev/manual/arrays/index.html b/dev/manual/arrays/index.html index 54f4b61a5..86ef58d43 100644 --- a/dev/manual/arrays/index.html +++ b/dev/manual/arrays/index.html @@ -12,9 +12,9 @@ \]

is a vector of two symbolic variables. As shorthand,

u2 = Symbolics.variables(:x, 1:3, 3:6)

\[ \begin{equation} \left[ \begin{array}{cccc} -x_{1}ˏ_3 & x_{1}ˏ_4 & x_{1}ˏ_5 & x_{1}ˏ_6 \\ -x_{2}ˏ_3 & x_{2}ˏ_4 & x_{2}ˏ_5 & x_{2}ˏ_6 \\ -x_{3}ˏ_3 & x_{3}ˏ_4 & x_{3}ˏ_5 & x_{3}ˏ_6 \\ +x{{_1}}ˏ{_3} & x{{_1}}ˏ{_4} & x{{_1}}ˏ{_5} & x{{_1}}ˏ{_6} \\ +x{{_2}}ˏ{_3} & x{{_2}}ˏ{_4} & x{{_2}}ˏ{_5} & x{{_2}}ˏ{_6} \\ +x{{_3}}ˏ{_3} & x{{_3}}ˏ{_4} & x{{_3}}ˏ{_5} & x{{_3}}ˏ{_6} \\ \end{array} \right] \end{equation} @@ -43,7 +43,7 @@ A b \end{equation} \]

size(c)
(5,)
eltype(c)
Real

Adjoints, matrix-matrix, and matrix-vector multiplications are supported. Dot product returns a scalar-valued expression:

b'b

\[ \begin{equation} -\mathrm{adjoint}\left( b \right) b_{1} +adjoint\left( b \right) b_{1} \end{equation} \]

size(b'b)
()

Outer product returns a matrix:

b * b'

\[ \begin{equation} b \mathrm{adjoint}\left( b \right) @@ -58,10 +58,10 @@ A \mathrm{adjoint}\left( A \right) \end{equation} \]

AAt[2,3]

\[ \begin{equation} -A \mathrm{adjoint}\left( A \right)_{2,3} +A adjoint\left( A \right)_{2,3} \end{equation} \]

Here we indexed for the element (2,3), but we got back a symbolic indexing expression. You may want to force the element to be computed in terms of the elements of A. This can be done, using the scalarize function.

Symbolics.scalarize(AAt[2,3])

\[ \begin{equation} A_{2,1} A_{3,1} + A_{2,2} A_{3,2} + A_{2,3} A_{3,3} \end{equation} \]

@syms i::Int j::Int
-Symbolics.scalarize(AAt[i,j])

In general, any scalar expression which is derived from array expressions can be scalarized.

#sum(A[:,1]) + sum(A[2,:])#latexify not working
Symbolics.scalarize(sum(A[:,1]) + sum(A[2,:]))
+Symbolics.scalarize(AAt[i,j])

In general, any scalar expression which is derived from array expressions can be scalarized.

#sum(A[:,1]) + sum(A[2,:])#latexify not working
Symbolics.scalarize(sum(A[:,1]) + sum(A[2,:]))
diff --git a/dev/manual/build_function/index.html b/dev/manual/build_function/index.html index c67dad1b6..314936cc5 100644 --- a/dev/manual/build_function/index.html +++ b/dev/manual/build_function/index.html @@ -3,7 +3,7 @@ expression = Val{true}, target = JuliaTarget(), parallel=nothing, - kwargs...)

Arguments:

Keyword Arguments:

Note that not all build targets support the full compilation interface. Check the individual target documentation for details.

source

Target-Specific Definitions

Symbolics._build_functionMethod
_build_function(target::JuliaTarget, rhss::AbstractArray, args...;
+               kwargs...)

Arguments:

  • ex: The Num to compile
  • args: The arguments of the function
  • expression: Whether to generate code or whether to generate the compiled form. By default, expression = Val{true}, which means that the code for the function is returned. If Val{false}, then the returned value is compiled.

Keyword Arguments:

  • target: The output target of the compilation process. Possible options are:
    • JuliaTarget: Generates a Julia function
    • CTarget: Generates a C function
    • StanTarget: Generates a function for compiling with the Stan probabilistic programming language
    • MATLABTarget: Generates an anonymous function for use in MATLAB and Octave environments
  • parallel: The kind of parallelism to use in the generated function. Defaults to SerialForm(), i.e. no parallelism, if ex is a single expression or an array containing <= 1500 non-zero expressions. If ex is an array of > 1500 non-zero expressions, then ShardedForm(80, 4) is used. See below for more on ShardedForm. Note that the parallel forms are not exported and thus need to be chosen like Symbolics.SerialForm(). The choices are:
    • SerialForm(): Serial execution.
    • ShardedForm(cutoff, ncalls): splits the output function into sub-functions which contain at most cutoff number of output rhss. These sub-functions are called by the top-level function that buildfunction returns. This helps in reducing the compile time of the generated function.
    • MultithreadedForm(): Multithreaded execution with a static split, evenly splitting the number of expressions per thread.
  • fname: Used by some targets for the name of the function in the target space.

Note that not all build targets support the full compilation interface. Check the individual target documentation for details.

source

Target-Specific Definitions

Symbolics._build_functionMethod
_build_function(target::JuliaTarget, rhss::AbstractArray, args...;
                    conv=toexpr,
                    expression = Val{true},
                    expression_module = @__MODULE__(),
@@ -26,14 +26,14 @@
                          convert_oop = true, force_SA = false,
                          skipzeros = outputidxs===nothing,
                          fillzeros = skipzeros && !(typeof(rhss)<:SparseMatrixCSC),
-                         parallel=SerialForm(), kwargs...)

Generates a Julia function which can then be utilized for further evaluations. If expression=Val{false}, the return is a Julia function which utilizes RuntimeGeneratedFunctions.jl to be free of world-age issues.

If the rhss is a scalar, the generated function is a function with a scalar output. Otherwise, if it's an AbstractArray, the output is two functions, one for out-of-place AbstractArray output and a second which is a mutating function. The outputted functions match the given argument order, i.e., f(u,p,args...) for the out-of-place and scalar functions and f!(du,u,p,args..) for the in-place version.

Special Keyword Arguments:

  • parallel: The kind of parallelism to use in the generated function. Defaults to SerialForm(), i.e. no parallelism. Note that the parallel forms are not exported and thus need to be chosen like Symbolics.SerialForm(). The choices are:
    • SerialForm(): Serial execution.
    • ShardedForm(cutoff, ncalls): splits the output function into sub-functions which contain at most cutoff number of output rhss. These sub-functions are called by the top-level function that buildfunction returns.
    • MultithreadedForm(): Multithreaded execution with a static split, evenly splitting the number of expressions per thread.
  • conv: The conversion function of symbolic types to Expr. By default, this uses the toexpr function.
  • checkbounds: For whether to enable bounds checking inside the generated function. Defaults to false, meaning that @inbounds is applied.
  • linenumbers: Determines whether the generated function expression retains the line numbers. Defaults to true.
  • convert_oop: Determines whether the OOP version should try to convert the output to match the type of the first input. This is useful for cases like LabelledArrays or other array types that carry extra information. Defaults to true.
  • force_SA: Forces the output of the OOP version to be a StaticArray. Defaults to false, and outputs a static array when the first argument is a static array.
  • skipzeros: Whether to skip filling zeros in the in-place version if the filling function is 0.
  • fillzeros: Whether to perform fill(out,0) before the calculations to ensure safety with skipzeros.
source
Symbolics._build_functionMethod

Build function target: CTarget

function _build_function(target::CTarget, eqs::Array{<:Equation}, args...;
+                         parallel=SerialForm(), kwargs...)

Generates a Julia function which can then be utilized for further evaluations. If expression=Val{false}, the return is a Julia function which utilizes RuntimeGeneratedFunctions.jl to be free of world-age issues.

If the rhss is a scalar, the generated function is a function with a scalar output. Otherwise, if it's an AbstractArray, the output is two functions, one for out-of-place AbstractArray output and a second which is a mutating function. The outputted functions match the given argument order, i.e., f(u,p,args...) for the out-of-place and scalar functions and f!(du,u,p,args..) for the in-place version.

Special Keyword Arguments:

  • parallel: The kind of parallelism to use in the generated function. Defaults to SerialForm(), i.e. no parallelism. Note that the parallel forms are not exported and thus need to be chosen like Symbolics.SerialForm(). The choices are:
    • SerialForm(): Serial execution.
    • ShardedForm(cutoff, ncalls): splits the output function into sub-functions which contain at most cutoff number of output rhss. These sub-functions are called by the top-level function that buildfunction returns.
    • MultithreadedForm(): Multithreaded execution with a static split, evenly splitting the number of expressions per thread.
  • conv: The conversion function of symbolic types to Expr. By default, this uses the toexpr function.
  • checkbounds: For whether to enable bounds checking inside the generated function. Defaults to false, meaning that @inbounds is applied.
  • linenumbers: Determines whether the generated function expression retains the line numbers. Defaults to true.
  • convert_oop: Determines whether the OOP version should try to convert the output to match the type of the first input. This is useful for cases like LabelledArrays or other array types that carry extra information. Defaults to true.
  • force_SA: Forces the output of the OOP version to be a StaticArray. Defaults to false, and outputs a static array when the first argument is a static array.
  • skipzeros: Whether to skip filling zeros in the in-place version if the filling function is 0.
  • fillzeros: Whether to perform fill(out,0) before the calculations to ensure safety with skipzeros.
source
Symbolics._build_functionMethod

Build function target: CTarget

function _build_function(target::CTarget, eqs::Array{<:Equation}, args...;
                          conv = toexpr, expression = Val{true},
                          fname = :diffeqf,
                          lhsname=:du,rhsnames=[Symbol("RHS$i") for i in 1:length(args)],
-                         libpath=tempname(),compiler=:gcc)

This builds an in-place C function. Only works on arrays of equations. If expression == Val{false}, then this builds a function in C, compiles it, and returns a lambda to that compiled function. These special keyword arguments control the compilation:

  • libpath: the path to store the binary. Defaults to a temporary path.
  • compiler: which C compiler to use. Defaults to :gcc, which is currently the only available option.
source
Symbolics._build_functionMethod

Build function target: StanTarget

function _build_function(target::StanTarget, eqs::Array{<:Equation}, vs, ps, iv;
+                         libpath=tempname(),compiler=:gcc)

This builds an in-place C function. Only works on arrays of equations. If expression == Val{false}, then this builds a function in C, compiles it, and returns a lambda to that compiled function. These special keyword arguments control the compilation:

  • libpath: the path to store the binary. Defaults to a temporary path.
  • compiler: which C compiler to use. Defaults to :gcc, which is currently the only available option.
source
Symbolics._build_functionMethod

Build function target: StanTarget

function _build_function(target::StanTarget, eqs::Array{<:Equation}, vs, ps, iv;
                          conv = toexpr, expression = Val{true},
                          fname = :diffeqf, lhsname=:internal_var___du,
-                         rhsnames=[:internal_var___u,:internal_var___p,:internal_var___t])

This builds an in-place Stan function compatible with the Stan differential equation solvers. Unlike other build targets, this one requires (vs, ps, iv) as the function arguments. Only allowed on arrays of equations.

source
Symbolics._build_functionMethod

Build function target: MATLABTarget

function _build_function(target::MATLABTarget, eqs::Array{<:Equation}, args...;
+                         rhsnames=[:internal_var___u,:internal_var___p,:internal_var___t])

This builds an in-place Stan function compatible with the Stan differential equation solvers. Unlike other build targets, this one requires (vs, ps, iv) as the function arguments. Only allowed on arrays of equations.

source
Symbolics._build_functionMethod

Build function target: MATLABTarget

function _build_function(target::MATLABTarget, eqs::Array{<:Equation}, args...;
                          conv = toexpr, expression = Val{true},
                          lhsname=:internal_var___du,
-                         rhsnames=[:internal_var___u,:internal_var___p,:internal_var___t])

This builds an out of place anonymous function @(t,rhsnames[1]) to be used in MATLAB. Compatible with the MATLAB differential equation solvers. Only allowed on expressions, and arrays of expressions.

source

Limitations

build_function

+ rhsnames=[:internal_var___u,:internal_var___p,:internal_var___t])

This builds an out of place anonymous function @(t,rhsnames[1]) to be used in MATLAB. Compatible with the MATLAB differential equation solvers. Only allowed on expressions, and arrays of expressions.

source

Limitations

build_function

diff --git a/dev/manual/derivatives/index.html b/dev/manual/derivatives/index.html index 2ceb12bd5..61b39f1ea 100644 --- a/dev/manual/derivatives/index.html +++ b/dev/manual/derivatives/index.html @@ -13,7 +13,7 @@ (D'~x(t)) ∘ (D'~y(t)) julia> D3 = Differential(x)^3 # 3rd order differential operator -(D'~x(t)) ∘ (D'~x(t)) ∘ (D'~x(t))source
Symbolics.expand_derivativesFunction
expand_derivatives(O; ...)
+(D'~x(t)) ∘ (D'~x(t)) ∘ (D'~x(t))
source
Symbolics.expand_derivativesFunction
expand_derivatives(O; ...)
 expand_derivatives(O, simplify; occurrences)
 

Expands derivatives within a symbolic expression O.

This function recursively traverses a symbolic expression, applying the chain rule and other derivative rules to expand any derivatives it encounters.

Arguments

  • O::Symbolic: The symbolic expression to expand.
  • simplify::Bool=false: Whether to simplify the resulting expression using SymbolicUtils.simplify.
  • occurrences=nothing: Information about the occurrences of the independent variable in the argument of the derivative. This is used internally for optimization purposes.

Examples

julia> @variables x y z k;
 
@@ -24,15 +24,15 @@
 (::Differential) (generic function with 2 methods)
 
 julia> dfx=expand_derivatives(Dx(f))
-(k*((2abs(x - y)) / y - 2z)*IfElse.ifelse(signbit(x - y), -1, 1)) / y
source
Missing docstring.

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

Note

For symbolic differentiation, all registered functions in the symbolic expression need a registered derivative. For more information, see the function registration page.

High-Level Differentiation Functions

The following functions are not exported and thus must be accessed in a namespaced way, i.e. Symbolics.jacobian.

Symbolics.derivativeFunction
derivative(O, var; simplify)
-

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

source
Symbolics.jacobianFunction
jacobian(ops, vars; simplify, scalarize)
-

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

source
Symbolics.sparsejacobianFunction
sparsejacobian(ops, vars; simplify)
-

A helper function for computing the sparse Jacobian of an array of expressions with respect to an array of variable expressions.

source
Symbolics.sparsejacobian_valsFunction
sparsejacobian_vals(ops, vars, I, J; simplify)
-

A helper function for computing the values of the sparse Jacobian of an array of expressions with respect to an array of variable expressions given the sparsity structure.

source
Symbolics.gradientFunction
gradient(O, vars; simplify)
-

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

source
Symbolics.hessianFunction
hessian(O, vars; simplify)
-

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

source
Symbolics.sparsehessianFunction
sparsehessian(op, vars; simplify, full)
-

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

source
Symbolics.sparsehessian_valsFunction
sparsehessian_vals(op, vars, I, J; simplify)
-

A helper function for computing the values of the sparse Hessian of an expression with respect to an array of variable expressions given the sparsity structure.

source

Adding Analytical Derivatives

There are many derivatives pre-defined by DiffRules.jl. For example,

using Symbolics
+(k*((2abs(x - y)) / y - 2z)*IfElse.ifelse(signbit(x - y), -1, 1)) / y
source
Missing docstring.

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

Note

For symbolic differentiation, all registered functions in the symbolic expression need a registered derivative. For more information, see the function registration page.

High-Level Differentiation Functions

The following functions are not exported and thus must be accessed in a namespaced way, i.e. Symbolics.jacobian.

Symbolics.derivativeFunction
derivative(O, var; simplify)
+

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

source
Symbolics.jacobianFunction
jacobian(ops, vars; simplify, scalarize)
+

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

source
Symbolics.sparsejacobianFunction
sparsejacobian(ops, vars; simplify)
+

A helper function for computing the sparse Jacobian of an array of expressions with respect to an array of variable expressions.

source
Symbolics.sparsejacobian_valsFunction
sparsejacobian_vals(ops, vars, I, J; simplify)
+

A helper function for computing the values of the sparse Jacobian of an array of expressions with respect to an array of variable expressions given the sparsity structure.

source
Symbolics.gradientFunction
gradient(O, vars; simplify)
+

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

source
Symbolics.hessianFunction
hessian(O, vars; simplify)
+

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

source
Symbolics.sparsehessianFunction
sparsehessian(op, vars; simplify, full)
+

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

source
Symbolics.sparsehessian_valsFunction
sparsehessian_vals(op, vars, I, J; simplify)
+

A helper function for computing the values of the sparse Hessian of an expression with respect to an array of variable expressions given the sparsity structure.

source

Adding Analytical Derivatives

There are many derivatives pre-defined by DiffRules.jl. For example,

using Symbolics
 @variables x y z
 f(x,y,z) = x^2 + sin(x+y) - z
f (generic function with 1 method)

f automatically has the derivatives defined via the tracing mechanism. It will do this by directly building the internals of your function and differentiating that.

However, often you may want to define your own derivatives so that way automatic Jacobian etc. calculations can utilize this information. This can allow for more succinct versions of the derivatives to be calculated to scale to larger systems. You can define derivatives for your function via the dispatch:

# `N` arguments are accepted by the relevant method of `my_function`
-Symbolics.derivative(::typeof(my_function), args::NTuple{N,Any}, ::Val{i})

where i means that it's the derivative with respect to the ith argument. args is the array of arguments, so, for example, if your function is f(x,t), then args = [x,t]. You should return an Term for the derivative of your function.

For example, sin(t)'s derivative (by t) is given by the following:

Symbolics.derivative(::typeof(sin), args::NTuple{1,Any}, ::Val{1}) = cos(args[1])
+Symbolics.derivative(::typeof(my_function), args::NTuple{N,Any}, ::Val{i})

where i means that it's the derivative with respect to the ith argument. args is the array of arguments, so, for example, if your function is f(x,t), then args = [x,t]. You should return an Term for the derivative of your function.

For example, sin(t)'s derivative (by t) is given by the following:

Symbolics.derivative(::typeof(sin), args::NTuple{1,Any}, ::Val{1}) = cos(args[1])
diff --git a/dev/manual/expression_manipulation/index.html b/dev/manual/expression_manipulation/index.html index 44bd0928d..e321479ea 100644 --- a/dev/manual/expression_manipulation/index.html +++ b/dev/manual/expression_manipulation/index.html @@ -8,7 +8,7 @@ julia> ex = x + y + sin(z) (x + y) + sin(z(t)) julia> substitute(ex, Dict([x => z, sin(z) => z^2])) -(z(t) + y) + (z(t) ^ 2)source
SymbolicUtils.simplifyFunction
simplify(x; expand=false,
+(z(t) + y) + (z(t) ^ 2)
source
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

Documentation for rewriter can be found here, using the @rule macro or the @acrule macro from SymbolicUtils.jl.

Additional Manipulation Functions

Other additional manipulation functions are given below.

Symbolics.get_variablesFunction
get_variables(e, varlist = nothing; sort::Bool = false)

Return a vector of variables appearing in e, optionally restricting to variables in varlist.

Note that the returned variables are not wrapped in the Num type.

Examples

julia> @variables t x y z(t);
@@ -22,7 +22,7 @@
 julia> Symbolics.get_variables(x - y; sort = true)
 2-element Vector{SymbolicUtils.BasicSymbolic}:
  x
- y
source
Symbolics.tosymbolFunction
tosymbol(x::Union{Num,Symbolic}; states=nothing, escape=true) -> Symbol

Convert x to a symbol. states are the states of a system, and escape means if the target has escapes like val"y(t)". If escape is false, then it will only output y instead of y(t).

Examples

julia> @variables t z(t)
+ y
source
Symbolics.tosymbolFunction
tosymbol(x::Union{Num,Symbolic}; states=nothing, escape=true) -> Symbol

Convert x to a symbol. states are the states of a system, and escape means if the target has escapes like val"y(t)". If escape is false, then it will only output y instead of y(t).

Examples

julia> @variables t z(t)
 2-element Vector{Num}:
     t
  z(t)
@@ -31,13 +31,13 @@
 Symbol("z(t)")
 
 julia> Symbolics.tosymbol(z; escape=false)
-:z
source
Symbolics.diff2termFunction
diff2term(x, x_metadata::Dict{Datatype, Any}) -> Symbolic

Convert a differential variable to a Term. Note that it only takes a Term not a Num. Any upstream metadata can be passed via x_metadata

julia> @variables x t u(x, t) z(t)[1:2]; Dt = Differential(t); Dx = Differential(x);
+:z
source
Symbolics.diff2termFunction
diff2term(x, x_metadata::Dict{Datatype, Any}) -> Symbolic

Convert a differential variable to a Term. Note that it only takes a Term not a Num. Any upstream metadata can be passed via x_metadata

julia> @variables x t u(x, t) z(t)[1:2]; Dt = Differential(t); Dx = Differential(x);
 
 julia> Symbolics.diff2term(Symbolics.value(Dx(Dt(u))))
 uˍtx(x, t)
 
 julia> Symbolics.diff2term(Symbolics.value(Dt(z[1])))
-var"z(t)[1]ˍt"
source
Symbolics.degreeFunction
degree(p, sym=nothing)

Extract the degree of p with respect to sym.

Examples

julia> @variables x;
+var"z(t)[1]ˍt"
source
Symbolics.degreeFunction
degree(p, sym=nothing)

Extract the degree of p with respect to sym.

Examples

julia> @variables x;
 
 julia> Symbolics.degree(x^0)
 0
@@ -46,7 +46,7 @@
 1
 
 julia> Symbolics.degree(x^2)
-2
source
Symbolics.coeffFunction
coeff(p, sym=nothing)

Extract the coefficient of p with respect to sym. Note that p might need to be expanded and/or simplified with expand and/or simplify.

Examples

julia> @variables a x y;
+2
source
Symbolics.coeffFunction
coeff(p, sym=nothing)

Extract the coefficient of p with respect to sym. Note that p might need to be expanded and/or simplified with expand and/or simplify.

Examples

julia> @variables a x y;
 
 julia> Symbolics.coeff(2a, x)
 0
@@ -58,9 +58,9 @@
 1
 
 julia> Symbolics.coeff(2*x*y + y, x*y)
-2
source
Missing docstring.

Missing docstring for Symbolics.replace. Check Documenter's build log for details.

Base.occursinFunction
occursin(needle::Symbolic, haystack::Symbolic)

Determine whether the second argument contains the first argument. Note that this function doesn't handle associativity, commutativity, or distributivity.

source
Symbolics.filterchildrenFunction

filterchildren(c, x) Returns all parts of x that fufills the condition given in c. c can be a function or an expression. If it is a function, returns everything for which the function is true. If c is an expression, returns all expressions that matches it.

Examples:

@syms x
+2
source
Missing docstring.

Missing docstring for Symbolics.replace. Check Documenter's build log for details.

Base.occursinFunction
occursin(needle::Symbolic, haystack::Symbolic)

Determine whether the second argument contains the first argument. Note that this function doesn't handle associativity, commutativity, or distributivity.

source
Symbolics.filterchildrenFunction

filterchildren(c, x) Returns all parts of x that fulfills the condition given in c. c can be a function or an expression. If it is a function, returns everything for which the function is true. If c is an expression, returns all expressions that matches it.

Examples:

@syms x
 Symbolics.filterchildren(x, log(x) + x + 1)

returns [x, x]

@variables t X(t)
 D = Differential(t)
-Symbolics.filterchildren(Symbolics.is_derivative, X + D(X) + D(X^2))

returns [Differential(t)(X(t)^2), Differential(t)(X(t))]

source
Symbolics.fixpoint_subFunction
fixpoint_sub(expr, dict; operator = Nothing)

Given a symbolic expression, equation or inequality expr perform the substitutions in dict recursively until the expression does not change. Substitutions that depend on one another will thus be recursively expanded. For example, fixpoint_sub(x, Dict(x => y, y => 3)) will return 3. The operator keyword can be specified to prevent substitution of expressions inside operators of the given type.

See also: fast_substitute.

source
Symbolics.fast_substituteFunction
fast_substitute(expr, dict; operator = Nothing)

Given a symbolic expression, equation or inequality expr perform the substitutions in dict. This only performs the substitutions once. For example, fast_substitute(x, Dict(x => y, y => 3)) will return y. The operator keyword can be specified to prevent substitution of expressions inside operators of the given type.

See also: fixpoint_sub.

source
Symbolics.symbolic_to_floatFunction
symbolic_to_float(x::Union{Num, BasicSymbolic})::Union{AbstractFloat, BasicSymbolic}

If the symbolic value is exactly equal to a number, converts the symbolic value to a floating point number. Otherwise retains the symbolic value.

Examples

symbolic_to_float((1//2 * x)/x) # 0.5
+Symbolics.filterchildren(Symbolics.is_derivative, X + D(X) + D(X^2))

returns [Differential(t)(X(t)^2), Differential(t)(X(t))]

source
Symbolics.fixpoint_subFunction
fixpoint_sub(expr, dict; operator = Nothing)

Given a symbolic expression, equation or inequality expr perform the substitutions in dict recursively until the expression does not change. Substitutions that depend on one another will thus be recursively expanded. For example, fixpoint_sub(x, Dict(x => y, y => 3)) will return 3. The operator keyword can be specified to prevent substitution of expressions inside operators of the given type.

See also: fast_substitute.

source
Symbolics.fast_substituteFunction
fast_substitute(expr, dict; operator = Nothing)

Given a symbolic expression, equation or inequality expr perform the substitutions in dict. This only performs the substitutions once. For example, fast_substitute(x, Dict(x => y, y => 3)) will return y. The operator keyword can be specified to prevent substitution of expressions inside operators of the given type.

See also: fixpoint_sub.

source
Symbolics.symbolic_to_floatFunction
symbolic_to_float(x::Union{Num, BasicSymbolic})::Union{AbstractFloat, BasicSymbolic}

If the symbolic value is exactly equal to a number, converts the symbolic value to a floating point number. Otherwise retains the symbolic value.

Examples

symbolic_to_float((1//2 * x)/x) # 0.5
 symbolic_to_float((1/2 * x)/x) # 0.5
-symbolic_to_float((1//2)*√(279//4)) # 4.175823272122517
source
+symbolic_to_float((1//2)*√(279//4)) # 4.175823272122517source diff --git a/dev/manual/faq/index.html b/dev/manual/faq/index.html index 2fefb7b83..beb6af41f 100644 --- a/dev/manual/faq/index.html +++ b/dev/manual/faq/index.html @@ -30,4 +30,4 @@ b = only(@variables($a))

\[ \begin{equation} c \end{equation} - \]

In this example, @variables($a) created a variable named c, and set this variable to b.

+ \]

In this example, @variables($a) created a variable named c, and set this variable to b.

diff --git a/dev/manual/functions/index.html b/dev/manual/functions/index.html index a30ad9672..456665f73 100644 --- a/dev/manual/functions/index.html +++ b/dev/manual/functions/index.html @@ -41,14 +41,14 @@ \]

Note that at this time array derivatives cannot be defined.

Registration API

Symbolics.@register_symbolicMacro
@register_symbolic(expr, define_promotion = true, Ts = [Real])

Overload appropriate methods so that Symbolics can stop tracing into the registered function. If define_promotion is true, then a promotion method in the form of

SymbolicUtils.promote_symtype(::typeof(f_registered), args...) = Real # or the annotated return type

is defined for the register function. Note that when defining multiple register overloads for one function, all the rest of the registers must set define_promotion to false except for the first one, to avoid method overwriting.

Examples

@register_symbolic foo(x, y)
 @register_symbolic foo(x, y::Bool) false # do not overload a duplicate promotion rule
 @register_symbolic goo(x, y::Int) # `y` is not overloaded to take symbolic objects
-@register_symbolic hoo(x, y)::Int # `hoo` returns `Int`

See @register_array_symbolic to register functions which return arrays.

source
Symbolics.@register_array_symbolicMacro
@register_array_symbolic(expr, define_promotion = true)

Example:

# Let's say vandermonde takes an n-vector and returns an n x n matrix
+@register_symbolic hoo(x, y)::Int # `hoo` returns `Int`

See @register_array_symbolic to register functions which return arrays.

source
Symbolics.@register_array_symbolicMacro
@register_array_symbolic(expr, define_promotion = true)

Example:

# Let's say vandermonde takes an n-vector and returns an n x n matrix
 @register_array_symbolic vandermonde(x::AbstractVector) begin
     size=(length(x), length(x))
     eltype=eltype(x) # optional, will default to the promoted eltypes of x
 end

You can also register calls on callable structs:

@register_array_symbolic (c::Conv)(x::AbstractMatrix) begin
     size=size(x) .- size(c.kernel) .+ 1
     eltype=promote_type(eltype(x), eltype(c))
-end

If define_promotion = true then a promotion method in the form of

SymbolicUtils.promote_symtype(::typeof(f_registered), args...) = # inferred or annotated return type

is defined for the register function. Note that when defining multiple register overloads for one function, all the rest of the registers must set define_promotion to false except for the first one, to avoid method overwriting.

source

Direct Registration API (Advanced, Experimental)

Warn

This is a lower level API which is not as stable as the macro APIs.

In some circumstances you may need to use the direct API in order to define registration on functions or types without using the macro. This is done by directly defining dispatches on symbolic objects.

A good example of this is DataInterpolations.jl's interpolations object. On an interpolation by a symbolic variable, we generate the symbolic function (the term) for the interpolation function. This looks like:

using DataInterpolations, Symbolics, SymbolicUtils
+end

If define_promotion = true then a promotion method in the form of

SymbolicUtils.promote_symtype(::typeof(f_registered), args...) = # inferred or annotated return type

is defined for the register function. Note that when defining multiple register overloads for one function, all the rest of the registers must set define_promotion to false except for the first one, to avoid method overwriting.

source

Direct Registration API (Advanced, Experimental)

Warn

This is a lower level API which is not as stable as the macro APIs.

In some circumstances you may need to use the direct API in order to define registration on functions or types without using the macro. This is done by directly defining dispatches on symbolic objects.

A good example of this is DataInterpolations.jl's interpolations object. On an interpolation by a symbolic variable, we generate the symbolic function (the term) for the interpolation function. This looks like:

using DataInterpolations, Symbolics, SymbolicUtils
 (interp::AbstractInterpolation)(t::Num) = SymbolicUtils.term(interp, unwrap(t))

In order for this to work, it is required that we define the symtype for the symbolic type inference. This is done via:

SymbolicUtils.promote_symtype(t::AbstractInterpolation, args...) = Real

Additionally a symbolic name is required:

Base.nameof(interp::AbstractInterpolation) = :Interpolation

The derivative is defined similarly to the macro case:

function Symbolics.derivative(interp::AbstractInterpolation, args::NTuple{1, Any}, ::Val{1})
     Symbolics.unwrap(derivative(interp, Symbolics.wrap(args[1])))
-end
+end diff --git a/dev/manual/groebner/index.html b/dev/manual/groebner/index.html index 36a23ad2e..ee3482f96 100644 --- a/dev/manual/groebner/index.html +++ b/dev/manual/groebner/index.html @@ -1,2 +1,2 @@ -Groebner bases · Symbolics.jl

Groebner bases

Groebner bases use the implementation of the F4 algorithm from Groebner.jl package as its backend. We refer to the documentation of Groebner.jl, which lists some implementations details and possible use-cases of Groebner bases.

Symbolics.groebner_basisFunction
groebner_basis(polynomials)

Computes a Groebner basis of the ideal generated by the given polynomials.

This function requires a Groebner bases backend (such as Groebner.jl) to be loaded.

source
+Groebner bases · Symbolics.jl

Groebner bases

Groebner bases use the implementation of the F4 algorithm from Groebner.jl package as its backend. We refer to the documentation of Groebner.jl, which lists some implementations details and possible use-cases of Groebner bases.

Symbolics.groebner_basisFunction
groebner_basis(polynomials)

Computes a Groebner basis of the ideal generated by the given polynomials.

This function requires a Groebner bases backend (such as Groebner.jl) to be loaded.

source
diff --git a/dev/manual/io/index.html b/dev/manual/io/index.html index fc5c6b56a..b51d95d72 100644 --- a/dev/manual/io/index.html +++ b/dev/manual/io/index.html @@ -6,4 +6,4 @@ end ex1, ex2 = build_function(f(u),u) write("function.jl", string(ex2))
898

Now we can do something like:

g = include("function.jl")
#1 (generic function with 1 method)

and that will load the function back in. Note that this can be done to save the transformation results of Symbolics.jl so that they can be stored and used in a precompiled Julia package.

Latexification

Symbolics.jl's expressions support Latexify.jl, and thus

using Latexify
-latexify(ex)

will produce LaTeX output from Symbolics models and expressions. This works on basics like Term all the way to higher primitives like ODESystem and ReactionSystem.

+latexify(ex)

will produce LaTeX output from Symbolics models and expressions. This works on basics like Term all the way to higher primitives like ODESystem and ReactionSystem.

diff --git a/dev/manual/limits/index.html b/dev/manual/limits/index.html index f393a6364..a73909420 100644 --- a/dev/manual/limits/index.html +++ b/dev/manual/limits/index.html @@ -1,2 +1,2 @@ -Symbolic Limits · Symbolics.jl

Symbolic Limits

Experimental symbolic limit support is provided by the limit function, documented below. See SymbolicLimits.jl for more information and implementation details.

Symbolics.limitFunction
limit(expr, var, h[, side::Symbol])

Compute the limit of expr as var approaches h.

side indicates the direction from which var approaches h. It may be one of :left, :right, or :both. If side is :both and the two sides do not align, an error is thrown. Side defaults to :both for finite h, :left for h = Inf, and :right for h = -Inf.

expr must be composed of log, exp, constants, and the rational operators +, -, *, and /. This limitation may eventually be relaxed.

Warning

Because symbolic limit computation is undecidable, this function necessarily employs heuristics and may occasionally return wrong answers. Nevertheless, please report wrong answers as issues as we aim to have heuristics that produce correct answers in all practical cases.

source
+Symbolic Limits · Symbolics.jl

Symbolic Limits

Experimental symbolic limit support is provided by the limit function, documented below. See SymbolicLimits.jl for more information and implementation details.

Symbolics.limitFunction
limit(expr, var, h[, side::Symbol])

Compute the limit of expr as var approaches h.

side indicates the direction from which var approaches h. It may be one of :left, :right, or :both. If side is :both and the two sides do not align, an error is thrown. Side defaults to :both for finite h, :left for h = Inf, and :right for h = -Inf.

expr must be composed of log, exp, constants, and the rational operators +, -, *, and /. This limitation may eventually be relaxed.

Warning

Because symbolic limit computation is undecidable, this function necessarily employs heuristics and may occasionally return wrong answers. Nevertheless, please report wrong answers as issues as we aim to have heuristics that produce correct answers in all practical cases.

source
diff --git a/dev/manual/parsing/index.html b/dev/manual/parsing/index.html index 10c22d893..2e7b89dab 100644 --- a/dev/manual/parsing/index.html +++ b/dev/manual/parsing/index.html @@ -15,4 +15,4 @@ z ~ 2] all(isequal.(eqs,ex)) # true

Limitations

Symbolic-ness Tied to Environment Definitions

The parsing to a symbolic expression has to be able to recognize the difference between functions, numbers, and globals defined within one's Julia environment and those that are to be made symbolic. The way this functionality handles this problem is that it does not define anything as symbolic that is already defined in the chosen mod module. For example, f(x,y) will have f as non-symbolic if the function f (named f) is defined in mod, i.e. if isdefined(mod,:f) is true. When the symbol is defined, it will be replaced by its value. Notably, this means that the parsing behavior changes depending on the environment that it is applied.

For example:

parse_expr_to_symbolic(:(x - y),@__MODULE__) # x - y
 x = 2.0
-parse_expr_to_symbolic(:(x - y),@__MODULE__) # 2.0 - y

This is required to detect that standard functions like - are functions instead of symbolic symbols. For safety, one should create anonymous modules or other sub-environments to ensure no stray variables are defined.

Metadata is Blank

Because all the variables defined by the expressions are not defined with the standard @variables, there is no metadata that is or can be associated with any of the generated variables. Instead, they all have blank metadata, but are defined in the Real domain. Thus, the variables which come out of this parsing may not evaluate as equal to a symbolic variable defined elsewhere.

source
Missing docstring.

Missing docstring for @parse_expr_to_symbolic. Check Documenter's build log for details.

+parse_expr_to_symbolic(:(x - y),@__MODULE__) # 2.0 - y

This is required to detect that standard functions like - are functions instead of symbolic symbols. For safety, one should create anonymous modules or other sub-environments to ensure no stray variables are defined.

Metadata is Blank

Because all the variables defined by the expressions are not defined with the standard @variables, there is no metadata that is or can be associated with any of the generated variables. Instead, they all have blank metadata, but are defined in the Real domain. Thus, the variables which come out of this parsing may not evaluate as equal to a symbolic variable defined elsewhere.

source
Missing docstring.

Missing docstring for @parse_expr_to_symbolic. Check Documenter's build log for details.

diff --git a/dev/manual/solver/index.html b/dev/manual/solver/index.html index bef13c5b5..bda606b30 100644 --- a/dev/manual/solver/index.html +++ b/dev/manual/solver/index.html @@ -65,7 +65,7 @@ julia> Symbolics.symbolic_to_float.(roots) 1-element Vector{Complex{BigFloat}}: - -4.512941594732059759689023145584186058252768936052415430071569066192919491762214 + 3.428598090438030380369414618548038962770087500755160535832807433942464545729382imsource

One other symbolic solver is symbolic_linear_solve which is limited compared to symbolic_solve as it only solves linear equations.

Symbolics.symbolic_linear_solveFunction
symbolic_linear_solve(eq, var; simplify, check) -> Any
+ -4.512941594732059759689023145584186058252768936052415430071569066192919491762214 + 3.428598090438030380369414618548038962770087500755160535832807433942464545729382im
source

One other symbolic solver is symbolic_linear_solve which is limited compared to symbolic_solve as it only solves linear equations.

Symbolics.symbolic_linear_solveFunction
symbolic_linear_solve(eq, var; simplify, check) -> Any
 

Solve equation(s) eqs for a set of variables vars.

Assumes length(eqs) == length(vars)

Currently only works if all equations are linear. check if the expr is linear w.r.t vars.

Examples

julia> @variables x y
 2-element Vector{Num}:
  x
@@ -77,7 +77,7 @@
 julia> Symbolics.symbolic_linear_solve([x + y ~ 0, x - y ~ 2], [x, y])
 2-element Vector{Float64}:
   1.0
- -1.0
source

symbolic_solve only supports symbolic, i.e. non-floating point computations, and thus prefers equations where the coefficients are integer, rational, or symbolic. Floating point coefficients are transformed into rational values and BigInt values are used internally with a potential performance loss, and thus it is recommended that this functionality is only used with floating point values if necessary. In contrast, symbolic_linear_solve directly handles floating point values using standard factorizations.

More technical details and examples

Technical details

The symbolic_solve function uses 4 hidden solvers in order to solve the user's input. Its base, solve_univar, uses analytic solutions up to polynomials of degree 4 and factoring as its method for solving univariate polynomials. The function's solve_multipoly uses GCD on the input polynomials then throws passes the result to solve_univar. The function's solve_multivar uses Groebner basis and a separating form in order to create linear equations in the input variables and a single high degree equation in the separating variable [1]. Each equation resulting from the basis is then passed to solve_univar. We can see that essentially, solve_univar is the building block of symbolic_solve. If the input is not a valid polynomial and can not be solved by the algorithm above, symbolic_solve passes it to ia_solve, which attempts solving by attraction and isolation [2]. This only works when the input is a single expression and the user wants the answer in terms of a single variable. Say log(x) - a == 0 gives us [e^a].

Nice examples

using Symbolics, Nemo;
+ -1.0
source

symbolic_solve only supports symbolic, i.e. non-floating point computations, and thus prefers equations where the coefficients are integer, rational, or symbolic. Floating point coefficients are transformed into rational values and BigInt values are used internally with a potential performance loss, and thus it is recommended that this functionality is only used with floating point values if necessary. In contrast, symbolic_linear_solve directly handles floating point values using standard factorizations.

More technical details and examples

Technical details

The symbolic_solve function uses 4 hidden solvers in order to solve the user's input. Its base, solve_univar, uses analytic solutions up to polynomials of degree 4 and factoring as its method for solving univariate polynomials. The function's solve_multipoly uses GCD on the input polynomials then throws passes the result to solve_univar. The function's solve_multivar uses Groebner basis and a separating form in order to create linear equations in the input variables and a single high degree equation in the separating variable [1]. Each equation resulting from the basis is then passed to solve_univar. We can see that essentially, solve_univar is the building block of symbolic_solve. If the input is not a valid polynomial and can not be solved by the algorithm above, symbolic_solve passes it to ia_solve, which attempts solving by attraction and isolation [2]. This only works when the input is a single expression and the user wants the answer in terms of a single variable. Say log(x) - a == 0 gives us [e^a].

Nice examples

using Symbolics, Nemo;
 @variables x;
 Symbolics.symbolic_solve(9^x + 3^x ~ 8, x)
2-element Vector{SymbolicUtils.BasicSymbolic{Real}}:
  slog(-(1//2) + (1//2)*√(33)) / slog(3)
@@ -103,4 +103,4 @@
 Out[2]= a \[Element] Reals && x > -a
 
 In[3]:= Solve[x^(x)  + 3 == 0, x]
-Out[3]= {{x -> (I \[Pi] + Log[3])/ProductLog[I \[Pi] + Log[3]]}}

References

+Out[3]= {{x -> (I \[Pi] + Log[3])/ProductLog[I \[Pi] + Log[3]]}}

References

diff --git a/dev/manual/sparsity_detection/index.html b/dev/manual/sparsity_detection/index.html index 8cd0cfaa5..eb916e866 100644 --- a/dev/manual/sparsity_detection/index.html +++ b/dev/manual/sparsity_detection/index.html @@ -17,7 +17,7 @@ 3×2 SparseArrays.SparseMatrixCSC{Bool, Int64} with 4 stored entries: 1 ⋅ ⋅ 1 - 1 1source
jacobian_sparsity(
+ 1  1
source
jacobian_sparsity(
     f!::Function,
     output::AbstractArray,
     input::AbstractArray,
@@ -36,7 +36,7 @@
 3×2 SparseArrays.SparseMatrixCSC{Bool, Int64} with 4 stored entries:
  ⋅  1
  1  ⋅
- 1  1
source
Symbolics.hessian_sparsityFunction
hessian_sparsity(
+ 1  1
source
Symbolics.hessian_sparsityFunction
hessian_sparsity(
     expr,
     vars::AbstractVector;
     full
@@ -50,7 +50,7 @@
 julia> Symbolics.hessian_sparsity(expr, vars)
 2×2 SparseArrays.SparseMatrixCSC{Bool, Int64} with 3 stored entries:
  1  1
- 1  ⋅
source
hessian_sparsity(
+ 1  ⋅
source
hessian_sparsity(
     f::Function,
     input::AbstractVector,
     args...;
@@ -66,6 +66,6 @@
 julia> Symbolics.hessian_sparsity(f, input)
 2×2 SparseArrays.SparseMatrixCSC{Bool, Int64} with 3 stored entries:
  ⋅  1
- 1  1
source

Structure Detection

Symbolics.islinearFunction
islinear(ex, u)
-

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

source
Symbolics.isaffineFunction
isaffine(ex, u)
-

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

source

ADTypes.jl interface

Symbolics.SymbolicsSparsityDetectorType
SymbolicsSparsityDetector <: ADTypes.AbstractSparsityDetector

Sparsity detection algorithm based on the Symbolics.jl tracing system.

This type makes Symbolics.jl compatible with the ADTypes.jl sparsity detection framework. The following functions are implemented:

Reference

Sparsity Programming: Automated Sparsity-Aware Optimizations in Differentiable Programming, Gowda et al. (2019)

source
+ 1 1source

Structure Detection

Symbolics.islinearFunction
islinear(ex, u)
+

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

source
Symbolics.isaffineFunction
isaffine(ex, u)
+

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

source

ADTypes.jl interface

Symbolics.SymbolicsSparsityDetectorType
SymbolicsSparsityDetector <: ADTypes.AbstractSparsityDetector

Sparsity detection algorithm based on the Symbolics.jl tracing system.

This type makes Symbolics.jl compatible with the ADTypes.jl sparsity detection framework. The following functions are implemented:

Reference

Sparsity Programming: Automated Sparsity-Aware Optimizations in Differentiable Programming, Gowda et al. (2019)

source
diff --git a/dev/manual/types/index.html b/dev/manual/types/index.html index 4964a8ff4..4f29b7bb3 100644 --- a/dev/manual/types/index.html +++ b/dev/manual/types/index.html @@ -5,4 +5,4 @@ z X[1:10,1:10] Z[1:10] - s
typeof(x)
Num
typeof(z)
Complex{Num}
typeof(X)
Symbolics.Arr{Num, 2}
typeof(Z)
Symbolics.Arr{Complex{Num}, 1}
typeof(s)
SymbolicUtils.BasicSymbolic{String}
+ s
typeof(x)
Num
typeof(z)
Complex{Num}
typeof(X)
Symbolics.Arr{Num, 2}
typeof(Z)
Symbolics.Arr{Complex{Num}, 1}
typeof(s)
SymbolicUtils.BasicSymbolic{String}
diff --git a/dev/manual/variables/index.html b/dev/manual/variables/index.html index b33fefc8b..b1b466f63 100644 --- a/dev/manual/variables/index.html +++ b/dev/manual/variables/index.html @@ -28,15 +28,15 @@ (value_c(t))[1:3] julia> (t, a, b, c) -(t, :runtime_symbol_value, :value_b, :value_c)source
Symbolics.variableFunction
variable(name::Symbol, idx::Integer...; T=Real)

Create a variable with the given name along with subscripted indices with the symtype=T. When T=FnType, it creates a symbolic function.

julia> Symbolics.variable(:x, 4, 2, 0)
+(t, :runtime_symbol_value, :value_b, :value_c)
source
Symbolics.variableFunction
variable(name::Symbol, idx::Integer...; T=Real)

Create a variable with the given name along with subscripted indices with the symtype=T. When T=FnType, it creates a symbolic function.

julia> Symbolics.variable(:x, 4, 2, 0)
 x₄ˏ₂ˏ₀
 
 julia> Symbolics.variable(:x, 4, 2, 0, T=Symbolics.FnType)
-x₄ˏ₂ˏ₀⋆

Also see variables.

source
Symbolics.variablesFunction
variables(name::Symbol, indices...)

Create a multi-dimensional array of individual variables named with subscript notation. Use @variables instead to create symbolic array variables (as opposed to array of variables). See variable to create one variable with subscripts.

julia> Symbolics.variables(:x, 1:3, 3:6)
+x₄ˏ₂ˏ₀⋆

Also see variables.

source
Symbolics.variablesFunction
variables(name::Symbol, indices...)

Create a multi-dimensional array of individual variables named with subscript notation. Use @variables instead to create symbolic array variables (as opposed to array of variables). See variable to create one variable with subscripts.

julia> Symbolics.variables(:x, 1:3, 3:6)
 3×4 Matrix{Num}:
  x₁ˏ₃  x₁ˏ₄  x₁ˏ₅  x₁ˏ₆
  x₂ˏ₃  x₂ˏ₄  x₂ˏ₅  x₂ˏ₆
- x₃ˏ₃  x₃ˏ₄  x₃ˏ₅  x₃ˏ₆
source
Symbolics.EquationType
struct Equation

An equality relationship between two expressions.

Fields

  • lhs: The expression on the left-hand side of the equation.

  • rhs: The expression on the right-hand side of the equation.

source
Base.:~Method
~(lhs, rhs) -> Any
+ x₃ˏ₃  x₃ˏ₄  x₃ˏ₅  x₃ˏ₆
source
Symbolics.EquationType
struct Equation

An equality relationship between two expressions.

Fields

  • lhs: The expression on the left-hand side of the equation.

  • rhs: The expression on the right-hand side of the equation.

source
Base.:~Method
~(lhs, rhs) -> Any
 

Create an Equation out of two Num instances, or an Num and a Number.

Examples

julia> using Symbolics
 
 julia> @variables x y;
@@ -53,7 +53,7 @@
 (broadcast(~, A, B))[1:3,1:3]
 
 julia> A .~ 3x
-(broadcast(~, A, 3x))[1:3,1:3]
source

A note about functions restricted to Numbers

Sym and Term objects are NOT subtypes of Number. Symbolics provides a simple wrapper type called Num which is a subtype of Real. Num wraps either a Sym or a Term or any other object, defines the same set of operations as symbolic expressions and forwards those to the values it wraps. You can use Symbolics.value function to unwrap a Num.

By default, the @variables macros return Num-wrapped objects to allow calling functions which are restricted to Number or Real.

using Symbolics
+(broadcast(~, A, 3x))[1:3,1:3]
source

A note about functions restricted to Numbers

Sym and Term objects are NOT subtypes of Number. Symbolics provides a simple wrapper type called Num which is a subtype of Real. Num wraps either a Sym or a Term or any other object, defines the same set of operations as symbolic expressions and forwards those to the values it wraps. You can use Symbolics.value function to unwrap a Num.

By default, the @variables macros return Num-wrapped objects to allow calling functions which are restricted to Number or Real.

using Symbolics
 @variables t x y z(t);
 Symbolics.operation(Symbolics.value(x + y))
+ (generic function with 1047 methods)
Symbolics.operation(Symbolics.value(z))

\[ \begin{equation} z @@ -68,4 +68,4 @@ f(t)

\[ \begin{equation} 1 + t \left( \frac{2}{3} + \frac{4}{5} \pi \right) \end{equation} - \]

This will work for any floating-point input, as well as symbolic input.

Symbolic Control Flow

Control flow can be expressed in Symbolics.jl in the following ways:

Inspection Functions

Missing docstring.

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

Missing docstring.

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

Missing docstring.

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

+ \]

This will work for any floating-point input, as well as symbolic input.

Symbolic Control Flow

Control flow can be expressed in Symbolics.jl in the following ways:

Inspection Functions

Missing docstring.

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

Missing docstring.

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

Missing docstring.

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

diff --git a/dev/search_index.js b/dev/search_index.js index 0de865fd0..af318f71b 100644 --- a/dev/search_index.js +++ b/dev/search_index.js @@ -1,3 +1,3 @@ var documenterSearchIndex = {"docs": -[{"location":"manual/faq/#Frequently-Asked-Questions","page":"Frequently Asked Questions","title":"Frequently Asked Questions","text":"","category":"section"},{"location":"manual/faq/#Limits-of-Symbolic-Computation","page":"Frequently Asked Questions","title":"Limits of Symbolic Computation","text":"","category":"section"},{"location":"manual/faq/#Transforming-my-function-to-a-symbolic-equation-has-failed.-What-do-I-do?","page":"Frequently Asked Questions","title":"Transforming my function to a symbolic equation has failed. What do I do?","text":"","category":"section"},{"location":"manual/faq/","page":"Frequently Asked Questions","title":"Frequently Asked Questions","text":"If you see the error:","category":"page"},{"location":"manual/faq/","page":"Frequently Asked Questions","title":"Frequently Asked Questions","text":"ERROR: TypeError: non-boolean (Num) used in boolean context","category":"page"},{"location":"manual/faq/","page":"Frequently Asked Questions","title":"Frequently Asked Questions","text":"this is likely coming from an algorithm which cannot be traced into a purely symbolic algorithm. Many numerical solvers, for instance, have this property. It shows up when you're doing something like if x < tol. If x is a number, then this is true or false. If x is a symbol, then it's x < tol, so Julia just cannot know how many iterations to do and throws an error.","category":"page"},{"location":"manual/faq/","page":"Frequently Asked Questions","title":"Frequently Asked Questions","text":"This shows up in adaptive algorithms, for example:","category":"page"},{"location":"manual/faq/","page":"Frequently Asked Questions","title":"Frequently Asked Questions","text":"function factorial(x)\n out = x\n while x > 1\n x -= 1\n out *= x\n end\n out\nend","category":"page"},{"location":"manual/faq/","page":"Frequently Asked Questions","title":"Frequently Asked Questions","text":"The number of iterations this algorithm runs for is dependent on the value of x, and so there is no static representation of the algorithm. If x is 5, then it's out = x*(x-1)*(x-2)*(x-3)*(x-4), while if x is 3, then it's out = x*(x-1)*(x-2). It should thus be no surprise that:","category":"page"},{"location":"manual/faq/","page":"Frequently Asked Questions","title":"Frequently Asked Questions","text":"using Symbolics\n@variables x\ntry\n factorial(x)\ncatch e\n e\nend","category":"page"},{"location":"manual/faq/","page":"Frequently Asked Questions","title":"Frequently Asked Questions","text":"fails. It's not that there is anything wrong with this code, but it's not going to work because fundamentally this is not a symbolically-representable algorithm.","category":"page"},{"location":"manual/faq/","page":"Frequently Asked Questions","title":"Frequently Asked Questions","text":"The space of algorithms which can be turned into symbolic algorithms is what we call quasi-static, that is, there is a way to represent the algorithm as static. Loops are allowed, but the amount of loop iterations should not require that you know the value of the symbol x. If the algorithm is quasi-static, then Symbolics.jl tracing will produce the static form of the code, unrolling the operations, and generating a flat representation of the algorithm.","category":"page"},{"location":"manual/faq/#What-can-be-done?","page":"Frequently Asked Questions","title":"What can be done?","text":"","category":"section"},{"location":"manual/faq/","page":"Frequently Asked Questions","title":"Frequently Asked Questions","text":"If you need to represent this function f symbolically, then you'll need to make sure it's not traced and instead is directly represented in the underlying computational graph. Just like how sqrt(x) symbolically does not try to represent the underlying algorithm, this must be done to your f. This is done by doing @register_symbolic f(x). If you have to define things like derivatives to f, then the function registration documentation.","category":"page"},{"location":"manual/faq/#Equality-and-set-membership-tests","page":"Frequently Asked Questions","title":"Equality and set membership tests","text":"","category":"section"},{"location":"manual/faq/","page":"Frequently Asked Questions","title":"Frequently Asked Questions","text":"Comparing symbols with == produces a symbolic equality, not a Bool. To produce a Bool, call isequal.","category":"page"},{"location":"manual/faq/","page":"Frequently Asked Questions","title":"Frequently Asked Questions","text":"To test if a symbol is part of a collection of symbols, i.e., a vector, either create a Set and use in, e.g.","category":"page"},{"location":"manual/faq/","page":"Frequently Asked Questions","title":"Frequently Asked Questions","text":"try \n x in [x]\ncatch e\n e\nend","category":"page"},{"location":"manual/faq/","page":"Frequently Asked Questions","title":"Frequently Asked Questions","text":"x in Set([x])","category":"page"},{"location":"manual/faq/","page":"Frequently Asked Questions","title":"Frequently Asked Questions","text":"any(isequal(x), [x])","category":"page"},{"location":"manual/faq/","page":"Frequently Asked Questions","title":"Frequently Asked Questions","text":"If == is used instead, you will receive TypeError: non-boolean (Num) used in boolean context. What this error is telling you is that the symbolic x == y expression is being used where a Bool is required, such as if x == y, and since the symbolic expression is held lazily this will error because the appropriate branch cannot be selected (since x == y is unknown for arbitrary symbolic values!). This is why the check isequal(x,y) is required, since this is a non-lazy check of whether the symbol x is always equal to the symbol y, rather than an expression of whether x and y currently have the same value.","category":"page"},{"location":"manual/faq/#Understanding-the-Difference-Between-the-Julia-Variable-and-the-Symbolic-Variable","page":"Frequently Asked Questions","title":"Understanding the Difference Between the Julia Variable and the Symbolic Variable","text":"","category":"section"},{"location":"manual/faq/","page":"Frequently Asked Questions","title":"Frequently Asked Questions","text":"In the most basic usage of Symbolics, the name of the Julia variable and the symbolic variable are the same. For example, when we do:","category":"page"},{"location":"manual/faq/","page":"Frequently Asked Questions","title":"Frequently Asked Questions","text":"@variables a","category":"page"},{"location":"manual/faq/","page":"Frequently Asked Questions","title":"Frequently Asked Questions","text":"the name of the symbolic variable is a and same with the Julia variable. However, we can de-couple these by setting a to a new symbolic variable, for example:","category":"page"},{"location":"manual/faq/","page":"Frequently Asked Questions","title":"Frequently Asked Questions","text":"b = only(@variables(a))","category":"page"},{"location":"manual/faq/","page":"Frequently Asked Questions","title":"Frequently Asked Questions","text":"Now the Julia variable b refers to the variable named a. However, the downside of this current approach is that it requires that the user writing the script knows the name a that they want to place to the variable. But what if for example we needed to get the variable's name from a file?","category":"page"},{"location":"manual/faq/","page":"Frequently Asked Questions","title":"Frequently Asked Questions","text":"To do this, one can interpolate a symbol into the @variables macro using $. For example:","category":"page"},{"location":"manual/faq/","page":"Frequently Asked Questions","title":"Frequently Asked Questions","text":"a = :c\nb = only(@variables($a))","category":"page"},{"location":"manual/faq/","page":"Frequently Asked Questions","title":"Frequently Asked Questions","text":"In this example, @variables($a) created a variable named c, and set this variable to b.","category":"page"},{"location":"comparison/#Comparison-of-Julia's-Symbolics.jl-vs-SymPy-for-Symbolic-Computation","page":"Comparison Against SymPy","title":"Comparison of Julia's Symbolics.jl vs SymPy for Symbolic Computation","text":"","category":"section"},{"location":"comparison/","page":"Comparison Against SymPy","title":"Comparison Against SymPy","text":"Symbolics.jl is a symbolic modeling language for Julia, built in Julia. Its goal is very different from Sympy: it was made to support symbolic-numerics, the combination of symbolic computing with numerical methods to allow for extreme performance computing that would not be possible without modifying the model. Because of this, Symbolics.jl excels in many areas due to purposeful design decisions:","category":"page"},{"location":"comparison/","page":"Comparison Against SymPy","title":"Comparison Against SymPy","text":"Performance: Symbolics.jl is built in Julia, whereas SymPy was built in Python. Thus, the performance bar for Symbolics.jl is much higher. Symbolics.jl started because SymPy was far too slow and SymEngine was far too inflexible for the projects they were doing. Performance is key to Symbolics.jl. If you find any performance issues, please file an issue.\nbuild_function: lambdify is “fine” for some people, but if you're building a super fast MPI-enabled Julia/C/Fortran simulation code, having a function that hits the Python interpreter is less than optimal. By default, build_function builds fast JIT-compiled functions due to being in Julia. However, it has support for things like static arrays, non-allocating functions via mutation, fast functions on sparse matrices and arrays of arrays, etc.: all core details of doing high performance computing.\nParallelism: Symbolics.jl has pervasive parallelism. The symbolic simplification via SymbolicUtils.jl has built-in parallelism, Symbolics.jl builds functions that parallelize across threads. Symbolics.jl is compatible with GPU libraries like CUDA.jl.\nExtensible: Symbolics.jl and its underlying tools are written in pure Julia. Want to add new or better simplification rules? Add some Julia code! Need to add new derivatives? Add some Julia code! You get the picture. Breaking down these barriers makes it easier for the user to tailor the program to their needs and accelerates the development of the library.\nDeep integration with the Julia ecosystem: Symbolics.jl's integration with neural networks is not the only thing that's deep. Symbolics.jl is built with the same philosophy as other SciML packages, eschewing “monorepos” for a distributed development approach that ties together the work of many developers. The differentiation parts utilize tools from automatic differentiation libraries, all linear algebra functionality comes from tracing Julia Base itself, symbolic rewriting (simplification and substitution) comes from SymbolicUtils.jl, parallelism comes from Julia Base libraries, Dagger.jl, etc. SciML Tools like DataDrivenDiffEq.jl can reconstruct symbolic expressions from neural networks and data, while NeuralPDE.jl can automatically solve partial differential equations from symbolic descriptions using physics-informed neural networks. The list keeps going. All told, by design Symbolics.jl's development moves fast because it's effectively using the work of hundreds of Julia developers, allowing it to grow fast.\nWhile Symbolics.jl has many features missing from SymPy, it does not superset SymPy's functionality. For a list of missing features, see this issue.","category":"page"},{"location":"manual/groebner/#Groebner-bases","page":"Groebner bases","title":"Groebner bases","text":"","category":"section"},{"location":"manual/groebner/","page":"Groebner bases","title":"Groebner bases","text":"Groebner bases use the implementation of the F4 algorithm from Groebner.jl package as its backend. We refer to the documentation of Groebner.jl, which lists some implementations details and possible use-cases of Groebner bases.","category":"page"},{"location":"manual/groebner/","page":"Groebner bases","title":"Groebner bases","text":"groebner_basis","category":"page"},{"location":"manual/groebner/#Symbolics.groebner_basis","page":"Groebner bases","title":"Symbolics.groebner_basis","text":"groebner_basis(polynomials)\n\nComputes a Groebner basis of the ideal generated by the given polynomials.\n\nThis function requires a Groebner bases backend (such as Groebner.jl) to be loaded.\n\n\n\n\n\n","category":"function"},{"location":"manual/types/#Supported-types-and-dispatch-in-Symbolics","page":"Supported types and dispatch in Symbolics","title":"Supported types and dispatch in Symbolics","text":"","category":"section"},{"location":"manual/types/","page":"Supported types and dispatch in Symbolics","title":"Supported types and dispatch in Symbolics","text":"There is a tension between types as a representation of expression trees and types that are a subtype of types already present in Julia.","category":"page"},{"location":"manual/types/","page":"Supported types and dispatch in Symbolics","title":"Supported types and dispatch in Symbolics","text":"We want to be able to deal with expression trees in a unified way and not constrain expression trees themselves to be under an abstract type in Julia's type hierarchy. (For example, if we said that all expression trees are subtype of Real, then we couldn't represent array operations using the same expression tree.). But we also want to be able to pass in expression trees into places in existing code that accept Real values.","category":"page"},{"location":"manual/types/","page":"Supported types and dispatch in Symbolics","title":"Supported types and dispatch in Symbolics","text":"We accomplish this by wrapping expression trees in a simple wrapper type which is a subtype of our desired abstract type. For example, we wrap expression trees in the type Num which is a subtype of Real to make it behave like a Real number.","category":"page"},{"location":"manual/types/","page":"Supported types and dispatch in Symbolics","title":"Supported types and dispatch in Symbolics","text":"The methods on Num objects are forwarded to the wrapped expression tree. And care is taken so that an expression tree never internally contains Num – this is both for performance and separation of concerns.","category":"page"},{"location":"manual/types/","page":"Supported types and dispatch in Symbolics","title":"Supported types and dispatch in Symbolics","text":"User-facing APIs in Symbolics always take wrapped objects like Num, they are then internally unwrapped for expression tree manipulation.","category":"page"},{"location":"manual/types/","page":"Supported types and dispatch in Symbolics","title":"Supported types and dispatch in Symbolics","text":"Due to it requiring such wrappers, we only fully support a limited number of types as both the types of expression trees and the type as Julia sees them.","category":"page"},{"location":"manual/types/","page":"Supported types and dispatch in Symbolics","title":"Supported types and dispatch in Symbolics","text":"These types are","category":"page"},{"location":"manual/types/","page":"Supported types and dispatch in Symbolics","title":"Supported types and dispatch in Symbolics","text":"Real numbers (wrapped using Num)\ncomplex numbers (stored as Complex{Num} where Complex is from Base Julia)\narrays of Real and complex numbers (wrapped using Arr, so Arr{Num} or Arr{Complex{Num}})","category":"page"},{"location":"manual/types/#@variables-and-types","page":"Supported types and dispatch in Symbolics","title":"@variables and types","text":"","category":"section"},{"location":"manual/types/","page":"Supported types and dispatch in Symbolics","title":"Supported types and dispatch in Symbolics","text":"Use the syntax @variables x::T to create a symbol named x of symbolic type T. If T is a subtype of any of the above listed types which support a wrapper, the resulting variable will be wrapped in that type. As seen in the examples below, x,z,X,Z all have a suitable wrapper type. Hence, their types are shown. However, s being of symbolic type String does not have a corresponding wrapper supported by Symbolics, and hence, it returns a Sym{String} object. This is the trivial expression tree of a single variable without a wrapper, and is not a subtype of String or AbstractString.","category":"page"},{"location":"manual/types/","page":"Supported types and dispatch in Symbolics","title":"Supported types and dispatch in Symbolics","text":"using Symbolics\n@variables x::Real z::Complex{Real} (X::Real)[1:10, 1:10] (Z::Complex{Real})[1:10] s::String","category":"page"},{"location":"manual/types/","page":"Supported types and dispatch in Symbolics","title":"Supported types and dispatch in Symbolics","text":"typeof(x)","category":"page"},{"location":"manual/types/","page":"Supported types and dispatch in Symbolics","title":"Supported types and dispatch in Symbolics","text":"typeof(z)","category":"page"},{"location":"manual/types/","page":"Supported types and dispatch in Symbolics","title":"Supported types and dispatch in Symbolics","text":"typeof(X)","category":"page"},{"location":"manual/types/","page":"Supported types and dispatch in Symbolics","title":"Supported types and dispatch in Symbolics","text":"typeof(Z)","category":"page"},{"location":"manual/types/","page":"Supported types and dispatch in Symbolics","title":"Supported types and dispatch in Symbolics","text":"typeof(s)","category":"page"},{"location":"manual/parsing/#Parsing-Julia-Expressions-to-Symbolic-Expressions","page":"Parsing Julia Expressions to Symbolic Expressions","title":"Parsing Julia Expressions to Symbolic Expressions","text":"","category":"section"},{"location":"manual/parsing/","page":"Parsing Julia Expressions to Symbolic Expressions","title":"Parsing Julia Expressions to Symbolic Expressions","text":"Julia expressions such as :(y - x) are fundamentally different from symbolic expressions, as they do not have an algebra defined on them. Thus, it can be very helpful when building domain-specific languages (DSLs) and parsing files to convert from Julia expressions to Symbolics.jl expressions for further manipulation. Towards this end is the parse_expr_to_symbolic which performs the parsing.","category":"page"},{"location":"manual/parsing/","page":"Parsing Julia Expressions to Symbolic Expressions","title":"Parsing Julia Expressions to Symbolic Expressions","text":"warn: Warn\nTake the limitations mentioned in the parse_expr_to_symbolic docstrings seriously! Because Julia expressions contain no symbolic metadata, there is limited information and thus the parsing requires heuristics to work. ","category":"page"},{"location":"manual/parsing/","page":"Parsing Julia Expressions to Symbolic Expressions","title":"Parsing Julia Expressions to Symbolic Expressions","text":"parse_expr_to_symbolic\n@parse_expr_to_symbolic","category":"page"},{"location":"manual/parsing/#Symbolics.parse_expr_to_symbolic","page":"Parsing Julia Expressions to Symbolic Expressions","title":"Symbolics.parse_expr_to_symbolic","text":"parse_expr_to_symbolic(ex, mod::Module)\n\nApplies the parse_expr_to_symbolic function in the current module, i.e. parse_expr_to_symbolic(ex, mod) where mod is the module of the function caller.\n\nArguments\n\nex: the expression to parse\nmod: the module to apply the parsing in. See the limitations section for details.\n\nExample\n\nex = :(y(t) ~ x(t))\nparse_expr_to_symbolic(ex,Main) # gives the symbolic expression `y(t) ~ x(t)` in empty Main\n\n# Now do a whole system\n\nex = [:(y ~ x)\n :(y ~ -2x + 3 / z)\n :(z ~ 2)]\neqs = parse_expr_to_symbolic.(ex, (Main,))\n\n@variables x y z\nex = [y ~ x\n y ~ -2x + 3 / z\n z ~ 2]\nall(isequal.(eqs,ex)) # true\n\nLimitations\n\nSymbolic-ness Tied to Environment Definitions\n\nThe parsing to a symbolic expression has to be able to recognize the difference between functions, numbers, and globals defined within one's Julia environment and those that are to be made symbolic. The way this functionality handles this problem is that it does not define anything as symbolic that is already defined in the chosen mod module. For example, f(x,y) will have f as non-symbolic if the function f (named f) is defined in mod, i.e. if isdefined(mod,:f) is true. When the symbol is defined, it will be replaced by its value. Notably, this means that the parsing behavior changes depending on the environment that it is applied.\n\nFor example:\n\nparse_expr_to_symbolic(:(x - y),@__MODULE__) # x - y\nx = 2.0\nparse_expr_to_symbolic(:(x - y),@__MODULE__) # 2.0 - y\n\nThis is required to detect that standard functions like - are functions instead of symbolic symbols. For safety, one should create anonymous modules or other sub-environments to ensure no stray variables are defined.\n\nMetadata is Blank\n\nBecause all the variables defined by the expressions are not defined with the standard @variables, there is no metadata that is or can be associated with any of the generated variables. Instead, they all have blank metadata, but are defined in the Real domain. Thus, the variables which come out of this parsing may not evaluate as equal to a symbolic variable defined elsewhere.\n\n\n\n\n\n","category":"function"},{"location":"manual/variables/#Variable-and-Equation-Types","page":"Variable and Equation Types","title":"Variable and Equation Types","text":"","category":"section"},{"location":"manual/variables/","page":"Variable and Equation Types","title":"Variable and Equation Types","text":"Symbolics IR mirrors the Julia AST but allows for easy mathematical manipulation by itself following mathematical semantics. The base of the IR is the Sym type, which defines a symbolic variable. Registered (mathematical) functions on Syms (or iscall objects) return an expression that iscall. For example, op1 = x+y is one symbolic object and op2 = 2z is another, and so op1*op2 is another tree object. Then, at the top, an Equation, normally written as op1 ~ op2, defines the symbolic equality between two operations.","category":"page"},{"location":"manual/variables/#Types","page":"Variable and Equation Types","title":"Types","text":"","category":"section"},{"location":"manual/variables/","page":"Variable and Equation Types","title":"Variable and Equation Types","text":"Sym, Term, and FnType are from SymbolicUtils.jl. Note that in Symbolics, we always use Sym{Real}, Term{Real}, and FnType{Tuple{Any}, Real}. To get the arguments of an iscall object, use arguments(t::Term), and to get the operation, use operation(t::Term). However, note that one should never dispatch on Term or test isa Term. Instead, one needs to use SymbolicUtils.iscall to check if arguments and operation is defined.","category":"page"},{"location":"manual/variables/","page":"Variable and Equation Types","title":"Variable and Equation Types","text":"@variables\nSymbolics.variable\nSymbolics.variables\nEquation\nBase.:~(::Num, ::Num)","category":"page"},{"location":"manual/variables/#Symbolics.@variables","page":"Variable and Equation Types","title":"Symbolics.@variables","text":"Define one or more unknown variables.\n\n@variables t α σ(..) β[1:2]\n@variables w(..) x(t) y z(t, α, x)\n\nexpr = β[1]* x + y^α + σ(3) * (z - t) - β[2] * w(t - 1)\n\n(..) signifies that the value should be left uncalled.\n\nSymbolics supports creating variables that denote an array of some size.\n\njulia> @variables x[1:3]\n1-element Vector{Symbolics.Arr{Num, 1}}:\n x[1:3]\n\njulia> @variables y[1:3, 1:6] # support for tensors\n1-element Vector{Symbolics.Arr{Num, 2}}:\n y[1:3,1:6]\n\njulia> @variables t z(t)[1:3] # also works for dependent variables\n2-element Vector{Any}:\n t\n (z(t))[1:3]\n\nA symbol or expression that represents an array can be turned into an array of symbols or expressions using the scalarize function.\n\njulia> Symbolics.scalarize(z)\n3-element Vector{Num}:\n (z(t))[1]\n (z(t))[2]\n (z(t))[3]\n\nNote that @variables returns a vector of all the defined variables.\n\n@variables can also take runtime symbol values by the $ interpolation operator, and in this case, @variables doesn't automatically assign the value, instead, it only returns a vector of symbolic variables. All the rest of the syntax also applies here.\n\njulia> a, b, c = :runtime_symbol_value, :value_b, :value_c\n(:runtime_symbol_value, :value_b, :value_c)\n\njulia> vars = @variables t $a $b(t) $c(t)[1:3]\n4-element Vector{Any}:\n t\n runtime_symbol_value\n value_b(t)\n (value_c(t))[1:3]\n\njulia> (t, a, b, c)\n(t, :runtime_symbol_value, :value_b, :value_c)\n\n\n\n\n\n","category":"macro"},{"location":"manual/variables/#Symbolics.variable","page":"Variable and Equation Types","title":"Symbolics.variable","text":"variable(name::Symbol, idx::Integer...; T=Real)\n\nCreate a variable with the given name along with subscripted indices with the symtype=T. When T=FnType, it creates a symbolic function.\n\njulia> Symbolics.variable(:x, 4, 2, 0)\nx₄ˏ₂ˏ₀\n\njulia> Symbolics.variable(:x, 4, 2, 0, T=Symbolics.FnType)\nx₄ˏ₂ˏ₀⋆\n\nAlso see variables.\n\n\n\n\n\n","category":"function"},{"location":"manual/variables/#Symbolics.variables","page":"Variable and Equation Types","title":"Symbolics.variables","text":"variables(name::Symbol, indices...)\n\nCreate a multi-dimensional array of individual variables named with subscript notation. Use @variables instead to create symbolic array variables (as opposed to array of variables). See variable to create one variable with subscripts.\n\njulia> Symbolics.variables(:x, 1:3, 3:6)\n3×4 Matrix{Num}:\n x₁ˏ₃ x₁ˏ₄ x₁ˏ₅ x₁ˏ₆\n x₂ˏ₃ x₂ˏ₄ x₂ˏ₅ x₂ˏ₆\n x₃ˏ₃ x₃ˏ₄ x₃ˏ₅ x₃ˏ₆\n\n\n\n\n\n","category":"function"},{"location":"manual/variables/#Symbolics.Equation","page":"Variable and Equation Types","title":"Symbolics.Equation","text":"struct Equation\n\nAn equality relationship between two expressions.\n\nFields\n\nlhs: The expression on the left-hand side of the equation.\nrhs: The expression on the right-hand side of the equation.\n\n\n\n\n\n","category":"type"},{"location":"manual/variables/#Base.:~-Tuple{Num, Num}","page":"Variable and Equation Types","title":"Base.:~","text":"~(lhs, rhs) -> Any\n\n\nCreate an Equation out of two Num instances, or an Num and a Number.\n\nExamples\n\njulia> using Symbolics\n\njulia> @variables x y;\n\njulia> @variables A[1:3, 1:3] B[1:3, 1:3];\n\njulia> x ~ y\nx ~ y\n\njulia> x - y ~ 0\nx - y ~ 0\n\njulia> A ~ B\n(broadcast(~, A, B))[1:3,1:3]\n\njulia> A .~ 3x\n(broadcast(~, A, 3x))[1:3,1:3]\n\n\n\n\n\n","category":"method"},{"location":"manual/variables/#A-note-about-functions-restricted-to-Numbers","page":"Variable and Equation Types","title":"A note about functions restricted to Numbers","text":"","category":"section"},{"location":"manual/variables/","page":"Variable and Equation Types","title":"Variable and Equation Types","text":"Sym and Term objects are NOT subtypes of Number. Symbolics provides a simple wrapper type called Num which is a subtype of Real. Num wraps either a Sym or a Term or any other object, defines the same set of operations as symbolic expressions and forwards those to the values it wraps. You can use Symbolics.value function to unwrap a Num.","category":"page"},{"location":"manual/variables/","page":"Variable and Equation Types","title":"Variable and Equation Types","text":"By default, the @variables macros return Num-wrapped objects to allow calling functions which are restricted to Number or Real.","category":"page"},{"location":"manual/variables/","page":"Variable and Equation Types","title":"Variable and Equation Types","text":"using Symbolics\n@variables t x y z(t);\nSymbolics.operation(Symbolics.value(x + y))","category":"page"},{"location":"manual/variables/","page":"Variable and Equation Types","title":"Variable and Equation Types","text":"Symbolics.operation(Symbolics.value(z))","category":"page"},{"location":"manual/variables/","page":"Variable and Equation Types","title":"Variable and Equation Types","text":"Symbolics.arguments(Symbolics.value(x + y))","category":"page"},{"location":"manual/variables/","page":"Variable and Equation Types","title":"Variable and Equation Types","text":"Note that Julia converts irrationals — like π and ℯ — to Float64 whenever they are involved in arithmetic with other numbers, including integers. An expression like 2π will be converted to a float immediately, so an expression like 2π * x will leave the symbolic x multiplied by a Float64. It may be preferable to have a symbolic representation of π also, which can be achieved with Num(π). For generic programming, it may be helpful to simply redefine the variable π to be of the same type as some other argument, as in","category":"page"},{"location":"manual/variables/","page":"Variable and Equation Types","title":"Variable and Equation Types","text":"function f(x)\n let π=oftype(x, π)\n 1 + (2//3 + 4π/5) * x\n end\nend\nf(t)","category":"page"},{"location":"manual/variables/","page":"Variable and Equation Types","title":"Variable and Equation Types","text":"This will work for any floating-point input, as well as symbolic input.","category":"page"},{"location":"manual/variables/#Symbolic-Control-Flow","page":"Variable and Equation Types","title":"Symbolic Control Flow","text":"","category":"section"},{"location":"manual/variables/","page":"Variable and Equation Types","title":"Variable and Equation Types","text":"Control flow can be expressed in Symbolics.jl in the following ways:","category":"page"},{"location":"manual/variables/","page":"Variable and Equation Types","title":"Variable and Equation Types","text":"IfElse.ifelse(cond,x,y): this is a dispatch-able version of the ifelse function provided by IfElse.jl which allows for encoding conditionals in the symbolic branches.","category":"page"},{"location":"manual/variables/#Inspection-Functions","page":"Variable and Equation Types","title":"Inspection Functions","text":"","category":"section"},{"location":"manual/variables/","page":"Variable and Equation Types","title":"Variable and Equation Types","text":"SymbolicUtils.iscall\nSymbolicUtils.operation\nSymbolicUtils.arguments","category":"page"},{"location":"manual/sparsity_detection/#Structure-and-Sparsity-Detection","page":"Structure and Sparsity Detection","title":"Structure and Sparsity Detection","text":"","category":"section"},{"location":"manual/sparsity_detection/","page":"Structure and Sparsity Detection","title":"Structure and Sparsity Detection","text":"Using the tracing system provided by Symbolics.jl expressions, Symbolics.jl can automatically detect the sparsity patterns of Julia functions efficiently. This functionality is described in more detail in the paper:","category":"page"},{"location":"manual/sparsity_detection/","page":"Structure and Sparsity Detection","title":"Structure and Sparsity Detection","text":"@article{gowda2019sparsity,\n title={Sparsity Programming: Automated Sparsity-Aware Optimizations in Differentiable Programming},\n author={Gowda, Shashi and Ma, Yingbo and Churavy, Valentin and Edelman, Alan and Rackauckas, Christopher},\n year={2019}\n}","category":"page"},{"location":"manual/sparsity_detection/","page":"Structure and Sparsity Detection","title":"Structure and Sparsity Detection","text":"Please cite this work if the functionality is used.","category":"page"},{"location":"manual/sparsity_detection/#Sparsity-Detection","page":"Structure and Sparsity Detection","title":"Sparsity Detection","text":"","category":"section"},{"location":"manual/sparsity_detection/","page":"Structure and Sparsity Detection","title":"Structure and Sparsity Detection","text":"Symbolics.jacobian_sparsity\nSymbolics.hessian_sparsity","category":"page"},{"location":"manual/sparsity_detection/#Symbolics.jacobian_sparsity","page":"Structure and Sparsity Detection","title":"Symbolics.jacobian_sparsity","text":"jacobian_sparsity(\n exprs::AbstractArray,\n vars::AbstractArray\n) -> SparseArrays.SparseMatrixCSC{Bool, Int64}\n\n\nReturn the sparsity pattern of the Jacobian of an array of expressions with respect to an array of variable expressions.\n\nArguments\n\nexprs: an array of symbolic expressions.\nvars: an array of symbolic variables.\n\nExamples\n\njulia> using Symbolics\n\njulia> vars = @variables x₁ x₂;\n\njulia> exprs = [2x₁, 3x₂, 4x₁ * x₂];\n\njulia> Symbolics.jacobian_sparsity(exprs, vars)\n3×2 SparseArrays.SparseMatrixCSC{Bool, Int64} with 4 stored entries:\n 1 ⋅\n ⋅ 1\n 1 1\n\n\n\n\n\njacobian_sparsity(\n f!::Function,\n output::AbstractArray,\n input::AbstractArray,\n args...;\n kwargs...\n) -> SparseArrays.SparseMatrixCSC{Bool, Int64}\n\n\nReturn the sparsity pattern of the Jacobian of the mutating function f!.\n\nArguments\n\nf!: an in-place function f!(output, input, args...; kwargs...).\noutput: output array.\ninput: input array.\n\nThe eltype of output and input can be either symbolic or primitive.\n\nExamples\n\njulia> using Symbolics\n\njulia> f!(y, x) = y .= [x[2], 2x[1], 3x[1] * x[2]];\n\njulia> output = Vector{Float64}(undef, 3);\n\njulia> input = Vector{Float64}(undef, 2);\n\njulia> Symbolics.jacobian_sparsity(f!, output, input)\n3×2 SparseArrays.SparseMatrixCSC{Bool, Int64} with 4 stored entries:\n ⋅ 1\n 1 ⋅\n 1 1\n\n\n\n\n\n","category":"function"},{"location":"manual/sparsity_detection/#Symbolics.hessian_sparsity","page":"Structure and Sparsity Detection","title":"Symbolics.hessian_sparsity","text":"hessian_sparsity(\n expr,\n vars::AbstractVector;\n full\n) -> Union{SparseArrays.SparseMatrixCSC{Bool, Int64}, SparseArrays.SparseMatrixCSC{Int64, Int64}}\n\n\nReturn the sparsity pattern of the Hessian of an expression with respect to an array of variable expressions.\n\nArguments\n\nexpr: a symbolic expression.\nvars: a vector of symbolic variables.\n\nExamples\n\njulia> using Symbolics\n\njulia> vars = @variables x₁ x₂;\n\njulia> expr = 3x₁^2 + 4x₁ * x₂;\n\njulia> Symbolics.hessian_sparsity(expr, vars)\n2×2 SparseArrays.SparseMatrixCSC{Bool, Int64} with 3 stored entries:\n 1 1\n 1 ⋅\n\n\n\n\n\nhessian_sparsity(\n f::Function,\n input::AbstractVector,\n args...;\n full,\n kwargs...\n) -> Union{SparseArrays.SparseMatrixCSC{Bool, Int64}, SparseArrays.SparseMatrixCSC{Int64, Int64}}\n\n\nReturn the sparsity pattern of the Hessian of the given function f.\n\nArguments\n\nf: an out-of-place function f(input, args...; kwargs...).\ninput: a vector of input values whose eltype can be either symbolic or primitive.\n\nExamples\n\njulia> using Symbolics\n\njulia> f(x) = 4x[1] * x[2] - 5x[2]^2;\n\njulia> input = Vector{Float64}(undef, 2);\n\njulia> Symbolics.hessian_sparsity(f, input)\n2×2 SparseArrays.SparseMatrixCSC{Bool, Int64} with 3 stored entries:\n ⋅ 1\n 1 1\n\n\n\n\n\n","category":"function"},{"location":"manual/sparsity_detection/#Structure-Detection","page":"Structure and Sparsity Detection","title":"Structure Detection","text":"","category":"section"},{"location":"manual/sparsity_detection/","page":"Structure and Sparsity Detection","title":"Structure and Sparsity Detection","text":"Symbolics.islinear\nSymbolics.isaffine","category":"page"},{"location":"manual/sparsity_detection/#Symbolics.islinear","page":"Structure and Sparsity Detection","title":"Symbolics.islinear","text":"islinear(ex, u)\n\n\nCheck if an expression is linear with respect to a list of variable expressions.\n\n\n\n\n\n","category":"function"},{"location":"manual/sparsity_detection/#Symbolics.isaffine","page":"Structure and Sparsity Detection","title":"Symbolics.isaffine","text":"isaffine(ex, u)\n\n\nCheck if an expression is affine with respect to a list of variable expressions.\n\n\n\n\n\n","category":"function"},{"location":"manual/sparsity_detection/#ADTypes.jl-interface","page":"Structure and Sparsity Detection","title":"ADTypes.jl interface","text":"","category":"section"},{"location":"manual/sparsity_detection/","page":"Structure and Sparsity Detection","title":"Structure and Sparsity Detection","text":"Symbolics.SymbolicsSparsityDetector","category":"page"},{"location":"manual/sparsity_detection/#Symbolics.SymbolicsSparsityDetector","page":"Structure and Sparsity Detection","title":"Symbolics.SymbolicsSparsityDetector","text":"SymbolicsSparsityDetector <: ADTypes.AbstractSparsityDetector\n\nSparsity detection algorithm based on the Symbolics.jl tracing system.\n\nThis type makes Symbolics.jl compatible with the ADTypes.jl sparsity detection framework. The following functions are implemented:\n\nADTypes.jacobian_sparsity based on Symbolics.jacobian_sparsity\nADTypes.hessian_sparsity based on Symbolics.hessian_sparsity\n\nReference\n\nSparsity Programming: Automated Sparsity-Aware Optimizations in Differentiable Programming, Gowda et al. (2019)\n\n\n\n\n\n","category":"type"},{"location":"tutorials/auto_parallel/#Automated-Sparse-Parallelism-of-Julia-Functions-via-Tracing","page":"Automated Sparse Parallelism of Julia Functions via Tracing","title":"Automated Sparse Parallelism of Julia Functions via Tracing","text":"","category":"section"},{"location":"tutorials/auto_parallel/","page":"Automated Sparse Parallelism of Julia Functions via Tracing","title":"Automated Sparse Parallelism of Julia Functions via Tracing","text":"Because the Symbolics.jl expressions obey Julia semantics, one can directly transform existing Julia functions into Symbolics.jl symbolic representations of the function by simply inputting the symbolic values into the function and using what is returned. For example, let's take the following numerical PDE discretization:","category":"page"},{"location":"tutorials/auto_parallel/","page":"Automated Sparse Parallelism of Julia Functions via Tracing","title":"Automated Sparse Parallelism of Julia Functions via Tracing","text":"using Symbolics, LinearAlgebra, SparseArrays, Plots\n\n# Define the constants for the PDE\nconst α₂ = 1.0\nconst α₃ = 1.0\nconst β₁ = 1.0\nconst β₂ = 1.0\nconst β₃ = 1.0\nconst r₁ = 1.0\nconst r₂ = 1.0\nconst _DD = 100.0\nconst γ₁ = 0.1\nconst γ₂ = 0.1\nconst γ₃ = 0.1\nconst N = 32\nconst X = reshape([i for i in 1:N for j in 1:N], N, N)\nconst Y = reshape([j for i in 1:N for j in 1:N], N, N)\nconst α₁ = 1.0 .* (X .>= 4*N/5)\n\nconst Mx = Array(Tridiagonal([1.0 for i in 1:N-1], [-2.0 for i in 1:N], [1.0 for i in 1:N-1]))\nconst My = copy(Mx)\nMx[2, 1] = 2.0\nMx[end-1,end] = 2.0\nMy[1, 2] = 2.0\nMy[end,end-1] = 2.0\n\n# Define the discretized PDE as an ODE function\nfunction f(u, p, t)\n A = u[:,:,1]\n B = u[:,:,2]\n C = u[:,:,3]\n MyA = My*A\n AMx = A*Mx\n DA = @. _DD*(MyA + AMx)\n dA = @. DA + α₁ - β₁*A - r₁*A*B + r₂*C\n dB = @. α₂ - β₂*B - r₁*A*B + r₂*C\n dC = @. α₃ - β₃*C + r₁*A*B - r₂*C\n cat(dA, dB, dC, dims=3)\nend","category":"page"},{"location":"tutorials/auto_parallel/","page":"Automated Sparse Parallelism of Julia Functions via Tracing","title":"Automated Sparse Parallelism of Julia Functions via Tracing","text":"We can build the Symbolics version of this model by tracing the model function:","category":"page"},{"location":"tutorials/auto_parallel/","page":"Automated Sparse Parallelism of Julia Functions via Tracing","title":"Automated Sparse Parallelism of Julia Functions via Tracing","text":"# Define the initial condition as normal arrays\n@variables u[1:N, 1:N, 1:3]\ndu = simplify.(f(collect(u), nothing, 0.0))\nvec(du)[1:10]","category":"page"},{"location":"tutorials/auto_parallel/","page":"Automated Sparse Parallelism of Julia Functions via Tracing","title":"Automated Sparse Parallelism of Julia Functions via Tracing","text":"The output, here the in-place modified du, is a symbolic representation of each output of the function. We can then utilize this in the Symbolics functionality. For example, let's build a parallel version of f first:","category":"page"},{"location":"tutorials/auto_parallel/","page":"Automated Sparse Parallelism of Julia Functions via Tracing","title":"Automated Sparse Parallelism of Julia Functions via Tracing","text":"fastf = eval(Symbolics.build_function(du,u,\n parallel=Symbolics.MultithreadedForm())[2])","category":"page"},{"location":"tutorials/auto_parallel/","page":"Automated Sparse Parallelism of Julia Functions via Tracing","title":"Automated Sparse Parallelism of Julia Functions via Tracing","text":"Now let's compute the sparse Jacobian function and compile a fast multithreaded version:","category":"page"},{"location":"tutorials/auto_parallel/","page":"Automated Sparse Parallelism of Julia Functions via Tracing","title":"Automated Sparse Parallelism of Julia Functions via Tracing","text":"jac = Symbolics.sparsejacobian(vec(du), vec(u))\nrow,col,val = findnz(jac)\nscatter(row,col,legend=false,ms=1,c=:black)","category":"page"},{"location":"tutorials/auto_parallel/","page":"Automated Sparse Parallelism of Julia Functions via Tracing","title":"Automated Sparse Parallelism of Julia Functions via Tracing","text":"fjac = eval(Symbolics.build_function(jac,u,\n parallel=Symbolics.MultithreadedForm())[2])","category":"page"},{"location":"tutorials/auto_parallel/","page":"Automated Sparse Parallelism of Julia Functions via Tracing","title":"Automated Sparse Parallelism of Julia Functions via Tracing","text":"It takes awhile for this to generate, but the results will be worth it! Now let's set up the parabolic PDE to be solved by DifferentialEquations.jl. We will set up the vanilla version and the sparse multithreaded version:","category":"page"},{"location":"tutorials/auto_parallel/","page":"Automated Sparse Parallelism of Julia Functions via Tracing","title":"Automated Sparse Parallelism of Julia Functions via Tracing","text":"using OrdinaryDiffEq\nu0 = zeros(N, N, 3)\nMyA = zeros(N, N);\nAMx = zeros(N, N);\nDA = zeros(N, N);\nprob = ODEProblem(f, u0, (0.0, 10.0))\nfastprob = ODEProblem(ODEFunction((du, u, p, t) -> fastf(du, u),\n jac = (du, u, p, t) -> fjac(du, u),\n jac_prototype = similar(jac, Float64)),\n u0, (0.0, 10.0))","category":"page"},{"location":"tutorials/auto_parallel/","page":"Automated Sparse Parallelism of Julia Functions via Tracing","title":"Automated Sparse Parallelism of Julia Functions via Tracing","text":"Let's see the timing difference:","category":"page"},{"location":"tutorials/auto_parallel/","page":"Automated Sparse Parallelism of Julia Functions via Tracing","title":"Automated Sparse Parallelism of Julia Functions via Tracing","text":"using BenchmarkTools\n#@btime solve(prob, TRBDF2()); # 33.073 s (895404 allocations: 23.87 GiB)\n#warning the following solve takes a long time to compile, but afterwards is very fast.\n#@btime solve(fastprob, TRBDF2()); # 209.670 ms (8208 allocations: 109.25 MiB)","category":"page"},{"location":"tutorials/auto_parallel/","page":"Automated Sparse Parallelism of Julia Functions via Tracing","title":"Automated Sparse Parallelism of Julia Functions via Tracing","text":"Boom, an automatic 157x acceleration that grows as the size of the problem increases!","category":"page"},{"location":"manual/io/#I/O,-Saving,-and-Latex","page":"I/O, Saving, and Latex","title":"I/O, Saving, and Latex","text":"","category":"section"},{"location":"manual/io/","page":"I/O, Saving, and Latex","title":"I/O, Saving, and Latex","text":"Note that Julia's standard I/O functionality can be used to save Symbolics expressions out to files. For example, here we will generate an in-place version of f and save the anonymous function to a .jl file:","category":"page"},{"location":"manual/io/","page":"I/O, Saving, and Latex","title":"I/O, Saving, and Latex","text":"using Symbolics\n@variables u[1:3]\nfunction f(u)\n [u[1]-u[3],u[1]^2-u[2],u[3]+u[2]]\nend\nex1, ex2 = build_function(f(u),u)\nwrite(\"function.jl\", string(ex2))","category":"page"},{"location":"manual/io/","page":"I/O, Saving, and Latex","title":"I/O, Saving, and Latex","text":"Now we can do something like:","category":"page"},{"location":"manual/io/","page":"I/O, Saving, and Latex","title":"I/O, Saving, and Latex","text":"g = include(\"function.jl\")","category":"page"},{"location":"manual/io/","page":"I/O, Saving, and Latex","title":"I/O, Saving, and Latex","text":"and that will load the function back in. Note that this can be done to save the transformation results of Symbolics.jl so that they can be stored and used in a precompiled Julia package.","category":"page"},{"location":"manual/io/#Latexification","page":"I/O, Saving, and Latex","title":"Latexification","text":"","category":"section"},{"location":"manual/io/","page":"I/O, Saving, and Latex","title":"I/O, Saving, and Latex","text":"Symbolics.jl's expressions support Latexify.jl, and thus","category":"page"},{"location":"manual/io/","page":"I/O, Saving, and Latex","title":"I/O, Saving, and Latex","text":"using Latexify\nlatexify(ex)","category":"page"},{"location":"manual/io/","page":"I/O, Saving, and Latex","title":"I/O, Saving, and Latex","text":"will produce LaTeX output from Symbolics models and expressions. This works on basics like Term all the way to higher primitives like ODESystem and ReactionSystem.","category":"page"},{"location":"manual/solver/#Solver","page":"Solver","title":"Solver","text":"","category":"section"},{"location":"manual/solver/","page":"Solver","title":"Solver","text":"The main symbolic solver for Symbolics.jl is symbolic_solve. Symbolic solving means that it only uses symbolic (algebraic) methods and outputs exact solutions.","category":"page"},{"location":"manual/solver/","page":"Solver","title":"Solver","text":"Symbolics.symbolic_solve","category":"page"},{"location":"manual/solver/#Symbolics.symbolic_solve","page":"Solver","title":"Symbolics.symbolic_solve","text":"symbolic_solve(expr, x; dropmultiplicity=true, warns=true)\n\nsymbolic_solve is a function which attempts to solve input equations/expressions symbolically using various methods.\n\nArguments\n\nexpr: Could be a single univar expression in the form of a poly or multiple univar expressions or multiple multivar polys or a transcendental nonlinear function.\nx: Could be a single variable or an array of variables which should be solved\ndropmultiplicity (optional): Should the output be printed n times where n is the number of occurrence of the root? Say we have (x+1)^2, we then have 2 roots x = -1, by default the output is [-1], If dropmultiplicity is inputted as false, then the output is [-1, -1].\nwarns (optional): When invalid expressions or cases are inputted, should the solver warn you of such cases before returning nothing? if this is set to false, the solver returns nothing. By default, warns are set to true.\n\nSupported input\n\nThe base solver (symbolic_solve) has multiple solvers which chooses from depending on the the type of input (multiple/uni var and multiple/single expression) only after ensuring that the input is valid.\n\nThe expressions inputted can contain parameters, which are assumed to be transcendental. A parameter \"a\" is transcendental if there exists no polynomial P with rational coefficients such that P(a) = 0. Check the examples section.\n\nCurrently, symbolic_solve supports\n\nLinear and polynomial equations (with parameters)\nSystems of linear and polynomials equations (without extra parameters, for now)\nEquations with transcendental functions (with parameters)\n\nExamples\n\nsolve_univar (uses factoring and analytic solutions up to degree 4)\n\nnote: Note\nThe package Nemo is needed in order to use solve_univar as well as solve_multipoly, so executing using Nemo as you will see in the following examples is necessary; otherwise, the function will throw an error.\n\njulia> using Symbolics, Nemo;\n\njulia> @variables x a b;\n\njulia> expr = expand((x + b)*(x^2 + 2x + 1)*(x^2 - a))\n-a*b - a*x - 2a*b*x - 2a*(x^2) + b*(x^2) + x^3 - a*b*(x^2) - a*(x^3) + 2b*(x^3) + 2(x^4) + b*(x^4) + x^5\n\njulia> symbolic_solve(expr, x)\n4-element Vector{Any}:\n -1\n -b\n (1//2)*√(4a)\n (-1//2)*√(4a)\n\njulia> symbolic_solve(expr, x, dropmultiplicity=false)\n5-element Vector{Any}:\n -1\n -1\n -b\n (1//2)*√(4a)\n (-1//2)*√(4a)\n\njulia> symbolic_solve(x^2 + a*x + 6, x)\n2-element Vector{SymbolicUtils.BasicSymbolic{Real}}:\n (1//2)*(-a + √(-24 + a^2))\n (1//2)*(-a - √(-24 + a^2))\n\njulia> symbolic_solve(x^7 - 1, x)\n2-element Vector{Any}:\n roots_of((1//1) + x + x^2 + x^3 + x^4 + x^5 + x^6, x)\n 1\n\nsolve_multivar (uses Groebner basis and solve_univar to find roots)\n\nnote: Note\nSimilar to solve_univar, Groebner is needed for solve_multivar or to be fully functional.\n\njulia> using Groebner\n\njulia> @variables x y z\n3-element Vector{Num}:\n x\n y\n z\n\njulia> eqs = [x+y^2+z, z*x*y, z+3x+y]\n3-element Vector{Num}:\n x + z + y^2\n x*y*z\n 3x + y + z\n\njulia> symbolic_solve(eqs, [x,y,z])\n3-element Vector{Any}:\n Dict{Num, Any}(z => 0, y => 1//3, x => -1//9)\n Dict{Num, Any}(z => 0, y => 0, x => 0)\n Dict{Num, Any}(z => -1, y => 1, x => 0)\n\nnote: Note\nIf Nemo or Groebner are not imported when needed, the solver throws an error.\n\njulia> using Symbolics\n\njulia> @variables x y z;\n\njulia> symbolic_solve(x+1, x)\nERROR: \"Nemo is required. Execute `using Nemo` to enable this functionality.\"\n\njulia> symbolic_solve([x+1, y], [x, y])\nERROR: \"Groebner bases engine is required. Execute `using Groebner` to enable this functionality.\"\n\nsolve_multipoly (uses GCD between the input polys)\n\njulia> symbolic_solve([x-1, x^3 - 1, x^2 - 1, (x-1)^20], x)\n1-element Vector{BigInt}:\n 1\n\nia_solve (solving by isolation and attraction)\n\njulia> symbolic_solve(2^(x+1) + 5^(x+3), x)\n1-element Vector{SymbolicUtils.BasicSymbolic{Real}}:\n (-slog(2) - log(complex(-1)) + 3slog(5)) / (slog(2) - slog(5))\n\njulia> symbolic_solve(log(x+1)+log(x-1), x)\n2-element Vector{SymbolicUtils.BasicSymbolic{BigFloat}}:\n (1//2)*√(8.0)\n (-1//2)*√(8.0)\n\njulia> symbolic_solve(a*x^b + c, x)\n((-c)^(1 / b)) / (a^(1 / b))\n\nEvaluating output (converting to floats)\n\nIf you want to evaluate the exact expressions found by symbolic_solve, you can do the following:\n\njulia> roots = symbolic_solve(2^(x+1) + 5^(x+3), x)\n1-element Vector{SymbolicUtils.BasicSymbolic{Real}}:\n (-slog(2) - log(complex(-1)) + 3slog(5)) / (slog(2) - slog(5))\n\njulia> Symbolics.symbolic_to_float.(roots)\n1-element Vector{Complex{BigFloat}}:\n -4.512941594732059759689023145584186058252768936052415430071569066192919491762214 + 3.428598090438030380369414618548038962770087500755160535832807433942464545729382im\n\n\n\n\n\n","category":"function"},{"location":"manual/solver/","page":"Solver","title":"Solver","text":"One other symbolic solver is symbolic_linear_solve which is limited compared to symbolic_solve as it only solves linear equations.","category":"page"},{"location":"manual/solver/","page":"Solver","title":"Solver","text":"Symbolics.symbolic_linear_solve","category":"page"},{"location":"manual/solver/#Symbolics.symbolic_linear_solve","page":"Solver","title":"Symbolics.symbolic_linear_solve","text":"symbolic_linear_solve(eq, var; simplify, check) -> Any\n\n\nSolve equation(s) eqs for a set of variables vars.\n\nAssumes length(eqs) == length(vars)\n\nCurrently only works if all equations are linear. check if the expr is linear w.r.t vars.\n\nExamples\n\njulia> @variables x y\n2-element Vector{Num}:\n x\n y\n\njulia> Symbolics.symbolic_linear_solve(x + y ~ 0, x)\n-y\n\njulia> Symbolics.symbolic_linear_solve([x + y ~ 0, x - y ~ 2], [x, y])\n2-element Vector{Float64}:\n 1.0\n -1.0\n\n\n\n\n\n","category":"function"},{"location":"manual/solver/","page":"Solver","title":"Solver","text":"symbolic_solve only supports symbolic, i.e. non-floating point computations, and thus prefers equations where the coefficients are integer, rational, or symbolic. Floating point coefficients are transformed into rational values and BigInt values are used internally with a potential performance loss, and thus it is recommended that this functionality is only used with floating point values if necessary. In contrast, symbolic_linear_solve directly handles floating point values using standard factorizations.","category":"page"},{"location":"manual/solver/#More-technical-details-and-examples","page":"Solver","title":"More technical details and examples","text":"","category":"section"},{"location":"manual/solver/#Technical-details","page":"Solver","title":"Technical details","text":"","category":"section"},{"location":"manual/solver/","page":"Solver","title":"Solver","text":"The symbolic_solve function uses 4 hidden solvers in order to solve the user's input. Its base, solve_univar, uses analytic solutions up to polynomials of degree 4 and factoring as its method for solving univariate polynomials. The function's solve_multipoly uses GCD on the input polynomials then throws passes the result to solve_univar. The function's solve_multivar uses Groebner basis and a separating form in order to create linear equations in the input variables and a single high degree equation in the separating variable [1]. Each equation resulting from the basis is then passed to solve_univar. We can see that essentially, solve_univar is the building block of symbolic_solve. If the input is not a valid polynomial and can not be solved by the algorithm above, symbolic_solve passes it to ia_solve, which attempts solving by attraction and isolation [2]. This only works when the input is a single expression and the user wants the answer in terms of a single variable. Say log(x) - a == 0 gives us [e^a].","category":"page"},{"location":"manual/solver/#Nice-examples","page":"Solver","title":"Nice examples","text":"","category":"section"},{"location":"manual/solver/","page":"Solver","title":"Solver","text":"using Symbolics, Nemo;\n@variables x;\nSymbolics.symbolic_solve(9^x + 3^x ~ 8, x)","category":"page"},{"location":"manual/solver/","page":"Solver","title":"Solver","text":"@variables x y z;\nSymbolics.symbolic_linear_solve(2//1*x + y - 2//1*z ~ 9//1*x, 1//1*x)","category":"page"},{"location":"manual/solver/","page":"Solver","title":"Solver","text":"using Groebner;\n@variables x y z;\n\neqs = [x^2 + y + z - 1, x + y^2 + z - 1, x + y + z^2 - 1]\nSymbolics.symbolic_solve(eqs, [x,y,z])","category":"page"},{"location":"manual/solver/#Feature-completeness","page":"Solver","title":"Feature completeness","text":"","category":"section"},{"location":"manual/solver/","page":"Solver","title":"Solver","text":"[x] Linear and polynomial equations\n[x] Systems of linear and polynomial equations\n[x] Some transcendental functions\n[x] Systems of linear equations with parameters (via symbolic_linear_solve)\n[ ] Equations with radicals\n[x] Systems of polynomial equations with parameters and positive dimensional systems\n[ ] Inequalities","category":"page"},{"location":"manual/solver/#Expressions-we-can-not-solve-(but-aim-to)","page":"Solver","title":"Expressions we can not solve (but aim to)","text":"","category":"section"},{"location":"manual/solver/","page":"Solver","title":"Solver","text":"# Mathematica\n\nIn[1]:= Reduce[x^2 - x - 6 > 0, x]\nOut[1]= x < -2 || x > 3\n\nIn[2]:= Reduce[x+a > 0, x]\nOut[2]= a \\[Element] Reals && x > -a\n\nIn[3]:= Solve[x^(x) + 3 == 0, x]\nOut[3]= {{x -> (I \\[Pi] + Log[3])/ProductLog[I \\[Pi] + Log[3]]}}","category":"page"},{"location":"manual/solver/#References","page":"Solver","title":"References","text":"","category":"section"},{"location":"manual/solver/","page":"Solver","title":"Solver","text":"[1]: Rouillier, F. Solving Zero-Dimensional Systems Through the Rational Univariate Representation. AAECC 9, 433–461 (1999).","category":"page"},{"location":"manual/solver/","page":"Solver","title":"Solver","text":"[2]: R. W. Hamming, Coding and Information Theory, ScienceDirect, 1980.","category":"page"},{"location":"tutorials/converting_to_C/#Automatic-Conversion-of-Julia-Code-to-C-Functions","page":"Automatic Conversion of Julia Code to C Functions","title":"Automatic Conversion of Julia Code to C Functions","text":"","category":"section"},{"location":"tutorials/converting_to_C/","page":"Automatic Conversion of Julia Code to C Functions","title":"Automatic Conversion of Julia Code to C Functions","text":"Since Symbolics.jl can trace Julia code into Symbolics IR that can be built and compiled via build_function to C, this gives us a nifty way to automatically generate C functions from Julia code! To see this in action, let's start with the Lotka-Volterra equations:","category":"page"},{"location":"tutorials/converting_to_C/","page":"Automatic Conversion of Julia Code to C Functions","title":"Automatic Conversion of Julia Code to C Functions","text":"using Symbolics\nfunction lotka_volterra!(du, u, p, t)\n x, y = u\n α, β, δ, γ = p\n du[1] = dx = α*x - β*x*y\n du[2] = dy = -δ*y + γ*x*y\nend","category":"page"},{"location":"tutorials/converting_to_C/","page":"Automatic Conversion of Julia Code to C Functions","title":"Automatic Conversion of Julia Code to C Functions","text":"Now we trace this into Symbolics:","category":"page"},{"location":"tutorials/converting_to_C/","page":"Automatic Conversion of Julia Code to C Functions","title":"Automatic Conversion of Julia Code to C Functions","text":"@variables t du[1:2] u[1:2] p[1:4]\ndu = collect(du)\nlotka_volterra!(du, u, p, t)\ndu","category":"page"},{"location":"tutorials/converting_to_C/","page":"Automatic Conversion of Julia Code to C Functions","title":"Automatic Conversion of Julia Code to C Functions","text":"and then we build the function:","category":"page"},{"location":"tutorials/converting_to_C/","page":"Automatic Conversion of Julia Code to C Functions","title":"Automatic Conversion of Julia Code to C Functions","text":"build_function(du, u, p, t, target=Symbolics.CTarget())","category":"page"},{"location":"tutorials/converting_to_C/","page":"Automatic Conversion of Julia Code to C Functions","title":"Automatic Conversion of Julia Code to C Functions","text":"If we want to compile this, we do expression=Val{false}:","category":"page"},{"location":"tutorials/converting_to_C/","page":"Automatic Conversion of Julia Code to C Functions","title":"Automatic Conversion of Julia Code to C Functions","text":"f = build_function(du, u, p, t, target=Symbolics.CTarget(), expression=Val{false})","category":"page"},{"location":"tutorials/converting_to_C/","page":"Automatic Conversion of Julia Code to C Functions","title":"Automatic Conversion of Julia Code to C Functions","text":"now we check it computes the same thing:","category":"page"},{"location":"tutorials/converting_to_C/","page":"Automatic Conversion of Julia Code to C Functions","title":"Automatic Conversion of Julia Code to C Functions","text":"du = rand(2); du2 = rand(2)\nu = rand(2)\np = rand(4)\nt = rand()\nf(du, u, p, t)\nlotka_volterra!(du2, u, p, t)\ndu == du2 # true!","category":"page"},{"location":"manual/build_function/#Function-Building-and-Compilation-(build_function)","page":"Function Building and Compilation (build_function)","title":"Function Building and Compilation (build_function)","text":"","category":"section"},{"location":"manual/build_function/","page":"Function Building and Compilation (build_function)","title":"Function Building and Compilation (build_function)","text":"At any time, callable functions can be generated from Symbolics IR by using Symbolics.toexpr. This performs some cleaning to return an expression without extraneous pieces that commonly matches expressions one would write in functions like those for differential equation solvers and optimization libraries. These functions can be automatically parallelized and specialize on Julia types like static arrays and sparse matrices.","category":"page"},{"location":"manual/build_function/","page":"Function Building and Compilation (build_function)","title":"Function Building and Compilation (build_function)","text":"The core compilation process of Symbolics IR is build_function. build_function takes an operation or an AbstractArray of operations and generates a compilable version of the model for numerical solvers. The form of this output is dependent on the target. By default, the target outputs Julia code, but other formats, such as C, Stan, and MATLAB are available. These can be generated as expressions which can then be evaluated into a callable function, or the compilers for the respective targets can be invoked to directly give back the function handle.","category":"page"},{"location":"manual/build_function/#build_function","page":"Function Building and Compilation (build_function)","title":"build_function","text":"","category":"section"},{"location":"manual/build_function/","page":"Function Building and Compilation (build_function)","title":"Function Building and Compilation (build_function)","text":"build_function","category":"page"},{"location":"manual/build_function/#Symbolics.build_function","page":"Function Building and Compilation (build_function)","title":"Symbolics.build_function","text":"build_function\n\nGenerates a numerically-usable function from a Symbolics Num.\n\nbuild_function(ex, args...;\n expression = Val{true},\n target = JuliaTarget(),\n parallel=nothing,\n kwargs...)\n\nArguments:\n\nex: The Num to compile\nargs: The arguments of the function\nexpression: Whether to generate code or whether to generate the compiled form. By default, expression = Val{true}, which means that the code for the function is returned. If Val{false}, then the returned value is compiled.\n\nKeyword Arguments:\n\ntarget: The output target of the compilation process. Possible options are:\nJuliaTarget: Generates a Julia function\nCTarget: Generates a C function\nStanTarget: Generates a function for compiling with the Stan probabilistic programming language\nMATLABTarget: Generates an anonymous function for use in MATLAB and Octave environments\nparallel: The kind of parallelism to use in the generated function. Defaults to SerialForm(), i.e. no parallelism, if ex is a single expression or an array containing <= 1500 non-zero expressions. If ex is an array of > 1500 non-zero expressions, then ShardedForm(80, 4) is used. See below for more on ShardedForm. Note that the parallel forms are not exported and thus need to be chosen like Symbolics.SerialForm(). The choices are:\nSerialForm(): Serial execution.\nShardedForm(cutoff, ncalls): splits the output function into sub-functions which contain at most cutoff number of output rhss. These sub-functions are called by the top-level function that buildfunction returns. This helps in reducing the compile time of the generated function.\nMultithreadedForm(): Multithreaded execution with a static split, evenly splitting the number of expressions per thread.\nfname: Used by some targets for the name of the function in the target space.\n\nNote that not all build targets support the full compilation interface. Check the individual target documentation for details.\n\n\n\n\n\n","category":"function"},{"location":"manual/build_function/#Target-Specific-Definitions","page":"Function Building and Compilation (build_function)","title":"Target-Specific Definitions","text":"","category":"section"},{"location":"manual/build_function/","page":"Function Building and Compilation (build_function)","title":"Function Building and Compilation (build_function)","text":"Symbolics._build_function(target::Symbolics.JuliaTarget,rhss::AbstractArray,args...;kwargs...)\nSymbolics._build_function(target::Symbolics.CTarget,eqs::Array{<:Equation},args...;kwargs...)\nSymbolics._build_function(target::Symbolics.StanTarget,eqs::Array{<:Equation}, vs, ps, iv;kwargs...)\nSymbolics._build_function(target::Symbolics.MATLABTarget,eqs::Array{<:Equation},args...;kwargs...)","category":"page"},{"location":"manual/build_function/#Symbolics._build_function-Tuple{Symbolics.JuliaTarget, AbstractArray, Vararg{Any}}","page":"Function Building and Compilation (build_function)","title":"Symbolics._build_function","text":"_build_function(target::JuliaTarget, rhss::AbstractArray, args...;\n conv=toexpr,\n expression = Val{true},\n expression_module = @__MODULE__(),\n checkbounds = false,\n postprocess_fbody=ex -> ex,\n linenumbers = false,\n outputidxs=nothing,\n skipzeros = false,\n force_SA = false,\n wrap_code = (nothing, nothing),\n fillzeros = skipzeros && !(rhss isa SparseMatrixCSC),\n states = LazyState(),\n iip_config = (true, true),\n parallel=nothing, cse = false, kwargs...)\n\nBuild function target: JuliaTarget\n\nfunction _build_function(target::JuliaTarget, rhss, args...;\n conv = toexpr,\n expression = Val{true},\n checkbounds = false,\n linenumbers = false,\n headerfun = addheader, outputidxs=nothing,\n convert_oop = true, force_SA = false,\n skipzeros = outputidxs===nothing,\n fillzeros = skipzeros && !(typeof(rhss)<:SparseMatrixCSC),\n parallel=SerialForm(), kwargs...)\n\nGenerates a Julia function which can then be utilized for further evaluations. If expression=Val{false}, the return is a Julia function which utilizes RuntimeGeneratedFunctions.jl to be free of world-age issues.\n\nIf the rhss is a scalar, the generated function is a function with a scalar output. Otherwise, if it's an AbstractArray, the output is two functions, one for out-of-place AbstractArray output and a second which is a mutating function. The outputted functions match the given argument order, i.e., f(u,p,args...) for the out-of-place and scalar functions and f!(du,u,p,args..) for the in-place version.\n\nSpecial Keyword Arguments:\n\nparallel: The kind of parallelism to use in the generated function. Defaults to SerialForm(), i.e. no parallelism. Note that the parallel forms are not exported and thus need to be chosen like Symbolics.SerialForm(). The choices are:\nSerialForm(): Serial execution.\nShardedForm(cutoff, ncalls): splits the output function into sub-functions which contain at most cutoff number of output rhss. These sub-functions are called by the top-level function that buildfunction returns.\nMultithreadedForm(): Multithreaded execution with a static split, evenly splitting the number of expressions per thread.\nconv: The conversion function of symbolic types to Expr. By default, this uses the toexpr function.\ncheckbounds: For whether to enable bounds checking inside the generated function. Defaults to false, meaning that @inbounds is applied.\nlinenumbers: Determines whether the generated function expression retains the line numbers. Defaults to true.\nconvert_oop: Determines whether the OOP version should try to convert the output to match the type of the first input. This is useful for cases like LabelledArrays or other array types that carry extra information. Defaults to true.\nforce_SA: Forces the output of the OOP version to be a StaticArray. Defaults to false, and outputs a static array when the first argument is a static array.\nskipzeros: Whether to skip filling zeros in the in-place version if the filling function is 0.\nfillzeros: Whether to perform fill(out,0) before the calculations to ensure safety with skipzeros.\n\n\n\n\n\n","category":"method"},{"location":"manual/build_function/#Symbolics._build_function-Tuple{Symbolics.CTarget, Array{<:Equation}, Vararg{Any}}","page":"Function Building and Compilation (build_function)","title":"Symbolics._build_function","text":"Build function target: CTarget\n\nfunction _build_function(target::CTarget, eqs::Array{<:Equation}, args...;\n conv = toexpr, expression = Val{true},\n fname = :diffeqf,\n lhsname=:du,rhsnames=[Symbol(\"RHS$i\") for i in 1:length(args)],\n libpath=tempname(),compiler=:gcc)\n\nThis builds an in-place C function. Only works on arrays of equations. If expression == Val{false}, then this builds a function in C, compiles it, and returns a lambda to that compiled function. These special keyword arguments control the compilation:\n\nlibpath: the path to store the binary. Defaults to a temporary path.\ncompiler: which C compiler to use. Defaults to :gcc, which is currently the only available option.\n\n\n\n\n\n","category":"method"},{"location":"manual/build_function/#Symbolics._build_function-Tuple{Symbolics.StanTarget, Array{<:Equation}, Any, Any, Any}","page":"Function Building and Compilation (build_function)","title":"Symbolics._build_function","text":"Build function target: StanTarget\n\nfunction _build_function(target::StanTarget, eqs::Array{<:Equation}, vs, ps, iv;\n conv = toexpr, expression = Val{true},\n fname = :diffeqf, lhsname=:internal_var___du,\n rhsnames=[:internal_var___u,:internal_var___p,:internal_var___t])\n\nThis builds an in-place Stan function compatible with the Stan differential equation solvers. Unlike other build targets, this one requires (vs, ps, iv) as the function arguments. Only allowed on arrays of equations.\n\n\n\n\n\n","category":"method"},{"location":"manual/build_function/#Symbolics._build_function-Tuple{Symbolics.MATLABTarget, Array{<:Equation}, Vararg{Any}}","page":"Function Building and Compilation (build_function)","title":"Symbolics._build_function","text":"Build function target: MATLABTarget\n\nfunction _build_function(target::MATLABTarget, eqs::Array{<:Equation}, args...;\n conv = toexpr, expression = Val{true},\n lhsname=:internal_var___du,\n rhsnames=[:internal_var___u,:internal_var___p,:internal_var___t])\n\nThis builds an out of place anonymous function @(t,rhsnames[1]) to be used in MATLAB. Compatible with the MATLAB differential equation solvers. Only allowed on expressions, and arrays of expressions.\n\n\n\n\n\n","category":"method"},{"location":"manual/build_function/#Limitations","page":"Function Building and Compilation (build_function)","title":"Limitations","text":"","category":"section"},{"location":"manual/build_function/","page":"Function Building and Compilation (build_function)","title":"Function Building and Compilation (build_function)","text":"build_function ","category":"page"},{"location":"manual/arrays/#symbolic_arrays","page":"Symbolic Arrays","title":"Symbolic Arrays","text":"","category":"section"},{"location":"manual/arrays/#Symbolic-Arrays-vs-Arrays-of-Symbolic-Expressions","page":"Symbolic Arrays","title":"Symbolic Arrays vs Arrays of Symbolic Expressions","text":"","category":"section"},{"location":"manual/arrays/","page":"Symbolic Arrays","title":"Symbolic Arrays","text":"Symbolics.jl contains two forms for handling symbolic arrays:","category":"page"},{"location":"manual/arrays/","page":"Symbolic Arrays","title":"Symbolic Arrays","text":"Arrays of symbolic expressions: these are Julia arrays with Symbolics.jl objects in them.\nSymbolic Arrays: these are symbolic (O(1)) representations of arrays.","category":"page"},{"location":"manual/arrays/","page":"Symbolic Arrays","title":"Symbolic Arrays","text":"Arrays of symbolic expressions are simply Symbolics.jl objects put into Julia arrays. For example:","category":"page"},{"location":"manual/arrays/","page":"Symbolic Arrays","title":"Symbolic Arrays","text":"using Symbolics\n@variables x y\nu = [x,y]","category":"page"},{"location":"manual/arrays/","page":"Symbolic Arrays","title":"Symbolic Arrays","text":"is a vector of two symbolic variables. As shorthand,","category":"page"},{"location":"manual/arrays/","page":"Symbolic Arrays","title":"Symbolic Arrays","text":"u2 = Symbolics.variables(:x, 1:3, 3:6)","category":"page"},{"location":"manual/arrays/","page":"Symbolic Arrays","title":"Symbolic Arrays","text":"creates a Julia matrix of symbolic variables. Indexing u or u2 gives symbolic values which act as a normal scalar symbolic value. This form these uses Julia's array functionality and performs symbolic operations on the scalar values.","category":"page"},{"location":"manual/arrays/","page":"Symbolic Arrays","title":"Symbolic Arrays","text":"On the otherhand, Julia's symbolic array form is an O(1) representation of the whole array.","category":"page"},{"location":"manual/arrays/","page":"Symbolic Arrays","title":"Symbolic Arrays","text":"@variables A[1:5, 1:3]","category":"page"},{"location":"manual/arrays/","page":"Symbolic Arrays","title":"Symbolic Arrays","text":"When using this form, A[1,1] is not a symbolic variable but a symbolic expression for indexing the variable A. This representation holds linear algebra expressions in a non-expanded form. For example:","category":"page"},{"location":"manual/arrays/","page":"Symbolic Arrays","title":"Symbolic Arrays","text":"@variables B[1:3, 1:3]\nA * B","category":"page"},{"location":"manual/arrays/","page":"Symbolic Arrays","title":"Symbolic Arrays","text":"in comparison to:","category":"page"},{"location":"manual/arrays/","page":"Symbolic Arrays","title":"Symbolic Arrays","text":"a = Symbolics.variables(:a, 1:5, 1:3)\nb = Symbolics.variables(:b, 1:3, 1:3)\na * b","category":"page"},{"location":"manual/arrays/","page":"Symbolic Arrays","title":"Symbolic Arrays","text":"This makes the symbolic array form much more efficient, but requires that the expressions uses things with registered symbolic array functions which currently has much lower coverage. Also, there are many fallbacks for which arrays of symbolics which makes this approach more accessible but with larger expressions.","category":"page"},{"location":"manual/arrays/","page":"Symbolic Arrays","title":"Symbolic Arrays","text":"We recommend defaulting to arrays of symbolics unless you need the expression symplifications of the symbolic array approach.","category":"page"},{"location":"manual/arrays/#Using-Symbolic-Arrays","page":"Symbolic Arrays","title":"Using Symbolic Arrays","text":"","category":"section"},{"location":"manual/arrays/","page":"Symbolic Arrays","title":"Symbolic Arrays","text":"Symbolic array-valued expressions (symbolic arrays) are supported by Symbolics. Symbolic array expressions propagate useful metadata that depends on input arrays: array dimension, element type and shape.","category":"page"},{"location":"manual/arrays/","page":"Symbolic Arrays","title":"Symbolic Arrays","text":"You can create a symbolic array variable with the following syntax:","category":"page"},{"location":"manual/arrays/","page":"Symbolic Arrays","title":"Symbolic Arrays","text":"using Symbolics\n@variables A[1:5, 1:3] b[1:3]","category":"page"},{"location":"manual/arrays/","page":"Symbolic Arrays","title":"Symbolic Arrays","text":"Here, A is a symbolic matrix of size (5, 3) and b is a symbolic vector of length 3.","category":"page"},{"location":"manual/arrays/","page":"Symbolic Arrays","title":"Symbolic Arrays","text":"size(A)","category":"page"},{"location":"manual/arrays/","page":"Symbolic Arrays","title":"Symbolic Arrays","text":"size(b)","category":"page"},{"location":"manual/arrays/","page":"Symbolic Arrays","title":"Symbolic Arrays","text":"ndims(A)","category":"page"},{"location":"manual/arrays/","page":"Symbolic Arrays","title":"Symbolic Arrays","text":"ndims(b)","category":"page"},{"location":"manual/arrays/","page":"Symbolic Arrays","title":"Symbolic Arrays","text":"eltype(A)","category":"page"},{"location":"manual/arrays/","page":"Symbolic Arrays","title":"Symbolic Arrays","text":"eltype(b)","category":"page"},{"location":"manual/arrays/#Array-operations","page":"Symbolic Arrays","title":"Array operations","text":"","category":"section"},{"location":"manual/arrays/","page":"Symbolic Arrays","title":"Symbolic Arrays","text":"Operations on symbolic arrays return symbolic array expressions:","category":"page"},{"location":"manual/arrays/","page":"Symbolic Arrays","title":"Symbolic Arrays","text":"c = A * b","category":"page"},{"location":"manual/arrays/","page":"Symbolic Arrays","title":"Symbolic Arrays","text":"size(c)","category":"page"},{"location":"manual/arrays/","page":"Symbolic Arrays","title":"Symbolic Arrays","text":"eltype(c)","category":"page"},{"location":"manual/arrays/","page":"Symbolic Arrays","title":"Symbolic Arrays","text":"Adjoints, matrix-matrix, and matrix-vector multiplications are supported. Dot product returns a scalar-valued expression:","category":"page"},{"location":"manual/arrays/","page":"Symbolic Arrays","title":"Symbolic Arrays","text":"b'b","category":"page"},{"location":"manual/arrays/","page":"Symbolic Arrays","title":"Symbolic Arrays","text":"size(b'b)","category":"page"},{"location":"manual/arrays/","page":"Symbolic Arrays","title":"Symbolic Arrays","text":"Outer product returns a matrix:","category":"page"},{"location":"manual/arrays/","page":"Symbolic Arrays","title":"Symbolic Arrays","text":"b * b'","category":"page"},{"location":"manual/arrays/","page":"Symbolic Arrays","title":"Symbolic Arrays","text":"size(b*b')","category":"page"},{"location":"manual/arrays/#Broadcast,-map-and-reduce","page":"Symbolic Arrays","title":"Broadcast, map and reduce","text":"","category":"section"},{"location":"manual/arrays/","page":"Symbolic Arrays","title":"Symbolic Arrays","text":"A .* b'","category":"page"},{"location":"manual/arrays/","page":"Symbolic Arrays","title":"Symbolic Arrays","text":"map(asin, (A*b))","category":"page"},{"location":"manual/arrays/","page":"Symbolic Arrays","title":"Symbolic Arrays","text":"#sum(A) #latexify not working","category":"page"},{"location":"manual/arrays/","page":"Symbolic Arrays","title":"Symbolic Arrays","text":"typeof(sum(A))","category":"page"},{"location":"manual/arrays/","page":"Symbolic Arrays","title":"Symbolic Arrays","text":"typeof(sum(A, dims=2))","category":"page"},{"location":"manual/arrays/#Indexing-and-delayed-computation","page":"Symbolic Arrays","title":"Indexing and delayed computation","text":"","category":"section"},{"location":"manual/arrays/","page":"Symbolic Arrays","title":"Symbolic Arrays","text":"Indexing array expressions is fairly flexible in Symbolics. Let's go through all the possible ways to index arrays.","category":"page"},{"location":"manual/arrays/#Scalar-indexing-and-scalarization","page":"Symbolic Arrays","title":"Scalar indexing and scalarization","text":"","category":"section"},{"location":"manual/arrays/","page":"Symbolic Arrays","title":"Symbolic Arrays","text":"AAt = A*A'","category":"page"},{"location":"manual/arrays/","page":"Symbolic Arrays","title":"Symbolic Arrays","text":"AAt[2,3]","category":"page"},{"location":"manual/arrays/","page":"Symbolic Arrays","title":"Symbolic Arrays","text":"Here we indexed for the element (2,3), but we got back a symbolic indexing expression. You may want to force the element to be computed in terms of the elements of A. This can be done, using the scalarize function.","category":"page"},{"location":"manual/arrays/","page":"Symbolic Arrays","title":"Symbolic Arrays","text":"Symbolics.scalarize(AAt[2,3])","category":"page"},{"location":"manual/arrays/","page":"Symbolic Arrays","title":"Symbolic Arrays","text":"@syms i::Int j::Int\nSymbolics.scalarize(AAt[i,j])","category":"page"},{"location":"manual/arrays/","page":"Symbolic Arrays","title":"Symbolic Arrays","text":"In general, any scalar expression which is derived from array expressions can be scalarized.","category":"page"},{"location":"manual/arrays/","page":"Symbolic Arrays","title":"Symbolic Arrays","text":"#sum(A[:,1]) + sum(A[2,:])#latexify not working","category":"page"},{"location":"manual/arrays/","page":"Symbolic Arrays","title":"Symbolic Arrays","text":"Symbolics.scalarize(sum(A[:,1]) + sum(A[2,:]))","category":"page"},{"location":"manual/limits/#Symbolic-Limits","page":"Symbolic Limits","title":"Symbolic Limits","text":"","category":"section"},{"location":"manual/limits/","page":"Symbolic Limits","title":"Symbolic Limits","text":"Experimental symbolic limit support is provided by the limit function, documented below. See SymbolicLimits.jl for more information and implementation details.","category":"page"},{"location":"manual/limits/","page":"Symbolic Limits","title":"Symbolic Limits","text":"limit","category":"page"},{"location":"manual/limits/#Symbolics.limit","page":"Symbolic Limits","title":"Symbolics.limit","text":"limit(expr, var, h[, side::Symbol])\n\nCompute the limit of expr as var approaches h.\n\nside indicates the direction from which var approaches h. It may be one of :left, :right, or :both. If side is :both and the two sides do not align, an error is thrown. Side defaults to :both for finite h, :left for h = Inf, and :right for h = -Inf.\n\nexpr must be composed of log, exp, constants, and the rational operators +, -, *, and /. This limitation may eventually be relaxed.\n\nwarning: Warning\nBecause symbolic limit computation is undecidable, this function necessarily employs heuristics and may occasionally return wrong answers. Nevertheless, please report wrong answers as issues as we aim to have heuristics that produce correct answers in all practical cases.\n\n\n\n\n\n","category":"function"},{"location":"manual/functions/#function_registration","page":"Function Registration and Tracing","title":"Function Registration and Tracing","text":"","category":"section"},{"location":"manual/functions/","page":"Function Registration and Tracing","title":"Function Registration and Tracing","text":"Function registration is the ability to define new nodes in the symbolic graph. This is useful because symbolic computing is declarative, i.e. symbolic computations express what should be computed, not how it should be computed. However, at some level someone must describe how a given operation is computed. These are the primitive functions, and a symbolic expression is made up of primitive functions.","category":"page"},{"location":"manual/functions/","page":"Function Registration and Tracing","title":"Function Registration and Tracing","text":"Symbolics.jl comes pre-registered with a large set of standard mathematical functions, like * and sin to special functions like erf, and even deeper operations like DataInterpolations.jl's AbstractInterpolation. However, in many cases you may need to add your own function, i.e. you may want to give an imperative code and use this to define a new symbolic code. Symbolics.jl calls the declaration of new declarative primitives from an imperative function definition registration. This page describes both the registration process and its companion process, tracing, for interacting with code written in Julia.","category":"page"},{"location":"manual/functions/#Direct-Tracing","page":"Function Registration and Tracing","title":"Direct Tracing","text":"","category":"section"},{"location":"manual/functions/","page":"Function Registration and Tracing","title":"Function Registration and Tracing","text":"Because Symbolics expressions respect Julia semantics, one way to generate symbolic expressions is to simply place Symbolics variables as inputs into existing Julia code. For example, the following uses the standard Julia function for the Lorenz equations to generate the symbolic expression for the Lorenz equations:","category":"page"},{"location":"manual/functions/","page":"Function Registration and Tracing","title":"Function Registration and Tracing","text":"using Symbolics\nfunction lorenz(du,u,p,t)\n du[1] = 10.0(u[2]-u[1])\n du[2] = u[1]*(28.0-u[3]) - u[2]\n du[3] = u[1]*u[2] - (8/3)*u[3]\nend\n@variables t p[1:3] u(t)[1:3]\ndu = Array{Any}(undef, 3)\nlorenz(du,u,p,t)\ndu","category":"page"},{"location":"manual/functions/","page":"Function Registration and Tracing","title":"Function Registration and Tracing","text":"Or similarly:","category":"page"},{"location":"manual/functions/","page":"Function Registration and Tracing","title":"Function Registration and Tracing","text":"@variables t x(t) y(t) z(t) dx(t) dy(t) dz(t) σ ρ β\ndu = [dx,dy,dz]\nu = [x,y,z]\np = [σ,ρ,β]\nlorenz(du,u,p,t)\ndu","category":"page"},{"location":"manual/functions/","page":"Function Registration and Tracing","title":"Function Registration and Tracing","text":"Note that what has been done here is that the imperative Julia code for the function lorenz has been transformed into a declarative symbolic graph. Importantly, the code of lorenz is transformed into an expression consisting only of primitive registered functions, things like * and -, which come pre-registered with Symbolics.jl This then allows for symbolic manipulation of the expressions, allowing things like simplification and operation reordering done on its generated expressions. ","category":"page"},{"location":"manual/functions/#Utility-and-Scope-of-Tracing","page":"Function Registration and Tracing","title":"Utility and Scope of Tracing","text":"","category":"section"},{"location":"manual/functions/","page":"Function Registration and Tracing","title":"Function Registration and Tracing","text":"This notably describes one limitation of tracing: tracing only works if the expression being traced is composed of already registered functions. If unregistered functions, such as calls to C code, are used, then the tracing process will error.","category":"page"},{"location":"manual/functions/","page":"Function Registration and Tracing","title":"Function Registration and Tracing","text":"However, we note that symbolic tracing by definition does not guarantee that the exact choices. The symbolic expressions may re-distribute the arithmetic, simplify out expressions, or do other modifications. Thus if this function is function is sensitive to numerical details in its calculation, one would not want to trace the function and thus would instead register it as a new primitive function.","category":"page"},{"location":"manual/functions/","page":"Function Registration and Tracing","title":"Function Registration and Tracing","text":"For the symbolic system to be as powerful in its manipulations as possible, it is recommended that the registration of functions be minimized to the simplest possible set, and thus registration should only be used when necessary. This is because any code within a registered function is treated as a blackbox imperative code that cannot be manipulated, thus decreasing the potential for simplifications.","category":"page"},{"location":"manual/functions/#Registering-Functions","page":"Function Registration and Tracing","title":"Registering Functions","text":"","category":"section"},{"location":"manual/functions/","page":"Function Registration and Tracing","title":"Function Registration and Tracing","text":"The Symbolics graph only allows registered Julia functions within its type. All other functions are automatically traced down to registered functions. By default, Symbolics.jl pre-registers the common functions utilized in SymbolicUtils.jl and pre-defines their derivatives. However, the user can utilize the @register_symbolic macro to add their function to allowed functions of the computation graph.","category":"page"},{"location":"manual/functions/","page":"Function Registration and Tracing","title":"Function Registration and Tracing","text":"Additionally, @register_array_symbolic can be used to define array functions. For size propagation it's required that a computation of how the sizes are computed is also supplied.","category":"page"},{"location":"manual/functions/#Defining-Derivatives-of-Registered-Functions","page":"Function Registration and Tracing","title":"Defining Derivatives of Registered Functions","text":"","category":"section"},{"location":"manual/functions/","page":"Function Registration and Tracing","title":"Function Registration and Tracing","text":"In order for symbolic differentiation to work, an overload of Symbolics.derivative is required. The syntax is derivative(typeof(f), args::NTuple{i,Any}, ::Val{j}) where i is the number of arguments to the function and j is which argument is being differentiated. So for example:","category":"page"},{"location":"manual/functions/","page":"Function Registration and Tracing","title":"Function Registration and Tracing","text":"function derivative(::typeof(min), args::NTuple{2,Any}, ::Val{1})\n x, y = args\n IfElse.ifelse(x < y, one(x), zero(x))\nend","category":"page"},{"location":"manual/functions/","page":"Function Registration and Tracing","title":"Function Registration and Tracing","text":"is the partial derivative of the Julia min(x,y) function with respect to x.","category":"page"},{"location":"manual/functions/","page":"Function Registration and Tracing","title":"Function Registration and Tracing","text":"note: Note\nDownstream symbolic derivative functionality only work if every partial derivative that is required in the derivative expression is defined. Note that you only need to define the partial derivatives which are actually computed.","category":"page"},{"location":"manual/functions/#Registration-of-Array-Functions","page":"Function Registration and Tracing","title":"Registration of Array Functions","text":"","category":"section"},{"location":"manual/functions/","page":"Function Registration and Tracing","title":"Function Registration and Tracing","text":"Similar to scalar functions, array functions can be registered to define new primitives for functions which either take in or return arrays. This is done by using the @register_array_symbolic macro. It acts similarly to the scalar function registration but requires a calculation of the input and output sizes. For example, let's assume we wanted to have a function that computes the solution to Ax = b, i.e. a linear solve, using an SVD factorization. In Julia, the code for this would be svdsolve(A,b) = svd(A)\\b. We would create this function as follows:","category":"page"},{"location":"manual/functions/","page":"Function Registration and Tracing","title":"Function Registration and Tracing","text":"using LinearAlgebra, Symbolics\n\nsvdsolve(A, b) = svd(A)\\b\n@register_array_symbolic svdsolve(A::AbstractMatrix, b::AbstractVector) begin\n size = size(b)\n eltype = promote_type(eltype(A), eltype(b))\nend","category":"page"},{"location":"manual/functions/","page":"Function Registration and Tracing","title":"Function Registration and Tracing","text":"Now using the function svdsolve with symbolic array variables will be kept lazy:","category":"page"},{"location":"manual/functions/","page":"Function Registration and Tracing","title":"Function Registration and Tracing","text":"@variables A[1:3, 1:3] b[1:3]\nsvdsolve(A,b)","category":"page"},{"location":"manual/functions/","page":"Function Registration and Tracing","title":"Function Registration and Tracing","text":"Note that at this time array derivatives cannot be defined.","category":"page"},{"location":"manual/functions/#Registration-API","page":"Function Registration and Tracing","title":"Registration API","text":"","category":"section"},{"location":"manual/functions/","page":"Function Registration and Tracing","title":"Function Registration and Tracing","text":"@register_symbolic\n@register_array_symbolic","category":"page"},{"location":"manual/functions/#Symbolics.@register_symbolic","page":"Function Registration and Tracing","title":"Symbolics.@register_symbolic","text":"@register_symbolic(expr, define_promotion = true, Ts = [Real])\n\nOverload appropriate methods so that Symbolics can stop tracing into the registered function. If define_promotion is true, then a promotion method in the form of\n\nSymbolicUtils.promote_symtype(::typeof(f_registered), args...) = Real # or the annotated return type\n\nis defined for the register function. Note that when defining multiple register overloads for one function, all the rest of the registers must set define_promotion to false except for the first one, to avoid method overwriting.\n\nExamples\n\n@register_symbolic foo(x, y)\n@register_symbolic foo(x, y::Bool) false # do not overload a duplicate promotion rule\n@register_symbolic goo(x, y::Int) # `y` is not overloaded to take symbolic objects\n@register_symbolic hoo(x, y)::Int # `hoo` returns `Int`\n\nSee @register_array_symbolic to register functions which return arrays.\n\n\n\n\n\n","category":"macro"},{"location":"manual/functions/#Symbolics.@register_array_symbolic","page":"Function Registration and Tracing","title":"Symbolics.@register_array_symbolic","text":"@register_array_symbolic(expr, define_promotion = true)\n\nExample:\n\n# Let's say vandermonde takes an n-vector and returns an n x n matrix\n@register_array_symbolic vandermonde(x::AbstractVector) begin\n size=(length(x), length(x))\n eltype=eltype(x) # optional, will default to the promoted eltypes of x\nend\n\nYou can also register calls on callable structs:\n\n@register_array_symbolic (c::Conv)(x::AbstractMatrix) begin\n size=size(x) .- size(c.kernel) .+ 1\n eltype=promote_type(eltype(x), eltype(c))\nend\n\nIf define_promotion = true then a promotion method in the form of\n\nSymbolicUtils.promote_symtype(::typeof(f_registered), args...) = # inferred or annotated return type\n\nis defined for the register function. Note that when defining multiple register overloads for one function, all the rest of the registers must set define_promotion to false except for the first one, to avoid method overwriting.\n\n\n\n\n\n","category":"macro"},{"location":"manual/functions/#Direct-Registration-API-(Advanced,-Experimental)","page":"Function Registration and Tracing","title":"Direct Registration API (Advanced, Experimental)","text":"","category":"section"},{"location":"manual/functions/","page":"Function Registration and Tracing","title":"Function Registration and Tracing","text":"warn: Warn\nThis is a lower level API which is not as stable as the macro APIs.","category":"page"},{"location":"manual/functions/","page":"Function Registration and Tracing","title":"Function Registration and Tracing","text":"In some circumstances you may need to use the direct API in order to define registration on functions or types without using the macro. This is done by directly defining dispatches on symbolic objects.","category":"page"},{"location":"manual/functions/","page":"Function Registration and Tracing","title":"Function Registration and Tracing","text":"A good example of this is DataInterpolations.jl's interpolations object. On an interpolation by a symbolic variable, we generate the symbolic function (the term) for the interpolation function. This looks like:","category":"page"},{"location":"manual/functions/","page":"Function Registration and Tracing","title":"Function Registration and Tracing","text":"using DataInterpolations, Symbolics, SymbolicUtils\n(interp::AbstractInterpolation)(t::Num) = SymbolicUtils.term(interp, unwrap(t))","category":"page"},{"location":"manual/functions/","page":"Function Registration and Tracing","title":"Function Registration and Tracing","text":"In order for this to work, it is required that we define the symtype for the symbolic type inference. This is done via:","category":"page"},{"location":"manual/functions/","page":"Function Registration and Tracing","title":"Function Registration and Tracing","text":"SymbolicUtils.promote_symtype(t::AbstractInterpolation, args...) = Real","category":"page"},{"location":"manual/functions/","page":"Function Registration and Tracing","title":"Function Registration and Tracing","text":"Additionally a symbolic name is required:","category":"page"},{"location":"manual/functions/","page":"Function Registration and Tracing","title":"Function Registration and Tracing","text":"Base.nameof(interp::AbstractInterpolation) = :Interpolation","category":"page"},{"location":"manual/functions/","page":"Function Registration and Tracing","title":"Function Registration and Tracing","text":"The derivative is defined similarly to the macro case:","category":"page"},{"location":"manual/functions/","page":"Function Registration and Tracing","title":"Function Registration and Tracing","text":"function Symbolics.derivative(interp::AbstractInterpolation, args::NTuple{1, Any}, ::Val{1})\n Symbolics.unwrap(derivative(interp, Symbolics.wrap(args[1])))\nend","category":"page"},{"location":"getting_started/#Getting-Started-with-Symbolics.jl","page":"Getting Started with Symbolics.jl","title":"Getting Started with Symbolics.jl","text":"","category":"section"},{"location":"getting_started/","page":"Getting Started with Symbolics.jl","title":"Getting Started with Symbolics.jl","text":"Symbolics.jl is a symbolic modeling language. In this tutorial, we will walk you through the process of getting Symbolics.jl up and running, and start doing our first symbolic calculations.","category":"page"},{"location":"getting_started/#Installing-Symbolics.jl","page":"Getting Started with Symbolics.jl","title":"Installing Symbolics.jl","text":"","category":"section"},{"location":"getting_started/","page":"Getting Started with Symbolics.jl","title":"Getting Started with Symbolics.jl","text":"Installing Symbolics.jl is as simple as using the Julia package manager. This is done via the command ]add Symbolics. After precompilation is complete, do using Symbolics in the terminal (REPL) and when that command is completed, you're ready to start!","category":"page"},{"location":"getting_started/#Building-Symbolic-Expressions","page":"Getting Started with Symbolics.jl","title":"Building Symbolic Expressions","text":"","category":"section"},{"location":"getting_started/","page":"Getting Started with Symbolics.jl","title":"Getting Started with Symbolics.jl","text":"The way to define symbolic variables is via the @variables macro:","category":"page"},{"location":"getting_started/","page":"Getting Started with Symbolics.jl","title":"Getting Started with Symbolics.jl","text":"using Symbolics\n@variables x y","category":"page"},{"location":"getting_started/","page":"Getting Started with Symbolics.jl","title":"Getting Started with Symbolics.jl","text":"After defining variables as symbolic, symbolic expressions, which we call a iscall object, can be generated by utilizing Julia expressions. For example:","category":"page"},{"location":"getting_started/","page":"Getting Started with Symbolics.jl","title":"Getting Started with Symbolics.jl","text":"z = x^2 + y","category":"page"},{"location":"getting_started/","page":"Getting Started with Symbolics.jl","title":"Getting Started with Symbolics.jl","text":"Here, z is an expression tree for “square x and add y”. To make an array of symbolic expressions, simply make an array of symbolic expressions:","category":"page"},{"location":"getting_started/","page":"Getting Started with Symbolics.jl","title":"Getting Started with Symbolics.jl","text":"A = [x^2 + y 0 2x\n 0 0 2y\n y^2 + x 0 0]","category":"page"},{"location":"getting_started/","page":"Getting Started with Symbolics.jl","title":"Getting Started with Symbolics.jl","text":"Note that by default, @variables returns Sym or iscall objects wrapped in Num to make them behave like subtypes of Real. Any operation on these Num objects will return a new Num object, wrapping the result of computing symbolically on the underlying values.","category":"page"},{"location":"getting_started/","page":"Getting Started with Symbolics.jl","title":"Getting Started with Symbolics.jl","text":"If you are following this tutorial in the Julia REPL, A will not be shown with LaTeX equations. To get these equations, we can use Latexify.jl. Symbolics.jl comes with Latexify recipes, so it works automatically:","category":"page"},{"location":"getting_started/","page":"Getting Started with Symbolics.jl","title":"Getting Started with Symbolics.jl","text":"using Latexify\nlatexify(A)","category":"page"},{"location":"getting_started/","page":"Getting Started with Symbolics.jl","title":"Getting Started with Symbolics.jl","text":"Normal Julia functions work on Symbolics expressions, so if we want to create the sparse version of A we would just call sparse:","category":"page"},{"location":"getting_started/","page":"Getting Started with Symbolics.jl","title":"Getting Started with Symbolics.jl","text":"using SparseArrays\nspA = sparse(A)\nlatexify(A)","category":"page"},{"location":"getting_started/","page":"Getting Started with Symbolics.jl","title":"Getting Started with Symbolics.jl","text":"We can thus use normal Julia functions as generators for sparse expressions. For example, here we will define","category":"page"},{"location":"getting_started/","page":"Getting Started with Symbolics.jl","title":"Getting Started with Symbolics.jl","text":"function f(u)\n [u[1] - u[3], u[1]^2 - u[2], u[3] + u[2]]\nend\nf([x, y, z]) # Recall that z = x^2 + y","category":"page"},{"location":"getting_started/","page":"Getting Started with Symbolics.jl","title":"Getting Started with Symbolics.jl","text":"Or we can build an array of variables and use it to trace the function:","category":"page"},{"location":"getting_started/","page":"Getting Started with Symbolics.jl","title":"Getting Started with Symbolics.jl","text":"u = Symbolics.variables(:u, 1:5)\nf(u)","category":"page"},{"location":"getting_started/","page":"Getting Started with Symbolics.jl","title":"Getting Started with Symbolics.jl","text":"note: Note\nSymbolics.variables(:u, 1:5) creates a Julia array of symbolic variables. This uses O(n) compute and memory but is a very general representation. Symbolics.jl also has the ability to represent symbolic arrays which gives an O(1) representation but is more limited in its functionality. For more information, see the Symbolic Arrays page.","category":"page"},{"location":"getting_started/#Derivatives","page":"Getting Started with Symbolics.jl","title":"Derivatives","text":"","category":"section"},{"location":"getting_started/","page":"Getting Started with Symbolics.jl","title":"Getting Started with Symbolics.jl","text":"One common thing to compute in a symbolic system is derivatives. In Symbolics.jl, derivatives are represented lazily via operations, just like any other function. To build a differential operator, use Differential like:","category":"page"},{"location":"getting_started/","page":"Getting Started with Symbolics.jl","title":"Getting Started with Symbolics.jl","text":"@variables t\nD = Differential(t)","category":"page"},{"location":"getting_started/","page":"Getting Started with Symbolics.jl","title":"Getting Started with Symbolics.jl","text":"This is the differential operator D = fracpartialpartial t. We can compose the differential operator by *, e.g. Differential(t) * Differential(x) or Differential(t)^2. Now let's write down the derivative of some expression:","category":"page"},{"location":"getting_started/","page":"Getting Started with Symbolics.jl","title":"Getting Started with Symbolics.jl","text":"z = t + t^2\nD(z)","category":"page"},{"location":"getting_started/","page":"Getting Started with Symbolics.jl","title":"Getting Started with Symbolics.jl","text":"Notice that this hasn't computed anything yet: D is a lazy operator because it lets us symbolically represent “The derivative of z with respect to t”, which is useful for example when representing our favorite thing in the world, differential equations. However, if we want to expand the derivative operators, we'd use expand_derivatives:","category":"page"},{"location":"getting_started/","page":"Getting Started with Symbolics.jl","title":"Getting Started with Symbolics.jl","text":"expand_derivatives(D(z))","category":"page"},{"location":"getting_started/","page":"Getting Started with Symbolics.jl","title":"Getting Started with Symbolics.jl","text":"The variable, that you are taking the derivative with respect to, is accessed with:","category":"page"},{"location":"getting_started/","page":"Getting Started with Symbolics.jl","title":"Getting Started with Symbolics.jl","text":"D.x","category":"page"},{"location":"getting_started/","page":"Getting Started with Symbolics.jl","title":"Getting Started with Symbolics.jl","text":"We can also have simplified functions for multivariable calculus. For example, we can compute the Jacobian of an array of expressions like:","category":"page"},{"location":"getting_started/","page":"Getting Started with Symbolics.jl","title":"Getting Started with Symbolics.jl","text":"Symbolics.jacobian([x + x*y, x^2 + y], [x, y])","category":"page"},{"location":"getting_started/","page":"Getting Started with Symbolics.jl","title":"Getting Started with Symbolics.jl","text":"and similarly we can do Hessians, gradients, and define whatever other derivatives you want.","category":"page"},{"location":"getting_started/#Simplification-and-Substitution","page":"Getting Started with Symbolics.jl","title":"Simplification and Substitution","text":"","category":"section"},{"location":"getting_started/","page":"Getting Started with Symbolics.jl","title":"Getting Started with Symbolics.jl","text":"Symbolics interfaces with SymbolicUtils.jl to allow for simplifying symbolic expressions. This is done simply through the simplify command:","category":"page"},{"location":"getting_started/","page":"Getting Started with Symbolics.jl","title":"Getting Started with Symbolics.jl","text":"simplify(2x + 2y)","category":"page"},{"location":"getting_started/","page":"Getting Started with Symbolics.jl","title":"Getting Started with Symbolics.jl","text":"This can be applied to arrays by using Julia's broadcast mechanism:","category":"page"},{"location":"getting_started/","page":"Getting Started with Symbolics.jl","title":"Getting Started with Symbolics.jl","text":"B = simplify.([t + t^2 + t + t^2 2t + 4t\n x + y + y + 2t x^2 - x^2 + y^2])","category":"page"},{"location":"getting_started/","page":"Getting Started with Symbolics.jl","title":"Getting Started with Symbolics.jl","text":"We can then use substitute to change values of an expression around:","category":"page"},{"location":"getting_started/","page":"Getting Started with Symbolics.jl","title":"Getting Started with Symbolics.jl","text":"simplify.(substitute.(B, (Dict(x => y^2),)))","category":"page"},{"location":"getting_started/","page":"Getting Started with Symbolics.jl","title":"Getting Started with Symbolics.jl","text":"and we can use this to interactively evaluate expressions without generating and compiling Julia functions:","category":"page"},{"location":"getting_started/","page":"Getting Started with Symbolics.jl","title":"Getting Started with Symbolics.jl","text":"V = substitute.(B, (Dict(x => 2.0, y => 3.0, t => 4.0),))","category":"page"},{"location":"getting_started/","page":"Getting Started with Symbolics.jl","title":"Getting Started with Symbolics.jl","text":"Where we can reference the values via:","category":"page"},{"location":"getting_started/","page":"Getting Started with Symbolics.jl","title":"Getting Started with Symbolics.jl","text":"Symbolics.value.(V)","category":"page"},{"location":"getting_started/#Non-Interactive-Development","page":"Getting Started with Symbolics.jl","title":"Non-Interactive Development","text":"","category":"section"},{"location":"getting_started/","page":"Getting Started with Symbolics.jl","title":"Getting Started with Symbolics.jl","text":"Note that the macros are for the high-level case where you're doing symbolic computation on your own code. If you want to do symbolic computation on someone else's code, like in a macro, you may not want to do @variables x because you might want the name “x” to come from the user's code. For these cases, you can use the interpolation operator to interpolate the runtime value of x, i.e. @variables $x. Check the documentation of @variables for more details.","category":"page"},{"location":"getting_started/","page":"Getting Started with Symbolics.jl","title":"Getting Started with Symbolics.jl","text":"a, b, c = :runtime_symbol_value, :value_b, :value_c","category":"page"},{"location":"getting_started/","page":"Getting Started with Symbolics.jl","title":"Getting Started with Symbolics.jl","text":"vars = @variables t $a $b(t) $c(t)[1:3]","category":"page"},{"location":"getting_started/","page":"Getting Started with Symbolics.jl","title":"Getting Started with Symbolics.jl","text":"(t, a, b, c)","category":"page"},{"location":"getting_started/","page":"Getting Started with Symbolics.jl","title":"Getting Started with Symbolics.jl","text":"One could also use variable and variables. Read their documentation for more details.","category":"page"},{"location":"getting_started/","page":"Getting Started with Symbolics.jl","title":"Getting Started with Symbolics.jl","text":"If we need to use this to generate new Julia code, we can simply convert the output to an Expr:","category":"page"},{"location":"getting_started/","page":"Getting Started with Symbolics.jl","title":"Getting Started with Symbolics.jl","text":"Symbolics.toexpr(x + y^2)","category":"page"},{"location":"getting_started/","page":"Getting Started with Symbolics.jl","title":"Getting Started with Symbolics.jl","text":"The other way around is also possible, parsing Julia expressions into symbolic expressions","category":"page"},{"location":"getting_started/","page":"Getting Started with Symbolics.jl","title":"Getting Started with Symbolics.jl","text":"ex = [:(v ~ w)\n :(w ~ -v)]\neqs = parse_expr_to_symbolic.(ex, (Main,))\neqs_lhs = [eq.lhs for eq in eqs]\neqs_rhs = [eq.rhs for eq in eqs]\nSymbolics.jacobian(eqs_rhs, eqs_lhs)","category":"page"},{"location":"getting_started/#Syms-and-callable-Syms","page":"Getting Started with Symbolics.jl","title":"Syms and callable Syms","text":"","category":"section"},{"location":"getting_started/","page":"Getting Started with Symbolics.jl","title":"Getting Started with Symbolics.jl","text":"In the definition","category":"page"},{"location":"getting_started/","page":"Getting Started with Symbolics.jl","title":"Getting Started with Symbolics.jl","text":"@variables t x(t) y(t)","category":"page"},{"location":"getting_started/","page":"Getting Started with Symbolics.jl","title":"Getting Started with Symbolics.jl","text":"t is of type Sym{Real}, but the name x refers to an object that represents the Term x(t). The operation of this expression is itself the object Sym{FnType{Tuple{Real}, Real}}(:x). The type Sym{FnType{...}} represents a callable object. In this case specifically, it's a function that takes 1 Real argument (noted by Tuple{Real}) and returns a Real result. You can call such a callable Sym with either a number or a symbolic expression of a permissible type.","category":"page"},{"location":"getting_started/","page":"Getting Started with Symbolics.jl","title":"Getting Started with Symbolics.jl","text":"This expression also defines t as an independent variable, while x(t) and y(t) are dependent variables. This is accounted for in differentiation:","category":"page"},{"location":"getting_started/","page":"Getting Started with Symbolics.jl","title":"Getting Started with Symbolics.jl","text":"z = x + y*t\nexpand_derivatives(D(z))","category":"page"},{"location":"getting_started/","page":"Getting Started with Symbolics.jl","title":"Getting Started with Symbolics.jl","text":"Since x and y are time-dependent, they are not automatically eliminated from the expression, and thus the D(x) and D(y) operations still exist in the expanded derivatives for correctness.","category":"page"},{"location":"getting_started/","page":"Getting Started with Symbolics.jl","title":"Getting Started with Symbolics.jl","text":"We can also define unrestricted functions:","category":"page"},{"location":"getting_started/","page":"Getting Started with Symbolics.jl","title":"Getting Started with Symbolics.jl","text":"@variables g(..)","category":"page"},{"location":"getting_started/","page":"Getting Started with Symbolics.jl","title":"Getting Started with Symbolics.jl","text":"Here, g is a variable that is a function of other variables. Any time that we reference g we have to utilize it as a function:","category":"page"},{"location":"getting_started/","page":"Getting Started with Symbolics.jl","title":"Getting Started with Symbolics.jl","text":"z = g(x) + g(y)","category":"page"},{"location":"getting_started/#Registering-Functions","page":"Getting Started with Symbolics.jl","title":"Registering Functions","text":"","category":"section"},{"location":"getting_started/","page":"Getting Started with Symbolics.jl","title":"Getting Started with Symbolics.jl","text":"One of the benefits of a one-language Julia symbolic stack is that the primitives are all written in Julia, and therefore it's trivially extendable from Julia itself. By default, new functions are traced to the primitives and the symbolic expressions are written on the primitives. However, we can expand the allowed primitives by registering new functions. For example, let's register a new function h:","category":"page"},{"location":"getting_started/","page":"Getting Started with Symbolics.jl","title":"Getting Started with Symbolics.jl","text":"h(x, y) = x^2 + y\n@register_symbolic h(x, y)","category":"page"},{"location":"getting_started/","page":"Getting Started with Symbolics.jl","title":"Getting Started with Symbolics.jl","text":"When we use h(x, y), it is a symbolic expression and doesn't expand:","category":"page"},{"location":"getting_started/","page":"Getting Started with Symbolics.jl","title":"Getting Started with Symbolics.jl","text":"h(x, y) + y^2","category":"page"},{"location":"getting_started/","page":"Getting Started with Symbolics.jl","title":"Getting Started with Symbolics.jl","text":"To use it with the differentiation system, we need to register its derivatives. We would do it like this:","category":"page"},{"location":"getting_started/","page":"Getting Started with Symbolics.jl","title":"Getting Started with Symbolics.jl","text":"# Derivative w.r.t. the first argument\nSymbolics.derivative(::typeof(h), args::NTuple{2,Any}, ::Val{1}) = 2args[1]\n# Derivative w.r.t. the second argument\nSymbolics.derivative(::typeof(h), args::NTuple{2,Any}, ::Val{2}) = 1","category":"page"},{"location":"getting_started/","page":"Getting Started with Symbolics.jl","title":"Getting Started with Symbolics.jl","text":"and now it works with the rest of the system:","category":"page"},{"location":"getting_started/","page":"Getting Started with Symbolics.jl","title":"Getting Started with Symbolics.jl","text":"Symbolics.derivative(h(x, y) + y^2, x)","category":"page"},{"location":"getting_started/","page":"Getting Started with Symbolics.jl","title":"Getting Started with Symbolics.jl","text":"Symbolics.derivative(h(x, y) + y^2, y)","category":"page"},{"location":"getting_started/","page":"Getting Started with Symbolics.jl","title":"Getting Started with Symbolics.jl","text":"note: Note\n@register_symbolic only allows for scalar outputs. If full array functions are needed, then see @register_array_symbolic for registering functions of symbolic arrays.","category":"page"},{"location":"getting_started/#Building-Functions","page":"Getting Started with Symbolics.jl","title":"Building Functions","text":"","category":"section"},{"location":"getting_started/","page":"Getting Started with Symbolics.jl","title":"Getting Started with Symbolics.jl","text":"The function for building functions from symbolic expressions is the aptly-named build_function. The first argument is the symbolic expression or the array of symbolic expressions to compile, and the trailing arguments are the arguments for the function. For example:","category":"page"},{"location":"getting_started/","page":"Getting Started with Symbolics.jl","title":"Getting Started with Symbolics.jl","text":"to_compute = [x^2 + y, y^2 + x]\nf_expr = build_function(to_compute, [x, y])\nBase.remove_linenums!.(f_expr)","category":"page"},{"location":"getting_started/","page":"Getting Started with Symbolics.jl","title":"Getting Started with Symbolics.jl","text":"gives back two codes. The first is a function f([x, y]) that computes and builds an output vector [x^2 + y, y^2 + x]. Because this tool was made to be used by all the cool kids writing fast Julia codes, it is specialized to Julia and supports features like StaticArrays. For example:","category":"page"},{"location":"getting_started/","page":"Getting Started with Symbolics.jl","title":"Getting Started with Symbolics.jl","text":"using StaticArrays\nmyf = eval(f_expr[1])\nmyf(SA[2.0, 3.0])","category":"page"},{"location":"getting_started/","page":"Getting Started with Symbolics.jl","title":"Getting Started with Symbolics.jl","text":"The second function is an in-place non-allocating mutating function which mutates its first value. Thus, we'd use it like the following:","category":"page"},{"location":"getting_started/","page":"Getting Started with Symbolics.jl","title":"Getting Started with Symbolics.jl","text":"myf! = eval(f_expr[2])\nout = zeros(2)\nmyf!(out, [2.0, 3.0])\nout","category":"page"},{"location":"getting_started/","page":"Getting Started with Symbolics.jl","title":"Getting Started with Symbolics.jl","text":"To save the symbolic calculations for later, we can take this expression and save it out to a file:","category":"page"},{"location":"getting_started/","page":"Getting Started with Symbolics.jl","title":"Getting Started with Symbolics.jl","text":"write(\"function.jl\", string(f_expr[2]))","category":"page"},{"location":"getting_started/","page":"Getting Started with Symbolics.jl","title":"Getting Started with Symbolics.jl","text":"Note that if we need to avoid eval, for example to avoid world-age issues, one could do expression = Val{false}:","category":"page"},{"location":"getting_started/","page":"Getting Started with Symbolics.jl","title":"Getting Started with Symbolics.jl","text":"Base.remove_linenums!(build_function(to_compute, [x, y], expression=Val{false})[1])","category":"page"},{"location":"getting_started/","page":"Getting Started with Symbolics.jl","title":"Getting Started with Symbolics.jl","text":"which will use RuntimeGeneratedFunctions.jl to build Julia functions which avoid world-age issues.","category":"page"},{"location":"getting_started/#Building-Non-Allocating-Parallel-Functions-for-Sparse-Matrices","page":"Getting Started with Symbolics.jl","title":"Building Non-Allocating Parallel Functions for Sparse Matrices","text":"","category":"section"},{"location":"getting_started/","page":"Getting Started with Symbolics.jl","title":"Getting Started with Symbolics.jl","text":"Now let's show off a little bit. build_function is kind of like if lambdify ate its spinach. To show this, let's build a non-allocating function that fills sparse matrices in a multithreaded manner. To do this, we just have to represent the operation that we're turning into a function via a sparse matrix. For example:","category":"page"},{"location":"getting_started/","page":"Getting Started with Symbolics.jl","title":"Getting Started with Symbolics.jl","text":"using LinearAlgebra\nN = 8\nA = sparse(Tridiagonal([x^i for i in 1:N-1], [x^i * y^(8-i) for i in 1:N], [y^i for i in 1:N-1]))\nshow(A)","category":"page"},{"location":"getting_started/","page":"Getting Started with Symbolics.jl","title":"Getting Started with Symbolics.jl","text":"Now we call build_function:","category":"page"},{"location":"getting_started/","page":"Getting Started with Symbolics.jl","title":"Getting Started with Symbolics.jl","text":"Base.remove_linenums!(build_function(A,[x,y],parallel=Symbolics.MultithreadedForm())[2])","category":"page"},{"location":"getting_started/","page":"Getting Started with Symbolics.jl","title":"Getting Started with Symbolics.jl","text":"Let's unpack what that's doing. It's using A.nzval to linearly index through the sparse matrix, avoiding the A[i,j] form because that is a more expensive way to index a sparse matrix if you know exactly the order that the data is stored. Then, it's splitting up the calculation into Julia threads, so they can be operated on in parallel. It synchronizes after spawning all the tasks, so the computation is ensured to be complete before moving on. And it does this with all in-place operations to the original matrix instead of generating arrays. This is the fanciest way you could fill a sparse matrix, and Symbolics makes this dead simple.","category":"page"},{"location":"getting_started/","page":"Getting Started with Symbolics.jl","title":"Getting Started with Symbolics.jl","text":"(Note: this example may be slower with multithreading due to the thread spawning overhead, but the full version was not included in the documentation for brevity. It will be the faster version if N is sufficiently large!)","category":"page"},{"location":"getting_started/","page":"Getting Started with Symbolics.jl","title":"Getting Started with Symbolics.jl","text":"Importantly, when using the in-place functions generated by build_function, it is important that the mutating argument matches the sparse structure of the original function, as demonstrated below.","category":"page"},{"location":"getting_started/","page":"Getting Started with Symbolics.jl","title":"Getting Started with Symbolics.jl","text":"using Symbolics, SparseArrays, LinearAlgebra\n\nN = 10\n_S = sprand(N, N, 0.1)\n_Q = sprand(N, N, 0.1)\n\nF(z) = [ # some complicated sparse amenable function\n _S * z\n _Q * z.^2\n]\n\nSymbolics.@variables z[1:N]\n\nsj = Symbolics.sparsejacobian(F(z), z) # sparse jacobian\n\nf_expr = build_function(sj, z)\n\nrows, cols, _ = findnz(sj)\nout = sparse(rows, cols, zeros(length(cols)), size(sj)...) # pre-allocate, and correct structure\nmyf = eval(last(f_expr))\nmyf(out, rand(N)) # note that out matches the sparsity structure of sj\nout","category":"page"},{"location":"manual/expression_manipulation/#Expression-Manipulation","page":"Expression Manipulation","title":"Expression Manipulation","text":"","category":"section"},{"location":"manual/expression_manipulation/","page":"Expression Manipulation","title":"Expression Manipulation","text":"Symbolics.jl provides functionality for easily manipulating expressions. Most of the functionality comes by the expression objects obeying the standard mathematical semantics. For example, if one has A a matrix of symbolic expressions wrapped in Num, then A^2 calculates the expressions for the squared matrix. It is thus encouraged to use standard Julia for performing many of the manipulation on the IR. For example, calculating the sparse form of the matrix via sparse(A) is valid, legible, and easily understandable to all Julia programmers.","category":"page"},{"location":"manual/expression_manipulation/#Functionality-Inherited-From-SymbolicUtils.jl","page":"Expression Manipulation","title":"Functionality Inherited From SymbolicUtils.jl","text":"","category":"section"},{"location":"manual/expression_manipulation/","page":"Expression Manipulation","title":"Expression Manipulation","text":"SymbolicUtils.substitute\nSymbolicUtils.simplify","category":"page"},{"location":"manual/expression_manipulation/#SymbolicUtils.substitute","page":"Expression Manipulation","title":"SymbolicUtils.substitute","text":"substitute(expr, s)\n\nPerforms the substitution on expr according to rule(s) s.\n\nExamples\n\njulia> @variables t x y z(t)\n4-element Vector{Num}:\n t\n x\n y\n z(t)\njulia> ex = x + y + sin(z)\n(x + y) + sin(z(t))\njulia> substitute(ex, Dict([x => z, sin(z) => z^2]))\n(z(t) + y) + (z(t) ^ 2)\n\n\n\n\n\n","category":"function"},{"location":"manual/expression_manipulation/#SymbolicUtils.simplify","page":"Expression Manipulation","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":"manual/expression_manipulation/","page":"Expression Manipulation","title":"Expression Manipulation","text":"Documentation for rewriter can be found here, using the @rule macro or the @acrule macro from SymbolicUtils.jl.","category":"page"},{"location":"manual/expression_manipulation/#Additional-Manipulation-Functions","page":"Expression Manipulation","title":"Additional Manipulation Functions","text":"","category":"section"},{"location":"manual/expression_manipulation/","page":"Expression Manipulation","title":"Expression Manipulation","text":"Other additional manipulation functions are given below.","category":"page"},{"location":"manual/expression_manipulation/","page":"Expression Manipulation","title":"Expression Manipulation","text":"Symbolics.get_variables\nSymbolics.tosymbol\nSymbolics.diff2term\nSymbolics.degree\nSymbolics.coeff\nSymbolics.replace\nSymbolics.occursin\nSymbolics.filterchildren\nSymbolics.fixpoint_sub\nSymbolics.fast_substitute\nSymbolics.symbolic_to_float","category":"page"},{"location":"manual/expression_manipulation/#Symbolics.get_variables","page":"Expression Manipulation","title":"Symbolics.get_variables","text":"get_variables(e, varlist = nothing; sort::Bool = false)\n\nReturn a vector of variables appearing in e, optionally restricting to variables in varlist.\n\nNote that the returned variables are not wrapped in the Num type.\n\nExamples\n\njulia> @variables t x y z(t);\n\njulia> Symbolics.get_variables(x + y + sin(z); sort = true)\n3-element Vector{SymbolicUtils.BasicSymbolic}:\n x\n y\n z(t)\n\njulia> Symbolics.get_variables(x - y; sort = true)\n2-element Vector{SymbolicUtils.BasicSymbolic}:\n x\n y\n\n\n\n\n\n","category":"function"},{"location":"manual/expression_manipulation/#Symbolics.tosymbol","page":"Expression Manipulation","title":"Symbolics.tosymbol","text":"tosymbol(x::Union{Num,Symbolic}; states=nothing, escape=true) -> Symbol\n\nConvert x to a symbol. states are the states of a system, and escape means if the target has escapes like val\"y(t)\". If escape is false, then it will only output y instead of y(t).\n\nExamples\n\njulia> @variables t z(t)\n2-element Vector{Num}:\n t\n z(t)\n\njulia> Symbolics.tosymbol(z)\nSymbol(\"z(t)\")\n\njulia> Symbolics.tosymbol(z; escape=false)\n:z\n\n\n\n\n\n","category":"function"},{"location":"manual/expression_manipulation/#Symbolics.diff2term","page":"Expression Manipulation","title":"Symbolics.diff2term","text":"diff2term(x, x_metadata::Dict{Datatype, Any}) -> Symbolic\n\nConvert a differential variable to a Term. Note that it only takes a Term not a Num. Any upstream metadata can be passed via x_metadata\n\njulia> @variables x t u(x, t) z(t)[1:2]; Dt = Differential(t); Dx = Differential(x);\n\njulia> Symbolics.diff2term(Symbolics.value(Dx(Dt(u))))\nuˍtx(x, t)\n\njulia> Symbolics.diff2term(Symbolics.value(Dt(z[1])))\nvar\"z(t)[1]ˍt\"\n\n\n\n\n\n","category":"function"},{"location":"manual/expression_manipulation/#Symbolics.degree","page":"Expression Manipulation","title":"Symbolics.degree","text":"degree(p, sym=nothing)\n\nExtract the degree of p with respect to sym.\n\nExamples\n\njulia> @variables x;\n\njulia> Symbolics.degree(x^0)\n0\n\njulia> Symbolics.degree(x)\n1\n\njulia> Symbolics.degree(x^2)\n2\n\n\n\n\n\n","category":"function"},{"location":"manual/expression_manipulation/#Symbolics.coeff","page":"Expression Manipulation","title":"Symbolics.coeff","text":"coeff(p, sym=nothing)\n\nExtract the coefficient of p with respect to sym. Note that p might need to be expanded and/or simplified with expand and/or simplify.\n\nExamples\n\njulia> @variables a x y;\n\njulia> Symbolics.coeff(2a, x)\n0\n\njulia> Symbolics.coeff(3x + 2y, y)\n2\n\njulia> Symbolics.coeff(x^2 + y, x^2)\n1\n\njulia> Symbolics.coeff(2*x*y + y, x*y)\n2\n\n\n\n\n\n","category":"function"},{"location":"manual/expression_manipulation/#Base.occursin","page":"Expression Manipulation","title":"Base.occursin","text":"occursin(needle::Symbolic, haystack::Symbolic)\n\nDetermine whether the second argument contains the first argument. Note that this function doesn't handle associativity, commutativity, or distributivity.\n\n\n\n\n\n","category":"function"},{"location":"manual/expression_manipulation/#Symbolics.filterchildren","page":"Expression Manipulation","title":"Symbolics.filterchildren","text":"filterchildren(c, x) Returns all parts of x that fufills the condition given in c. c can be a function or an expression. If it is a function, returns everything for which the function is true. If c is an expression, returns all expressions that matches it.\n\nExamples:\n\n@syms x\nSymbolics.filterchildren(x, log(x) + x + 1)\n\nreturns [x, x]\n\n@variables t X(t)\nD = Differential(t)\nSymbolics.filterchildren(Symbolics.is_derivative, X + D(X) + D(X^2))\n\nreturns [Differential(t)(X(t)^2), Differential(t)(X(t))]\n\n\n\n\n\n","category":"function"},{"location":"manual/expression_manipulation/#Symbolics.fixpoint_sub","page":"Expression Manipulation","title":"Symbolics.fixpoint_sub","text":"fixpoint_sub(expr, dict; operator = Nothing)\n\nGiven a symbolic expression, equation or inequality expr perform the substitutions in dict recursively until the expression does not change. Substitutions that depend on one another will thus be recursively expanded. For example, fixpoint_sub(x, Dict(x => y, y => 3)) will return 3. The operator keyword can be specified to prevent substitution of expressions inside operators of the given type.\n\nSee also: fast_substitute.\n\n\n\n\n\n","category":"function"},{"location":"manual/expression_manipulation/#Symbolics.fast_substitute","page":"Expression Manipulation","title":"Symbolics.fast_substitute","text":"fast_substitute(expr, dict; operator = Nothing)\n\nGiven a symbolic expression, equation or inequality expr perform the substitutions in dict. This only performs the substitutions once. For example, fast_substitute(x, Dict(x => y, y => 3)) will return y. The operator keyword can be specified to prevent substitution of expressions inside operators of the given type.\n\nSee also: fixpoint_sub.\n\n\n\n\n\n","category":"function"},{"location":"manual/expression_manipulation/#Symbolics.symbolic_to_float","page":"Expression Manipulation","title":"Symbolics.symbolic_to_float","text":"symbolic_to_float(x::Union{Num, BasicSymbolic})::Union{AbstractFloat, BasicSymbolic}\n\nIf the symbolic value is exactly equal to a number, converts the symbolic value to a floating point number. Otherwise retains the symbolic value.\n\nExamples\n\nsymbolic_to_float((1//2 * x)/x) # 0.5\nsymbolic_to_float((1/2 * x)/x) # 0.5\nsymbolic_to_float((1//2)*√(279//4)) # 4.175823272122517\n\n\n\n\n\n","category":"function"},{"location":"manual/derivatives/#Derivatives-and-Differentials","page":"Derivatives and Differentials","title":"Derivatives and Differentials","text":"","category":"section"},{"location":"manual/derivatives/","page":"Derivatives and Differentials","title":"Derivatives and Differentials","text":"A Differential(op) is a partial derivative with respect to op, which can then be applied to some other operations. For example, D=Differential(t) is what would commonly be referred to as d/dt, which can then be applied to other operations using its function call, so D(x+y) is d(x+y)/dt.","category":"page"},{"location":"manual/derivatives/","page":"Derivatives and Differentials","title":"Derivatives and Differentials","text":"By default, the derivatives are left unexpanded to capture the symbolic representation of the differential equation. If the user would like to expand out all the differentials, the expand_derivatives function eliminates all the differentials down to basic one-variable expressions.","category":"page"},{"location":"manual/derivatives/","page":"Derivatives and Differentials","title":"Derivatives and Differentials","text":"Differential\nexpand_derivatives\nis_derivative","category":"page"},{"location":"manual/derivatives/#Symbolics.Differential","page":"Derivatives and Differentials","title":"Symbolics.Differential","text":"struct Differential <: Symbolics.Operator\n\nRepresents a differential operator.\n\nFields\n\nx: The variable or expression to differentiate with respect to.\n\nExamples\n\njulia> using Symbolics\n\njulia> @variables x y;\n\njulia> D = Differential(x)\n(D'~x)\n\njulia> D(y) # Differentiate y wrt. x\n(D'~x)(y)\n\njulia> Dx = Differential(x) * Differential(y) # d^2/dxy operator\n(D'~x(t)) ∘ (D'~y(t))\n\njulia> D3 = Differential(x)^3 # 3rd order differential operator\n(D'~x(t)) ∘ (D'~x(t)) ∘ (D'~x(t))\n\n\n\n\n\n","category":"type"},{"location":"manual/derivatives/#Symbolics.expand_derivatives","page":"Derivatives and Differentials","title":"Symbolics.expand_derivatives","text":"expand_derivatives(O; ...)\nexpand_derivatives(O, simplify; occurrences)\n\n\nExpands derivatives within a symbolic expression O.\n\nThis function recursively traverses a symbolic expression, applying the chain rule and other derivative rules to expand any derivatives it encounters.\n\nArguments\n\nO::Symbolic: The symbolic expression to expand.\nsimplify::Bool=false: Whether to simplify the resulting expression using SymbolicUtils.simplify.\noccurrences=nothing: Information about the occurrences of the independent variable in the argument of the derivative. This is used internally for optimization purposes.\n\nExamples\n\njulia> @variables x y z k;\n\njulia> f=k*(abs(x-y)/y-z)^2\nk*((abs(x - y) / y - z)^2)\n\njulia> Dx=Differential(x) # Differentiate wrt x\n(::Differential) (generic function with 2 methods)\n\njulia> dfx=expand_derivatives(Dx(f))\n(k*((2abs(x - y)) / y - 2z)*IfElse.ifelse(signbit(x - y), -1, 1)) / y\n\n\n\n\n\n","category":"function"},{"location":"manual/derivatives/","page":"Derivatives and Differentials","title":"Derivatives and Differentials","text":"note: Note\nFor symbolic differentiation, all registered functions in the symbolic expression need a registered derivative. For more information, see the function registration page.","category":"page"},{"location":"manual/derivatives/#High-Level-Differentiation-Functions","page":"Derivatives and Differentials","title":"High-Level Differentiation Functions","text":"","category":"section"},{"location":"manual/derivatives/","page":"Derivatives and Differentials","title":"Derivatives and Differentials","text":"The following functions are not exported and thus must be accessed in a namespaced way, i.e. Symbolics.jacobian.","category":"page"},{"location":"manual/derivatives/","page":"Derivatives and Differentials","title":"Derivatives and Differentials","text":"Symbolics.derivative\nSymbolics.jacobian\nSymbolics.sparsejacobian\nSymbolics.sparsejacobian_vals\nSymbolics.gradient\nSymbolics.hessian\nSymbolics.sparsehessian\nSymbolics.sparsehessian_vals","category":"page"},{"location":"manual/derivatives/#Symbolics.derivative","page":"Derivatives and Differentials","title":"Symbolics.derivative","text":"derivative(O, var; simplify)\n\n\nA helper function for computing the derivative of the expression O with respect to var.\n\n\n\n\n\n","category":"function"},{"location":"manual/derivatives/#Symbolics.jacobian","page":"Derivatives and Differentials","title":"Symbolics.jacobian","text":"jacobian(ops, vars; simplify, scalarize)\n\n\nA helper function for computing the Jacobian of an array of expressions with respect to an array of variable expressions.\n\n\n\n\n\n","category":"function"},{"location":"manual/derivatives/#Symbolics.sparsejacobian","page":"Derivatives and Differentials","title":"Symbolics.sparsejacobian","text":"sparsejacobian(ops, vars; simplify)\n\n\nA helper function for computing the sparse Jacobian of an array of expressions with respect to an array of variable expressions.\n\n\n\n\n\n","category":"function"},{"location":"manual/derivatives/#Symbolics.sparsejacobian_vals","page":"Derivatives and Differentials","title":"Symbolics.sparsejacobian_vals","text":"sparsejacobian_vals(ops, vars, I, J; simplify)\n\n\nA helper function for computing the values of the sparse Jacobian of an array of expressions with respect to an array of variable expressions given the sparsity structure.\n\n\n\n\n\n","category":"function"},{"location":"manual/derivatives/#Symbolics.gradient","page":"Derivatives and Differentials","title":"Symbolics.gradient","text":"gradient(O, vars; simplify)\n\n\nA helper function for computing the gradient of the expression O with respect to an array of variable expressions.\n\n\n\n\n\n","category":"function"},{"location":"manual/derivatives/#Symbolics.hessian","page":"Derivatives and Differentials","title":"Symbolics.hessian","text":"hessian(O, vars; simplify)\n\n\nA helper function for computing the Hessian of the expression O with respect to an array of variable expressions.\n\n\n\n\n\n","category":"function"},{"location":"manual/derivatives/#Symbolics.sparsehessian","page":"Derivatives and Differentials","title":"Symbolics.sparsehessian","text":"sparsehessian(op, vars; simplify, full)\n\n\nA helper function for computing the sparse Hessian of an expression with respect to an array of variable expressions.\n\n\n\n\n\n","category":"function"},{"location":"manual/derivatives/#Symbolics.sparsehessian_vals","page":"Derivatives and Differentials","title":"Symbolics.sparsehessian_vals","text":"sparsehessian_vals(op, vars, I, J; simplify)\n\n\nA helper function for computing the values of the sparse Hessian of an expression with respect to an array of variable expressions given the sparsity structure.\n\n\n\n\n\n","category":"function"},{"location":"manual/derivatives/#Adding-Analytical-Derivatives","page":"Derivatives and Differentials","title":"Adding Analytical Derivatives","text":"","category":"section"},{"location":"manual/derivatives/","page":"Derivatives and Differentials","title":"Derivatives and Differentials","text":"There are many derivatives pre-defined by DiffRules.jl. For example,","category":"page"},{"location":"manual/derivatives/","page":"Derivatives and Differentials","title":"Derivatives and Differentials","text":"using Symbolics\n@variables x y z\nf(x,y,z) = x^2 + sin(x+y) - z","category":"page"},{"location":"manual/derivatives/","page":"Derivatives and Differentials","title":"Derivatives and Differentials","text":"f automatically has the derivatives defined via the tracing mechanism. It will do this by directly building the internals of your function and differentiating that.","category":"page"},{"location":"manual/derivatives/","page":"Derivatives and Differentials","title":"Derivatives and Differentials","text":"However, often you may want to define your own derivatives so that way automatic Jacobian etc. calculations can utilize this information. This can allow for more succinct versions of the derivatives to be calculated to scale to larger systems. You can define derivatives for your function via the dispatch:","category":"page"},{"location":"manual/derivatives/","page":"Derivatives and Differentials","title":"Derivatives and Differentials","text":"# `N` arguments are accepted by the relevant method of `my_function`\nSymbolics.derivative(::typeof(my_function), args::NTuple{N,Any}, ::Val{i})","category":"page"},{"location":"manual/derivatives/","page":"Derivatives and Differentials","title":"Derivatives and Differentials","text":"where i means that it's the derivative with respect to the ith argument. args is the array of arguments, so, for example, if your function is f(x,t), then args = [x,t]. You should return an Term for the derivative of your function.","category":"page"},{"location":"manual/derivatives/","page":"Derivatives and Differentials","title":"Derivatives and Differentials","text":"For example, sin(t)'s derivative (by t) is given by the following:","category":"page"},{"location":"manual/derivatives/","page":"Derivatives and Differentials","title":"Derivatives and Differentials","text":"Symbolics.derivative(::typeof(sin), args::NTuple{1,Any}, ::Val{1}) = cos(args[1])","category":"page"},{"location":"#Symbolics.jl","page":"Home","title":"Symbolics.jl","text":"","category":"section"},{"location":"","page":"Home","title":"Home","text":"Symbolics.jl is a fast and modern Computer Algebra System (CAS) for a fast and modern programming language (Julia). The goal is to have a high-performance and parallelized symbolic algebra system that is directly extendable in the same language as that of the users.","category":"page"},{"location":"#Installation","page":"Home","title":"Installation","text":"","category":"section"},{"location":"","page":"Home","title":"Home","text":"To install Symbolics.jl, use the Julia package manager:","category":"page"},{"location":"","page":"Home","title":"Home","text":"using Pkg\nPkg.add(\"Symbolics\")","category":"page"},{"location":"#Citation","page":"Home","title":"Citation","text":"","category":"section"},{"location":"","page":"Home","title":"Home","text":"If you use Symbolics.jl, please cite this paper","category":"page"},{"location":"","page":"Home","title":"Home","text":"@article{gowda2021high,\n title={High-performance symbolic-numerics via multiple dispatch},\n author={Gowda, Shashi and Ma, Yingbo and Cheli, Alessandro and Gwozdz, Maja and Shah, Viral B and Edelman, Alan and Rackauckas, Christopher},\n journal={arXiv preprint arXiv:2105.03949},\n year={2021}\n}","category":"page"},{"location":"#Feature-Summary","page":"Home","title":"Feature Summary","text":"","category":"section"},{"location":"","page":"Home","title":"Home","text":"Because Symbolics.jl is built into the Julia language and works with its dispatches, generic functions in Base Julia will work with symbolic expressions! Make matrices of symbolic expressions and multiply them: it will just work. Take the LU-factorization. Etc. Thus, see the Julia Documentation for a large list of functionality available in Symbolics.jl.","category":"page"},{"location":"","page":"Home","title":"Home","text":"A general list of the features is:","category":"page"},{"location":"","page":"Home","title":"Home","text":"Symbolic arithmetic with type information and multiple dispatch\nSymbolic polynomials and trigonometric functions\nPattern matching, simplification and substitution\nDifferentiation\nSymbolic linear algebra (factorizations, inversion, determinants, eigencomputations, etc.)\nDiscrete math (representations of summations, products, binomial coefficients, etc.)\nLogical and Boolean expressions\nSymbolic equation solving and conversion to arbitrary precision\nSupport for non-standard algebras (non-commutative symbols and customizable rulesets)\nSpecial functions (list provided by SpecialFunctions.jl)\nAutomatic conversion of Julia code to symbolic code\nGeneration of (high performance and parallel) functions from symbolic expressions\nFast automated sparsity detection and generation of sparse Jacobians and Hessians","category":"page"},{"location":"","page":"Home","title":"Home","text":"and much more.","category":"page"},{"location":"#Extension-Packages","page":"Home","title":"Extension Packages","text":"","category":"section"},{"location":"","page":"Home","title":"Home","text":"Below is a list of known extension packages. If you would like your package to be listed here, feel free to open a pull request!","category":"page"},{"location":"","page":"Home","title":"Home","text":"ModelingToolkit.jl: Symbolic representations of common numerical systems\nOrdinary differential equations\nStochastic differential equations\nPartial differential equations\nNonlinear systems\nOptimization problems\nOptimal Control\nCausal and acausal modeling (Simulink/Modelica)\nAutomated model transformation, simplification, and composition\nCatalyst.jl: Symbolic representations of chemical reactions\nSymbolically build and represent large systems of chemical reactions\nGenerate code for ODEs, SDEs, continuous-time Markov Chains, and more\nSimulate the models using the SciML ecosystem with O(1) Gillespie methods\nDataDrivenDiffEq.jl: Automatic identification of equations from data\nAutomated construction of ODEs and DAEs from data\nRepresentations of Koopman operators and Dynamic Mode Decomposition (DMD)\nSymbolicRegression.jl: Distributed High-Performance symbolic regression\nParallelized generic algorithms for finding equations from data\nPareto frontier-based scoring\nReversePropagation.jl: Source-to-source reverse mode automatic differentiation\nAutomated tracing of code and construction of backpropagation equations\nComposes with symbolic transformation and simplification functionality","category":"page"},{"location":"#Reproducibility","page":"Home","title":"Reproducibility","text":"","category":"section"},{"location":"","page":"Home","title":"Home","text":"
The documentation of this SciML package was built using these direct dependencies,","category":"page"},{"location":"","page":"Home","title":"Home","text":"using Pkg # hide\nPkg.status() # hide","category":"page"},{"location":"","page":"Home","title":"Home","text":"
","category":"page"},{"location":"","page":"Home","title":"Home","text":"
and using this machine and Julia version.","category":"page"},{"location":"","page":"Home","title":"Home","text":"using InteractiveUtils # hide\nversioninfo() # hide","category":"page"},{"location":"","page":"Home","title":"Home","text":"
","category":"page"},{"location":"","page":"Home","title":"Home","text":"
A more complete overview of all dependencies and their versions is also provided.","category":"page"},{"location":"","page":"Home","title":"Home","text":"using Pkg # hide\nPkg.status(; mode = PKGMODE_MANIFEST) # hide","category":"page"},{"location":"","page":"Home","title":"Home","text":"
","category":"page"},{"location":"","page":"Home","title":"Home","text":"using TOML\nusing Markdown\nversion = TOML.parse(read(\"../../Project.toml\", String))[\"version\"]\nname = TOML.parse(read(\"../../Project.toml\", String))[\"name\"]\nlink_manifest = \"https://github.com/SciML/\" * name * \".jl/tree/gh-pages/v\" * version *\n \"/assets/Manifest.toml\"\nlink_project = \"https://github.com/SciML/\" * name * \".jl/tree/gh-pages/v\" * version *\n \"/assets/Project.toml\"\nMarkdown.parse(\"\"\"You can also download the\n[manifest]($link_manifest)\nfile and the\n[project]($link_project)\nfile.\n\"\"\")","category":"page"},{"location":"examples/perturbation/#perturb_alg","page":"Mixed Symbolic-Numeric Perturbation Theory","title":"Mixed Symbolic-Numeric Perturbation Theory","text":"","category":"section"},{"location":"examples/perturbation/#Background","page":"Mixed Symbolic-Numeric Perturbation Theory","title":"Background","text":"","category":"section"},{"location":"examples/perturbation/","page":"Mixed Symbolic-Numeric Perturbation Theory","title":"Mixed Symbolic-Numeric Perturbation Theory","text":"Symbolics.jl is a fast and modern Computer Algebra System (CAS) written in the Julia Programming Language. It is an integral part of the SciML ecosystem of differential equation solvers and scientific machine learning packages. While Symbolics.jl is primarily designed for modern scientific computing (e.g., auto-differentiation, machine learning), it is a powerful CAS that can also be useful for classic scientific computing. One such application is using the perturbation theory to solve algebraic and differential equations.","category":"page"},{"location":"examples/perturbation/","page":"Mixed Symbolic-Numeric Perturbation Theory","title":"Mixed Symbolic-Numeric Perturbation Theory","text":"Perturbation methods are a collection of techniques to solve intractable problems that generally don't have a closed solution but depend on a tunable parameter and have closed or easy solutions for some values of the parameter. The main idea is to assume a solution as a power series in the tunable parameter (say ϵ), such that ϵ = 0 corresponds to an easy solution.","category":"page"},{"location":"examples/perturbation/","page":"Mixed Symbolic-Numeric Perturbation Theory","title":"Mixed Symbolic-Numeric Perturbation Theory","text":"We will discuss the general steps of the perturbation methods to solve algebraic (this tutorial) and differential equations","category":"page"},{"location":"examples/perturbation/","page":"Mixed Symbolic-Numeric Perturbation Theory","title":"Mixed Symbolic-Numeric Perturbation Theory","text":"The hallmark of the perturbation method is the generation of long and convoluted intermediate equations, which are subjected to algorithmic and mechanical manipulations. Therefore, these problems are well suited for CAS. In fact, CAS software packages have been used to help with the perturbation calculations since the early 1970s.","category":"page"},{"location":"examples/perturbation/","page":"Mixed Symbolic-Numeric Perturbation Theory","title":"Mixed Symbolic-Numeric Perturbation Theory","text":"In this tutorial, our goal is to show how to use a mix of symbolic manipulations (Symbolics.jl) and numerical methods to solve simple perturbation problems.","category":"page"},{"location":"examples/perturbation/#Solving-the-Quintic","page":"Mixed Symbolic-Numeric Perturbation Theory","title":"Solving the Quintic","text":"","category":"section"},{"location":"examples/perturbation/","page":"Mixed Symbolic-Numeric Perturbation Theory","title":"Mixed Symbolic-Numeric Perturbation Theory","text":"We start with the “hello world!” analog of the perturbation problems, solving the quintic (fifth-order) equations. We want to find a real valued x such that x^5 + x = 1. According to the Abel's theorem, a general quintic equation does not have a closed form solution. Of course, we can easily solve this equation numerically; for example, by using the Newton's method. We use the following implementation of the Newton's method:","category":"page"},{"location":"examples/perturbation/","page":"Mixed Symbolic-Numeric Perturbation Theory","title":"Mixed Symbolic-Numeric Perturbation Theory","text":"using Symbolics, SymbolicUtils\n\nfunction solve_newton(f, x, x₀; abstol=1e-8, maxiter=50)\n xₙ = Float64(x₀)\n fₙ₊₁ = x - f / Symbolics.derivative(f, x)\n\n for i = 1:maxiter\n xₙ₊₁ = substitute(fₙ₊₁, Dict(x => xₙ))\n if abs(xₙ₊₁ - xₙ) < abstol\n return xₙ₊₁\n else\n xₙ = xₙ₊₁\n end\n end\n return xₙ₊₁\nend","category":"page"},{"location":"examples/perturbation/","page":"Mixed Symbolic-Numeric Perturbation Theory","title":"Mixed Symbolic-Numeric Perturbation Theory","text":"In this code, Symbolics.derivative(eq, x) does exactly what it names implies: it calculates the symbolic derivative of eq (a Symbolics.jl expression) with respect to x (a Symbolics.jl variable). We use Symbolics.substitute(eq, D) to evaluate the update formula by substituting variables or sub-expressions (defined in a dictionary D) in eq. It should be noted that substitute is the workhorse of our code and will be used multiple times in the rest of these tutorials. solve_newton is written with simplicity and clarity in mind, and not performance.","category":"page"},{"location":"examples/perturbation/","page":"Mixed Symbolic-Numeric Perturbation Theory","title":"Mixed Symbolic-Numeric Perturbation Theory","text":"Let's go back to our quintic. We can define a Symbolics variable as @variables x and then solve the equation solve_newton(x^5 + x - 1, x, 1.0) (here, x₀ = 1.0 is our first guess). The answer is 0.7549. Now, let's see how we can solve the same problem using the perturbation methods.","category":"page"},{"location":"examples/perturbation/","page":"Mixed Symbolic-Numeric Perturbation Theory","title":"Mixed Symbolic-Numeric Perturbation Theory","text":"We introduce a tuning parameter epsilon into our equation: x^5 + epsilon x = 1. If epsilon = 1, we get our original problem. For epsilon = 0, the problem transforms to an easy one: x^5 = 1 which has an exact real solution x = 1 (and four complex solutions which we ignore here). We expand x as a power series on epsilon:","category":"page"},{"location":"examples/perturbation/","page":"Mixed Symbolic-Numeric Perturbation Theory","title":"Mixed Symbolic-Numeric Perturbation Theory","text":" x(epsilon) = a_0 + a_1 epsilon + a_2 epsilon^2 + O(epsilon^3)","category":"page"},{"location":"examples/perturbation/","page":"Mixed Symbolic-Numeric Perturbation Theory","title":"Mixed Symbolic-Numeric Perturbation Theory","text":"a_0","category":"page"},{"location":"examples/perturbation/","page":"Mixed Symbolic-Numeric Perturbation Theory","title":"Mixed Symbolic-Numeric Perturbation Theory","text":"is the solution of the easy equation, therefore a_0 = 1. Substituting into the original problem,","category":"page"},{"location":"examples/perturbation/","page":"Mixed Symbolic-Numeric Perturbation Theory","title":"Mixed Symbolic-Numeric Perturbation Theory","text":" (a_0 + a_1 epsilon + a_2 epsilon^2)^5 + epsilon (a_0 + a_1 epsilon + a_2 epsilon^2) - 1 = 0","category":"page"},{"location":"examples/perturbation/","page":"Mixed Symbolic-Numeric Perturbation Theory","title":"Mixed Symbolic-Numeric Perturbation Theory","text":"Expanding the equations, we get","category":"page"},{"location":"examples/perturbation/","page":"Mixed Symbolic-Numeric Perturbation Theory","title":"Mixed Symbolic-Numeric Perturbation Theory","text":" epsilon (1 + 5 a_1) + epsilon^2 (a_1 + 5 a_2 + 10 a_1^2) + 𝑂(epsilon^3) = 0","category":"page"},{"location":"examples/perturbation/","page":"Mixed Symbolic-Numeric Perturbation Theory","title":"Mixed Symbolic-Numeric Perturbation Theory","text":"This equation should hold for each power of epsilon. Therefore,","category":"page"},{"location":"examples/perturbation/","page":"Mixed Symbolic-Numeric Perturbation Theory","title":"Mixed Symbolic-Numeric Perturbation Theory","text":" 1 + 5 a_1 = 0","category":"page"},{"location":"examples/perturbation/","page":"Mixed Symbolic-Numeric Perturbation Theory","title":"Mixed Symbolic-Numeric Perturbation Theory","text":"and","category":"page"},{"location":"examples/perturbation/","page":"Mixed Symbolic-Numeric Perturbation Theory","title":"Mixed Symbolic-Numeric Perturbation Theory","text":" a_1 + 5 a_2 + 10 a_1^2 = 0","category":"page"},{"location":"examples/perturbation/","page":"Mixed Symbolic-Numeric Perturbation Theory","title":"Mixed Symbolic-Numeric Perturbation Theory","text":"This system of equations does not initially seem to be linear because of the presence of terms like 10 a_1^2, but upon closer inspection is found to be linear (this is a feature of the perturbation methods). In addition, the system is in a triangular form, meaning the first equation depends only on a_1, the second one on a_1 and a_2, such that we can replace the result of a_1 from the first one into the second equation and remove the non-linear term. We solve the first equation to get a_1 = -frac15. Substituting in the second one and solve for a_2:","category":"page"},{"location":"examples/perturbation/","page":"Mixed Symbolic-Numeric Perturbation Theory","title":"Mixed Symbolic-Numeric Perturbation Theory","text":" a_2 = frac(-frac15 + 10(-(frac15)²)5 = -frac125","category":"page"},{"location":"examples/perturbation/","page":"Mixed Symbolic-Numeric Perturbation Theory","title":"Mixed Symbolic-Numeric Perturbation Theory","text":"Finally,","category":"page"},{"location":"examples/perturbation/","page":"Mixed Symbolic-Numeric Perturbation Theory","title":"Mixed Symbolic-Numeric Perturbation Theory","text":" x(epsilon) = 1 - fracepsilon5 - fracepsilon^225 + O(epsilon^3)","category":"page"},{"location":"examples/perturbation/","page":"Mixed Symbolic-Numeric Perturbation Theory","title":"Mixed Symbolic-Numeric Perturbation Theory","text":"Solving the original problem, x(1) = 076, compared to 0.7548 calculated numerically. We can improve the accuracy by including more terms in the expansion of x. However, the calculations, while straightforward, become messy and intractable to do manually very quickly. This is why a CAS is very helpful to solve perturbation problems.","category":"page"},{"location":"examples/perturbation/","page":"Mixed Symbolic-Numeric Perturbation Theory","title":"Mixed Symbolic-Numeric Perturbation Theory","text":"Now, let's see how we can do these calculations in Julia. Let n be the order of the expansion. We start by defining the symbolic variables:","category":"page"},{"location":"examples/perturbation/","page":"Mixed Symbolic-Numeric Perturbation Theory","title":"Mixed Symbolic-Numeric Perturbation Theory","text":"n = 2\n@variables ϵ a[1:n]","category":"page"},{"location":"examples/perturbation/","page":"Mixed Symbolic-Numeric Perturbation Theory","title":"Mixed Symbolic-Numeric Perturbation Theory","text":"Then, we define","category":"page"},{"location":"examples/perturbation/","page":"Mixed Symbolic-Numeric Perturbation Theory","title":"Mixed Symbolic-Numeric Perturbation Theory","text":"x = 1 + a[1]*ϵ + a[2]*ϵ^2","category":"page"},{"location":"examples/perturbation/","page":"Mixed Symbolic-Numeric Perturbation Theory","title":"Mixed Symbolic-Numeric Perturbation Theory","text":"The next step is to substitute x in the problem equation","category":"page"},{"location":"examples/perturbation/","page":"Mixed Symbolic-Numeric Perturbation Theory","title":"Mixed Symbolic-Numeric Perturbation Theory","text":" eq = x^5 + ϵ*x - 1","category":"page"},{"location":"examples/perturbation/","page":"Mixed Symbolic-Numeric Perturbation Theory","title":"Mixed Symbolic-Numeric Perturbation Theory","text":"The expanded form of eq is","category":"page"},{"location":"examples/perturbation/","page":"Mixed Symbolic-Numeric Perturbation Theory","title":"Mixed Symbolic-Numeric Perturbation Theory","text":"expand(eq)","category":"page"},{"location":"examples/perturbation/","page":"Mixed Symbolic-Numeric Perturbation Theory","title":"Mixed Symbolic-Numeric Perturbation Theory","text":"We need a way to get the coefficients of different powers of ϵ. Function collect_powers(eq, x, ns) returns the powers of variable x in expression eq. Argument ns is the range of the powers.","category":"page"},{"location":"examples/perturbation/","page":"Mixed Symbolic-Numeric Perturbation Theory","title":"Mixed Symbolic-Numeric Perturbation Theory","text":"function collect_powers(eq, x, ns)\n eq = expand(eq)\n [Symbolics.coeff(eq, x^i) for i in ns]\nend","category":"page"},{"location":"examples/perturbation/","page":"Mixed Symbolic-Numeric Perturbation Theory","title":"Mixed Symbolic-Numeric Perturbation Theory","text":"To return the coefficients of ϵ and ϵ^2 in eq, we can write","category":"page"},{"location":"examples/perturbation/","page":"Mixed Symbolic-Numeric Perturbation Theory","title":"Mixed Symbolic-Numeric Perturbation Theory","text":"eqs = collect_powers(eq, ϵ, 1:2)","category":"page"},{"location":"examples/perturbation/","page":"Mixed Symbolic-Numeric Perturbation Theory","title":"Mixed Symbolic-Numeric Perturbation Theory","text":"Having the coefficients of the powers of ϵ, we can set each equation in eqs to 0 (remember, we rearrange the problem such that eq is 0) and solve the system of linear equations to find the numerical values of the coefficients. Symbolics.jl has a function symbolic_linear_solve that can solve systems of linear equations. However, the presence of higher-order terms in eqs prevents symbolic_linear_solve(eqs, a) from workings properly. Instead, we can exploit the fact that our system is in a triangular form and start by solving eqs[1] for a₁ and then substitute this in eqs[2] and solve for a₂, and so on. This cascading process is done by function solve_coef(eqs, ps):","category":"page"},{"location":"examples/perturbation/","page":"Mixed Symbolic-Numeric Perturbation Theory","title":"Mixed Symbolic-Numeric Perturbation Theory","text":"function solve_coef(eqs, ps)\n vals = Dict()\n\n for i = 1:length(ps)\n eq = substitute(eqs[i], vals)\n vals[ps[i]] = symbolic_linear_solve(eq, ps[i])\n end\n vals\nend","category":"page"},{"location":"examples/perturbation/","page":"Mixed Symbolic-Numeric Perturbation Theory","title":"Mixed Symbolic-Numeric Perturbation Theory","text":"Here, eqs is an array of expressions (assumed to be equal to 0) and ps is an array of variables. The result is a dictionary of variable => value pairs. We apply solve_coef to eqs to get the numerical values of the parameters:","category":"page"},{"location":"examples/perturbation/","page":"Mixed Symbolic-Numeric Perturbation Theory","title":"Mixed Symbolic-Numeric Perturbation Theory","text":"vals = solve_coef(eqs, a)","category":"page"},{"location":"examples/perturbation/","page":"Mixed Symbolic-Numeric Perturbation Theory","title":"Mixed Symbolic-Numeric Perturbation Theory","text":"Finally, we substitute back the values of a in the definition of x as a function of 𝜀. Note that 𝜀 is a number (usually Float64), whereas ϵ is a symbolic variable.","category":"page"},{"location":"examples/perturbation/","page":"Mixed Symbolic-Numeric Perturbation Theory","title":"Mixed Symbolic-Numeric Perturbation Theory","text":"X = 𝜀 -> 1 + vals[a[1]]*𝜀 + vals[a[2]]*𝜀^2","category":"page"},{"location":"examples/perturbation/","page":"Mixed Symbolic-Numeric Perturbation Theory","title":"Mixed Symbolic-Numeric Perturbation Theory","text":"Therefore, the solution to our original problem becomes X(1), which is equal to 0.76. We can use larger values of n to improve the accuracy of estimations.","category":"page"},{"location":"examples/perturbation/","page":"Mixed Symbolic-Numeric Perturbation Theory","title":"Mixed Symbolic-Numeric Perturbation Theory","text":"n x\n1 0.8\n2 0.76\n3 0.752\n4 0.752\n5 0.7533\n6 0.7543\n7 0.7548\n8 0.7550","category":"page"},{"location":"examples/perturbation/","page":"Mixed Symbolic-Numeric Perturbation Theory","title":"Mixed Symbolic-Numeric Perturbation Theory","text":"Remember, the numerical value is 0.7549. The two functions collect_powers and solve_coef(eqs, a) are used in all the examples in this and the next tutorial.","category":"page"},{"location":"examples/perturbation/#Solving-the-Kepler's-Equation","page":"Mixed Symbolic-Numeric Perturbation Theory","title":"Solving the Kepler's Equation","text":"","category":"section"},{"location":"examples/perturbation/","page":"Mixed Symbolic-Numeric Perturbation Theory","title":"Mixed Symbolic-Numeric Perturbation Theory","text":"Historically, the perturbation methods were first invented to solve orbital calculations of the Moon and the planets. In homage to this history, our second example has a celestial theme. Our goal is solving the Kepler's equation:","category":"page"},{"location":"examples/perturbation/","page":"Mixed Symbolic-Numeric Perturbation Theory","title":"Mixed Symbolic-Numeric Perturbation Theory","text":" E - esin(E) = M","category":"page"},{"location":"examples/perturbation/","page":"Mixed Symbolic-Numeric Perturbation Theory","title":"Mixed Symbolic-Numeric Perturbation Theory","text":"where e is the eccentricity of the elliptical orbit, M is the mean anomaly, and E (unknown) is the eccentric anomaly (the angle between the position of a planet in an elliptical orbit and the point of periapsis). This equation is central to solving two-body Keplerian orbits.","category":"page"},{"location":"examples/perturbation/","page":"Mixed Symbolic-Numeric Perturbation Theory","title":"Mixed Symbolic-Numeric Perturbation Theory","text":"Similar to the first example, it is easy to solve this problem using the Newton's method. For example, let e = 001671 (the eccentricity of the Earth) and M = pi2. We have solve_newton(x - e*sin(x) - M, x, M) equals to 1.5875 (compared to π/2 = 1.5708). Now, we try to solve the same problem using the perturbation techniques (see function test_kepler).","category":"page"},{"location":"examples/perturbation/","page":"Mixed Symbolic-Numeric Perturbation Theory","title":"Mixed Symbolic-Numeric Perturbation Theory","text":"For e = 0, we get E = M. Therefore, we can use e as our perturbation parameter. For consistency with other problems, we also rename e to epsilon and E to x.","category":"page"},{"location":"examples/perturbation/","page":"Mixed Symbolic-Numeric Perturbation Theory","title":"Mixed Symbolic-Numeric Perturbation Theory","text":"From here on, we use the helper function def_taylor to define Taylor's series by calling it as x = def_taylor(ϵ, a, 1), where the arguments are, respectively, the perturbation variable, which is an array of coefficients (starting from the coefficient of epsilon^1), and an optional constant term.","category":"page"},{"location":"examples/perturbation/","page":"Mixed Symbolic-Numeric Perturbation Theory","title":"Mixed Symbolic-Numeric Perturbation Theory","text":"def_taylor(x, ps) = sum([a*x^i for (i,a) in enumerate(ps)])\ndef_taylor(x, ps, p₀) = p₀ + def_taylor(x, ps)","category":"page"},{"location":"examples/perturbation/","page":"Mixed Symbolic-Numeric Perturbation Theory","title":"Mixed Symbolic-Numeric Perturbation Theory","text":"We start by defining the variables (assuming n = 3):","category":"page"},{"location":"examples/perturbation/","page":"Mixed Symbolic-Numeric Perturbation Theory","title":"Mixed Symbolic-Numeric Perturbation Theory","text":"n = 3\n@variables ϵ M a[1:n]\nx = def_taylor(ϵ, a, M)","category":"page"},{"location":"examples/perturbation/","page":"Mixed Symbolic-Numeric Perturbation Theory","title":"Mixed Symbolic-Numeric Perturbation Theory","text":"We further simplify by substituting sin with its power series using the expand_sin helper function:","category":"page"},{"location":"examples/perturbation/","page":"Mixed Symbolic-Numeric Perturbation Theory","title":"Mixed Symbolic-Numeric Perturbation Theory","text":"expand_sin(x, n) = sum([(isodd(k) ? -1 : 1)*(-x)^(2k-1)/factorial(2k-1) for k=1:n])","category":"page"},{"location":"examples/perturbation/","page":"Mixed Symbolic-Numeric Perturbation Theory","title":"Mixed Symbolic-Numeric Perturbation Theory","text":"To test,","category":"page"},{"location":"examples/perturbation/","page":"Mixed Symbolic-Numeric Perturbation Theory","title":"Mixed Symbolic-Numeric Perturbation Theory","text":"expand_sin(0.1, 10) ≈ sin(0.1)","category":"page"},{"location":"examples/perturbation/","page":"Mixed Symbolic-Numeric Perturbation Theory","title":"Mixed Symbolic-Numeric Perturbation Theory","text":"The problem equation is","category":"page"},{"location":"examples/perturbation/","page":"Mixed Symbolic-Numeric Perturbation Theory","title":"Mixed Symbolic-Numeric Perturbation Theory","text":"eq = x - ϵ * expand_sin(x, n) - M","category":"page"},{"location":"examples/perturbation/","page":"Mixed Symbolic-Numeric Perturbation Theory","title":"Mixed Symbolic-Numeric Perturbation Theory","text":"We follow the same process as the first example. We collect the coefficients of the powers of ϵ","category":"page"},{"location":"examples/perturbation/","page":"Mixed Symbolic-Numeric Perturbation Theory","title":"Mixed Symbolic-Numeric Perturbation Theory","text":"eqs = collect_powers(eq, ϵ, 1:n)","category":"page"},{"location":"examples/perturbation/","page":"Mixed Symbolic-Numeric Perturbation Theory","title":"Mixed Symbolic-Numeric Perturbation Theory","text":"and then solve for a:","category":"page"},{"location":"examples/perturbation/","page":"Mixed Symbolic-Numeric Perturbation Theory","title":"Mixed Symbolic-Numeric Perturbation Theory","text":"vals = solve_coef(eqs, a)","category":"page"},{"location":"examples/perturbation/","page":"Mixed Symbolic-Numeric Perturbation Theory","title":"Mixed Symbolic-Numeric Perturbation Theory","text":"Finally, we substitute vals back in x:","category":"page"},{"location":"examples/perturbation/","page":"Mixed Symbolic-Numeric Perturbation Theory","title":"Mixed Symbolic-Numeric Perturbation Theory","text":"x′ = substitute(x, vals)\nX = (𝜀, 𝑀) -> substitute(x′, Dict(ϵ => 𝜀, M => 𝑀))\nX(0.01671, π/2)","category":"page"},{"location":"examples/perturbation/","page":"Mixed Symbolic-Numeric Perturbation Theory","title":"Mixed Symbolic-Numeric Perturbation Theory","text":"The result is 1.5876, compared to the numerical value of 1.5875. It is customary to order X based on the powers of 𝑀 instead of 𝜀. We can calculate this series as collect_powers(x′, M, 0:5). The result (after manual cleanup) is","category":"page"},{"location":"examples/perturbation/","page":"Mixed Symbolic-Numeric Perturbation Theory","title":"Mixed Symbolic-Numeric Perturbation Theory","text":"(1 + 𝜀 + 𝜀^2 + 𝜀^3)*𝑀\n- (𝜀 + 4*𝜀^2 + 10*𝜀^3)*𝑀^3/6\n+ (𝜀 + 16*𝜀^2 + 91*𝜀^3)*𝑀^5/120","category":"page"},{"location":"examples/perturbation/","page":"Mixed Symbolic-Numeric Perturbation Theory","title":"Mixed Symbolic-Numeric Perturbation Theory","text":"Comparing the formula to the one for 𝐸 in the Wikipedia article on the Kepler's equation:","category":"page"},{"location":"examples/perturbation/","page":"Mixed Symbolic-Numeric Perturbation Theory","title":"Mixed Symbolic-Numeric Perturbation Theory","text":" E = frac11-epsilonM\n -fracepsilon(1-epsilon)^4 fracM^33 + frac(9epsilon^2\n + epsilon)(1-epsilon)^7fracM^55cdots","category":"page"},{"location":"examples/perturbation/","page":"Mixed Symbolic-Numeric Perturbation Theory","title":"Mixed Symbolic-Numeric Perturbation Theory","text":"The first deviation is in the coefficient of epsilon^3 M^5.","category":"page"}] +[{"location":"manual/faq/#Frequently-Asked-Questions","page":"Frequently Asked Questions","title":"Frequently Asked Questions","text":"","category":"section"},{"location":"manual/faq/#Limits-of-Symbolic-Computation","page":"Frequently Asked Questions","title":"Limits of Symbolic Computation","text":"","category":"section"},{"location":"manual/faq/#Transforming-my-function-to-a-symbolic-equation-has-failed.-What-do-I-do?","page":"Frequently Asked Questions","title":"Transforming my function to a symbolic equation has failed. What do I do?","text":"","category":"section"},{"location":"manual/faq/","page":"Frequently Asked Questions","title":"Frequently Asked Questions","text":"If you see the error:","category":"page"},{"location":"manual/faq/","page":"Frequently Asked Questions","title":"Frequently Asked Questions","text":"ERROR: TypeError: non-boolean (Num) used in boolean context","category":"page"},{"location":"manual/faq/","page":"Frequently Asked Questions","title":"Frequently Asked Questions","text":"this is likely coming from an algorithm which cannot be traced into a purely symbolic algorithm. Many numerical solvers, for instance, have this property. It shows up when you're doing something like if x < tol. If x is a number, then this is true or false. If x is a symbol, then it's x < tol, so Julia just cannot know how many iterations to do and throws an error.","category":"page"},{"location":"manual/faq/","page":"Frequently Asked Questions","title":"Frequently Asked Questions","text":"This shows up in adaptive algorithms, for example:","category":"page"},{"location":"manual/faq/","page":"Frequently Asked Questions","title":"Frequently Asked Questions","text":"function factorial(x)\n out = x\n while x > 1\n x -= 1\n out *= x\n end\n out\nend","category":"page"},{"location":"manual/faq/","page":"Frequently Asked Questions","title":"Frequently Asked Questions","text":"The number of iterations this algorithm runs for is dependent on the value of x, and so there is no static representation of the algorithm. If x is 5, then it's out = x*(x-1)*(x-2)*(x-3)*(x-4), while if x is 3, then it's out = x*(x-1)*(x-2). It should thus be no surprise that:","category":"page"},{"location":"manual/faq/","page":"Frequently Asked Questions","title":"Frequently Asked Questions","text":"using Symbolics\n@variables x\ntry\n factorial(x)\ncatch e\n e\nend","category":"page"},{"location":"manual/faq/","page":"Frequently Asked Questions","title":"Frequently Asked Questions","text":"fails. It's not that there is anything wrong with this code, but it's not going to work because fundamentally this is not a symbolically-representable algorithm.","category":"page"},{"location":"manual/faq/","page":"Frequently Asked Questions","title":"Frequently Asked Questions","text":"The space of algorithms which can be turned into symbolic algorithms is what we call quasi-static, that is, there is a way to represent the algorithm as static. Loops are allowed, but the amount of loop iterations should not require that you know the value of the symbol x. If the algorithm is quasi-static, then Symbolics.jl tracing will produce the static form of the code, unrolling the operations, and generating a flat representation of the algorithm.","category":"page"},{"location":"manual/faq/#What-can-be-done?","page":"Frequently Asked Questions","title":"What can be done?","text":"","category":"section"},{"location":"manual/faq/","page":"Frequently Asked Questions","title":"Frequently Asked Questions","text":"If you need to represent this function f symbolically, then you'll need to make sure it's not traced and instead is directly represented in the underlying computational graph. Just like how sqrt(x) symbolically does not try to represent the underlying algorithm, this must be done to your f. This is done by doing @register_symbolic f(x). If you have to define things like derivatives to f, then the function registration documentation.","category":"page"},{"location":"manual/faq/#Equality-and-set-membership-tests","page":"Frequently Asked Questions","title":"Equality and set membership tests","text":"","category":"section"},{"location":"manual/faq/","page":"Frequently Asked Questions","title":"Frequently Asked Questions","text":"Comparing symbols with == produces a symbolic equality, not a Bool. To produce a Bool, call isequal.","category":"page"},{"location":"manual/faq/","page":"Frequently Asked Questions","title":"Frequently Asked Questions","text":"To test if a symbol is part of a collection of symbols, i.e., a vector, either create a Set and use in, e.g.","category":"page"},{"location":"manual/faq/","page":"Frequently Asked Questions","title":"Frequently Asked Questions","text":"try \n x in [x]\ncatch e\n e\nend","category":"page"},{"location":"manual/faq/","page":"Frequently Asked Questions","title":"Frequently Asked Questions","text":"x in Set([x])","category":"page"},{"location":"manual/faq/","page":"Frequently Asked Questions","title":"Frequently Asked Questions","text":"any(isequal(x), [x])","category":"page"},{"location":"manual/faq/","page":"Frequently Asked Questions","title":"Frequently Asked Questions","text":"If == is used instead, you will receive TypeError: non-boolean (Num) used in boolean context. What this error is telling you is that the symbolic x == y expression is being used where a Bool is required, such as if x == y, and since the symbolic expression is held lazily this will error because the appropriate branch cannot be selected (since x == y is unknown for arbitrary symbolic values!). This is why the check isequal(x,y) is required, since this is a non-lazy check of whether the symbol x is always equal to the symbol y, rather than an expression of whether x and y currently have the same value.","category":"page"},{"location":"manual/faq/#Understanding-the-Difference-Between-the-Julia-Variable-and-the-Symbolic-Variable","page":"Frequently Asked Questions","title":"Understanding the Difference Between the Julia Variable and the Symbolic Variable","text":"","category":"section"},{"location":"manual/faq/","page":"Frequently Asked Questions","title":"Frequently Asked Questions","text":"In the most basic usage of Symbolics, the name of the Julia variable and the symbolic variable are the same. For example, when we do:","category":"page"},{"location":"manual/faq/","page":"Frequently Asked Questions","title":"Frequently Asked Questions","text":"@variables a","category":"page"},{"location":"manual/faq/","page":"Frequently Asked Questions","title":"Frequently Asked Questions","text":"the name of the symbolic variable is a and same with the Julia variable. However, we can de-couple these by setting a to a new symbolic variable, for example:","category":"page"},{"location":"manual/faq/","page":"Frequently Asked Questions","title":"Frequently Asked Questions","text":"b = only(@variables(a))","category":"page"},{"location":"manual/faq/","page":"Frequently Asked Questions","title":"Frequently Asked Questions","text":"Now the Julia variable b refers to the variable named a. However, the downside of this current approach is that it requires that the user writing the script knows the name a that they want to place to the variable. But what if for example we needed to get the variable's name from a file?","category":"page"},{"location":"manual/faq/","page":"Frequently Asked Questions","title":"Frequently Asked Questions","text":"To do this, one can interpolate a symbol into the @variables macro using $. For example:","category":"page"},{"location":"manual/faq/","page":"Frequently Asked Questions","title":"Frequently Asked Questions","text":"a = :c\nb = only(@variables($a))","category":"page"},{"location":"manual/faq/","page":"Frequently Asked Questions","title":"Frequently Asked Questions","text":"In this example, @variables($a) created a variable named c, and set this variable to b.","category":"page"},{"location":"comparison/#Comparison-of-Julia's-Symbolics.jl-vs-SymPy-for-Symbolic-Computation","page":"Comparison Against SymPy","title":"Comparison of Julia's Symbolics.jl vs SymPy for Symbolic Computation","text":"","category":"section"},{"location":"comparison/","page":"Comparison Against SymPy","title":"Comparison Against SymPy","text":"Symbolics.jl is a symbolic modeling language for Julia, built in Julia. Its goal is very different from Sympy: it was made to support symbolic-numerics, the combination of symbolic computing with numerical methods to allow for extreme performance computing that would not be possible without modifying the model. Because of this, Symbolics.jl excels in many areas due to purposeful design decisions:","category":"page"},{"location":"comparison/","page":"Comparison Against SymPy","title":"Comparison Against SymPy","text":"Performance: Symbolics.jl is built in Julia, whereas SymPy was built in Python. Thus, the performance bar for Symbolics.jl is much higher. Symbolics.jl started because SymPy was far too slow and SymEngine was far too inflexible for the projects they were doing. Performance is key to Symbolics.jl. If you find any performance issues, please file an issue.\nbuild_function: lambdify is “fine” for some people, but if you're building a super fast MPI-enabled Julia/C/Fortran simulation code, having a function that hits the Python interpreter is less than optimal. By default, build_function builds fast JIT-compiled functions due to being in Julia. However, it has support for things like static arrays, non-allocating functions via mutation, fast functions on sparse matrices and arrays of arrays, etc.: all core details of doing high performance computing.\nParallelism: Symbolics.jl has pervasive parallelism. The symbolic simplification via SymbolicUtils.jl has built-in parallelism, Symbolics.jl builds functions that parallelize across threads. Symbolics.jl is compatible with GPU libraries like CUDA.jl.\nExtensible: Symbolics.jl and its underlying tools are written in pure Julia. Want to add new or better simplification rules? Add some Julia code! Need to add new derivatives? Add some Julia code! You get the picture. Breaking down these barriers makes it easier for the user to tailor the program to their needs and accelerates the development of the library.\nDeep integration with the Julia ecosystem: Symbolics.jl's integration with neural networks is not the only thing that's deep. Symbolics.jl is built with the same philosophy as other SciML packages, eschewing “monorepos” for a distributed development approach that ties together the work of many developers. The differentiation parts utilize tools from automatic differentiation libraries, all linear algebra functionality comes from tracing Julia Base itself, symbolic rewriting (simplification and substitution) comes from SymbolicUtils.jl, parallelism comes from Julia Base libraries, Dagger.jl, etc. SciML Tools like DataDrivenDiffEq.jl can reconstruct symbolic expressions from neural networks and data, while NeuralPDE.jl can automatically solve partial differential equations from symbolic descriptions using physics-informed neural networks. The list keeps going. All told, by design Symbolics.jl's development moves fast because it's effectively using the work of hundreds of Julia developers, allowing it to grow fast.\nWhile Symbolics.jl has many features missing from SymPy, it does not superset SymPy's functionality. For a list of missing features, see this issue.","category":"page"},{"location":"manual/groebner/#Groebner-bases","page":"Groebner bases","title":"Groebner bases","text":"","category":"section"},{"location":"manual/groebner/","page":"Groebner bases","title":"Groebner bases","text":"Groebner bases use the implementation of the F4 algorithm from Groebner.jl package as its backend. We refer to the documentation of Groebner.jl, which lists some implementations details and possible use-cases of Groebner bases.","category":"page"},{"location":"manual/groebner/","page":"Groebner bases","title":"Groebner bases","text":"groebner_basis","category":"page"},{"location":"manual/groebner/#Symbolics.groebner_basis","page":"Groebner bases","title":"Symbolics.groebner_basis","text":"groebner_basis(polynomials)\n\nComputes a Groebner basis of the ideal generated by the given polynomials.\n\nThis function requires a Groebner bases backend (such as Groebner.jl) to be loaded.\n\n\n\n\n\n","category":"function"},{"location":"manual/types/#Supported-types-and-dispatch-in-Symbolics","page":"Supported types and dispatch in Symbolics","title":"Supported types and dispatch in Symbolics","text":"","category":"section"},{"location":"manual/types/","page":"Supported types and dispatch in Symbolics","title":"Supported types and dispatch in Symbolics","text":"There is a tension between types as a representation of expression trees and types that are a subtype of types already present in Julia.","category":"page"},{"location":"manual/types/","page":"Supported types and dispatch in Symbolics","title":"Supported types and dispatch in Symbolics","text":"We want to be able to deal with expression trees in a unified way and not constrain expression trees themselves to be under an abstract type in Julia's type hierarchy. (For example, if we said that all expression trees are subtype of Real, then we couldn't represent array operations using the same expression tree.). But we also want to be able to pass in expression trees into places in existing code that accept Real values.","category":"page"},{"location":"manual/types/","page":"Supported types and dispatch in Symbolics","title":"Supported types and dispatch in Symbolics","text":"We accomplish this by wrapping expression trees in a simple wrapper type which is a subtype of our desired abstract type. For example, we wrap expression trees in the type Num which is a subtype of Real to make it behave like a Real number.","category":"page"},{"location":"manual/types/","page":"Supported types and dispatch in Symbolics","title":"Supported types and dispatch in Symbolics","text":"The methods on Num objects are forwarded to the wrapped expression tree. And care is taken so that an expression tree never internally contains Num – this is both for performance and separation of concerns.","category":"page"},{"location":"manual/types/","page":"Supported types and dispatch in Symbolics","title":"Supported types and dispatch in Symbolics","text":"User-facing APIs in Symbolics always take wrapped objects like Num, they are then internally unwrapped for expression tree manipulation.","category":"page"},{"location":"manual/types/","page":"Supported types and dispatch in Symbolics","title":"Supported types and dispatch in Symbolics","text":"Due to it requiring such wrappers, we only fully support a limited number of types as both the types of expression trees and the type as Julia sees them.","category":"page"},{"location":"manual/types/","page":"Supported types and dispatch in Symbolics","title":"Supported types and dispatch in Symbolics","text":"These types are","category":"page"},{"location":"manual/types/","page":"Supported types and dispatch in Symbolics","title":"Supported types and dispatch in Symbolics","text":"Real numbers (wrapped using Num)\ncomplex numbers (stored as Complex{Num} where Complex is from Base Julia)\narrays of Real and complex numbers (wrapped using Arr, so Arr{Num} or Arr{Complex{Num}})","category":"page"},{"location":"manual/types/#@variables-and-types","page":"Supported types and dispatch in Symbolics","title":"@variables and types","text":"","category":"section"},{"location":"manual/types/","page":"Supported types and dispatch in Symbolics","title":"Supported types and dispatch in Symbolics","text":"Use the syntax @variables x::T to create a symbol named x of symbolic type T. If T is a subtype of any of the above listed types which support a wrapper, the resulting variable will be wrapped in that type. As seen in the examples below, x,z,X,Z all have a suitable wrapper type. Hence, their types are shown. However, s being of symbolic type String does not have a corresponding wrapper supported by Symbolics, and hence, it returns a Sym{String} object. This is the trivial expression tree of a single variable without a wrapper, and is not a subtype of String or AbstractString.","category":"page"},{"location":"manual/types/","page":"Supported types and dispatch in Symbolics","title":"Supported types and dispatch in Symbolics","text":"using Symbolics\n@variables x::Real z::Complex{Real} (X::Real)[1:10, 1:10] (Z::Complex{Real})[1:10] s::String","category":"page"},{"location":"manual/types/","page":"Supported types and dispatch in Symbolics","title":"Supported types and dispatch in Symbolics","text":"typeof(x)","category":"page"},{"location":"manual/types/","page":"Supported types and dispatch in Symbolics","title":"Supported types and dispatch in Symbolics","text":"typeof(z)","category":"page"},{"location":"manual/types/","page":"Supported types and dispatch in Symbolics","title":"Supported types and dispatch in Symbolics","text":"typeof(X)","category":"page"},{"location":"manual/types/","page":"Supported types and dispatch in Symbolics","title":"Supported types and dispatch in Symbolics","text":"typeof(Z)","category":"page"},{"location":"manual/types/","page":"Supported types and dispatch in Symbolics","title":"Supported types and dispatch in Symbolics","text":"typeof(s)","category":"page"},{"location":"manual/parsing/#Parsing-Julia-Expressions-to-Symbolic-Expressions","page":"Parsing Julia Expressions to Symbolic Expressions","title":"Parsing Julia Expressions to Symbolic Expressions","text":"","category":"section"},{"location":"manual/parsing/","page":"Parsing Julia Expressions to Symbolic Expressions","title":"Parsing Julia Expressions to Symbolic Expressions","text":"Julia expressions such as :(y - x) are fundamentally different from symbolic expressions, as they do not have an algebra defined on them. Thus, it can be very helpful when building domain-specific languages (DSLs) and parsing files to convert from Julia expressions to Symbolics.jl expressions for further manipulation. Towards this end is the parse_expr_to_symbolic which performs the parsing.","category":"page"},{"location":"manual/parsing/","page":"Parsing Julia Expressions to Symbolic Expressions","title":"Parsing Julia Expressions to Symbolic Expressions","text":"warn: Warn\nTake the limitations mentioned in the parse_expr_to_symbolic docstrings seriously! Because Julia expressions contain no symbolic metadata, there is limited information and thus the parsing requires heuristics to work. ","category":"page"},{"location":"manual/parsing/","page":"Parsing Julia Expressions to Symbolic Expressions","title":"Parsing Julia Expressions to Symbolic Expressions","text":"parse_expr_to_symbolic\n@parse_expr_to_symbolic","category":"page"},{"location":"manual/parsing/#Symbolics.parse_expr_to_symbolic","page":"Parsing Julia Expressions to Symbolic Expressions","title":"Symbolics.parse_expr_to_symbolic","text":"parse_expr_to_symbolic(ex, mod::Module)\n\nApplies the parse_expr_to_symbolic function in the current module, i.e. parse_expr_to_symbolic(ex, mod) where mod is the module of the function caller.\n\nArguments\n\nex: the expression to parse\nmod: the module to apply the parsing in. See the limitations section for details.\n\nExample\n\nex = :(y(t) ~ x(t))\nparse_expr_to_symbolic(ex,Main) # gives the symbolic expression `y(t) ~ x(t)` in empty Main\n\n# Now do a whole system\n\nex = [:(y ~ x)\n :(y ~ -2x + 3 / z)\n :(z ~ 2)]\neqs = parse_expr_to_symbolic.(ex, (Main,))\n\n@variables x y z\nex = [y ~ x\n y ~ -2x + 3 / z\n z ~ 2]\nall(isequal.(eqs,ex)) # true\n\nLimitations\n\nSymbolic-ness Tied to Environment Definitions\n\nThe parsing to a symbolic expression has to be able to recognize the difference between functions, numbers, and globals defined within one's Julia environment and those that are to be made symbolic. The way this functionality handles this problem is that it does not define anything as symbolic that is already defined in the chosen mod module. For example, f(x,y) will have f as non-symbolic if the function f (named f) is defined in mod, i.e. if isdefined(mod,:f) is true. When the symbol is defined, it will be replaced by its value. Notably, this means that the parsing behavior changes depending on the environment that it is applied.\n\nFor example:\n\nparse_expr_to_symbolic(:(x - y),@__MODULE__) # x - y\nx = 2.0\nparse_expr_to_symbolic(:(x - y),@__MODULE__) # 2.0 - y\n\nThis is required to detect that standard functions like - are functions instead of symbolic symbols. For safety, one should create anonymous modules or other sub-environments to ensure no stray variables are defined.\n\nMetadata is Blank\n\nBecause all the variables defined by the expressions are not defined with the standard @variables, there is no metadata that is or can be associated with any of the generated variables. Instead, they all have blank metadata, but are defined in the Real domain. Thus, the variables which come out of this parsing may not evaluate as equal to a symbolic variable defined elsewhere.\n\n\n\n\n\n","category":"function"},{"location":"manual/variables/#Variable-and-Equation-Types","page":"Variable and Equation Types","title":"Variable and Equation Types","text":"","category":"section"},{"location":"manual/variables/","page":"Variable and Equation Types","title":"Variable and Equation Types","text":"Symbolics IR mirrors the Julia AST but allows for easy mathematical manipulation by itself following mathematical semantics. The base of the IR is the Sym type, which defines a symbolic variable. Registered (mathematical) functions on Syms (or iscall objects) return an expression that iscall. For example, op1 = x+y is one symbolic object and op2 = 2z is another, and so op1*op2 is another tree object. Then, at the top, an Equation, normally written as op1 ~ op2, defines the symbolic equality between two operations.","category":"page"},{"location":"manual/variables/#Types","page":"Variable and Equation Types","title":"Types","text":"","category":"section"},{"location":"manual/variables/","page":"Variable and Equation Types","title":"Variable and Equation Types","text":"Sym, Term, and FnType are from SymbolicUtils.jl. Note that in Symbolics, we always use Sym{Real}, Term{Real}, and FnType{Tuple{Any}, Real}. To get the arguments of an iscall object, use arguments(t::Term), and to get the operation, use operation(t::Term). However, note that one should never dispatch on Term or test isa Term. Instead, one needs to use SymbolicUtils.iscall to check if arguments and operation is defined.","category":"page"},{"location":"manual/variables/","page":"Variable and Equation Types","title":"Variable and Equation Types","text":"@variables\nSymbolics.variable\nSymbolics.variables\nEquation\nBase.:~(::Num, ::Num)","category":"page"},{"location":"manual/variables/#Symbolics.@variables","page":"Variable and Equation Types","title":"Symbolics.@variables","text":"Define one or more unknown variables.\n\n@variables t α σ(..) β[1:2]\n@variables w(..) x(t) y z(t, α, x)\n\nexpr = β[1]* x + y^α + σ(3) * (z - t) - β[2] * w(t - 1)\n\n(..) signifies that the value should be left uncalled.\n\nSymbolics supports creating variables that denote an array of some size.\n\njulia> @variables x[1:3]\n1-element Vector{Symbolics.Arr{Num, 1}}:\n x[1:3]\n\njulia> @variables y[1:3, 1:6] # support for tensors\n1-element Vector{Symbolics.Arr{Num, 2}}:\n y[1:3,1:6]\n\njulia> @variables t z(t)[1:3] # also works for dependent variables\n2-element Vector{Any}:\n t\n (z(t))[1:3]\n\nA symbol or expression that represents an array can be turned into an array of symbols or expressions using the scalarize function.\n\njulia> Symbolics.scalarize(z)\n3-element Vector{Num}:\n (z(t))[1]\n (z(t))[2]\n (z(t))[3]\n\nNote that @variables returns a vector of all the defined variables.\n\n@variables can also take runtime symbol values by the $ interpolation operator, and in this case, @variables doesn't automatically assign the value, instead, it only returns a vector of symbolic variables. All the rest of the syntax also applies here.\n\njulia> a, b, c = :runtime_symbol_value, :value_b, :value_c\n(:runtime_symbol_value, :value_b, :value_c)\n\njulia> vars = @variables t $a $b(t) $c(t)[1:3]\n4-element Vector{Any}:\n t\n runtime_symbol_value\n value_b(t)\n (value_c(t))[1:3]\n\njulia> (t, a, b, c)\n(t, :runtime_symbol_value, :value_b, :value_c)\n\n\n\n\n\n","category":"macro"},{"location":"manual/variables/#Symbolics.variable","page":"Variable and Equation Types","title":"Symbolics.variable","text":"variable(name::Symbol, idx::Integer...; T=Real)\n\nCreate a variable with the given name along with subscripted indices with the symtype=T. When T=FnType, it creates a symbolic function.\n\njulia> Symbolics.variable(:x, 4, 2, 0)\nx₄ˏ₂ˏ₀\n\njulia> Symbolics.variable(:x, 4, 2, 0, T=Symbolics.FnType)\nx₄ˏ₂ˏ₀⋆\n\nAlso see variables.\n\n\n\n\n\n","category":"function"},{"location":"manual/variables/#Symbolics.variables","page":"Variable and Equation Types","title":"Symbolics.variables","text":"variables(name::Symbol, indices...)\n\nCreate a multi-dimensional array of individual variables named with subscript notation. Use @variables instead to create symbolic array variables (as opposed to array of variables). See variable to create one variable with subscripts.\n\njulia> Symbolics.variables(:x, 1:3, 3:6)\n3×4 Matrix{Num}:\n x₁ˏ₃ x₁ˏ₄ x₁ˏ₅ x₁ˏ₆\n x₂ˏ₃ x₂ˏ₄ x₂ˏ₅ x₂ˏ₆\n x₃ˏ₃ x₃ˏ₄ x₃ˏ₅ x₃ˏ₆\n\n\n\n\n\n","category":"function"},{"location":"manual/variables/#Symbolics.Equation","page":"Variable and Equation Types","title":"Symbolics.Equation","text":"struct Equation\n\nAn equality relationship between two expressions.\n\nFields\n\nlhs: The expression on the left-hand side of the equation.\nrhs: The expression on the right-hand side of the equation.\n\n\n\n\n\n","category":"type"},{"location":"manual/variables/#Base.:~-Tuple{Num, Num}","page":"Variable and Equation Types","title":"Base.:~","text":"~(lhs, rhs) -> Any\n\n\nCreate an Equation out of two Num instances, or an Num and a Number.\n\nExamples\n\njulia> using Symbolics\n\njulia> @variables x y;\n\njulia> @variables A[1:3, 1:3] B[1:3, 1:3];\n\njulia> x ~ y\nx ~ y\n\njulia> x - y ~ 0\nx - y ~ 0\n\njulia> A ~ B\n(broadcast(~, A, B))[1:3,1:3]\n\njulia> A .~ 3x\n(broadcast(~, A, 3x))[1:3,1:3]\n\n\n\n\n\n","category":"method"},{"location":"manual/variables/#A-note-about-functions-restricted-to-Numbers","page":"Variable and Equation Types","title":"A note about functions restricted to Numbers","text":"","category":"section"},{"location":"manual/variables/","page":"Variable and Equation Types","title":"Variable and Equation Types","text":"Sym and Term objects are NOT subtypes of Number. Symbolics provides a simple wrapper type called Num which is a subtype of Real. Num wraps either a Sym or a Term or any other object, defines the same set of operations as symbolic expressions and forwards those to the values it wraps. You can use Symbolics.value function to unwrap a Num.","category":"page"},{"location":"manual/variables/","page":"Variable and Equation Types","title":"Variable and Equation Types","text":"By default, the @variables macros return Num-wrapped objects to allow calling functions which are restricted to Number or Real.","category":"page"},{"location":"manual/variables/","page":"Variable and Equation Types","title":"Variable and Equation Types","text":"using Symbolics\n@variables t x y z(t);\nSymbolics.operation(Symbolics.value(x + y))","category":"page"},{"location":"manual/variables/","page":"Variable and Equation Types","title":"Variable and Equation Types","text":"Symbolics.operation(Symbolics.value(z))","category":"page"},{"location":"manual/variables/","page":"Variable and Equation Types","title":"Variable and Equation Types","text":"Symbolics.arguments(Symbolics.value(x + y))","category":"page"},{"location":"manual/variables/","page":"Variable and Equation Types","title":"Variable and Equation Types","text":"Note that Julia converts irrationals — like π and ℯ — to Float64 whenever they are involved in arithmetic with other numbers, including integers. An expression like 2π will be converted to a float immediately, so an expression like 2π * x will leave the symbolic x multiplied by a Float64. It may be preferable to have a symbolic representation of π also, which can be achieved with Num(π). For generic programming, it may be helpful to simply redefine the variable π to be of the same type as some other argument, as in","category":"page"},{"location":"manual/variables/","page":"Variable and Equation Types","title":"Variable and Equation Types","text":"function f(x)\n let π=oftype(x, π)\n 1 + (2//3 + 4π/5) * x\n end\nend\nf(t)","category":"page"},{"location":"manual/variables/","page":"Variable and Equation Types","title":"Variable and Equation Types","text":"This will work for any floating-point input, as well as symbolic input.","category":"page"},{"location":"manual/variables/#Symbolic-Control-Flow","page":"Variable and Equation Types","title":"Symbolic Control Flow","text":"","category":"section"},{"location":"manual/variables/","page":"Variable and Equation Types","title":"Variable and Equation Types","text":"Control flow can be expressed in Symbolics.jl in the following ways:","category":"page"},{"location":"manual/variables/","page":"Variable and Equation Types","title":"Variable and Equation Types","text":"IfElse.ifelse(cond,x,y): this is a dispatch-able version of the ifelse function provided by IfElse.jl which allows for encoding conditionals in the symbolic branches.","category":"page"},{"location":"manual/variables/#Inspection-Functions","page":"Variable and Equation Types","title":"Inspection Functions","text":"","category":"section"},{"location":"manual/variables/","page":"Variable and Equation Types","title":"Variable and Equation Types","text":"SymbolicUtils.iscall\nSymbolicUtils.operation\nSymbolicUtils.arguments","category":"page"},{"location":"manual/sparsity_detection/#Structure-and-Sparsity-Detection","page":"Structure and Sparsity Detection","title":"Structure and Sparsity Detection","text":"","category":"section"},{"location":"manual/sparsity_detection/","page":"Structure and Sparsity Detection","title":"Structure and Sparsity Detection","text":"Using the tracing system provided by Symbolics.jl expressions, Symbolics.jl can automatically detect the sparsity patterns of Julia functions efficiently. This functionality is described in more detail in the paper:","category":"page"},{"location":"manual/sparsity_detection/","page":"Structure and Sparsity Detection","title":"Structure and Sparsity Detection","text":"@article{gowda2019sparsity,\n title={Sparsity Programming: Automated Sparsity-Aware Optimizations in Differentiable Programming},\n author={Gowda, Shashi and Ma, Yingbo and Churavy, Valentin and Edelman, Alan and Rackauckas, Christopher},\n year={2019}\n}","category":"page"},{"location":"manual/sparsity_detection/","page":"Structure and Sparsity Detection","title":"Structure and Sparsity Detection","text":"Please cite this work if the functionality is used.","category":"page"},{"location":"manual/sparsity_detection/#Sparsity-Detection","page":"Structure and Sparsity Detection","title":"Sparsity Detection","text":"","category":"section"},{"location":"manual/sparsity_detection/","page":"Structure and Sparsity Detection","title":"Structure and Sparsity Detection","text":"Symbolics.jacobian_sparsity\nSymbolics.hessian_sparsity","category":"page"},{"location":"manual/sparsity_detection/#Symbolics.jacobian_sparsity","page":"Structure and Sparsity Detection","title":"Symbolics.jacobian_sparsity","text":"jacobian_sparsity(\n exprs::AbstractArray,\n vars::AbstractArray\n) -> SparseArrays.SparseMatrixCSC{Bool, Int64}\n\n\nReturn the sparsity pattern of the Jacobian of an array of expressions with respect to an array of variable expressions.\n\nArguments\n\nexprs: an array of symbolic expressions.\nvars: an array of symbolic variables.\n\nExamples\n\njulia> using Symbolics\n\njulia> vars = @variables x₁ x₂;\n\njulia> exprs = [2x₁, 3x₂, 4x₁ * x₂];\n\njulia> Symbolics.jacobian_sparsity(exprs, vars)\n3×2 SparseArrays.SparseMatrixCSC{Bool, Int64} with 4 stored entries:\n 1 ⋅\n ⋅ 1\n 1 1\n\n\n\n\n\njacobian_sparsity(\n f!::Function,\n output::AbstractArray,\n input::AbstractArray,\n args...;\n kwargs...\n) -> SparseArrays.SparseMatrixCSC{Bool, Int64}\n\n\nReturn the sparsity pattern of the Jacobian of the mutating function f!.\n\nArguments\n\nf!: an in-place function f!(output, input, args...; kwargs...).\noutput: output array.\ninput: input array.\n\nThe eltype of output and input can be either symbolic or primitive.\n\nExamples\n\njulia> using Symbolics\n\njulia> f!(y, x) = y .= [x[2], 2x[1], 3x[1] * x[2]];\n\njulia> output = Vector{Float64}(undef, 3);\n\njulia> input = Vector{Float64}(undef, 2);\n\njulia> Symbolics.jacobian_sparsity(f!, output, input)\n3×2 SparseArrays.SparseMatrixCSC{Bool, Int64} with 4 stored entries:\n ⋅ 1\n 1 ⋅\n 1 1\n\n\n\n\n\n","category":"function"},{"location":"manual/sparsity_detection/#Symbolics.hessian_sparsity","page":"Structure and Sparsity Detection","title":"Symbolics.hessian_sparsity","text":"hessian_sparsity(\n expr,\n vars::AbstractVector;\n full\n) -> Union{SparseArrays.SparseMatrixCSC{Bool, Int64}, SparseArrays.SparseMatrixCSC{Int64, Int64}}\n\n\nReturn the sparsity pattern of the Hessian of an expression with respect to an array of variable expressions.\n\nArguments\n\nexpr: a symbolic expression.\nvars: a vector of symbolic variables.\n\nExamples\n\njulia> using Symbolics\n\njulia> vars = @variables x₁ x₂;\n\njulia> expr = 3x₁^2 + 4x₁ * x₂;\n\njulia> Symbolics.hessian_sparsity(expr, vars)\n2×2 SparseArrays.SparseMatrixCSC{Bool, Int64} with 3 stored entries:\n 1 1\n 1 ⋅\n\n\n\n\n\nhessian_sparsity(\n f::Function,\n input::AbstractVector,\n args...;\n full,\n kwargs...\n) -> Union{SparseArrays.SparseMatrixCSC{Bool, Int64}, SparseArrays.SparseMatrixCSC{Int64, Int64}}\n\n\nReturn the sparsity pattern of the Hessian of the given function f.\n\nArguments\n\nf: an out-of-place function f(input, args...; kwargs...).\ninput: a vector of input values whose eltype can be either symbolic or primitive.\n\nExamples\n\njulia> using Symbolics\n\njulia> f(x) = 4x[1] * x[2] - 5x[2]^2;\n\njulia> input = Vector{Float64}(undef, 2);\n\njulia> Symbolics.hessian_sparsity(f, input)\n2×2 SparseArrays.SparseMatrixCSC{Bool, Int64} with 3 stored entries:\n ⋅ 1\n 1 1\n\n\n\n\n\n","category":"function"},{"location":"manual/sparsity_detection/#Structure-Detection","page":"Structure and Sparsity Detection","title":"Structure Detection","text":"","category":"section"},{"location":"manual/sparsity_detection/","page":"Structure and Sparsity Detection","title":"Structure and Sparsity Detection","text":"Symbolics.islinear\nSymbolics.isaffine","category":"page"},{"location":"manual/sparsity_detection/#Symbolics.islinear","page":"Structure and Sparsity Detection","title":"Symbolics.islinear","text":"islinear(ex, u)\n\n\nCheck if an expression is linear with respect to a list of variable expressions.\n\n\n\n\n\n","category":"function"},{"location":"manual/sparsity_detection/#Symbolics.isaffine","page":"Structure and Sparsity Detection","title":"Symbolics.isaffine","text":"isaffine(ex, u)\n\n\nCheck if an expression is affine with respect to a list of variable expressions.\n\n\n\n\n\n","category":"function"},{"location":"manual/sparsity_detection/#ADTypes.jl-interface","page":"Structure and Sparsity Detection","title":"ADTypes.jl interface","text":"","category":"section"},{"location":"manual/sparsity_detection/","page":"Structure and Sparsity Detection","title":"Structure and Sparsity Detection","text":"Symbolics.SymbolicsSparsityDetector","category":"page"},{"location":"manual/sparsity_detection/#Symbolics.SymbolicsSparsityDetector","page":"Structure and Sparsity Detection","title":"Symbolics.SymbolicsSparsityDetector","text":"SymbolicsSparsityDetector <: ADTypes.AbstractSparsityDetector\n\nSparsity detection algorithm based on the Symbolics.jl tracing system.\n\nThis type makes Symbolics.jl compatible with the ADTypes.jl sparsity detection framework. The following functions are implemented:\n\nADTypes.jacobian_sparsity based on Symbolics.jacobian_sparsity\nADTypes.hessian_sparsity based on Symbolics.hessian_sparsity\n\nReference\n\nSparsity Programming: Automated Sparsity-Aware Optimizations in Differentiable Programming, Gowda et al. (2019)\n\n\n\n\n\n","category":"type"},{"location":"tutorials/auto_parallel/#Automated-Sparse-Parallelism-of-Julia-Functions-via-Tracing","page":"Automated Sparse Parallelism of Julia Functions via Tracing","title":"Automated Sparse Parallelism of Julia Functions via Tracing","text":"","category":"section"},{"location":"tutorials/auto_parallel/","page":"Automated Sparse Parallelism of Julia Functions via Tracing","title":"Automated Sparse Parallelism of Julia Functions via Tracing","text":"Because the Symbolics.jl expressions obey Julia semantics, one can directly transform existing Julia functions into Symbolics.jl symbolic representations of the function by simply inputting the symbolic values into the function and using what is returned. For example, let's take the following numerical PDE discretization:","category":"page"},{"location":"tutorials/auto_parallel/","page":"Automated Sparse Parallelism of Julia Functions via Tracing","title":"Automated Sparse Parallelism of Julia Functions via Tracing","text":"using Symbolics, LinearAlgebra, SparseArrays, Plots\n\n# Define the constants for the PDE\nconst α₂ = 1.0\nconst α₃ = 1.0\nconst β₁ = 1.0\nconst β₂ = 1.0\nconst β₃ = 1.0\nconst r₁ = 1.0\nconst r₂ = 1.0\nconst _DD = 100.0\nconst γ₁ = 0.1\nconst γ₂ = 0.1\nconst γ₃ = 0.1\nconst N = 32\nconst X = reshape([i for i in 1:N for j in 1:N], N, N)\nconst Y = reshape([j for i in 1:N for j in 1:N], N, N)\nconst α₁ = 1.0 .* (X .>= 4*N/5)\n\nconst Mx = Array(Tridiagonal([1.0 for i in 1:N-1], [-2.0 for i in 1:N], [1.0 for i in 1:N-1]))\nconst My = copy(Mx)\nMx[2, 1] = 2.0\nMx[end-1,end] = 2.0\nMy[1, 2] = 2.0\nMy[end,end-1] = 2.0\n\n# Define the discretized PDE as an ODE function\nfunction f(u, p, t)\n A = u[:,:,1]\n B = u[:,:,2]\n C = u[:,:,3]\n MyA = My*A\n AMx = A*Mx\n DA = @. _DD*(MyA + AMx)\n dA = @. DA + α₁ - β₁*A - r₁*A*B + r₂*C\n dB = @. α₂ - β₂*B - r₁*A*B + r₂*C\n dC = @. α₃ - β₃*C + r₁*A*B - r₂*C\n cat(dA, dB, dC, dims=3)\nend","category":"page"},{"location":"tutorials/auto_parallel/","page":"Automated Sparse Parallelism of Julia Functions via Tracing","title":"Automated Sparse Parallelism of Julia Functions via Tracing","text":"We can build the Symbolics version of this model by tracing the model function:","category":"page"},{"location":"tutorials/auto_parallel/","page":"Automated Sparse Parallelism of Julia Functions via Tracing","title":"Automated Sparse Parallelism of Julia Functions via Tracing","text":"# Define the initial condition as normal arrays\n@variables u[1:N, 1:N, 1:3]\ndu = simplify.(f(collect(u), nothing, 0.0))\nvec(du)[1:10]","category":"page"},{"location":"tutorials/auto_parallel/","page":"Automated Sparse Parallelism of Julia Functions via Tracing","title":"Automated Sparse Parallelism of Julia Functions via Tracing","text":"The output, here the in-place modified du, is a symbolic representation of each output of the function. We can then utilize this in the Symbolics functionality. For example, let's build a parallel version of f first:","category":"page"},{"location":"tutorials/auto_parallel/","page":"Automated Sparse Parallelism of Julia Functions via Tracing","title":"Automated Sparse Parallelism of Julia Functions via Tracing","text":"fastf = eval(Symbolics.build_function(du,u,\n parallel=Symbolics.MultithreadedForm())[2])","category":"page"},{"location":"tutorials/auto_parallel/","page":"Automated Sparse Parallelism of Julia Functions via Tracing","title":"Automated Sparse Parallelism of Julia Functions via Tracing","text":"Now let's compute the sparse Jacobian function and compile a fast multithreaded version:","category":"page"},{"location":"tutorials/auto_parallel/","page":"Automated Sparse Parallelism of Julia Functions via Tracing","title":"Automated Sparse Parallelism of Julia Functions via Tracing","text":"jac = Symbolics.sparsejacobian(vec(du), vec(u))\nrow,col,val = findnz(jac)\nscatter(row,col,legend=false,ms=1,c=:black)","category":"page"},{"location":"tutorials/auto_parallel/","page":"Automated Sparse Parallelism of Julia Functions via Tracing","title":"Automated Sparse Parallelism of Julia Functions via Tracing","text":"fjac = eval(Symbolics.build_function(jac,u,\n parallel=Symbolics.MultithreadedForm())[2])","category":"page"},{"location":"tutorials/auto_parallel/","page":"Automated Sparse Parallelism of Julia Functions via Tracing","title":"Automated Sparse Parallelism of Julia Functions via Tracing","text":"It takes awhile for this to generate, but the results will be worth it! Now let's set up the parabolic PDE to be solved by DifferentialEquations.jl. We will set up the vanilla version and the sparse multithreaded version:","category":"page"},{"location":"tutorials/auto_parallel/","page":"Automated Sparse Parallelism of Julia Functions via Tracing","title":"Automated Sparse Parallelism of Julia Functions via Tracing","text":"using OrdinaryDiffEq\nu0 = zeros(N, N, 3)\nMyA = zeros(N, N);\nAMx = zeros(N, N);\nDA = zeros(N, N);\nprob = ODEProblem(f, u0, (0.0, 10.0))\nfastprob = ODEProblem(ODEFunction((du, u, p, t) -> fastf(du, u),\n jac = (du, u, p, t) -> fjac(du, u),\n jac_prototype = similar(jac, Float64)),\n u0, (0.0, 10.0))","category":"page"},{"location":"tutorials/auto_parallel/","page":"Automated Sparse Parallelism of Julia Functions via Tracing","title":"Automated Sparse Parallelism of Julia Functions via Tracing","text":"Let's see the timing difference:","category":"page"},{"location":"tutorials/auto_parallel/","page":"Automated Sparse Parallelism of Julia Functions via Tracing","title":"Automated Sparse Parallelism of Julia Functions via Tracing","text":"using BenchmarkTools\n#@btime solve(prob, TRBDF2()); # 33.073 s (895404 allocations: 23.87 GiB)\n#warning the following solve takes a long time to compile, but afterwards is very fast.\n#@btime solve(fastprob, TRBDF2()); # 209.670 ms (8208 allocations: 109.25 MiB)","category":"page"},{"location":"tutorials/auto_parallel/","page":"Automated Sparse Parallelism of Julia Functions via Tracing","title":"Automated Sparse Parallelism of Julia Functions via Tracing","text":"Boom, an automatic 157x acceleration that grows as the size of the problem increases!","category":"page"},{"location":"manual/io/#I/O,-Saving,-and-Latex","page":"I/O, Saving, and Latex","title":"I/O, Saving, and Latex","text":"","category":"section"},{"location":"manual/io/","page":"I/O, Saving, and Latex","title":"I/O, Saving, and Latex","text":"Note that Julia's standard I/O functionality can be used to save Symbolics expressions out to files. For example, here we will generate an in-place version of f and save the anonymous function to a .jl file:","category":"page"},{"location":"manual/io/","page":"I/O, Saving, and Latex","title":"I/O, Saving, and Latex","text":"using Symbolics\n@variables u[1:3]\nfunction f(u)\n [u[1]-u[3],u[1]^2-u[2],u[3]+u[2]]\nend\nex1, ex2 = build_function(f(u),u)\nwrite(\"function.jl\", string(ex2))","category":"page"},{"location":"manual/io/","page":"I/O, Saving, and Latex","title":"I/O, Saving, and Latex","text":"Now we can do something like:","category":"page"},{"location":"manual/io/","page":"I/O, Saving, and Latex","title":"I/O, Saving, and Latex","text":"g = include(\"function.jl\")","category":"page"},{"location":"manual/io/","page":"I/O, Saving, and Latex","title":"I/O, Saving, and Latex","text":"and that will load the function back in. Note that this can be done to save the transformation results of Symbolics.jl so that they can be stored and used in a precompiled Julia package.","category":"page"},{"location":"manual/io/#Latexification","page":"I/O, Saving, and Latex","title":"Latexification","text":"","category":"section"},{"location":"manual/io/","page":"I/O, Saving, and Latex","title":"I/O, Saving, and Latex","text":"Symbolics.jl's expressions support Latexify.jl, and thus","category":"page"},{"location":"manual/io/","page":"I/O, Saving, and Latex","title":"I/O, Saving, and Latex","text":"using Latexify\nlatexify(ex)","category":"page"},{"location":"manual/io/","page":"I/O, Saving, and Latex","title":"I/O, Saving, and Latex","text":"will produce LaTeX output from Symbolics models and expressions. This works on basics like Term all the way to higher primitives like ODESystem and ReactionSystem.","category":"page"},{"location":"manual/solver/#Solver","page":"Solver","title":"Solver","text":"","category":"section"},{"location":"manual/solver/","page":"Solver","title":"Solver","text":"The main symbolic solver for Symbolics.jl is symbolic_solve. Symbolic solving means that it only uses symbolic (algebraic) methods and outputs exact solutions.","category":"page"},{"location":"manual/solver/","page":"Solver","title":"Solver","text":"Symbolics.symbolic_solve","category":"page"},{"location":"manual/solver/#Symbolics.symbolic_solve","page":"Solver","title":"Symbolics.symbolic_solve","text":"symbolic_solve(expr, x; dropmultiplicity=true, warns=true)\n\nsymbolic_solve is a function which attempts to solve input equations/expressions symbolically using various methods.\n\nArguments\n\nexpr: Could be a single univar expression in the form of a poly or multiple univar expressions or multiple multivar polys or a transcendental nonlinear function.\nx: Could be a single variable or an array of variables which should be solved\ndropmultiplicity (optional): Should the output be printed n times where n is the number of occurrence of the root? Say we have (x+1)^2, we then have 2 roots x = -1, by default the output is [-1], If dropmultiplicity is inputted as false, then the output is [-1, -1].\nwarns (optional): When invalid expressions or cases are inputted, should the solver warn you of such cases before returning nothing? if this is set to false, the solver returns nothing. By default, warns are set to true.\n\nSupported input\n\nThe base solver (symbolic_solve) has multiple solvers which chooses from depending on the the type of input (multiple/uni var and multiple/single expression) only after ensuring that the input is valid.\n\nThe expressions inputted can contain parameters, which are assumed to be transcendental. A parameter \"a\" is transcendental if there exists no polynomial P with rational coefficients such that P(a) = 0. Check the examples section.\n\nCurrently, symbolic_solve supports\n\nLinear and polynomial equations (with parameters)\nSystems of linear and polynomials equations (without extra parameters, for now)\nEquations with transcendental functions (with parameters)\n\nExamples\n\nsolve_univar (uses factoring and analytic solutions up to degree 4)\n\nnote: Note\nThe package Nemo is needed in order to use solve_univar as well as solve_multipoly, so executing using Nemo as you will see in the following examples is necessary; otherwise, the function will throw an error.\n\njulia> using Symbolics, Nemo;\n\njulia> @variables x a b;\n\njulia> expr = expand((x + b)*(x^2 + 2x + 1)*(x^2 - a))\n-a*b - a*x - 2a*b*x - 2a*(x^2) + b*(x^2) + x^3 - a*b*(x^2) - a*(x^3) + 2b*(x^3) + 2(x^4) + b*(x^4) + x^5\n\njulia> symbolic_solve(expr, x)\n4-element Vector{Any}:\n -1\n -b\n (1//2)*√(4a)\n (-1//2)*√(4a)\n\njulia> symbolic_solve(expr, x, dropmultiplicity=false)\n5-element Vector{Any}:\n -1\n -1\n -b\n (1//2)*√(4a)\n (-1//2)*√(4a)\n\njulia> symbolic_solve(x^2 + a*x + 6, x)\n2-element Vector{SymbolicUtils.BasicSymbolic{Real}}:\n (1//2)*(-a + √(-24 + a^2))\n (1//2)*(-a - √(-24 + a^2))\n\njulia> symbolic_solve(x^7 - 1, x)\n2-element Vector{Any}:\n roots_of((1//1) + x + x^2 + x^3 + x^4 + x^5 + x^6, x)\n 1\n\nsolve_multivar (uses Groebner basis and solve_univar to find roots)\n\nnote: Note\nSimilar to solve_univar, Groebner is needed for solve_multivar or to be fully functional.\n\njulia> using Groebner\n\njulia> @variables x y z\n3-element Vector{Num}:\n x\n y\n z\n\njulia> eqs = [x+y^2+z, z*x*y, z+3x+y]\n3-element Vector{Num}:\n x + z + y^2\n x*y*z\n 3x + y + z\n\njulia> symbolic_solve(eqs, [x,y,z])\n3-element Vector{Any}:\n Dict{Num, Any}(z => 0, y => 1//3, x => -1//9)\n Dict{Num, Any}(z => 0, y => 0, x => 0)\n Dict{Num, Any}(z => -1, y => 1, x => 0)\n\nnote: Note\nIf Nemo or Groebner are not imported when needed, the solver throws an error.\n\njulia> using Symbolics\n\njulia> @variables x y z;\n\njulia> symbolic_solve(x+1, x)\nERROR: \"Nemo is required. Execute `using Nemo` to enable this functionality.\"\n\njulia> symbolic_solve([x+1, y], [x, y])\nERROR: \"Groebner bases engine is required. Execute `using Groebner` to enable this functionality.\"\n\nsolve_multipoly (uses GCD between the input polys)\n\njulia> symbolic_solve([x-1, x^3 - 1, x^2 - 1, (x-1)^20], x)\n1-element Vector{BigInt}:\n 1\n\nia_solve (solving by isolation and attraction)\n\njulia> symbolic_solve(2^(x+1) + 5^(x+3), x)\n1-element Vector{SymbolicUtils.BasicSymbolic{Real}}:\n (-slog(2) - log(complex(-1)) + 3slog(5)) / (slog(2) - slog(5))\n\njulia> symbolic_solve(log(x+1)+log(x-1), x)\n2-element Vector{SymbolicUtils.BasicSymbolic{BigFloat}}:\n (1//2)*√(8.0)\n (-1//2)*√(8.0)\n\njulia> symbolic_solve(a*x^b + c, x)\n((-c)^(1 / b)) / (a^(1 / b))\n\nEvaluating output (converting to floats)\n\nIf you want to evaluate the exact expressions found by symbolic_solve, you can do the following:\n\njulia> roots = symbolic_solve(2^(x+1) + 5^(x+3), x)\n1-element Vector{SymbolicUtils.BasicSymbolic{Real}}:\n (-slog(2) - log(complex(-1)) + 3slog(5)) / (slog(2) - slog(5))\n\njulia> Symbolics.symbolic_to_float.(roots)\n1-element Vector{Complex{BigFloat}}:\n -4.512941594732059759689023145584186058252768936052415430071569066192919491762214 + 3.428598090438030380369414618548038962770087500755160535832807433942464545729382im\n\n\n\n\n\n","category":"function"},{"location":"manual/solver/","page":"Solver","title":"Solver","text":"One other symbolic solver is symbolic_linear_solve which is limited compared to symbolic_solve as it only solves linear equations.","category":"page"},{"location":"manual/solver/","page":"Solver","title":"Solver","text":"Symbolics.symbolic_linear_solve","category":"page"},{"location":"manual/solver/#Symbolics.symbolic_linear_solve","page":"Solver","title":"Symbolics.symbolic_linear_solve","text":"symbolic_linear_solve(eq, var; simplify, check) -> Any\n\n\nSolve equation(s) eqs for a set of variables vars.\n\nAssumes length(eqs) == length(vars)\n\nCurrently only works if all equations are linear. check if the expr is linear w.r.t vars.\n\nExamples\n\njulia> @variables x y\n2-element Vector{Num}:\n x\n y\n\njulia> Symbolics.symbolic_linear_solve(x + y ~ 0, x)\n-y\n\njulia> Symbolics.symbolic_linear_solve([x + y ~ 0, x - y ~ 2], [x, y])\n2-element Vector{Float64}:\n 1.0\n -1.0\n\n\n\n\n\n","category":"function"},{"location":"manual/solver/","page":"Solver","title":"Solver","text":"symbolic_solve only supports symbolic, i.e. non-floating point computations, and thus prefers equations where the coefficients are integer, rational, or symbolic. Floating point coefficients are transformed into rational values and BigInt values are used internally with a potential performance loss, and thus it is recommended that this functionality is only used with floating point values if necessary. In contrast, symbolic_linear_solve directly handles floating point values using standard factorizations.","category":"page"},{"location":"manual/solver/#More-technical-details-and-examples","page":"Solver","title":"More technical details and examples","text":"","category":"section"},{"location":"manual/solver/#Technical-details","page":"Solver","title":"Technical details","text":"","category":"section"},{"location":"manual/solver/","page":"Solver","title":"Solver","text":"The symbolic_solve function uses 4 hidden solvers in order to solve the user's input. Its base, solve_univar, uses analytic solutions up to polynomials of degree 4 and factoring as its method for solving univariate polynomials. The function's solve_multipoly uses GCD on the input polynomials then throws passes the result to solve_univar. The function's solve_multivar uses Groebner basis and a separating form in order to create linear equations in the input variables and a single high degree equation in the separating variable [1]. Each equation resulting from the basis is then passed to solve_univar. We can see that essentially, solve_univar is the building block of symbolic_solve. If the input is not a valid polynomial and can not be solved by the algorithm above, symbolic_solve passes it to ia_solve, which attempts solving by attraction and isolation [2]. This only works when the input is a single expression and the user wants the answer in terms of a single variable. Say log(x) - a == 0 gives us [e^a].","category":"page"},{"location":"manual/solver/#Nice-examples","page":"Solver","title":"Nice examples","text":"","category":"section"},{"location":"manual/solver/","page":"Solver","title":"Solver","text":"using Symbolics, Nemo;\n@variables x;\nSymbolics.symbolic_solve(9^x + 3^x ~ 8, x)","category":"page"},{"location":"manual/solver/","page":"Solver","title":"Solver","text":"@variables x y z;\nSymbolics.symbolic_linear_solve(2//1*x + y - 2//1*z ~ 9//1*x, 1//1*x)","category":"page"},{"location":"manual/solver/","page":"Solver","title":"Solver","text":"using Groebner;\n@variables x y z;\n\neqs = [x^2 + y + z - 1, x + y^2 + z - 1, x + y + z^2 - 1]\nSymbolics.symbolic_solve(eqs, [x,y,z])","category":"page"},{"location":"manual/solver/#Feature-completeness","page":"Solver","title":"Feature completeness","text":"","category":"section"},{"location":"manual/solver/","page":"Solver","title":"Solver","text":"[x] Linear and polynomial equations\n[x] Systems of linear and polynomial equations\n[x] Some transcendental functions\n[x] Systems of linear equations with parameters (via symbolic_linear_solve)\n[ ] Equations with radicals\n[x] Systems of polynomial equations with parameters and positive dimensional systems\n[ ] Inequalities","category":"page"},{"location":"manual/solver/#Expressions-we-can-not-solve-(but-aim-to)","page":"Solver","title":"Expressions we can not solve (but aim to)","text":"","category":"section"},{"location":"manual/solver/","page":"Solver","title":"Solver","text":"# Mathematica\n\nIn[1]:= Reduce[x^2 - x - 6 > 0, x]\nOut[1]= x < -2 || x > 3\n\nIn[2]:= Reduce[x+a > 0, x]\nOut[2]= a \\[Element] Reals && x > -a\n\nIn[3]:= Solve[x^(x) + 3 == 0, x]\nOut[3]= {{x -> (I \\[Pi] + Log[3])/ProductLog[I \\[Pi] + Log[3]]}}","category":"page"},{"location":"manual/solver/#References","page":"Solver","title":"References","text":"","category":"section"},{"location":"manual/solver/","page":"Solver","title":"Solver","text":"[1]: Rouillier, F. Solving Zero-Dimensional Systems Through the Rational Univariate Representation. AAECC 9, 433–461 (1999).","category":"page"},{"location":"manual/solver/","page":"Solver","title":"Solver","text":"[2]: R. W. Hamming, Coding and Information Theory, ScienceDirect, 1980.","category":"page"},{"location":"tutorials/converting_to_C/#Automatic-Conversion-of-Julia-Code-to-C-Functions","page":"Automatic Conversion of Julia Code to C Functions","title":"Automatic Conversion of Julia Code to C Functions","text":"","category":"section"},{"location":"tutorials/converting_to_C/","page":"Automatic Conversion of Julia Code to C Functions","title":"Automatic Conversion of Julia Code to C Functions","text":"Since Symbolics.jl can trace Julia code into Symbolics IR that can be built and compiled via build_function to C, this gives us a nifty way to automatically generate C functions from Julia code! To see this in action, let's start with the Lotka-Volterra equations:","category":"page"},{"location":"tutorials/converting_to_C/","page":"Automatic Conversion of Julia Code to C Functions","title":"Automatic Conversion of Julia Code to C Functions","text":"using Symbolics\nfunction lotka_volterra!(du, u, p, t)\n x, y = u\n α, β, δ, γ = p\n du[1] = dx = α*x - β*x*y\n du[2] = dy = -δ*y + γ*x*y\nend","category":"page"},{"location":"tutorials/converting_to_C/","page":"Automatic Conversion of Julia Code to C Functions","title":"Automatic Conversion of Julia Code to C Functions","text":"Now we trace this into Symbolics:","category":"page"},{"location":"tutorials/converting_to_C/","page":"Automatic Conversion of Julia Code to C Functions","title":"Automatic Conversion of Julia Code to C Functions","text":"@variables t du[1:2] u[1:2] p[1:4]\ndu = collect(du)\nlotka_volterra!(du, u, p, t)\ndu","category":"page"},{"location":"tutorials/converting_to_C/","page":"Automatic Conversion of Julia Code to C Functions","title":"Automatic Conversion of Julia Code to C Functions","text":"and then we build the function:","category":"page"},{"location":"tutorials/converting_to_C/","page":"Automatic Conversion of Julia Code to C Functions","title":"Automatic Conversion of Julia Code to C Functions","text":"build_function(du, u, p, t, target=Symbolics.CTarget())","category":"page"},{"location":"tutorials/converting_to_C/","page":"Automatic Conversion of Julia Code to C Functions","title":"Automatic Conversion of Julia Code to C Functions","text":"If we want to compile this, we do expression=Val{false}:","category":"page"},{"location":"tutorials/converting_to_C/","page":"Automatic Conversion of Julia Code to C Functions","title":"Automatic Conversion of Julia Code to C Functions","text":"f = build_function(du, u, p, t, target=Symbolics.CTarget(), expression=Val{false})","category":"page"},{"location":"tutorials/converting_to_C/","page":"Automatic Conversion of Julia Code to C Functions","title":"Automatic Conversion of Julia Code to C Functions","text":"now we check it computes the same thing:","category":"page"},{"location":"tutorials/converting_to_C/","page":"Automatic Conversion of Julia Code to C Functions","title":"Automatic Conversion of Julia Code to C Functions","text":"du = rand(2); du2 = rand(2)\nu = rand(2)\np = rand(4)\nt = rand()\nf(du, u, p, t)\nlotka_volterra!(du2, u, p, t)\ndu == du2 # true!","category":"page"},{"location":"manual/build_function/#Function-Building-and-Compilation-(build_function)","page":"Function Building and Compilation (build_function)","title":"Function Building and Compilation (build_function)","text":"","category":"section"},{"location":"manual/build_function/","page":"Function Building and Compilation (build_function)","title":"Function Building and Compilation (build_function)","text":"At any time, callable functions can be generated from Symbolics IR by using Symbolics.toexpr. This performs some cleaning to return an expression without extraneous pieces that commonly matches expressions one would write in functions like those for differential equation solvers and optimization libraries. These functions can be automatically parallelized and specialize on Julia types like static arrays and sparse matrices.","category":"page"},{"location":"manual/build_function/","page":"Function Building and Compilation (build_function)","title":"Function Building and Compilation (build_function)","text":"The core compilation process of Symbolics IR is build_function. build_function takes an operation or an AbstractArray of operations and generates a compilable version of the model for numerical solvers. The form of this output is dependent on the target. By default, the target outputs Julia code, but other formats, such as C, Stan, and MATLAB are available. These can be generated as expressions which can then be evaluated into a callable function, or the compilers for the respective targets can be invoked to directly give back the function handle.","category":"page"},{"location":"manual/build_function/#build_function","page":"Function Building and Compilation (build_function)","title":"build_function","text":"","category":"section"},{"location":"manual/build_function/","page":"Function Building and Compilation (build_function)","title":"Function Building and Compilation (build_function)","text":"build_function","category":"page"},{"location":"manual/build_function/#Symbolics.build_function","page":"Function Building and Compilation (build_function)","title":"Symbolics.build_function","text":"build_function\n\nGenerates a numerically-usable function from a Symbolics Num.\n\nbuild_function(ex, args...;\n expression = Val{true},\n target = JuliaTarget(),\n parallel=nothing,\n kwargs...)\n\nArguments:\n\nex: The Num to compile\nargs: The arguments of the function\nexpression: Whether to generate code or whether to generate the compiled form. By default, expression = Val{true}, which means that the code for the function is returned. If Val{false}, then the returned value is compiled.\n\nKeyword Arguments:\n\ntarget: The output target of the compilation process. Possible options are:\nJuliaTarget: Generates a Julia function\nCTarget: Generates a C function\nStanTarget: Generates a function for compiling with the Stan probabilistic programming language\nMATLABTarget: Generates an anonymous function for use in MATLAB and Octave environments\nparallel: The kind of parallelism to use in the generated function. Defaults to SerialForm(), i.e. no parallelism, if ex is a single expression or an array containing <= 1500 non-zero expressions. If ex is an array of > 1500 non-zero expressions, then ShardedForm(80, 4) is used. See below for more on ShardedForm. Note that the parallel forms are not exported and thus need to be chosen like Symbolics.SerialForm(). The choices are:\nSerialForm(): Serial execution.\nShardedForm(cutoff, ncalls): splits the output function into sub-functions which contain at most cutoff number of output rhss. These sub-functions are called by the top-level function that buildfunction returns. This helps in reducing the compile time of the generated function.\nMultithreadedForm(): Multithreaded execution with a static split, evenly splitting the number of expressions per thread.\nfname: Used by some targets for the name of the function in the target space.\n\nNote that not all build targets support the full compilation interface. Check the individual target documentation for details.\n\n\n\n\n\n","category":"function"},{"location":"manual/build_function/#Target-Specific-Definitions","page":"Function Building and Compilation (build_function)","title":"Target-Specific Definitions","text":"","category":"section"},{"location":"manual/build_function/","page":"Function Building and Compilation (build_function)","title":"Function Building and Compilation (build_function)","text":"Symbolics._build_function(target::Symbolics.JuliaTarget,rhss::AbstractArray,args...;kwargs...)\nSymbolics._build_function(target::Symbolics.CTarget,eqs::Array{<:Equation},args...;kwargs...)\nSymbolics._build_function(target::Symbolics.StanTarget,eqs::Array{<:Equation}, vs, ps, iv;kwargs...)\nSymbolics._build_function(target::Symbolics.MATLABTarget,eqs::Array{<:Equation},args...;kwargs...)","category":"page"},{"location":"manual/build_function/#Symbolics._build_function-Tuple{Symbolics.JuliaTarget, AbstractArray, Vararg{Any}}","page":"Function Building and Compilation (build_function)","title":"Symbolics._build_function","text":"_build_function(target::JuliaTarget, rhss::AbstractArray, args...;\n conv=toexpr,\n expression = Val{true},\n expression_module = @__MODULE__(),\n checkbounds = false,\n postprocess_fbody=ex -> ex,\n linenumbers = false,\n outputidxs=nothing,\n skipzeros = false,\n force_SA = false,\n wrap_code = (nothing, nothing),\n fillzeros = skipzeros && !(rhss isa SparseMatrixCSC),\n states = LazyState(),\n iip_config = (true, true),\n parallel=nothing, cse = false, kwargs...)\n\nBuild function target: JuliaTarget\n\nfunction _build_function(target::JuliaTarget, rhss, args...;\n conv = toexpr,\n expression = Val{true},\n checkbounds = false,\n linenumbers = false,\n headerfun = addheader, outputidxs=nothing,\n convert_oop = true, force_SA = false,\n skipzeros = outputidxs===nothing,\n fillzeros = skipzeros && !(typeof(rhss)<:SparseMatrixCSC),\n parallel=SerialForm(), kwargs...)\n\nGenerates a Julia function which can then be utilized for further evaluations. If expression=Val{false}, the return is a Julia function which utilizes RuntimeGeneratedFunctions.jl to be free of world-age issues.\n\nIf the rhss is a scalar, the generated function is a function with a scalar output. Otherwise, if it's an AbstractArray, the output is two functions, one for out-of-place AbstractArray output and a second which is a mutating function. The outputted functions match the given argument order, i.e., f(u,p,args...) for the out-of-place and scalar functions and f!(du,u,p,args..) for the in-place version.\n\nSpecial Keyword Arguments:\n\nparallel: The kind of parallelism to use in the generated function. Defaults to SerialForm(), i.e. no parallelism. Note that the parallel forms are not exported and thus need to be chosen like Symbolics.SerialForm(). The choices are:\nSerialForm(): Serial execution.\nShardedForm(cutoff, ncalls): splits the output function into sub-functions which contain at most cutoff number of output rhss. These sub-functions are called by the top-level function that buildfunction returns.\nMultithreadedForm(): Multithreaded execution with a static split, evenly splitting the number of expressions per thread.\nconv: The conversion function of symbolic types to Expr. By default, this uses the toexpr function.\ncheckbounds: For whether to enable bounds checking inside the generated function. Defaults to false, meaning that @inbounds is applied.\nlinenumbers: Determines whether the generated function expression retains the line numbers. Defaults to true.\nconvert_oop: Determines whether the OOP version should try to convert the output to match the type of the first input. This is useful for cases like LabelledArrays or other array types that carry extra information. Defaults to true.\nforce_SA: Forces the output of the OOP version to be a StaticArray. Defaults to false, and outputs a static array when the first argument is a static array.\nskipzeros: Whether to skip filling zeros in the in-place version if the filling function is 0.\nfillzeros: Whether to perform fill(out,0) before the calculations to ensure safety with skipzeros.\n\n\n\n\n\n","category":"method"},{"location":"manual/build_function/#Symbolics._build_function-Tuple{Symbolics.CTarget, Array{<:Equation}, Vararg{Any}}","page":"Function Building and Compilation (build_function)","title":"Symbolics._build_function","text":"Build function target: CTarget\n\nfunction _build_function(target::CTarget, eqs::Array{<:Equation}, args...;\n conv = toexpr, expression = Val{true},\n fname = :diffeqf,\n lhsname=:du,rhsnames=[Symbol(\"RHS$i\") for i in 1:length(args)],\n libpath=tempname(),compiler=:gcc)\n\nThis builds an in-place C function. Only works on arrays of equations. If expression == Val{false}, then this builds a function in C, compiles it, and returns a lambda to that compiled function. These special keyword arguments control the compilation:\n\nlibpath: the path to store the binary. Defaults to a temporary path.\ncompiler: which C compiler to use. Defaults to :gcc, which is currently the only available option.\n\n\n\n\n\n","category":"method"},{"location":"manual/build_function/#Symbolics._build_function-Tuple{Symbolics.StanTarget, Array{<:Equation}, Any, Any, Any}","page":"Function Building and Compilation (build_function)","title":"Symbolics._build_function","text":"Build function target: StanTarget\n\nfunction _build_function(target::StanTarget, eqs::Array{<:Equation}, vs, ps, iv;\n conv = toexpr, expression = Val{true},\n fname = :diffeqf, lhsname=:internal_var___du,\n rhsnames=[:internal_var___u,:internal_var___p,:internal_var___t])\n\nThis builds an in-place Stan function compatible with the Stan differential equation solvers. Unlike other build targets, this one requires (vs, ps, iv) as the function arguments. Only allowed on arrays of equations.\n\n\n\n\n\n","category":"method"},{"location":"manual/build_function/#Symbolics._build_function-Tuple{Symbolics.MATLABTarget, Array{<:Equation}, Vararg{Any}}","page":"Function Building and Compilation (build_function)","title":"Symbolics._build_function","text":"Build function target: MATLABTarget\n\nfunction _build_function(target::MATLABTarget, eqs::Array{<:Equation}, args...;\n conv = toexpr, expression = Val{true},\n lhsname=:internal_var___du,\n rhsnames=[:internal_var___u,:internal_var___p,:internal_var___t])\n\nThis builds an out of place anonymous function @(t,rhsnames[1]) to be used in MATLAB. Compatible with the MATLAB differential equation solvers. Only allowed on expressions, and arrays of expressions.\n\n\n\n\n\n","category":"method"},{"location":"manual/build_function/#Limitations","page":"Function Building and Compilation (build_function)","title":"Limitations","text":"","category":"section"},{"location":"manual/build_function/","page":"Function Building and Compilation (build_function)","title":"Function Building and Compilation (build_function)","text":"build_function ","category":"page"},{"location":"manual/arrays/#symbolic_arrays","page":"Symbolic Arrays","title":"Symbolic Arrays","text":"","category":"section"},{"location":"manual/arrays/#Symbolic-Arrays-vs-Arrays-of-Symbolic-Expressions","page":"Symbolic Arrays","title":"Symbolic Arrays vs Arrays of Symbolic Expressions","text":"","category":"section"},{"location":"manual/arrays/","page":"Symbolic Arrays","title":"Symbolic Arrays","text":"Symbolics.jl contains two forms for handling symbolic arrays:","category":"page"},{"location":"manual/arrays/","page":"Symbolic Arrays","title":"Symbolic Arrays","text":"Arrays of symbolic expressions: these are Julia arrays with Symbolics.jl objects in them.\nSymbolic Arrays: these are symbolic (O(1)) representations of arrays.","category":"page"},{"location":"manual/arrays/","page":"Symbolic Arrays","title":"Symbolic Arrays","text":"Arrays of symbolic expressions are simply Symbolics.jl objects put into Julia arrays. For example:","category":"page"},{"location":"manual/arrays/","page":"Symbolic Arrays","title":"Symbolic Arrays","text":"using Symbolics\n@variables x y\nu = [x,y]","category":"page"},{"location":"manual/arrays/","page":"Symbolic Arrays","title":"Symbolic Arrays","text":"is a vector of two symbolic variables. As shorthand,","category":"page"},{"location":"manual/arrays/","page":"Symbolic Arrays","title":"Symbolic Arrays","text":"u2 = Symbolics.variables(:x, 1:3, 3:6)","category":"page"},{"location":"manual/arrays/","page":"Symbolic Arrays","title":"Symbolic Arrays","text":"creates a Julia matrix of symbolic variables. Indexing u or u2 gives symbolic values which act as a normal scalar symbolic value. This form these uses Julia's array functionality and performs symbolic operations on the scalar values.","category":"page"},{"location":"manual/arrays/","page":"Symbolic Arrays","title":"Symbolic Arrays","text":"On the otherhand, Julia's symbolic array form is an O(1) representation of the whole array.","category":"page"},{"location":"manual/arrays/","page":"Symbolic Arrays","title":"Symbolic Arrays","text":"@variables A[1:5, 1:3]","category":"page"},{"location":"manual/arrays/","page":"Symbolic Arrays","title":"Symbolic Arrays","text":"When using this form, A[1,1] is not a symbolic variable but a symbolic expression for indexing the variable A. This representation holds linear algebra expressions in a non-expanded form. For example:","category":"page"},{"location":"manual/arrays/","page":"Symbolic Arrays","title":"Symbolic Arrays","text":"@variables B[1:3, 1:3]\nA * B","category":"page"},{"location":"manual/arrays/","page":"Symbolic Arrays","title":"Symbolic Arrays","text":"in comparison to:","category":"page"},{"location":"manual/arrays/","page":"Symbolic Arrays","title":"Symbolic Arrays","text":"a = Symbolics.variables(:a, 1:5, 1:3)\nb = Symbolics.variables(:b, 1:3, 1:3)\na * b","category":"page"},{"location":"manual/arrays/","page":"Symbolic Arrays","title":"Symbolic Arrays","text":"This makes the symbolic array form much more efficient, but requires that the expressions uses things with registered symbolic array functions which currently has much lower coverage. Also, there are many fallbacks for which arrays of symbolics which makes this approach more accessible but with larger expressions.","category":"page"},{"location":"manual/arrays/","page":"Symbolic Arrays","title":"Symbolic Arrays","text":"We recommend defaulting to arrays of symbolics unless you need the expression symplifications of the symbolic array approach.","category":"page"},{"location":"manual/arrays/#Using-Symbolic-Arrays","page":"Symbolic Arrays","title":"Using Symbolic Arrays","text":"","category":"section"},{"location":"manual/arrays/","page":"Symbolic Arrays","title":"Symbolic Arrays","text":"Symbolic array-valued expressions (symbolic arrays) are supported by Symbolics. Symbolic array expressions propagate useful metadata that depends on input arrays: array dimension, element type and shape.","category":"page"},{"location":"manual/arrays/","page":"Symbolic Arrays","title":"Symbolic Arrays","text":"You can create a symbolic array variable with the following syntax:","category":"page"},{"location":"manual/arrays/","page":"Symbolic Arrays","title":"Symbolic Arrays","text":"using Symbolics\n@variables A[1:5, 1:3] b[1:3]","category":"page"},{"location":"manual/arrays/","page":"Symbolic Arrays","title":"Symbolic Arrays","text":"Here, A is a symbolic matrix of size (5, 3) and b is a symbolic vector of length 3.","category":"page"},{"location":"manual/arrays/","page":"Symbolic Arrays","title":"Symbolic Arrays","text":"size(A)","category":"page"},{"location":"manual/arrays/","page":"Symbolic Arrays","title":"Symbolic Arrays","text":"size(b)","category":"page"},{"location":"manual/arrays/","page":"Symbolic Arrays","title":"Symbolic Arrays","text":"ndims(A)","category":"page"},{"location":"manual/arrays/","page":"Symbolic Arrays","title":"Symbolic Arrays","text":"ndims(b)","category":"page"},{"location":"manual/arrays/","page":"Symbolic Arrays","title":"Symbolic Arrays","text":"eltype(A)","category":"page"},{"location":"manual/arrays/","page":"Symbolic Arrays","title":"Symbolic Arrays","text":"eltype(b)","category":"page"},{"location":"manual/arrays/#Array-operations","page":"Symbolic Arrays","title":"Array operations","text":"","category":"section"},{"location":"manual/arrays/","page":"Symbolic Arrays","title":"Symbolic Arrays","text":"Operations on symbolic arrays return symbolic array expressions:","category":"page"},{"location":"manual/arrays/","page":"Symbolic Arrays","title":"Symbolic Arrays","text":"c = A * b","category":"page"},{"location":"manual/arrays/","page":"Symbolic Arrays","title":"Symbolic Arrays","text":"size(c)","category":"page"},{"location":"manual/arrays/","page":"Symbolic Arrays","title":"Symbolic Arrays","text":"eltype(c)","category":"page"},{"location":"manual/arrays/","page":"Symbolic Arrays","title":"Symbolic Arrays","text":"Adjoints, matrix-matrix, and matrix-vector multiplications are supported. Dot product returns a scalar-valued expression:","category":"page"},{"location":"manual/arrays/","page":"Symbolic Arrays","title":"Symbolic Arrays","text":"b'b","category":"page"},{"location":"manual/arrays/","page":"Symbolic Arrays","title":"Symbolic Arrays","text":"size(b'b)","category":"page"},{"location":"manual/arrays/","page":"Symbolic Arrays","title":"Symbolic Arrays","text":"Outer product returns a matrix:","category":"page"},{"location":"manual/arrays/","page":"Symbolic Arrays","title":"Symbolic Arrays","text":"b * b'","category":"page"},{"location":"manual/arrays/","page":"Symbolic Arrays","title":"Symbolic Arrays","text":"size(b*b')","category":"page"},{"location":"manual/arrays/#Broadcast,-map-and-reduce","page":"Symbolic Arrays","title":"Broadcast, map and reduce","text":"","category":"section"},{"location":"manual/arrays/","page":"Symbolic Arrays","title":"Symbolic Arrays","text":"A .* b'","category":"page"},{"location":"manual/arrays/","page":"Symbolic Arrays","title":"Symbolic Arrays","text":"map(asin, (A*b))","category":"page"},{"location":"manual/arrays/","page":"Symbolic Arrays","title":"Symbolic Arrays","text":"#sum(A) #latexify not working","category":"page"},{"location":"manual/arrays/","page":"Symbolic Arrays","title":"Symbolic Arrays","text":"typeof(sum(A))","category":"page"},{"location":"manual/arrays/","page":"Symbolic Arrays","title":"Symbolic Arrays","text":"typeof(sum(A, dims=2))","category":"page"},{"location":"manual/arrays/#Indexing-and-delayed-computation","page":"Symbolic Arrays","title":"Indexing and delayed computation","text":"","category":"section"},{"location":"manual/arrays/","page":"Symbolic Arrays","title":"Symbolic Arrays","text":"Indexing array expressions is fairly flexible in Symbolics. Let's go through all the possible ways to index arrays.","category":"page"},{"location":"manual/arrays/#Scalar-indexing-and-scalarization","page":"Symbolic Arrays","title":"Scalar indexing and scalarization","text":"","category":"section"},{"location":"manual/arrays/","page":"Symbolic Arrays","title":"Symbolic Arrays","text":"AAt = A*A'","category":"page"},{"location":"manual/arrays/","page":"Symbolic Arrays","title":"Symbolic Arrays","text":"AAt[2,3]","category":"page"},{"location":"manual/arrays/","page":"Symbolic Arrays","title":"Symbolic Arrays","text":"Here we indexed for the element (2,3), but we got back a symbolic indexing expression. You may want to force the element to be computed in terms of the elements of A. This can be done, using the scalarize function.","category":"page"},{"location":"manual/arrays/","page":"Symbolic Arrays","title":"Symbolic Arrays","text":"Symbolics.scalarize(AAt[2,3])","category":"page"},{"location":"manual/arrays/","page":"Symbolic Arrays","title":"Symbolic Arrays","text":"@syms i::Int j::Int\nSymbolics.scalarize(AAt[i,j])","category":"page"},{"location":"manual/arrays/","page":"Symbolic Arrays","title":"Symbolic Arrays","text":"In general, any scalar expression which is derived from array expressions can be scalarized.","category":"page"},{"location":"manual/arrays/","page":"Symbolic Arrays","title":"Symbolic Arrays","text":"#sum(A[:,1]) + sum(A[2,:])#latexify not working","category":"page"},{"location":"manual/arrays/","page":"Symbolic Arrays","title":"Symbolic Arrays","text":"Symbolics.scalarize(sum(A[:,1]) + sum(A[2,:]))","category":"page"},{"location":"manual/limits/#Symbolic-Limits","page":"Symbolic Limits","title":"Symbolic Limits","text":"","category":"section"},{"location":"manual/limits/","page":"Symbolic Limits","title":"Symbolic Limits","text":"Experimental symbolic limit support is provided by the limit function, documented below. See SymbolicLimits.jl for more information and implementation details.","category":"page"},{"location":"manual/limits/","page":"Symbolic Limits","title":"Symbolic Limits","text":"limit","category":"page"},{"location":"manual/limits/#Symbolics.limit","page":"Symbolic Limits","title":"Symbolics.limit","text":"limit(expr, var, h[, side::Symbol])\n\nCompute the limit of expr as var approaches h.\n\nside indicates the direction from which var approaches h. It may be one of :left, :right, or :both. If side is :both and the two sides do not align, an error is thrown. Side defaults to :both for finite h, :left for h = Inf, and :right for h = -Inf.\n\nexpr must be composed of log, exp, constants, and the rational operators +, -, *, and /. This limitation may eventually be relaxed.\n\nwarning: Warning\nBecause symbolic limit computation is undecidable, this function necessarily employs heuristics and may occasionally return wrong answers. Nevertheless, please report wrong answers as issues as we aim to have heuristics that produce correct answers in all practical cases.\n\n\n\n\n\n","category":"function"},{"location":"manual/functions/#function_registration","page":"Function Registration and Tracing","title":"Function Registration and Tracing","text":"","category":"section"},{"location":"manual/functions/","page":"Function Registration and Tracing","title":"Function Registration and Tracing","text":"Function registration is the ability to define new nodes in the symbolic graph. This is useful because symbolic computing is declarative, i.e. symbolic computations express what should be computed, not how it should be computed. However, at some level someone must describe how a given operation is computed. These are the primitive functions, and a symbolic expression is made up of primitive functions.","category":"page"},{"location":"manual/functions/","page":"Function Registration and Tracing","title":"Function Registration and Tracing","text":"Symbolics.jl comes pre-registered with a large set of standard mathematical functions, like * and sin to special functions like erf, and even deeper operations like DataInterpolations.jl's AbstractInterpolation. However, in many cases you may need to add your own function, i.e. you may want to give an imperative code and use this to define a new symbolic code. Symbolics.jl calls the declaration of new declarative primitives from an imperative function definition registration. This page describes both the registration process and its companion process, tracing, for interacting with code written in Julia.","category":"page"},{"location":"manual/functions/#Direct-Tracing","page":"Function Registration and Tracing","title":"Direct Tracing","text":"","category":"section"},{"location":"manual/functions/","page":"Function Registration and Tracing","title":"Function Registration and Tracing","text":"Because Symbolics expressions respect Julia semantics, one way to generate symbolic expressions is to simply place Symbolics variables as inputs into existing Julia code. For example, the following uses the standard Julia function for the Lorenz equations to generate the symbolic expression for the Lorenz equations:","category":"page"},{"location":"manual/functions/","page":"Function Registration and Tracing","title":"Function Registration and Tracing","text":"using Symbolics\nfunction lorenz(du,u,p,t)\n du[1] = 10.0(u[2]-u[1])\n du[2] = u[1]*(28.0-u[3]) - u[2]\n du[3] = u[1]*u[2] - (8/3)*u[3]\nend\n@variables t p[1:3] u(t)[1:3]\ndu = Array{Any}(undef, 3)\nlorenz(du,u,p,t)\ndu","category":"page"},{"location":"manual/functions/","page":"Function Registration and Tracing","title":"Function Registration and Tracing","text":"Or similarly:","category":"page"},{"location":"manual/functions/","page":"Function Registration and Tracing","title":"Function Registration and Tracing","text":"@variables t x(t) y(t) z(t) dx(t) dy(t) dz(t) σ ρ β\ndu = [dx,dy,dz]\nu = [x,y,z]\np = [σ,ρ,β]\nlorenz(du,u,p,t)\ndu","category":"page"},{"location":"manual/functions/","page":"Function Registration and Tracing","title":"Function Registration and Tracing","text":"Note that what has been done here is that the imperative Julia code for the function lorenz has been transformed into a declarative symbolic graph. Importantly, the code of lorenz is transformed into an expression consisting only of primitive registered functions, things like * and -, which come pre-registered with Symbolics.jl This then allows for symbolic manipulation of the expressions, allowing things like simplification and operation reordering done on its generated expressions. ","category":"page"},{"location":"manual/functions/#Utility-and-Scope-of-Tracing","page":"Function Registration and Tracing","title":"Utility and Scope of Tracing","text":"","category":"section"},{"location":"manual/functions/","page":"Function Registration and Tracing","title":"Function Registration and Tracing","text":"This notably describes one limitation of tracing: tracing only works if the expression being traced is composed of already registered functions. If unregistered functions, such as calls to C code, are used, then the tracing process will error.","category":"page"},{"location":"manual/functions/","page":"Function Registration and Tracing","title":"Function Registration and Tracing","text":"However, we note that symbolic tracing by definition does not guarantee that the exact choices. The symbolic expressions may re-distribute the arithmetic, simplify out expressions, or do other modifications. Thus if this function is function is sensitive to numerical details in its calculation, one would not want to trace the function and thus would instead register it as a new primitive function.","category":"page"},{"location":"manual/functions/","page":"Function Registration and Tracing","title":"Function Registration and Tracing","text":"For the symbolic system to be as powerful in its manipulations as possible, it is recommended that the registration of functions be minimized to the simplest possible set, and thus registration should only be used when necessary. This is because any code within a registered function is treated as a blackbox imperative code that cannot be manipulated, thus decreasing the potential for simplifications.","category":"page"},{"location":"manual/functions/#Registering-Functions","page":"Function Registration and Tracing","title":"Registering Functions","text":"","category":"section"},{"location":"manual/functions/","page":"Function Registration and Tracing","title":"Function Registration and Tracing","text":"The Symbolics graph only allows registered Julia functions within its type. All other functions are automatically traced down to registered functions. By default, Symbolics.jl pre-registers the common functions utilized in SymbolicUtils.jl and pre-defines their derivatives. However, the user can utilize the @register_symbolic macro to add their function to allowed functions of the computation graph.","category":"page"},{"location":"manual/functions/","page":"Function Registration and Tracing","title":"Function Registration and Tracing","text":"Additionally, @register_array_symbolic can be used to define array functions. For size propagation it's required that a computation of how the sizes are computed is also supplied.","category":"page"},{"location":"manual/functions/#Defining-Derivatives-of-Registered-Functions","page":"Function Registration and Tracing","title":"Defining Derivatives of Registered Functions","text":"","category":"section"},{"location":"manual/functions/","page":"Function Registration and Tracing","title":"Function Registration and Tracing","text":"In order for symbolic differentiation to work, an overload of Symbolics.derivative is required. The syntax is derivative(typeof(f), args::NTuple{i,Any}, ::Val{j}) where i is the number of arguments to the function and j is which argument is being differentiated. So for example:","category":"page"},{"location":"manual/functions/","page":"Function Registration and Tracing","title":"Function Registration and Tracing","text":"function derivative(::typeof(min), args::NTuple{2,Any}, ::Val{1})\n x, y = args\n IfElse.ifelse(x < y, one(x), zero(x))\nend","category":"page"},{"location":"manual/functions/","page":"Function Registration and Tracing","title":"Function Registration and Tracing","text":"is the partial derivative of the Julia min(x,y) function with respect to x.","category":"page"},{"location":"manual/functions/","page":"Function Registration and Tracing","title":"Function Registration and Tracing","text":"note: Note\nDownstream symbolic derivative functionality only work if every partial derivative that is required in the derivative expression is defined. Note that you only need to define the partial derivatives which are actually computed.","category":"page"},{"location":"manual/functions/#Registration-of-Array-Functions","page":"Function Registration and Tracing","title":"Registration of Array Functions","text":"","category":"section"},{"location":"manual/functions/","page":"Function Registration and Tracing","title":"Function Registration and Tracing","text":"Similar to scalar functions, array functions can be registered to define new primitives for functions which either take in or return arrays. This is done by using the @register_array_symbolic macro. It acts similarly to the scalar function registration but requires a calculation of the input and output sizes. For example, let's assume we wanted to have a function that computes the solution to Ax = b, i.e. a linear solve, using an SVD factorization. In Julia, the code for this would be svdsolve(A,b) = svd(A)\\b. We would create this function as follows:","category":"page"},{"location":"manual/functions/","page":"Function Registration and Tracing","title":"Function Registration and Tracing","text":"using LinearAlgebra, Symbolics\n\nsvdsolve(A, b) = svd(A)\\b\n@register_array_symbolic svdsolve(A::AbstractMatrix, b::AbstractVector) begin\n size = size(b)\n eltype = promote_type(eltype(A), eltype(b))\nend","category":"page"},{"location":"manual/functions/","page":"Function Registration and Tracing","title":"Function Registration and Tracing","text":"Now using the function svdsolve with symbolic array variables will be kept lazy:","category":"page"},{"location":"manual/functions/","page":"Function Registration and Tracing","title":"Function Registration and Tracing","text":"@variables A[1:3, 1:3] b[1:3]\nsvdsolve(A,b)","category":"page"},{"location":"manual/functions/","page":"Function Registration and Tracing","title":"Function Registration and Tracing","text":"Note that at this time array derivatives cannot be defined.","category":"page"},{"location":"manual/functions/#Registration-API","page":"Function Registration and Tracing","title":"Registration API","text":"","category":"section"},{"location":"manual/functions/","page":"Function Registration and Tracing","title":"Function Registration and Tracing","text":"@register_symbolic\n@register_array_symbolic","category":"page"},{"location":"manual/functions/#Symbolics.@register_symbolic","page":"Function Registration and Tracing","title":"Symbolics.@register_symbolic","text":"@register_symbolic(expr, define_promotion = true, Ts = [Real])\n\nOverload appropriate methods so that Symbolics can stop tracing into the registered function. If define_promotion is true, then a promotion method in the form of\n\nSymbolicUtils.promote_symtype(::typeof(f_registered), args...) = Real # or the annotated return type\n\nis defined for the register function. Note that when defining multiple register overloads for one function, all the rest of the registers must set define_promotion to false except for the first one, to avoid method overwriting.\n\nExamples\n\n@register_symbolic foo(x, y)\n@register_symbolic foo(x, y::Bool) false # do not overload a duplicate promotion rule\n@register_symbolic goo(x, y::Int) # `y` is not overloaded to take symbolic objects\n@register_symbolic hoo(x, y)::Int # `hoo` returns `Int`\n\nSee @register_array_symbolic to register functions which return arrays.\n\n\n\n\n\n","category":"macro"},{"location":"manual/functions/#Symbolics.@register_array_symbolic","page":"Function Registration and Tracing","title":"Symbolics.@register_array_symbolic","text":"@register_array_symbolic(expr, define_promotion = true)\n\nExample:\n\n# Let's say vandermonde takes an n-vector and returns an n x n matrix\n@register_array_symbolic vandermonde(x::AbstractVector) begin\n size=(length(x), length(x))\n eltype=eltype(x) # optional, will default to the promoted eltypes of x\nend\n\nYou can also register calls on callable structs:\n\n@register_array_symbolic (c::Conv)(x::AbstractMatrix) begin\n size=size(x) .- size(c.kernel) .+ 1\n eltype=promote_type(eltype(x), eltype(c))\nend\n\nIf define_promotion = true then a promotion method in the form of\n\nSymbolicUtils.promote_symtype(::typeof(f_registered), args...) = # inferred or annotated return type\n\nis defined for the register function. Note that when defining multiple register overloads for one function, all the rest of the registers must set define_promotion to false except for the first one, to avoid method overwriting.\n\n\n\n\n\n","category":"macro"},{"location":"manual/functions/#Direct-Registration-API-(Advanced,-Experimental)","page":"Function Registration and Tracing","title":"Direct Registration API (Advanced, Experimental)","text":"","category":"section"},{"location":"manual/functions/","page":"Function Registration and Tracing","title":"Function Registration and Tracing","text":"warn: Warn\nThis is a lower level API which is not as stable as the macro APIs.","category":"page"},{"location":"manual/functions/","page":"Function Registration and Tracing","title":"Function Registration and Tracing","text":"In some circumstances you may need to use the direct API in order to define registration on functions or types without using the macro. This is done by directly defining dispatches on symbolic objects.","category":"page"},{"location":"manual/functions/","page":"Function Registration and Tracing","title":"Function Registration and Tracing","text":"A good example of this is DataInterpolations.jl's interpolations object. On an interpolation by a symbolic variable, we generate the symbolic function (the term) for the interpolation function. This looks like:","category":"page"},{"location":"manual/functions/","page":"Function Registration and Tracing","title":"Function Registration and Tracing","text":"using DataInterpolations, Symbolics, SymbolicUtils\n(interp::AbstractInterpolation)(t::Num) = SymbolicUtils.term(interp, unwrap(t))","category":"page"},{"location":"manual/functions/","page":"Function Registration and Tracing","title":"Function Registration and Tracing","text":"In order for this to work, it is required that we define the symtype for the symbolic type inference. This is done via:","category":"page"},{"location":"manual/functions/","page":"Function Registration and Tracing","title":"Function Registration and Tracing","text":"SymbolicUtils.promote_symtype(t::AbstractInterpolation, args...) = Real","category":"page"},{"location":"manual/functions/","page":"Function Registration and Tracing","title":"Function Registration and Tracing","text":"Additionally a symbolic name is required:","category":"page"},{"location":"manual/functions/","page":"Function Registration and Tracing","title":"Function Registration and Tracing","text":"Base.nameof(interp::AbstractInterpolation) = :Interpolation","category":"page"},{"location":"manual/functions/","page":"Function Registration and Tracing","title":"Function Registration and Tracing","text":"The derivative is defined similarly to the macro case:","category":"page"},{"location":"manual/functions/","page":"Function Registration and Tracing","title":"Function Registration and Tracing","text":"function Symbolics.derivative(interp::AbstractInterpolation, args::NTuple{1, Any}, ::Val{1})\n Symbolics.unwrap(derivative(interp, Symbolics.wrap(args[1])))\nend","category":"page"},{"location":"getting_started/#Getting-Started-with-Symbolics.jl","page":"Getting Started with Symbolics.jl","title":"Getting Started with Symbolics.jl","text":"","category":"section"},{"location":"getting_started/","page":"Getting Started with Symbolics.jl","title":"Getting Started with Symbolics.jl","text":"Symbolics.jl is a symbolic modeling language. In this tutorial, we will walk you through the process of getting Symbolics.jl up and running, and start doing our first symbolic calculations.","category":"page"},{"location":"getting_started/#Installing-Symbolics.jl","page":"Getting Started with Symbolics.jl","title":"Installing Symbolics.jl","text":"","category":"section"},{"location":"getting_started/","page":"Getting Started with Symbolics.jl","title":"Getting Started with Symbolics.jl","text":"Installing Symbolics.jl is as simple as using the Julia package manager. This is done via the command ]add Symbolics. After precompilation is complete, do using Symbolics in the terminal (REPL) and when that command is completed, you're ready to start!","category":"page"},{"location":"getting_started/#Building-Symbolic-Expressions","page":"Getting Started with Symbolics.jl","title":"Building Symbolic Expressions","text":"","category":"section"},{"location":"getting_started/","page":"Getting Started with Symbolics.jl","title":"Getting Started with Symbolics.jl","text":"The way to define symbolic variables is via the @variables macro:","category":"page"},{"location":"getting_started/","page":"Getting Started with Symbolics.jl","title":"Getting Started with Symbolics.jl","text":"using Symbolics\n@variables x y","category":"page"},{"location":"getting_started/","page":"Getting Started with Symbolics.jl","title":"Getting Started with Symbolics.jl","text":"After defining variables as symbolic, symbolic expressions, which we call a iscall object, can be generated by utilizing Julia expressions. For example:","category":"page"},{"location":"getting_started/","page":"Getting Started with Symbolics.jl","title":"Getting Started with Symbolics.jl","text":"z = x^2 + y","category":"page"},{"location":"getting_started/","page":"Getting Started with Symbolics.jl","title":"Getting Started with Symbolics.jl","text":"Here, z is an expression tree for “square x and add y”. To make an array of symbolic expressions, simply make an array of symbolic expressions:","category":"page"},{"location":"getting_started/","page":"Getting Started with Symbolics.jl","title":"Getting Started with Symbolics.jl","text":"A = [x^2 + y 0 2x\n 0 0 2y\n y^2 + x 0 0]","category":"page"},{"location":"getting_started/","page":"Getting Started with Symbolics.jl","title":"Getting Started with Symbolics.jl","text":"Note that by default, @variables returns Sym or iscall objects wrapped in Num to make them behave like subtypes of Real. Any operation on these Num objects will return a new Num object, wrapping the result of computing symbolically on the underlying values.","category":"page"},{"location":"getting_started/","page":"Getting Started with Symbolics.jl","title":"Getting Started with Symbolics.jl","text":"If you are following this tutorial in the Julia REPL, A will not be shown with LaTeX equations. To get these equations, we can use Latexify.jl. Symbolics.jl comes with Latexify recipes, so it works automatically:","category":"page"},{"location":"getting_started/","page":"Getting Started with Symbolics.jl","title":"Getting Started with Symbolics.jl","text":"using Latexify\nlatexify(A)","category":"page"},{"location":"getting_started/","page":"Getting Started with Symbolics.jl","title":"Getting Started with Symbolics.jl","text":"Normal Julia functions work on Symbolics expressions, so if we want to create the sparse version of A we would just call sparse:","category":"page"},{"location":"getting_started/","page":"Getting Started with Symbolics.jl","title":"Getting Started with Symbolics.jl","text":"using SparseArrays\nspA = sparse(A)\nlatexify(A)","category":"page"},{"location":"getting_started/","page":"Getting Started with Symbolics.jl","title":"Getting Started with Symbolics.jl","text":"We can thus use normal Julia functions as generators for sparse expressions. For example, here we will define","category":"page"},{"location":"getting_started/","page":"Getting Started with Symbolics.jl","title":"Getting Started with Symbolics.jl","text":"function f(u)\n [u[1] - u[3], u[1]^2 - u[2], u[3] + u[2]]\nend\nf([x, y, z]) # Recall that z = x^2 + y","category":"page"},{"location":"getting_started/","page":"Getting Started with Symbolics.jl","title":"Getting Started with Symbolics.jl","text":"Or we can build an array of variables and use it to trace the function:","category":"page"},{"location":"getting_started/","page":"Getting Started with Symbolics.jl","title":"Getting Started with Symbolics.jl","text":"u = Symbolics.variables(:u, 1:5)\nf(u)","category":"page"},{"location":"getting_started/","page":"Getting Started with Symbolics.jl","title":"Getting Started with Symbolics.jl","text":"note: Note\nSymbolics.variables(:u, 1:5) creates a Julia array of symbolic variables. This uses O(n) compute and memory but is a very general representation. Symbolics.jl also has the ability to represent symbolic arrays which gives an O(1) representation but is more limited in its functionality. For more information, see the Symbolic Arrays page.","category":"page"},{"location":"getting_started/#Derivatives","page":"Getting Started with Symbolics.jl","title":"Derivatives","text":"","category":"section"},{"location":"getting_started/","page":"Getting Started with Symbolics.jl","title":"Getting Started with Symbolics.jl","text":"One common thing to compute in a symbolic system is derivatives. In Symbolics.jl, derivatives are represented lazily via operations, just like any other function. To build a differential operator, use Differential like:","category":"page"},{"location":"getting_started/","page":"Getting Started with Symbolics.jl","title":"Getting Started with Symbolics.jl","text":"@variables t\nD = Differential(t)","category":"page"},{"location":"getting_started/","page":"Getting Started with Symbolics.jl","title":"Getting Started with Symbolics.jl","text":"This is the differential operator D = fracpartialpartial t. We can compose the differential operator by *, e.g. Differential(t) * Differential(x) or Differential(t)^2. Now let's write down the derivative of some expression:","category":"page"},{"location":"getting_started/","page":"Getting Started with Symbolics.jl","title":"Getting Started with Symbolics.jl","text":"z = t + t^2\nD(z)","category":"page"},{"location":"getting_started/","page":"Getting Started with Symbolics.jl","title":"Getting Started with Symbolics.jl","text":"Notice that this hasn't computed anything yet: D is a lazy operator because it lets us symbolically represent “The derivative of z with respect to t”, which is useful for example when representing our favorite thing in the world, differential equations. However, if we want to expand the derivative operators, we'd use expand_derivatives:","category":"page"},{"location":"getting_started/","page":"Getting Started with Symbolics.jl","title":"Getting Started with Symbolics.jl","text":"expand_derivatives(D(z))","category":"page"},{"location":"getting_started/","page":"Getting Started with Symbolics.jl","title":"Getting Started with Symbolics.jl","text":"The variable, that you are taking the derivative with respect to, is accessed with:","category":"page"},{"location":"getting_started/","page":"Getting Started with Symbolics.jl","title":"Getting Started with Symbolics.jl","text":"D.x","category":"page"},{"location":"getting_started/","page":"Getting Started with Symbolics.jl","title":"Getting Started with Symbolics.jl","text":"We can also have simplified functions for multivariable calculus. For example, we can compute the Jacobian of an array of expressions like:","category":"page"},{"location":"getting_started/","page":"Getting Started with Symbolics.jl","title":"Getting Started with Symbolics.jl","text":"Symbolics.jacobian([x + x*y, x^2 + y], [x, y])","category":"page"},{"location":"getting_started/","page":"Getting Started with Symbolics.jl","title":"Getting Started with Symbolics.jl","text":"and similarly we can do Hessians, gradients, and define whatever other derivatives you want.","category":"page"},{"location":"getting_started/#Simplification-and-Substitution","page":"Getting Started with Symbolics.jl","title":"Simplification and Substitution","text":"","category":"section"},{"location":"getting_started/","page":"Getting Started with Symbolics.jl","title":"Getting Started with Symbolics.jl","text":"Symbolics interfaces with SymbolicUtils.jl to allow for simplifying symbolic expressions. This is done simply through the simplify command:","category":"page"},{"location":"getting_started/","page":"Getting Started with Symbolics.jl","title":"Getting Started with Symbolics.jl","text":"simplify(2x + 2y)","category":"page"},{"location":"getting_started/","page":"Getting Started with Symbolics.jl","title":"Getting Started with Symbolics.jl","text":"This can be applied to arrays by using Julia's broadcast mechanism:","category":"page"},{"location":"getting_started/","page":"Getting Started with Symbolics.jl","title":"Getting Started with Symbolics.jl","text":"B = simplify.([t + t^2 + t + t^2 2t + 4t\n x + y + y + 2t x^2 - x^2 + y^2])","category":"page"},{"location":"getting_started/","page":"Getting Started with Symbolics.jl","title":"Getting Started with Symbolics.jl","text":"We can then use substitute to change values of an expression around:","category":"page"},{"location":"getting_started/","page":"Getting Started with Symbolics.jl","title":"Getting Started with Symbolics.jl","text":"simplify.(substitute.(B, (Dict(x => y^2),)))","category":"page"},{"location":"getting_started/","page":"Getting Started with Symbolics.jl","title":"Getting Started with Symbolics.jl","text":"and we can use this to interactively evaluate expressions without generating and compiling Julia functions:","category":"page"},{"location":"getting_started/","page":"Getting Started with Symbolics.jl","title":"Getting Started with Symbolics.jl","text":"V = substitute.(B, (Dict(x => 2.0, y => 3.0, t => 4.0),))","category":"page"},{"location":"getting_started/","page":"Getting Started with Symbolics.jl","title":"Getting Started with Symbolics.jl","text":"Where we can reference the values via:","category":"page"},{"location":"getting_started/","page":"Getting Started with Symbolics.jl","title":"Getting Started with Symbolics.jl","text":"Symbolics.value.(V)","category":"page"},{"location":"getting_started/#Non-Interactive-Development","page":"Getting Started with Symbolics.jl","title":"Non-Interactive Development","text":"","category":"section"},{"location":"getting_started/","page":"Getting Started with Symbolics.jl","title":"Getting Started with Symbolics.jl","text":"Note that the macros are for the high-level case where you're doing symbolic computation on your own code. If you want to do symbolic computation on someone else's code, like in a macro, you may not want to do @variables x because you might want the name “x” to come from the user's code. For these cases, you can use the interpolation operator to interpolate the runtime value of x, i.e. @variables $x. Check the documentation of @variables for more details.","category":"page"},{"location":"getting_started/","page":"Getting Started with Symbolics.jl","title":"Getting Started with Symbolics.jl","text":"a, b, c = :runtime_symbol_value, :value_b, :value_c","category":"page"},{"location":"getting_started/","page":"Getting Started with Symbolics.jl","title":"Getting Started with Symbolics.jl","text":"vars = @variables t $a $b(t) $c(t)[1:3]","category":"page"},{"location":"getting_started/","page":"Getting Started with Symbolics.jl","title":"Getting Started with Symbolics.jl","text":"(t, a, b, c)","category":"page"},{"location":"getting_started/","page":"Getting Started with Symbolics.jl","title":"Getting Started with Symbolics.jl","text":"One could also use variable and variables. Read their documentation for more details.","category":"page"},{"location":"getting_started/","page":"Getting Started with Symbolics.jl","title":"Getting Started with Symbolics.jl","text":"If we need to use this to generate new Julia code, we can simply convert the output to an Expr:","category":"page"},{"location":"getting_started/","page":"Getting Started with Symbolics.jl","title":"Getting Started with Symbolics.jl","text":"Symbolics.toexpr(x + y^2)","category":"page"},{"location":"getting_started/","page":"Getting Started with Symbolics.jl","title":"Getting Started with Symbolics.jl","text":"The other way around is also possible, parsing Julia expressions into symbolic expressions","category":"page"},{"location":"getting_started/","page":"Getting Started with Symbolics.jl","title":"Getting Started with Symbolics.jl","text":"ex = [:(v ~ w)\n :(w ~ -v)]\neqs = parse_expr_to_symbolic.(ex, (Main,))\neqs_lhs = [eq.lhs for eq in eqs]\neqs_rhs = [eq.rhs for eq in eqs]\nSymbolics.jacobian(eqs_rhs, eqs_lhs)","category":"page"},{"location":"getting_started/#Syms-and-callable-Syms","page":"Getting Started with Symbolics.jl","title":"Syms and callable Syms","text":"","category":"section"},{"location":"getting_started/","page":"Getting Started with Symbolics.jl","title":"Getting Started with Symbolics.jl","text":"In the definition","category":"page"},{"location":"getting_started/","page":"Getting Started with Symbolics.jl","title":"Getting Started with Symbolics.jl","text":"@variables t x(t) y(t)","category":"page"},{"location":"getting_started/","page":"Getting Started with Symbolics.jl","title":"Getting Started with Symbolics.jl","text":"t is of type Sym{Real}, but the name x refers to an object that represents the Term x(t). The operation of this expression is itself the object Sym{FnType{Tuple{Real}, Real}}(:x). The type Sym{FnType{...}} represents a callable object. In this case specifically, it's a function that takes 1 Real argument (noted by Tuple{Real}) and returns a Real result. You can call such a callable Sym with either a number or a symbolic expression of a permissible type.","category":"page"},{"location":"getting_started/","page":"Getting Started with Symbolics.jl","title":"Getting Started with Symbolics.jl","text":"This expression also defines t as an independent variable, while x(t) and y(t) are dependent variables. This is accounted for in differentiation:","category":"page"},{"location":"getting_started/","page":"Getting Started with Symbolics.jl","title":"Getting Started with Symbolics.jl","text":"z = x + y*t\nexpand_derivatives(D(z))","category":"page"},{"location":"getting_started/","page":"Getting Started with Symbolics.jl","title":"Getting Started with Symbolics.jl","text":"Since x and y are time-dependent, they are not automatically eliminated from the expression, and thus the D(x) and D(y) operations still exist in the expanded derivatives for correctness.","category":"page"},{"location":"getting_started/","page":"Getting Started with Symbolics.jl","title":"Getting Started with Symbolics.jl","text":"We can also define unrestricted functions:","category":"page"},{"location":"getting_started/","page":"Getting Started with Symbolics.jl","title":"Getting Started with Symbolics.jl","text":"@variables g(..)","category":"page"},{"location":"getting_started/","page":"Getting Started with Symbolics.jl","title":"Getting Started with Symbolics.jl","text":"Here, g is a variable that is a function of other variables. Any time that we reference g we have to utilize it as a function:","category":"page"},{"location":"getting_started/","page":"Getting Started with Symbolics.jl","title":"Getting Started with Symbolics.jl","text":"z = g(x) + g(y)","category":"page"},{"location":"getting_started/#Registering-Functions","page":"Getting Started with Symbolics.jl","title":"Registering Functions","text":"","category":"section"},{"location":"getting_started/","page":"Getting Started with Symbolics.jl","title":"Getting Started with Symbolics.jl","text":"One of the benefits of a one-language Julia symbolic stack is that the primitives are all written in Julia, and therefore it's trivially extendable from Julia itself. By default, new functions are traced to the primitives and the symbolic expressions are written on the primitives. However, we can expand the allowed primitives by registering new functions. For example, let's register a new function h:","category":"page"},{"location":"getting_started/","page":"Getting Started with Symbolics.jl","title":"Getting Started with Symbolics.jl","text":"h(x, y) = x^2 + y\n@register_symbolic h(x, y)","category":"page"},{"location":"getting_started/","page":"Getting Started with Symbolics.jl","title":"Getting Started with Symbolics.jl","text":"When we use h(x, y), it is a symbolic expression and doesn't expand:","category":"page"},{"location":"getting_started/","page":"Getting Started with Symbolics.jl","title":"Getting Started with Symbolics.jl","text":"h(x, y) + y^2","category":"page"},{"location":"getting_started/","page":"Getting Started with Symbolics.jl","title":"Getting Started with Symbolics.jl","text":"To use it with the differentiation system, we need to register its derivatives. We would do it like this:","category":"page"},{"location":"getting_started/","page":"Getting Started with Symbolics.jl","title":"Getting Started with Symbolics.jl","text":"# Derivative w.r.t. the first argument\nSymbolics.derivative(::typeof(h), args::NTuple{2,Any}, ::Val{1}) = 2args[1]\n# Derivative w.r.t. the second argument\nSymbolics.derivative(::typeof(h), args::NTuple{2,Any}, ::Val{2}) = 1","category":"page"},{"location":"getting_started/","page":"Getting Started with Symbolics.jl","title":"Getting Started with Symbolics.jl","text":"and now it works with the rest of the system:","category":"page"},{"location":"getting_started/","page":"Getting Started with Symbolics.jl","title":"Getting Started with Symbolics.jl","text":"Symbolics.derivative(h(x, y) + y^2, x)","category":"page"},{"location":"getting_started/","page":"Getting Started with Symbolics.jl","title":"Getting Started with Symbolics.jl","text":"Symbolics.derivative(h(x, y) + y^2, y)","category":"page"},{"location":"getting_started/","page":"Getting Started with Symbolics.jl","title":"Getting Started with Symbolics.jl","text":"note: Note\n@register_symbolic only allows for scalar outputs. If full array functions are needed, then see @register_array_symbolic for registering functions of symbolic arrays.","category":"page"},{"location":"getting_started/#Building-Functions","page":"Getting Started with Symbolics.jl","title":"Building Functions","text":"","category":"section"},{"location":"getting_started/","page":"Getting Started with Symbolics.jl","title":"Getting Started with Symbolics.jl","text":"The function for building functions from symbolic expressions is the aptly-named build_function. The first argument is the symbolic expression or the array of symbolic expressions to compile, and the trailing arguments are the arguments for the function. For example:","category":"page"},{"location":"getting_started/","page":"Getting Started with Symbolics.jl","title":"Getting Started with Symbolics.jl","text":"to_compute = [x^2 + y, y^2 + x]\nf_expr = build_function(to_compute, [x, y])\nBase.remove_linenums!.(f_expr)","category":"page"},{"location":"getting_started/","page":"Getting Started with Symbolics.jl","title":"Getting Started with Symbolics.jl","text":"gives back two codes. The first is a function f([x, y]) that computes and builds an output vector [x^2 + y, y^2 + x]. Because this tool was made to be used by all the cool kids writing fast Julia codes, it is specialized to Julia and supports features like StaticArrays. For example:","category":"page"},{"location":"getting_started/","page":"Getting Started with Symbolics.jl","title":"Getting Started with Symbolics.jl","text":"using StaticArrays\nmyf = eval(f_expr[1])\nmyf(SA[2.0, 3.0])","category":"page"},{"location":"getting_started/","page":"Getting Started with Symbolics.jl","title":"Getting Started with Symbolics.jl","text":"The second function is an in-place non-allocating mutating function which mutates its first value. Thus, we'd use it like the following:","category":"page"},{"location":"getting_started/","page":"Getting Started with Symbolics.jl","title":"Getting Started with Symbolics.jl","text":"myf! = eval(f_expr[2])\nout = zeros(2)\nmyf!(out, [2.0, 3.0])\nout","category":"page"},{"location":"getting_started/","page":"Getting Started with Symbolics.jl","title":"Getting Started with Symbolics.jl","text":"To save the symbolic calculations for later, we can take this expression and save it out to a file:","category":"page"},{"location":"getting_started/","page":"Getting Started with Symbolics.jl","title":"Getting Started with Symbolics.jl","text":"write(\"function.jl\", string(f_expr[2]))","category":"page"},{"location":"getting_started/","page":"Getting Started with Symbolics.jl","title":"Getting Started with Symbolics.jl","text":"Note that if we need to avoid eval, for example to avoid world-age issues, one could do expression = Val{false}:","category":"page"},{"location":"getting_started/","page":"Getting Started with Symbolics.jl","title":"Getting Started with Symbolics.jl","text":"Base.remove_linenums!(build_function(to_compute, [x, y], expression=Val{false})[1])","category":"page"},{"location":"getting_started/","page":"Getting Started with Symbolics.jl","title":"Getting Started with Symbolics.jl","text":"which will use RuntimeGeneratedFunctions.jl to build Julia functions which avoid world-age issues.","category":"page"},{"location":"getting_started/#Building-Non-Allocating-Parallel-Functions-for-Sparse-Matrices","page":"Getting Started with Symbolics.jl","title":"Building Non-Allocating Parallel Functions for Sparse Matrices","text":"","category":"section"},{"location":"getting_started/","page":"Getting Started with Symbolics.jl","title":"Getting Started with Symbolics.jl","text":"Now let's show off a little bit. build_function is kind of like if lambdify ate its spinach. To show this, let's build a non-allocating function that fills sparse matrices in a multithreaded manner. To do this, we just have to represent the operation that we're turning into a function via a sparse matrix. For example:","category":"page"},{"location":"getting_started/","page":"Getting Started with Symbolics.jl","title":"Getting Started with Symbolics.jl","text":"using LinearAlgebra\nN = 8\nA = sparse(Tridiagonal([x^i for i in 1:N-1], [x^i * y^(8-i) for i in 1:N], [y^i for i in 1:N-1]))\nshow(A)","category":"page"},{"location":"getting_started/","page":"Getting Started with Symbolics.jl","title":"Getting Started with Symbolics.jl","text":"Now we call build_function:","category":"page"},{"location":"getting_started/","page":"Getting Started with Symbolics.jl","title":"Getting Started with Symbolics.jl","text":"Base.remove_linenums!(build_function(A,[x,y],parallel=Symbolics.MultithreadedForm())[2])","category":"page"},{"location":"getting_started/","page":"Getting Started with Symbolics.jl","title":"Getting Started with Symbolics.jl","text":"Let's unpack what that's doing. It's using A.nzval to linearly index through the sparse matrix, avoiding the A[i,j] form because that is a more expensive way to index a sparse matrix if you know exactly the order that the data is stored. Then, it's splitting up the calculation into Julia threads, so they can be operated on in parallel. It synchronizes after spawning all the tasks, so the computation is ensured to be complete before moving on. And it does this with all in-place operations to the original matrix instead of generating arrays. This is the fanciest way you could fill a sparse matrix, and Symbolics makes this dead simple.","category":"page"},{"location":"getting_started/","page":"Getting Started with Symbolics.jl","title":"Getting Started with Symbolics.jl","text":"(Note: this example may be slower with multithreading due to the thread spawning overhead, but the full version was not included in the documentation for brevity. It will be the faster version if N is sufficiently large!)","category":"page"},{"location":"getting_started/","page":"Getting Started with Symbolics.jl","title":"Getting Started with Symbolics.jl","text":"Importantly, when using the in-place functions generated by build_function, it is important that the mutating argument matches the sparse structure of the original function, as demonstrated below.","category":"page"},{"location":"getting_started/","page":"Getting Started with Symbolics.jl","title":"Getting Started with Symbolics.jl","text":"using Symbolics, SparseArrays, LinearAlgebra\n\nN = 10\n_S = sprand(N, N, 0.1)\n_Q = sprand(N, N, 0.1)\n\nF(z) = [ # some complicated sparse amenable function\n _S * z\n _Q * z.^2\n]\n\nSymbolics.@variables z[1:N]\n\nsj = Symbolics.sparsejacobian(F(z), z) # sparse jacobian\n\nf_expr = build_function(sj, z)\n\nrows, cols, _ = findnz(sj)\nout = sparse(rows, cols, zeros(length(cols)), size(sj)...) # pre-allocate, and correct structure\nmyf = eval(last(f_expr))\nmyf(out, rand(N)) # note that out matches the sparsity structure of sj\nout","category":"page"},{"location":"manual/expression_manipulation/#Expression-Manipulation","page":"Expression Manipulation","title":"Expression Manipulation","text":"","category":"section"},{"location":"manual/expression_manipulation/","page":"Expression Manipulation","title":"Expression Manipulation","text":"Symbolics.jl provides functionality for easily manipulating expressions. Most of the functionality comes by the expression objects obeying the standard mathematical semantics. For example, if one has A a matrix of symbolic expressions wrapped in Num, then A^2 calculates the expressions for the squared matrix. It is thus encouraged to use standard Julia for performing many of the manipulation on the IR. For example, calculating the sparse form of the matrix via sparse(A) is valid, legible, and easily understandable to all Julia programmers.","category":"page"},{"location":"manual/expression_manipulation/#Functionality-Inherited-From-SymbolicUtils.jl","page":"Expression Manipulation","title":"Functionality Inherited From SymbolicUtils.jl","text":"","category":"section"},{"location":"manual/expression_manipulation/","page":"Expression Manipulation","title":"Expression Manipulation","text":"SymbolicUtils.substitute\nSymbolicUtils.simplify","category":"page"},{"location":"manual/expression_manipulation/#SymbolicUtils.substitute","page":"Expression Manipulation","title":"SymbolicUtils.substitute","text":"substitute(expr, s)\n\nPerforms the substitution on expr according to rule(s) s.\n\nExamples\n\njulia> @variables t x y z(t)\n4-element Vector{Num}:\n t\n x\n y\n z(t)\njulia> ex = x + y + sin(z)\n(x + y) + sin(z(t))\njulia> substitute(ex, Dict([x => z, sin(z) => z^2]))\n(z(t) + y) + (z(t) ^ 2)\n\n\n\n\n\n","category":"function"},{"location":"manual/expression_manipulation/#SymbolicUtils.simplify","page":"Expression Manipulation","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":"manual/expression_manipulation/","page":"Expression Manipulation","title":"Expression Manipulation","text":"Documentation for rewriter can be found here, using the @rule macro or the @acrule macro from SymbolicUtils.jl.","category":"page"},{"location":"manual/expression_manipulation/#Additional-Manipulation-Functions","page":"Expression Manipulation","title":"Additional Manipulation Functions","text":"","category":"section"},{"location":"manual/expression_manipulation/","page":"Expression Manipulation","title":"Expression Manipulation","text":"Other additional manipulation functions are given below.","category":"page"},{"location":"manual/expression_manipulation/","page":"Expression Manipulation","title":"Expression Manipulation","text":"Symbolics.get_variables\nSymbolics.tosymbol\nSymbolics.diff2term\nSymbolics.degree\nSymbolics.coeff\nSymbolics.replace\nSymbolics.occursin\nSymbolics.filterchildren\nSymbolics.fixpoint_sub\nSymbolics.fast_substitute\nSymbolics.symbolic_to_float","category":"page"},{"location":"manual/expression_manipulation/#Symbolics.get_variables","page":"Expression Manipulation","title":"Symbolics.get_variables","text":"get_variables(e, varlist = nothing; sort::Bool = false)\n\nReturn a vector of variables appearing in e, optionally restricting to variables in varlist.\n\nNote that the returned variables are not wrapped in the Num type.\n\nExamples\n\njulia> @variables t x y z(t);\n\njulia> Symbolics.get_variables(x + y + sin(z); sort = true)\n3-element Vector{SymbolicUtils.BasicSymbolic}:\n x\n y\n z(t)\n\njulia> Symbolics.get_variables(x - y; sort = true)\n2-element Vector{SymbolicUtils.BasicSymbolic}:\n x\n y\n\n\n\n\n\n","category":"function"},{"location":"manual/expression_manipulation/#Symbolics.tosymbol","page":"Expression Manipulation","title":"Symbolics.tosymbol","text":"tosymbol(x::Union{Num,Symbolic}; states=nothing, escape=true) -> Symbol\n\nConvert x to a symbol. states are the states of a system, and escape means if the target has escapes like val\"y(t)\". If escape is false, then it will only output y instead of y(t).\n\nExamples\n\njulia> @variables t z(t)\n2-element Vector{Num}:\n t\n z(t)\n\njulia> Symbolics.tosymbol(z)\nSymbol(\"z(t)\")\n\njulia> Symbolics.tosymbol(z; escape=false)\n:z\n\n\n\n\n\n","category":"function"},{"location":"manual/expression_manipulation/#Symbolics.diff2term","page":"Expression Manipulation","title":"Symbolics.diff2term","text":"diff2term(x, x_metadata::Dict{Datatype, Any}) -> Symbolic\n\nConvert a differential variable to a Term. Note that it only takes a Term not a Num. Any upstream metadata can be passed via x_metadata\n\njulia> @variables x t u(x, t) z(t)[1:2]; Dt = Differential(t); Dx = Differential(x);\n\njulia> Symbolics.diff2term(Symbolics.value(Dx(Dt(u))))\nuˍtx(x, t)\n\njulia> Symbolics.diff2term(Symbolics.value(Dt(z[1])))\nvar\"z(t)[1]ˍt\"\n\n\n\n\n\n","category":"function"},{"location":"manual/expression_manipulation/#Symbolics.degree","page":"Expression Manipulation","title":"Symbolics.degree","text":"degree(p, sym=nothing)\n\nExtract the degree of p with respect to sym.\n\nExamples\n\njulia> @variables x;\n\njulia> Symbolics.degree(x^0)\n0\n\njulia> Symbolics.degree(x)\n1\n\njulia> Symbolics.degree(x^2)\n2\n\n\n\n\n\n","category":"function"},{"location":"manual/expression_manipulation/#Symbolics.coeff","page":"Expression Manipulation","title":"Symbolics.coeff","text":"coeff(p, sym=nothing)\n\nExtract the coefficient of p with respect to sym. Note that p might need to be expanded and/or simplified with expand and/or simplify.\n\nExamples\n\njulia> @variables a x y;\n\njulia> Symbolics.coeff(2a, x)\n0\n\njulia> Symbolics.coeff(3x + 2y, y)\n2\n\njulia> Symbolics.coeff(x^2 + y, x^2)\n1\n\njulia> Symbolics.coeff(2*x*y + y, x*y)\n2\n\n\n\n\n\n","category":"function"},{"location":"manual/expression_manipulation/#Base.occursin","page":"Expression Manipulation","title":"Base.occursin","text":"occursin(needle::Symbolic, haystack::Symbolic)\n\nDetermine whether the second argument contains the first argument. Note that this function doesn't handle associativity, commutativity, or distributivity.\n\n\n\n\n\n","category":"function"},{"location":"manual/expression_manipulation/#Symbolics.filterchildren","page":"Expression Manipulation","title":"Symbolics.filterchildren","text":"filterchildren(c, x) Returns all parts of x that fulfills the condition given in c. c can be a function or an expression. If it is a function, returns everything for which the function is true. If c is an expression, returns all expressions that matches it.\n\nExamples:\n\n@syms x\nSymbolics.filterchildren(x, log(x) + x + 1)\n\nreturns [x, x]\n\n@variables t X(t)\nD = Differential(t)\nSymbolics.filterchildren(Symbolics.is_derivative, X + D(X) + D(X^2))\n\nreturns [Differential(t)(X(t)^2), Differential(t)(X(t))]\n\n\n\n\n\n","category":"function"},{"location":"manual/expression_manipulation/#Symbolics.fixpoint_sub","page":"Expression Manipulation","title":"Symbolics.fixpoint_sub","text":"fixpoint_sub(expr, dict; operator = Nothing)\n\nGiven a symbolic expression, equation or inequality expr perform the substitutions in dict recursively until the expression does not change. Substitutions that depend on one another will thus be recursively expanded. For example, fixpoint_sub(x, Dict(x => y, y => 3)) will return 3. The operator keyword can be specified to prevent substitution of expressions inside operators of the given type.\n\nSee also: fast_substitute.\n\n\n\n\n\n","category":"function"},{"location":"manual/expression_manipulation/#Symbolics.fast_substitute","page":"Expression Manipulation","title":"Symbolics.fast_substitute","text":"fast_substitute(expr, dict; operator = Nothing)\n\nGiven a symbolic expression, equation or inequality expr perform the substitutions in dict. This only performs the substitutions once. For example, fast_substitute(x, Dict(x => y, y => 3)) will return y. The operator keyword can be specified to prevent substitution of expressions inside operators of the given type.\n\nSee also: fixpoint_sub.\n\n\n\n\n\n","category":"function"},{"location":"manual/expression_manipulation/#Symbolics.symbolic_to_float","page":"Expression Manipulation","title":"Symbolics.symbolic_to_float","text":"symbolic_to_float(x::Union{Num, BasicSymbolic})::Union{AbstractFloat, BasicSymbolic}\n\nIf the symbolic value is exactly equal to a number, converts the symbolic value to a floating point number. Otherwise retains the symbolic value.\n\nExamples\n\nsymbolic_to_float((1//2 * x)/x) # 0.5\nsymbolic_to_float((1/2 * x)/x) # 0.5\nsymbolic_to_float((1//2)*√(279//4)) # 4.175823272122517\n\n\n\n\n\n","category":"function"},{"location":"manual/derivatives/#Derivatives-and-Differentials","page":"Derivatives and Differentials","title":"Derivatives and Differentials","text":"","category":"section"},{"location":"manual/derivatives/","page":"Derivatives and Differentials","title":"Derivatives and Differentials","text":"A Differential(op) is a partial derivative with respect to op, which can then be applied to some other operations. For example, D=Differential(t) is what would commonly be referred to as d/dt, which can then be applied to other operations using its function call, so D(x+y) is d(x+y)/dt.","category":"page"},{"location":"manual/derivatives/","page":"Derivatives and Differentials","title":"Derivatives and Differentials","text":"By default, the derivatives are left unexpanded to capture the symbolic representation of the differential equation. If the user would like to expand out all the differentials, the expand_derivatives function eliminates all the differentials down to basic one-variable expressions.","category":"page"},{"location":"manual/derivatives/","page":"Derivatives and Differentials","title":"Derivatives and Differentials","text":"Differential\nexpand_derivatives\nis_derivative","category":"page"},{"location":"manual/derivatives/#Symbolics.Differential","page":"Derivatives and Differentials","title":"Symbolics.Differential","text":"struct Differential <: Symbolics.Operator\n\nRepresents a differential operator.\n\nFields\n\nx: The variable or expression to differentiate with respect to.\n\nExamples\n\njulia> using Symbolics\n\njulia> @variables x y;\n\njulia> D = Differential(x)\n(D'~x)\n\njulia> D(y) # Differentiate y wrt. x\n(D'~x)(y)\n\njulia> Dx = Differential(x) * Differential(y) # d^2/dxy operator\n(D'~x(t)) ∘ (D'~y(t))\n\njulia> D3 = Differential(x)^3 # 3rd order differential operator\n(D'~x(t)) ∘ (D'~x(t)) ∘ (D'~x(t))\n\n\n\n\n\n","category":"type"},{"location":"manual/derivatives/#Symbolics.expand_derivatives","page":"Derivatives and Differentials","title":"Symbolics.expand_derivatives","text":"expand_derivatives(O; ...)\nexpand_derivatives(O, simplify; occurrences)\n\n\nExpands derivatives within a symbolic expression O.\n\nThis function recursively traverses a symbolic expression, applying the chain rule and other derivative rules to expand any derivatives it encounters.\n\nArguments\n\nO::Symbolic: The symbolic expression to expand.\nsimplify::Bool=false: Whether to simplify the resulting expression using SymbolicUtils.simplify.\noccurrences=nothing: Information about the occurrences of the independent variable in the argument of the derivative. This is used internally for optimization purposes.\n\nExamples\n\njulia> @variables x y z k;\n\njulia> f=k*(abs(x-y)/y-z)^2\nk*((abs(x - y) / y - z)^2)\n\njulia> Dx=Differential(x) # Differentiate wrt x\n(::Differential) (generic function with 2 methods)\n\njulia> dfx=expand_derivatives(Dx(f))\n(k*((2abs(x - y)) / y - 2z)*IfElse.ifelse(signbit(x - y), -1, 1)) / y\n\n\n\n\n\n","category":"function"},{"location":"manual/derivatives/","page":"Derivatives and Differentials","title":"Derivatives and Differentials","text":"note: Note\nFor symbolic differentiation, all registered functions in the symbolic expression need a registered derivative. For more information, see the function registration page.","category":"page"},{"location":"manual/derivatives/#High-Level-Differentiation-Functions","page":"Derivatives and Differentials","title":"High-Level Differentiation Functions","text":"","category":"section"},{"location":"manual/derivatives/","page":"Derivatives and Differentials","title":"Derivatives and Differentials","text":"The following functions are not exported and thus must be accessed in a namespaced way, i.e. Symbolics.jacobian.","category":"page"},{"location":"manual/derivatives/","page":"Derivatives and Differentials","title":"Derivatives and Differentials","text":"Symbolics.derivative\nSymbolics.jacobian\nSymbolics.sparsejacobian\nSymbolics.sparsejacobian_vals\nSymbolics.gradient\nSymbolics.hessian\nSymbolics.sparsehessian\nSymbolics.sparsehessian_vals","category":"page"},{"location":"manual/derivatives/#Symbolics.derivative","page":"Derivatives and Differentials","title":"Symbolics.derivative","text":"derivative(O, var; simplify)\n\n\nA helper function for computing the derivative of the expression O with respect to var.\n\n\n\n\n\n","category":"function"},{"location":"manual/derivatives/#Symbolics.jacobian","page":"Derivatives and Differentials","title":"Symbolics.jacobian","text":"jacobian(ops, vars; simplify, scalarize)\n\n\nA helper function for computing the Jacobian of an array of expressions with respect to an array of variable expressions.\n\n\n\n\n\n","category":"function"},{"location":"manual/derivatives/#Symbolics.sparsejacobian","page":"Derivatives and Differentials","title":"Symbolics.sparsejacobian","text":"sparsejacobian(ops, vars; simplify)\n\n\nA helper function for computing the sparse Jacobian of an array of expressions with respect to an array of variable expressions.\n\n\n\n\n\n","category":"function"},{"location":"manual/derivatives/#Symbolics.sparsejacobian_vals","page":"Derivatives and Differentials","title":"Symbolics.sparsejacobian_vals","text":"sparsejacobian_vals(ops, vars, I, J; simplify)\n\n\nA helper function for computing the values of the sparse Jacobian of an array of expressions with respect to an array of variable expressions given the sparsity structure.\n\n\n\n\n\n","category":"function"},{"location":"manual/derivatives/#Symbolics.gradient","page":"Derivatives and Differentials","title":"Symbolics.gradient","text":"gradient(O, vars; simplify)\n\n\nA helper function for computing the gradient of the expression O with respect to an array of variable expressions.\n\n\n\n\n\n","category":"function"},{"location":"manual/derivatives/#Symbolics.hessian","page":"Derivatives and Differentials","title":"Symbolics.hessian","text":"hessian(O, vars; simplify)\n\n\nA helper function for computing the Hessian of the expression O with respect to an array of variable expressions.\n\n\n\n\n\n","category":"function"},{"location":"manual/derivatives/#Symbolics.sparsehessian","page":"Derivatives and Differentials","title":"Symbolics.sparsehessian","text":"sparsehessian(op, vars; simplify, full)\n\n\nA helper function for computing the sparse Hessian of an expression with respect to an array of variable expressions.\n\n\n\n\n\n","category":"function"},{"location":"manual/derivatives/#Symbolics.sparsehessian_vals","page":"Derivatives and Differentials","title":"Symbolics.sparsehessian_vals","text":"sparsehessian_vals(op, vars, I, J; simplify)\n\n\nA helper function for computing the values of the sparse Hessian of an expression with respect to an array of variable expressions given the sparsity structure.\n\n\n\n\n\n","category":"function"},{"location":"manual/derivatives/#Adding-Analytical-Derivatives","page":"Derivatives and Differentials","title":"Adding Analytical Derivatives","text":"","category":"section"},{"location":"manual/derivatives/","page":"Derivatives and Differentials","title":"Derivatives and Differentials","text":"There are many derivatives pre-defined by DiffRules.jl. For example,","category":"page"},{"location":"manual/derivatives/","page":"Derivatives and Differentials","title":"Derivatives and Differentials","text":"using Symbolics\n@variables x y z\nf(x,y,z) = x^2 + sin(x+y) - z","category":"page"},{"location":"manual/derivatives/","page":"Derivatives and Differentials","title":"Derivatives and Differentials","text":"f automatically has the derivatives defined via the tracing mechanism. It will do this by directly building the internals of your function and differentiating that.","category":"page"},{"location":"manual/derivatives/","page":"Derivatives and Differentials","title":"Derivatives and Differentials","text":"However, often you may want to define your own derivatives so that way automatic Jacobian etc. calculations can utilize this information. This can allow for more succinct versions of the derivatives to be calculated to scale to larger systems. You can define derivatives for your function via the dispatch:","category":"page"},{"location":"manual/derivatives/","page":"Derivatives and Differentials","title":"Derivatives and Differentials","text":"# `N` arguments are accepted by the relevant method of `my_function`\nSymbolics.derivative(::typeof(my_function), args::NTuple{N,Any}, ::Val{i})","category":"page"},{"location":"manual/derivatives/","page":"Derivatives and Differentials","title":"Derivatives and Differentials","text":"where i means that it's the derivative with respect to the ith argument. args is the array of arguments, so, for example, if your function is f(x,t), then args = [x,t]. You should return an Term for the derivative of your function.","category":"page"},{"location":"manual/derivatives/","page":"Derivatives and Differentials","title":"Derivatives and Differentials","text":"For example, sin(t)'s derivative (by t) is given by the following:","category":"page"},{"location":"manual/derivatives/","page":"Derivatives and Differentials","title":"Derivatives and Differentials","text":"Symbolics.derivative(::typeof(sin), args::NTuple{1,Any}, ::Val{1}) = cos(args[1])","category":"page"},{"location":"#Symbolics.jl","page":"Home","title":"Symbolics.jl","text":"","category":"section"},{"location":"","page":"Home","title":"Home","text":"Symbolics.jl is a fast and modern Computer Algebra System (CAS) for a fast and modern programming language (Julia). The goal is to have a high-performance and parallelized symbolic algebra system that is directly extendable in the same language as that of the users.","category":"page"},{"location":"#Installation","page":"Home","title":"Installation","text":"","category":"section"},{"location":"","page":"Home","title":"Home","text":"To install Symbolics.jl, use the Julia package manager:","category":"page"},{"location":"","page":"Home","title":"Home","text":"using Pkg\nPkg.add(\"Symbolics\")","category":"page"},{"location":"#Citation","page":"Home","title":"Citation","text":"","category":"section"},{"location":"","page":"Home","title":"Home","text":"If you use Symbolics.jl, please cite this paper","category":"page"},{"location":"","page":"Home","title":"Home","text":"@article{gowda2021high,\n title={High-performance symbolic-numerics via multiple dispatch},\n author={Gowda, Shashi and Ma, Yingbo and Cheli, Alessandro and Gwozdz, Maja and Shah, Viral B and Edelman, Alan and Rackauckas, Christopher},\n journal={arXiv preprint arXiv:2105.03949},\n year={2021}\n}","category":"page"},{"location":"#Feature-Summary","page":"Home","title":"Feature Summary","text":"","category":"section"},{"location":"","page":"Home","title":"Home","text":"Because Symbolics.jl is built into the Julia language and works with its dispatches, generic functions in Base Julia will work with symbolic expressions! Make matrices of symbolic expressions and multiply them: it will just work. Take the LU-factorization. Etc. Thus, see the Julia Documentation for a large list of functionality available in Symbolics.jl.","category":"page"},{"location":"","page":"Home","title":"Home","text":"A general list of the features is:","category":"page"},{"location":"","page":"Home","title":"Home","text":"Symbolic arithmetic with type information and multiple dispatch\nSymbolic polynomials and trigonometric functions\nPattern matching, simplification and substitution\nDifferentiation\nSymbolic linear algebra (factorizations, inversion, determinants, eigencomputations, etc.)\nDiscrete math (representations of summations, products, binomial coefficients, etc.)\nLogical and Boolean expressions\nSymbolic equation solving and conversion to arbitrary precision\nSupport for non-standard algebras (non-commutative symbols and customizable rulesets)\nSpecial functions (list provided by SpecialFunctions.jl)\nAutomatic conversion of Julia code to symbolic code\nGeneration of (high performance and parallel) functions from symbolic expressions\nFast automated sparsity detection and generation of sparse Jacobians and Hessians","category":"page"},{"location":"","page":"Home","title":"Home","text":"and much more.","category":"page"},{"location":"#Extension-Packages","page":"Home","title":"Extension Packages","text":"","category":"section"},{"location":"","page":"Home","title":"Home","text":"Below is a list of known extension packages. If you would like your package to be listed here, feel free to open a pull request!","category":"page"},{"location":"","page":"Home","title":"Home","text":"ModelingToolkit.jl: Symbolic representations of common numerical systems\nOrdinary differential equations\nStochastic differential equations\nPartial differential equations\nNonlinear systems\nOptimization problems\nOptimal Control\nCausal and acausal modeling (Simulink/Modelica)\nAutomated model transformation, simplification, and composition\nCatalyst.jl: Symbolic representations of chemical reactions\nSymbolically build and represent large systems of chemical reactions\nGenerate code for ODEs, SDEs, continuous-time Markov Chains, and more\nSimulate the models using the SciML ecosystem with O(1) Gillespie methods\nDataDrivenDiffEq.jl: Automatic identification of equations from data\nAutomated construction of ODEs and DAEs from data\nRepresentations of Koopman operators and Dynamic Mode Decomposition (DMD)\nSymbolicRegression.jl: Distributed High-Performance symbolic regression\nParallelized generic algorithms for finding equations from data\nPareto frontier-based scoring\nReversePropagation.jl: Source-to-source reverse mode automatic differentiation\nAutomated tracing of code and construction of backpropagation equations\nComposes with symbolic transformation and simplification functionality","category":"page"},{"location":"#Reproducibility","page":"Home","title":"Reproducibility","text":"","category":"section"},{"location":"","page":"Home","title":"Home","text":"
The documentation of this SciML package was built using these direct dependencies,","category":"page"},{"location":"","page":"Home","title":"Home","text":"using Pkg # hide\nPkg.status() # hide","category":"page"},{"location":"","page":"Home","title":"Home","text":"
","category":"page"},{"location":"","page":"Home","title":"Home","text":"
and using this machine and Julia version.","category":"page"},{"location":"","page":"Home","title":"Home","text":"using InteractiveUtils # hide\nversioninfo() # hide","category":"page"},{"location":"","page":"Home","title":"Home","text":"
","category":"page"},{"location":"","page":"Home","title":"Home","text":"
A more complete overview of all dependencies and their versions is also provided.","category":"page"},{"location":"","page":"Home","title":"Home","text":"using Pkg # hide\nPkg.status(; mode = PKGMODE_MANIFEST) # hide","category":"page"},{"location":"","page":"Home","title":"Home","text":"
","category":"page"},{"location":"","page":"Home","title":"Home","text":"using TOML\nusing Markdown\nversion = TOML.parse(read(\"../../Project.toml\", String))[\"version\"]\nname = TOML.parse(read(\"../../Project.toml\", String))[\"name\"]\nlink_manifest = \"https://github.com/SciML/\" * name * \".jl/tree/gh-pages/v\" * version *\n \"/assets/Manifest.toml\"\nlink_project = \"https://github.com/SciML/\" * name * \".jl/tree/gh-pages/v\" * version *\n \"/assets/Project.toml\"\nMarkdown.parse(\"\"\"You can also download the\n[manifest]($link_manifest)\nfile and the\n[project]($link_project)\nfile.\n\"\"\")","category":"page"},{"location":"examples/perturbation/#perturb_alg","page":"Mixed Symbolic-Numeric Perturbation Theory","title":"Mixed Symbolic-Numeric Perturbation Theory","text":"","category":"section"},{"location":"examples/perturbation/#Background","page":"Mixed Symbolic-Numeric Perturbation Theory","title":"Background","text":"","category":"section"},{"location":"examples/perturbation/","page":"Mixed Symbolic-Numeric Perturbation Theory","title":"Mixed Symbolic-Numeric Perturbation Theory","text":"Symbolics.jl is a fast and modern Computer Algebra System (CAS) written in the Julia Programming Language. It is an integral part of the SciML ecosystem of differential equation solvers and scientific machine learning packages. While Symbolics.jl is primarily designed for modern scientific computing (e.g., auto-differentiation, machine learning), it is a powerful CAS that can also be useful for classic scientific computing. One such application is using the perturbation theory to solve algebraic and differential equations.","category":"page"},{"location":"examples/perturbation/","page":"Mixed Symbolic-Numeric Perturbation Theory","title":"Mixed Symbolic-Numeric Perturbation Theory","text":"Perturbation methods are a collection of techniques to solve intractable problems that generally don't have a closed solution but depend on a tunable parameter and have closed or easy solutions for some values of the parameter. The main idea is to assume a solution as a power series in the tunable parameter (say ϵ), such that ϵ = 0 corresponds to an easy solution.","category":"page"},{"location":"examples/perturbation/","page":"Mixed Symbolic-Numeric Perturbation Theory","title":"Mixed Symbolic-Numeric Perturbation Theory","text":"We will discuss the general steps of the perturbation methods to solve algebraic (this tutorial) and differential equations","category":"page"},{"location":"examples/perturbation/","page":"Mixed Symbolic-Numeric Perturbation Theory","title":"Mixed Symbolic-Numeric Perturbation Theory","text":"The hallmark of the perturbation method is the generation of long and convoluted intermediate equations, which are subjected to algorithmic and mechanical manipulations. Therefore, these problems are well suited for CAS. In fact, CAS software packages have been used to help with the perturbation calculations since the early 1970s.","category":"page"},{"location":"examples/perturbation/","page":"Mixed Symbolic-Numeric Perturbation Theory","title":"Mixed Symbolic-Numeric Perturbation Theory","text":"In this tutorial, our goal is to show how to use a mix of symbolic manipulations (Symbolics.jl) and numerical methods to solve simple perturbation problems.","category":"page"},{"location":"examples/perturbation/#Solving-the-Quintic","page":"Mixed Symbolic-Numeric Perturbation Theory","title":"Solving the Quintic","text":"","category":"section"},{"location":"examples/perturbation/","page":"Mixed Symbolic-Numeric Perturbation Theory","title":"Mixed Symbolic-Numeric Perturbation Theory","text":"We start with the “hello world!” analog of the perturbation problems, solving the quintic (fifth-order) equations. We want to find a real valued x such that x^5 + x = 1. According to the Abel's theorem, a general quintic equation does not have a closed form solution. Of course, we can easily solve this equation numerically; for example, by using the Newton's method. We use the following implementation of the Newton's method:","category":"page"},{"location":"examples/perturbation/","page":"Mixed Symbolic-Numeric Perturbation Theory","title":"Mixed Symbolic-Numeric Perturbation Theory","text":"using Symbolics, SymbolicUtils\n\nfunction solve_newton(f, x, x₀; abstol=1e-8, maxiter=50)\n xₙ = Float64(x₀)\n fₙ₊₁ = x - f / Symbolics.derivative(f, x)\n\n for i = 1:maxiter\n xₙ₊₁ = substitute(fₙ₊₁, Dict(x => xₙ))\n if abs(xₙ₊₁ - xₙ) < abstol\n return xₙ₊₁\n else\n xₙ = xₙ₊₁\n end\n end\n return xₙ₊₁\nend","category":"page"},{"location":"examples/perturbation/","page":"Mixed Symbolic-Numeric Perturbation Theory","title":"Mixed Symbolic-Numeric Perturbation Theory","text":"In this code, Symbolics.derivative(eq, x) does exactly what it names implies: it calculates the symbolic derivative of eq (a Symbolics.jl expression) with respect to x (a Symbolics.jl variable). We use Symbolics.substitute(eq, D) to evaluate the update formula by substituting variables or sub-expressions (defined in a dictionary D) in eq. It should be noted that substitute is the workhorse of our code and will be used multiple times in the rest of these tutorials. solve_newton is written with simplicity and clarity in mind, and not performance.","category":"page"},{"location":"examples/perturbation/","page":"Mixed Symbolic-Numeric Perturbation Theory","title":"Mixed Symbolic-Numeric Perturbation Theory","text":"Let's go back to our quintic. We can define a Symbolics variable as @variables x and then solve the equation solve_newton(x^5 + x - 1, x, 1.0) (here, x₀ = 1.0 is our first guess). The answer is 0.7549. Now, let's see how we can solve the same problem using the perturbation methods.","category":"page"},{"location":"examples/perturbation/","page":"Mixed Symbolic-Numeric Perturbation Theory","title":"Mixed Symbolic-Numeric Perturbation Theory","text":"We introduce a tuning parameter epsilon into our equation: x^5 + epsilon x = 1. If epsilon = 1, we get our original problem. For epsilon = 0, the problem transforms to an easy one: x^5 = 1 which has an exact real solution x = 1 (and four complex solutions which we ignore here). We expand x as a power series on epsilon:","category":"page"},{"location":"examples/perturbation/","page":"Mixed Symbolic-Numeric Perturbation Theory","title":"Mixed Symbolic-Numeric Perturbation Theory","text":" x(epsilon) = a_0 + a_1 epsilon + a_2 epsilon^2 + O(epsilon^3)","category":"page"},{"location":"examples/perturbation/","page":"Mixed Symbolic-Numeric Perturbation Theory","title":"Mixed Symbolic-Numeric Perturbation Theory","text":"a_0","category":"page"},{"location":"examples/perturbation/","page":"Mixed Symbolic-Numeric Perturbation Theory","title":"Mixed Symbolic-Numeric Perturbation Theory","text":"is the solution of the easy equation, therefore a_0 = 1. Substituting into the original problem,","category":"page"},{"location":"examples/perturbation/","page":"Mixed Symbolic-Numeric Perturbation Theory","title":"Mixed Symbolic-Numeric Perturbation Theory","text":" (a_0 + a_1 epsilon + a_2 epsilon^2)^5 + epsilon (a_0 + a_1 epsilon + a_2 epsilon^2) - 1 = 0","category":"page"},{"location":"examples/perturbation/","page":"Mixed Symbolic-Numeric Perturbation Theory","title":"Mixed Symbolic-Numeric Perturbation Theory","text":"Expanding the equations, we get","category":"page"},{"location":"examples/perturbation/","page":"Mixed Symbolic-Numeric Perturbation Theory","title":"Mixed Symbolic-Numeric Perturbation Theory","text":" epsilon (1 + 5 a_1) + epsilon^2 (a_1 + 5 a_2 + 10 a_1^2) + 𝑂(epsilon^3) = 0","category":"page"},{"location":"examples/perturbation/","page":"Mixed Symbolic-Numeric Perturbation Theory","title":"Mixed Symbolic-Numeric Perturbation Theory","text":"This equation should hold for each power of epsilon. Therefore,","category":"page"},{"location":"examples/perturbation/","page":"Mixed Symbolic-Numeric Perturbation Theory","title":"Mixed Symbolic-Numeric Perturbation Theory","text":" 1 + 5 a_1 = 0","category":"page"},{"location":"examples/perturbation/","page":"Mixed Symbolic-Numeric Perturbation Theory","title":"Mixed Symbolic-Numeric Perturbation Theory","text":"and","category":"page"},{"location":"examples/perturbation/","page":"Mixed Symbolic-Numeric Perturbation Theory","title":"Mixed Symbolic-Numeric Perturbation Theory","text":" a_1 + 5 a_2 + 10 a_1^2 = 0","category":"page"},{"location":"examples/perturbation/","page":"Mixed Symbolic-Numeric Perturbation Theory","title":"Mixed Symbolic-Numeric Perturbation Theory","text":"This system of equations does not initially seem to be linear because of the presence of terms like 10 a_1^2, but upon closer inspection is found to be linear (this is a feature of the perturbation methods). In addition, the system is in a triangular form, meaning the first equation depends only on a_1, the second one on a_1 and a_2, such that we can replace the result of a_1 from the first one into the second equation and remove the non-linear term. We solve the first equation to get a_1 = -frac15. Substituting in the second one and solve for a_2:","category":"page"},{"location":"examples/perturbation/","page":"Mixed Symbolic-Numeric Perturbation Theory","title":"Mixed Symbolic-Numeric Perturbation Theory","text":" a_2 = frac(-frac15 + 10(-(frac15)²)5 = -frac125","category":"page"},{"location":"examples/perturbation/","page":"Mixed Symbolic-Numeric Perturbation Theory","title":"Mixed Symbolic-Numeric Perturbation Theory","text":"Finally,","category":"page"},{"location":"examples/perturbation/","page":"Mixed Symbolic-Numeric Perturbation Theory","title":"Mixed Symbolic-Numeric Perturbation Theory","text":" x(epsilon) = 1 - fracepsilon5 - fracepsilon^225 + O(epsilon^3)","category":"page"},{"location":"examples/perturbation/","page":"Mixed Symbolic-Numeric Perturbation Theory","title":"Mixed Symbolic-Numeric Perturbation Theory","text":"Solving the original problem, x(1) = 076, compared to 0.7548 calculated numerically. We can improve the accuracy by including more terms in the expansion of x. However, the calculations, while straightforward, become messy and intractable to do manually very quickly. This is why a CAS is very helpful to solve perturbation problems.","category":"page"},{"location":"examples/perturbation/","page":"Mixed Symbolic-Numeric Perturbation Theory","title":"Mixed Symbolic-Numeric Perturbation Theory","text":"Now, let's see how we can do these calculations in Julia. Let n be the order of the expansion. We start by defining the symbolic variables:","category":"page"},{"location":"examples/perturbation/","page":"Mixed Symbolic-Numeric Perturbation Theory","title":"Mixed Symbolic-Numeric Perturbation Theory","text":"n = 2\n@variables ϵ a[1:n]","category":"page"},{"location":"examples/perturbation/","page":"Mixed Symbolic-Numeric Perturbation Theory","title":"Mixed Symbolic-Numeric Perturbation Theory","text":"Then, we define","category":"page"},{"location":"examples/perturbation/","page":"Mixed Symbolic-Numeric Perturbation Theory","title":"Mixed Symbolic-Numeric Perturbation Theory","text":"x = 1 + a[1]*ϵ + a[2]*ϵ^2","category":"page"},{"location":"examples/perturbation/","page":"Mixed Symbolic-Numeric Perturbation Theory","title":"Mixed Symbolic-Numeric Perturbation Theory","text":"The next step is to substitute x in the problem equation","category":"page"},{"location":"examples/perturbation/","page":"Mixed Symbolic-Numeric Perturbation Theory","title":"Mixed Symbolic-Numeric Perturbation Theory","text":" eq = x^5 + ϵ*x - 1","category":"page"},{"location":"examples/perturbation/","page":"Mixed Symbolic-Numeric Perturbation Theory","title":"Mixed Symbolic-Numeric Perturbation Theory","text":"The expanded form of eq is","category":"page"},{"location":"examples/perturbation/","page":"Mixed Symbolic-Numeric Perturbation Theory","title":"Mixed Symbolic-Numeric Perturbation Theory","text":"expand(eq)","category":"page"},{"location":"examples/perturbation/","page":"Mixed Symbolic-Numeric Perturbation Theory","title":"Mixed Symbolic-Numeric Perturbation Theory","text":"We need a way to get the coefficients of different powers of ϵ. Function collect_powers(eq, x, ns) returns the powers of variable x in expression eq. Argument ns is the range of the powers.","category":"page"},{"location":"examples/perturbation/","page":"Mixed Symbolic-Numeric Perturbation Theory","title":"Mixed Symbolic-Numeric Perturbation Theory","text":"function collect_powers(eq, x, ns)\n eq = expand(eq)\n [Symbolics.coeff(eq, x^i) for i in ns]\nend","category":"page"},{"location":"examples/perturbation/","page":"Mixed Symbolic-Numeric Perturbation Theory","title":"Mixed Symbolic-Numeric Perturbation Theory","text":"To return the coefficients of ϵ and ϵ^2 in eq, we can write","category":"page"},{"location":"examples/perturbation/","page":"Mixed Symbolic-Numeric Perturbation Theory","title":"Mixed Symbolic-Numeric Perturbation Theory","text":"eqs = collect_powers(eq, ϵ, 1:2)","category":"page"},{"location":"examples/perturbation/","page":"Mixed Symbolic-Numeric Perturbation Theory","title":"Mixed Symbolic-Numeric Perturbation Theory","text":"Having the coefficients of the powers of ϵ, we can set each equation in eqs to 0 (remember, we rearrange the problem such that eq is 0) and solve the system of linear equations to find the numerical values of the coefficients. Symbolics.jl has a function symbolic_linear_solve that can solve systems of linear equations. However, the presence of higher-order terms in eqs prevents symbolic_linear_solve(eqs, a) from workings properly. Instead, we can exploit the fact that our system is in a triangular form and start by solving eqs[1] for a₁ and then substitute this in eqs[2] and solve for a₂, and so on. This cascading process is done by function solve_coef(eqs, ps):","category":"page"},{"location":"examples/perturbation/","page":"Mixed Symbolic-Numeric Perturbation Theory","title":"Mixed Symbolic-Numeric Perturbation Theory","text":"function solve_coef(eqs, ps)\n vals = Dict()\n\n for i = 1:length(ps)\n eq = substitute(eqs[i], vals)\n vals[ps[i]] = symbolic_linear_solve(eq, ps[i])\n end\n vals\nend","category":"page"},{"location":"examples/perturbation/","page":"Mixed Symbolic-Numeric Perturbation Theory","title":"Mixed Symbolic-Numeric Perturbation Theory","text":"Here, eqs is an array of expressions (assumed to be equal to 0) and ps is an array of variables. The result is a dictionary of variable => value pairs. We apply solve_coef to eqs to get the numerical values of the parameters:","category":"page"},{"location":"examples/perturbation/","page":"Mixed Symbolic-Numeric Perturbation Theory","title":"Mixed Symbolic-Numeric Perturbation Theory","text":"vals = solve_coef(eqs, a)","category":"page"},{"location":"examples/perturbation/","page":"Mixed Symbolic-Numeric Perturbation Theory","title":"Mixed Symbolic-Numeric Perturbation Theory","text":"Finally, we substitute back the values of a in the definition of x as a function of 𝜀. Note that 𝜀 is a number (usually Float64), whereas ϵ is a symbolic variable.","category":"page"},{"location":"examples/perturbation/","page":"Mixed Symbolic-Numeric Perturbation Theory","title":"Mixed Symbolic-Numeric Perturbation Theory","text":"X = 𝜀 -> 1 + vals[a[1]]*𝜀 + vals[a[2]]*𝜀^2","category":"page"},{"location":"examples/perturbation/","page":"Mixed Symbolic-Numeric Perturbation Theory","title":"Mixed Symbolic-Numeric Perturbation Theory","text":"Therefore, the solution to our original problem becomes X(1), which is equal to 0.76. We can use larger values of n to improve the accuracy of estimations.","category":"page"},{"location":"examples/perturbation/","page":"Mixed Symbolic-Numeric Perturbation Theory","title":"Mixed Symbolic-Numeric Perturbation Theory","text":"n x\n1 0.8\n2 0.76\n3 0.752\n4 0.752\n5 0.7533\n6 0.7543\n7 0.7548\n8 0.7550","category":"page"},{"location":"examples/perturbation/","page":"Mixed Symbolic-Numeric Perturbation Theory","title":"Mixed Symbolic-Numeric Perturbation Theory","text":"Remember, the numerical value is 0.7549. The two functions collect_powers and solve_coef(eqs, a) are used in all the examples in this and the next tutorial.","category":"page"},{"location":"examples/perturbation/#Solving-the-Kepler's-Equation","page":"Mixed Symbolic-Numeric Perturbation Theory","title":"Solving the Kepler's Equation","text":"","category":"section"},{"location":"examples/perturbation/","page":"Mixed Symbolic-Numeric Perturbation Theory","title":"Mixed Symbolic-Numeric Perturbation Theory","text":"Historically, the perturbation methods were first invented to solve orbital calculations of the Moon and the planets. In homage to this history, our second example has a celestial theme. Our goal is solving the Kepler's equation:","category":"page"},{"location":"examples/perturbation/","page":"Mixed Symbolic-Numeric Perturbation Theory","title":"Mixed Symbolic-Numeric Perturbation Theory","text":" E - esin(E) = M","category":"page"},{"location":"examples/perturbation/","page":"Mixed Symbolic-Numeric Perturbation Theory","title":"Mixed Symbolic-Numeric Perturbation Theory","text":"where e is the eccentricity of the elliptical orbit, M is the mean anomaly, and E (unknown) is the eccentric anomaly (the angle between the position of a planet in an elliptical orbit and the point of periapsis). This equation is central to solving two-body Keplerian orbits.","category":"page"},{"location":"examples/perturbation/","page":"Mixed Symbolic-Numeric Perturbation Theory","title":"Mixed Symbolic-Numeric Perturbation Theory","text":"Similar to the first example, it is easy to solve this problem using the Newton's method. For example, let e = 001671 (the eccentricity of the Earth) and M = pi2. We have solve_newton(x - e*sin(x) - M, x, M) equals to 1.5875 (compared to π/2 = 1.5708). Now, we try to solve the same problem using the perturbation techniques (see function test_kepler).","category":"page"},{"location":"examples/perturbation/","page":"Mixed Symbolic-Numeric Perturbation Theory","title":"Mixed Symbolic-Numeric Perturbation Theory","text":"For e = 0, we get E = M. Therefore, we can use e as our perturbation parameter. For consistency with other problems, we also rename e to epsilon and E to x.","category":"page"},{"location":"examples/perturbation/","page":"Mixed Symbolic-Numeric Perturbation Theory","title":"Mixed Symbolic-Numeric Perturbation Theory","text":"From here on, we use the helper function def_taylor to define Taylor's series by calling it as x = def_taylor(ϵ, a, 1), where the arguments are, respectively, the perturbation variable, which is an array of coefficients (starting from the coefficient of epsilon^1), and an optional constant term.","category":"page"},{"location":"examples/perturbation/","page":"Mixed Symbolic-Numeric Perturbation Theory","title":"Mixed Symbolic-Numeric Perturbation Theory","text":"def_taylor(x, ps) = sum([a*x^i for (i,a) in enumerate(ps)])\ndef_taylor(x, ps, p₀) = p₀ + def_taylor(x, ps)","category":"page"},{"location":"examples/perturbation/","page":"Mixed Symbolic-Numeric Perturbation Theory","title":"Mixed Symbolic-Numeric Perturbation Theory","text":"We start by defining the variables (assuming n = 3):","category":"page"},{"location":"examples/perturbation/","page":"Mixed Symbolic-Numeric Perturbation Theory","title":"Mixed Symbolic-Numeric Perturbation Theory","text":"n = 3\n@variables ϵ M a[1:n]\nx = def_taylor(ϵ, a, M)","category":"page"},{"location":"examples/perturbation/","page":"Mixed Symbolic-Numeric Perturbation Theory","title":"Mixed Symbolic-Numeric Perturbation Theory","text":"We further simplify by substituting sin with its power series using the expand_sin helper function:","category":"page"},{"location":"examples/perturbation/","page":"Mixed Symbolic-Numeric Perturbation Theory","title":"Mixed Symbolic-Numeric Perturbation Theory","text":"expand_sin(x, n) = sum([(isodd(k) ? -1 : 1)*(-x)^(2k-1)/factorial(2k-1) for k=1:n])","category":"page"},{"location":"examples/perturbation/","page":"Mixed Symbolic-Numeric Perturbation Theory","title":"Mixed Symbolic-Numeric Perturbation Theory","text":"To test,","category":"page"},{"location":"examples/perturbation/","page":"Mixed Symbolic-Numeric Perturbation Theory","title":"Mixed Symbolic-Numeric Perturbation Theory","text":"expand_sin(0.1, 10) ≈ sin(0.1)","category":"page"},{"location":"examples/perturbation/","page":"Mixed Symbolic-Numeric Perturbation Theory","title":"Mixed Symbolic-Numeric Perturbation Theory","text":"The problem equation is","category":"page"},{"location":"examples/perturbation/","page":"Mixed Symbolic-Numeric Perturbation Theory","title":"Mixed Symbolic-Numeric Perturbation Theory","text":"eq = x - ϵ * expand_sin(x, n) - M","category":"page"},{"location":"examples/perturbation/","page":"Mixed Symbolic-Numeric Perturbation Theory","title":"Mixed Symbolic-Numeric Perturbation Theory","text":"We follow the same process as the first example. We collect the coefficients of the powers of ϵ","category":"page"},{"location":"examples/perturbation/","page":"Mixed Symbolic-Numeric Perturbation Theory","title":"Mixed Symbolic-Numeric Perturbation Theory","text":"eqs = collect_powers(eq, ϵ, 1:n)","category":"page"},{"location":"examples/perturbation/","page":"Mixed Symbolic-Numeric Perturbation Theory","title":"Mixed Symbolic-Numeric Perturbation Theory","text":"and then solve for a:","category":"page"},{"location":"examples/perturbation/","page":"Mixed Symbolic-Numeric Perturbation Theory","title":"Mixed Symbolic-Numeric Perturbation Theory","text":"vals = solve_coef(eqs, a)","category":"page"},{"location":"examples/perturbation/","page":"Mixed Symbolic-Numeric Perturbation Theory","title":"Mixed Symbolic-Numeric Perturbation Theory","text":"Finally, we substitute vals back in x:","category":"page"},{"location":"examples/perturbation/","page":"Mixed Symbolic-Numeric Perturbation Theory","title":"Mixed Symbolic-Numeric Perturbation Theory","text":"x′ = substitute(x, vals)\nX = (𝜀, 𝑀) -> substitute(x′, Dict(ϵ => 𝜀, M => 𝑀))\nX(0.01671, π/2)","category":"page"},{"location":"examples/perturbation/","page":"Mixed Symbolic-Numeric Perturbation Theory","title":"Mixed Symbolic-Numeric Perturbation Theory","text":"The result is 1.5876, compared to the numerical value of 1.5875. It is customary to order X based on the powers of 𝑀 instead of 𝜀. We can calculate this series as collect_powers(x′, M, 0:5). The result (after manual cleanup) is","category":"page"},{"location":"examples/perturbation/","page":"Mixed Symbolic-Numeric Perturbation Theory","title":"Mixed Symbolic-Numeric Perturbation Theory","text":"(1 + 𝜀 + 𝜀^2 + 𝜀^3)*𝑀\n- (𝜀 + 4*𝜀^2 + 10*𝜀^3)*𝑀^3/6\n+ (𝜀 + 16*𝜀^2 + 91*𝜀^3)*𝑀^5/120","category":"page"},{"location":"examples/perturbation/","page":"Mixed Symbolic-Numeric Perturbation Theory","title":"Mixed Symbolic-Numeric Perturbation Theory","text":"Comparing the formula to the one for 𝐸 in the Wikipedia article on the Kepler's equation:","category":"page"},{"location":"examples/perturbation/","page":"Mixed Symbolic-Numeric Perturbation Theory","title":"Mixed Symbolic-Numeric Perturbation Theory","text":" E = frac11-epsilonM\n -fracepsilon(1-epsilon)^4 fracM^33 + frac(9epsilon^2\n + epsilon)(1-epsilon)^7fracM^55cdots","category":"page"},{"location":"examples/perturbation/","page":"Mixed Symbolic-Numeric Perturbation Theory","title":"Mixed Symbolic-Numeric Perturbation Theory","text":"The first deviation is in the coefficient of epsilon^3 M^5.","category":"page"}] } diff --git a/dev/tutorials/auto_parallel/5461512f.svg b/dev/tutorials/auto_parallel/d2cd0e1a.svg similarity index 58% rename from dev/tutorials/auto_parallel/5461512f.svg rename to dev/tutorials/auto_parallel/d2cd0e1a.svg index 112d685e5..21d8c96e3 100644 --- a/dev/tutorials/auto_parallel/5461512f.svg +++ b/dev/tutorials/auto_parallel/d2cd0e1a.svg @@ -1,13222 +1,13222 @@ - + - + - + - + - + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/dev/tutorials/auto_parallel/index.html b/dev/tutorials/auto_parallel/index.html index a50961c21..06c0d03e2 100644 --- a/dev/tutorials/auto_parallel/index.html +++ b/dev/tutorials/auto_parallel/index.html @@ -59,7 +59,7 @@ \]

The output, here the in-place modified du, is a symbolic representation of each output of the function. We can then utilize this in the Symbolics functionality. For example, let's build a parallel version of f first:

fastf = eval(Symbolics.build_function(du,u,
             parallel=Symbolics.MultithreadedForm())[2])
#13 (generic function with 1 method)

Now let's compute the sparse Jacobian function and compile a fast multithreaded version:

jac = Symbolics.sparsejacobian(vec(du), vec(u))
 row,col,val = findnz(jac)
-scatter(row,col,legend=false,ms=1,c=:black)
Example block output
fjac = eval(Symbolics.build_function(jac,u,
+scatter(row,col,legend=false,ms=1,c=:black)
Example block output
fjac = eval(Symbolics.build_function(jac,u,
             parallel=Symbolics.MultithreadedForm())[2])
#15 (generic function with 1 method)

It takes awhile for this to generate, but the results will be worth it! Now let's set up the parabolic PDE to be solved by DifferentialEquations.jl. We will set up the vanilla version and the sparse multithreaded version:

using OrdinaryDiffEq
 u0 = zeros(N, N, 3)
 MyA = zeros(N, N);
@@ -139,4 +139,4 @@
  0.0  0.0  0.0  0.0  0.0  0.0  0.0  0.0     0.0  0.0  0.0  0.0  0.0  0.0  0.0

Let's see the timing difference:

using BenchmarkTools
 #@btime solve(prob, TRBDF2()); # 33.073 s (895404 allocations: 23.87 GiB)
 #warning the following solve takes a long time to compile, but afterwards is very fast.
-#@btime solve(fastprob, TRBDF2()); # 209.670 ms (8208 allocations: 109.25 MiB)

Boom, an automatic 157x acceleration that grows as the size of the problem increases!

+#@btime solve(fastprob, TRBDF2()); # 209.670 ms (8208 allocations: 109.25 MiB)

Boom, an automatic 157x acceleration that grows as the size of the problem increases!

diff --git a/dev/tutorials/converting_to_C/index.html b/dev/tutorials/converting_to_C/index.html index 60439a8a2..a9cfa4d5e 100644 --- a/dev/tutorials/converting_to_C/index.html +++ b/dev/tutorials/converting_to_C/index.html @@ -18,11 +18,11 @@ \end{equation} \]

and then we build the function:

build_function(du, u, p, t, target=Symbolics.CTarget())
"#include <math.h>\nvoid diffeqf(double* du, const double* RHS1, const double* RHS2, const double RHS3) {\n  du[0] = RHS2[0] * RHS1[0] + -1 * RHS2[1] * RHS1[0] * RHS1[1];\n  du[1] = -1 * RHS2[2] * RHS1[1] + RHS2[3] * RHS1[0] * RHS1[1];\n}\n"

If we want to compile this, we do expression=Val{false}:

f = build_function(du, u, p, t, target=Symbolics.CTarget(), expression=Val{false})
RuntimeGeneratedFunction(#=in Symbolics=#, #=using Symbolics=#, :((du, u, p, t)->begin
           #= /home/runner/work/Symbolics.jl/Symbolics.jl/src/build_function.jl:809 =#
-          ccall(("diffeqf", "/tmp/jl_vY5afKD8fE"), Cvoid, (Ptr{Float64}, Ptr{Float64}, Ptr{Float64}, Float64), du, u, p, t)
+          ccall(("diffeqf", "/tmp/jl_PEFWNUHEHy"), Cvoid, (Ptr{Float64}, Ptr{Float64}, Ptr{Float64}, Float64), du, u, p, t)
       end))

now we check it computes the same thing:

du = rand(2); du2 = rand(2)
 u = rand(2)
 p = rand(4)
 t = rand()
 f(du, u, p, t)
 lotka_volterra!(du2, u, p, t)
-du == du2 # true!
true
+du == du2 # true!
true