diff --git a/previews/PR1068/.documenter-siteinfo.json b/previews/PR1068/.documenter-siteinfo.json index 397678155..428fd5019 100644 --- a/previews/PR1068/.documenter-siteinfo.json +++ b/previews/PR1068/.documenter-siteinfo.json @@ -1 +1 @@ -{"documenter":{"julia_version":"1.10.1","generation_timestamp":"2024-02-22T05:10:12","documenter_version":"1.2.1"}} \ No newline at end of file +{"documenter":{"julia_version":"1.10.1","generation_timestamp":"2024-02-22T05:45:58","documenter_version":"1.2.1"}} \ No newline at end of file diff --git a/previews/PR1068/comparison/index.html b/previews/PR1068/comparison/index.html index 3c5187a4e..032cecf72 100644 --- a/previews/PR1068/comparison/index.html +++ b/previews/PR1068/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/previews/PR1068/examples/perturbation/index.html b/previews/PR1068/examples/perturbation/index.html index 574eed4cb..d9368b6bd 100644 --- a/previews/PR1068/examples/perturbation/index.html +++ b/previews/PR1068/examples/perturbation/index.html @@ -83,4 +83,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/previews/PR1068/getting_started/index.html b/previews/PR1068/getting_started/index.html index 8b7484ffb..ef8d8fc83 100644 --- a/previews/PR1068/getting_started/index.html +++ b/previews/PR1068/getting_started/index.html @@ -220,9 +220,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 18 stored entries:
-⎡⡀⠁⠀⠀⠃⎤
-⎢⠀⠀⠀⠂⠌⎥
-⎢⢀⠠⡐⠅⢤⎥
-⎢⠀⠐⠀⠀⠀⎥
-⎣⠀⠀⠀⠀⠠⎦
+out
20×10 SparseArrays.SparseMatrixCSC{Float64, Int64} with 17 stored entries:
+⎡⢂⡀⠨⠀⠠⎤
+⎢⠀⠀⠠⢀⠀⎥
+⎢⠀⠀⠂⠁⢈⎥
+⎢⠀⡀⠀⠀⠀⎥
+⎣⠈⠀⠂⠀⠒⎦
diff --git a/previews/PR1068/index.html b/previews/PR1068/index.html index 183da5c5e..a6040e7a8 100644 --- a/previews/PR1068/index.html +++ b/previews/PR1068/index.html @@ -335,4 +335,4 @@ [8e850b90] libblastrampoline_jll v5.8.0+1 [8e850ede] nghttp2_jll v1.52.0+1 [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/previews/PR1068/manual/arrays/index.html b/previews/PR1068/manual/arrays/index.html index ec3f008ee..24f33241b 100644 --- a/previews/PR1068/manual/arrays/index.html +++ b/previews/PR1068/manual/arrays/index.html @@ -60,4 +60,8 @@ \]

AAt[2,3]

\[ \begin{equation} A * adjoint(A)_{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.

+ \]

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,:]))
diff --git a/previews/PR1068/manual/build_function/index.html b/previews/PR1068/manual/build_function/index.html index 86550d720..9cf7f7f8b 100644 --- a/previews/PR1068/manual/build_function/index.html +++ b/previews/PR1068/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/previews/PR1068/manual/derivatives/index.html b/previews/PR1068/manual/derivatives/index.html index 9c6faad25..c827d40bb 100644 --- a/previews/PR1068/manual/derivatives/index.html +++ b/previews/PR1068/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)
 

TODO

Examples


 julia> @variables x y z k;
@@ -25,15 +25,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
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, v; simplify)
-

A helper function for computing the derivative of an expression 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 an expression with respect to an array of variable expressions.

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

A helper function for computing the Hessian of an expression 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
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, v; simplify)
+

A helper function for computing the derivative of an expression 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 an expression with respect to an array of variable expressions.

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

A helper function for computing the Hessian of an expression 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/previews/PR1068/manual/expression_manipulation/index.html b/previews/PR1068/manual/expression_manipulation/index.html index 4b4b20e05..61163b866 100644 --- a/previews/PR1068/manual/expression_manipulation/index.html +++ b/previews/PR1068/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(O) -> Vector{BasicSymbolic}

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

Examples

julia> @variables t x y z(t)
@@ -25,7 +25,7 @@
 3-element Vector{Any}:
  x
  y
- z(t)
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)
+ z(t)
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)
@@ -34,13 +34,13 @@
 Symbol("z(t)")
 
 julia> Symbolics.tosymbol(z; escape=false)
-:z
source
Symbolics.diff2termFunction
diff2term(x) -> Symbolic

Convert a differential variable to a Term. Note that it only takes a Term not a Num.

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

Convert a differential variable to a Term. Note that it only takes a Term not a Num.

julia> @variables x t u(x, t) z(t)[1:2]; Dt = Differential(t); Dx = Differential(x);
 
 julia> Symbolics.diff2term(Symbolics.value(Dx(Dt(u))))
 uˍtx(x, t)
 
 julia> Symbolics.diff2term(Symbolics.value(Dt(z[1])))
-var"z(t)[1]ˍt"
source
Symbolics.solve_forFunction
solve_for(eq, var; simplify, check) -> Any
+var"z(t)[1]ˍt"
source
Symbolics.solve_forFunction
solve_for(eq, var; simplify, check) -> Any
 

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

Assumes length(eqs) == length(vars)

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

Examples

julia> @variables x y
 2-element Vector{Num}:
  x
@@ -52,7 +52,7 @@
 julia> Symbolics.solve_for([x + y ~ 0, x - y ~ 2], [x, y])
 2-element Vector{Float64}:
   1.0
- -1.0
source
Symbolics.degreeFunction
degree(p, sym=nothing)

Extract the degree of p with respect to sym.

Examples

julia> @variables x;
+ -1.0
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
@@ -61,7 +61,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
@@ -70,4 +70,4 @@
 2
 
 julia> Symbolics.coeff(x^2 + y, x^2)
-1
source
+1source diff --git a/previews/PR1068/manual/faq/index.html b/previews/PR1068/manual/faq/index.html index a805074c1..0dc0853af 100644 --- a/previews/PR1068/manual/faq/index.html +++ b/previews/PR1068/manual/faq/index.html @@ -16,4 +16,4 @@ x in [x] catch e e -end
TypeError(:if, "", Bool, x == x)
x in Set([x])
true
any(isequal(x), [x])
true

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.

+end
TypeError(:if, "", Bool, x == x)
x in Set([x])
true
any(isequal(x), [x])
true

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.

diff --git a/previews/PR1068/manual/functions/index.html b/previews/PR1068/manual/functions/index.html index 3899f1826..6581b1a86 100644 --- a/previews/PR1068/manual/functions/index.html +++ b/previews/PR1068/manual/functions/index.html @@ -31,11 +31,11 @@ end

is the partial derivative of the Julia min(x,y) function with respect to x.

Note

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

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)

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)

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
source
+endsource diff --git a/previews/PR1068/manual/groebner/index.html b/previews/PR1068/manual/groebner/index.html index a1bf34ec6..22fc7bc87 100644 --- a/previews/PR1068/manual/groebner/index.html +++ b/previews/PR1068/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/previews/PR1068/manual/io/index.html b/previews/PR1068/manual/io/index.html index c0ad2ee26..fdaecf3b0 100644 --- a/previews/PR1068/manual/io/index.html +++ b/previews/PR1068/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/previews/PR1068/manual/parsing/index.html b/previews/PR1068/manual/parsing/index.html index 86720b97b..8cbb56143 100644 --- a/previews/PR1068/manual/parsing/index.html +++ b/previews/PR1068/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/previews/PR1068/manual/sparsity_detection/index.html b/previews/PR1068/manual/sparsity_detection/index.html index 682f4ba5d..386161c32 100644 --- a/previews/PR1068/manual/sparsity_detection/index.html +++ b/previews/PR1068/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
+ 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
diff --git a/previews/PR1068/manual/types/index.html b/previews/PR1068/manual/types/index.html index 55ae41708..32560b0e8 100644 --- a/previews/PR1068/manual/types/index.html +++ b/previews/PR1068/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/previews/PR1068/manual/variables/index.html b/previews/PR1068/manual/variables/index.html index a21c99925..3e81bbb9f 100644 --- a/previews/PR1068/manual/variables/index.html +++ b/previews/PR1068/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 483 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} \mathrm{identity}\left( \pi \right) \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

SymbolicUtils.istreeFunction

istree(x)

Returns true if x is a term. If true, operation, arguments must also be defined for x appropriately.

source
SymbolicUtils.operationFunction

operation(x)

If x is a term as defined by istree(x), operation(x) returns the head of the term if x represents a function call, for example, the head is the function being called.

source
SymbolicUtils.argumentsFunction

arguments(x)

Get the arguments of x, must be defined if istree(x) is true.

source
+ \]

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

SymbolicUtils.istreeFunction

istree(x)

Returns true if x is a term. If true, operation, arguments must also be defined for x appropriately.

source
SymbolicUtils.operationFunction

operation(x)

If x is a term as defined by istree(x), operation(x) returns the head of the term if x represents a function call, for example, the head is the function being called.

source
SymbolicUtils.argumentsFunction

arguments(x)

Get the arguments of x, must be defined if istree(x) is true.

source
diff --git a/previews/PR1068/search_index.js b/previews/PR1068/search_index.js index 3f0945886..d8b73c15c 100644 --- a/previews/PR1068/search_index.js +++ b/previews/PR1068/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":"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 istree objects) return an expression that istree. 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 istree 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.istree 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.istree\nSymbolicUtils.operation\nSymbolicUtils.arguments","category":"page"},{"location":"manual/variables/#SymbolicUtils.istree","page":"Variable and Equation Types","title":"SymbolicUtils.istree","text":"istree(x)\n\nReturns true if x is a term. If true, operation, arguments must also be defined for x appropriately.\n\n\n\n\n\n","category":"function"},{"location":"manual/variables/#SymbolicUtils.operation","page":"Variable and Equation Types","title":"SymbolicUtils.operation","text":"operation(x)\n\nIf x is a term as defined by istree(x), operation(x) returns the head of the term if x represents a function call, for example, the head is the function being called.\n\n\n\n\n\n","category":"function"},{"location":"manual/variables/#SymbolicUtils.arguments","page":"Variable and Equation Types","title":"SymbolicUtils.arguments","text":"arguments(x)\n\nGet the arguments of x, must be defined if istree(x) is true.\n\n\n\n\n\n","category":"function"},{"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":"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":"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/functions/#function_registration","page":"Function Registration and Tracing","title":"Function Registration and Tracing","text":"","category":"section"},{"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/#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-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)\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\n\n\n\n\n","category":"macro"},{"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 istree 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 istree 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/#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 extendible 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.solve_for\nSymbolics.degree\nSymbolics.coeff","category":"page"},{"location":"manual/expression_manipulation/#Symbolics.get_variables","page":"Expression Manipulation","title":"Symbolics.get_variables","text":"get_variables(O) -> Vector{BasicSymbolic}\n\nReturns the variables in the expression. Note that the returned variables are not wrapped in the Num type.\n\nExamples\n\njulia> @variables t x y z(t)\n4-element Vector{Num}:\n t\n x\n y\n z(t)\n\njulia> ex = x + y + sin(z)\n(x + y) + sin(z(t))\n\njulia> Symbolics.get_variables(ex)\n3-element Vector{Any}:\n x\n y\n z(t)\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) -> Symbolic\n\nConvert a differential variable to a Term. Note that it only takes a Term not a Num.\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.solve_for","page":"Expression Manipulation","title":"Symbolics.solve_for","text":"solve_for(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.solve_for(x + y ~ 0, x)\n-y\n\njulia> Symbolics.solve_for([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/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\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","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\nTODO\n\nExamples\n\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, v; simplify)\n\n\nA helper function for computing the derivative of an expression 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 an expression 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 an expression 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₀ = 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 a1_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; max_power=100)\n eq = substitute(expand(eq), Dict(x^j => 0 for j=last(ns)+1:max_power))\n\n eqs = []\n for i in ns\n powers = Dict(x^j => (i==j ? 1 : 0) for j=1:last(ns))\n push!(eqs, substitute(eq, powers))\n end\n eqs\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":"A few words on how collect_powers works, It uses substitute to find the coefficient of a given power of x by passing a Dict with all powers of x set to 0, except the target power which is set to 1. For example, the following expression returns the coefficient of ϵ^2 in eq,","category":"page"},{"location":"examples/perturbation/","page":"Mixed Symbolic-Numeric Perturbation Theory","title":"Mixed Symbolic-Numeric Perturbation Theory","text":"substitute(expand(eq), Dict(\n ϵ => 0,\n ϵ^2 => 1,\n ϵ^3 => 0,\n ϵ^4 => 0,\n ϵ^5 => 0,\n ϵ^6 => 0,\n ϵ^7 => 0,\n ϵ^8 => 0)\n)","category":"page"},{"location":"examples/perturbation/","page":"Mixed Symbolic-Numeric Perturbation Theory","title":"Mixed Symbolic-Numeric Perturbation Theory","text":"Back to our problem. 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 Symbolics.solve_for that can solve systems of linear equations. However, the presence of higher-order terms in eqs prevents Symbolics.solve_for(eqs .~ 0, 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]] = Symbolics.solve_for(eq ~ 0, 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":"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 + a[1]*𝜀 + 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(sol, M, 0:3). 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":"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 istree objects) return an expression that istree. 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 istree 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.istree 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.istree\nSymbolicUtils.operation\nSymbolicUtils.arguments","category":"page"},{"location":"manual/variables/#SymbolicUtils.istree","page":"Variable and Equation Types","title":"SymbolicUtils.istree","text":"istree(x)\n\nReturns true if x is a term. If true, operation, arguments must also be defined for x appropriately.\n\n\n\n\n\n","category":"function"},{"location":"manual/variables/#SymbolicUtils.operation","page":"Variable and Equation Types","title":"SymbolicUtils.operation","text":"operation(x)\n\nIf x is a term as defined by istree(x), operation(x) returns the head of the term if x represents a function call, for example, the head is the function being called.\n\n\n\n\n\n","category":"function"},{"location":"manual/variables/#SymbolicUtils.arguments","page":"Variable and Equation Types","title":"SymbolicUtils.arguments","text":"arguments(x)\n\nGet the arguments of x, must be defined if istree(x) is true.\n\n\n\n\n\n","category":"function"},{"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":"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":"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/functions/#function_registration","page":"Function Registration and Tracing","title":"Function Registration and Tracing","text":"","category":"section"},{"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/#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-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)\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\n\n\n\n\n","category":"macro"},{"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 istree 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 istree 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/#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 extendible 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.solve_for\nSymbolics.degree\nSymbolics.coeff","category":"page"},{"location":"manual/expression_manipulation/#Symbolics.get_variables","page":"Expression Manipulation","title":"Symbolics.get_variables","text":"get_variables(O) -> Vector{BasicSymbolic}\n\nReturns the variables in the expression. Note that the returned variables are not wrapped in the Num type.\n\nExamples\n\njulia> @variables t x y z(t)\n4-element Vector{Num}:\n t\n x\n y\n z(t)\n\njulia> ex = x + y + sin(z)\n(x + y) + sin(z(t))\n\njulia> Symbolics.get_variables(ex)\n3-element Vector{Any}:\n x\n y\n z(t)\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) -> Symbolic\n\nConvert a differential variable to a Term. Note that it only takes a Term not a Num.\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.solve_for","page":"Expression Manipulation","title":"Symbolics.solve_for","text":"solve_for(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.solve_for(x + y ~ 0, x)\n-y\n\njulia> Symbolics.solve_for([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/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\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","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\nTODO\n\nExamples\n\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, v; simplify)\n\n\nA helper function for computing the derivative of an expression 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 an expression 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 an expression 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₀ = 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 a1_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; max_power=100)\n eq = substitute(expand(eq), Dict(x^j => 0 for j=last(ns)+1:max_power))\n\n eqs = []\n for i in ns\n powers = Dict(x^j => (i==j ? 1 : 0) for j=1:last(ns))\n push!(eqs, substitute(eq, powers))\n end\n eqs\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":"A few words on how collect_powers works, It uses substitute to find the coefficient of a given power of x by passing a Dict with all powers of x set to 0, except the target power which is set to 1. For example, the following expression returns the coefficient of ϵ^2 in eq,","category":"page"},{"location":"examples/perturbation/","page":"Mixed Symbolic-Numeric Perturbation Theory","title":"Mixed Symbolic-Numeric Perturbation Theory","text":"substitute(expand(eq), Dict(\n ϵ => 0,\n ϵ^2 => 1,\n ϵ^3 => 0,\n ϵ^4 => 0,\n ϵ^5 => 0,\n ϵ^6 => 0,\n ϵ^7 => 0,\n ϵ^8 => 0)\n)","category":"page"},{"location":"examples/perturbation/","page":"Mixed Symbolic-Numeric Perturbation Theory","title":"Mixed Symbolic-Numeric Perturbation Theory","text":"Back to our problem. 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 Symbolics.solve_for that can solve systems of linear equations. However, the presence of higher-order terms in eqs prevents Symbolics.solve_for(eqs .~ 0, 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]] = Symbolics.solve_for(eq ~ 0, 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":"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 + a[1]*𝜀 + 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(sol, M, 0:3). 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/previews/PR1068/tutorials/auto_parallel/196d764d.svg b/previews/PR1068/tutorials/auto_parallel/e505c5fc.svg similarity index 58% rename from previews/PR1068/tutorials/auto_parallel/196d764d.svg rename to previews/PR1068/tutorials/auto_parallel/e505c5fc.svg index b98031721..1111133a6 100644 --- a/previews/PR1068/tutorials/auto_parallel/196d764d.svg +++ b/previews/PR1068/tutorials/auto_parallel/e505c5fc.svg @@ -1,13222 +1,13222 @@ - + - + - + - + - + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/previews/PR1068/tutorials/auto_parallel/index.html b/previews/PR1068/tutorials/auto_parallel/index.html index 22e90a3f3..a302e90fd 100644 --- a/previews/PR1068/tutorials/auto_parallel/index.html +++ b/previews/PR1068/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/previews/PR1068/tutorials/converting_to_C/index.html b/previews/PR1068/tutorials/converting_to_C/index.html index dc1d6e6e1..a215b1246 100644 --- a/previews/PR1068/tutorials/converting_to_C/index.html +++ b/previews/PR1068/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:800 =#
-          ccall(("diffeqf", "/tmp/jl_kP03sLOraF"), Cvoid, (Ptr{Float64}, Ptr{Float64}, Ptr{Float64}, Float64), du, u, p, t)
+          ccall(("diffeqf", "/tmp/jl_dqbuD4N4jw"), 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