diff --git a/docs/src/getting_started.md b/docs/src/getting_started.md index 5f381a432..b17a0c467 100644 --- a/docs/src/getting_started.md +++ b/docs/src/getting_started.md @@ -68,13 +68,20 @@ end f([x, y, z]) # Recall that z = x^2 + y ``` -Or we can build an array variable and use it to trace the function: +Or we can build an array of variables and use it to trace the function: ```@example symbolic_basics -@variables u[1:4] +u = Symbolics.variables(:u, 1:5) f(u) ``` +!!! note + `Symbolics.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](@symbolic_arrays) page. + ## Derivatives One common thing to compute in a symbolic system is derivatives. In @@ -263,6 +270,10 @@ Symbolics.derivative(h(x, y) + y^2, x) Symbolics.derivative(h(x, y) + y^2, y) ``` +!!! note + `@register_symbolic` only allows for scalar outputs. If full array functions are needed, + then see `@register_array_symbolic` for registering functions of symbolic arrays. + ## Building Functions The function for building functions from symbolic expressions is the aptly-named `build_function`. diff --git a/docs/src/manual/arrays.md b/docs/src/manual/arrays.md index 4216af869..b7db7fafb 100644 --- a/docs/src/manual/arrays.md +++ b/docs/src/manual/arrays.md @@ -1,4 +1,63 @@ -# Symbolic arrays +# [Symbolic Arrays](@id symbolic_arrays) + +## Symbolic Arrays vs Arrays of Symbolic Expressions + +Symbolics.jl contains two forms for handling symbolic arrays: + +1. Arrays of symbolic expressions: these are Julia arrays with Symbolics.jl objects in them. +2. Symbolic Arrays: these are symbolic (O(1)) representations of arrays. + +Arrays of symbolic expressions are simply Symbolics.jl objects put into Julia arrays. For +example: + +```@example arrays +using Symbolics +@variables x y +u = [x,y] +``` + +is a vector of two symbolic variables. As shorthand, + +```@example arrays +u2 = Symbolics.variables(:x, 1:3, 3:6) +``` + +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. + +On the otherhand, Julia's symbolic array form is an O(1) representation of the whole array. + +```@example arrays +@variables A[1:5, 1:3] +``` + +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: + +```@example arrays +@variables B[1:3, 1:3] +A * B +``` + +in comparison to: + +```@example arrays +a = Symbolics.variables(:a, 1:5, 1:3) +b = Symbolics.variables(:b, 1:3, 1:3) +a * b +``` + +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. + +We recommend defaulting to arrays of symbolics unless you need the expression symplifications +of the symbolic array approach. + +## Using Symbolic Arrays 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. @@ -71,7 +130,7 @@ A .* b' ```@example arrays map(asin, (A*b)) ``` -```@example arrays +```julia #sum(A) #latexify not working ``` ```@example arrays diff --git a/docs/src/manual/build_function.md b/docs/src/manual/build_function.md index d59fc0446..19327610b 100644 --- a/docs/src/manual/build_function.md +++ b/docs/src/manual/build_function.md @@ -30,3 +30,7 @@ Symbolics._build_function(target::Symbolics.CTarget,eqs::Array{<:Equation},args. Symbolics._build_function(target::Symbolics.StanTarget,eqs::Array{<:Equation}, vs, ps, iv;kwargs...) Symbolics._build_function(target::Symbolics.MATLABTarget,eqs::Array{<:Equation},args...;kwargs...) ``` + +## Limitations + +`build_function` diff --git a/docs/src/manual/derivatives.md b/docs/src/manual/derivatives.md index 2c8061f26..ffd752794 100644 --- a/docs/src/manual/derivatives.md +++ b/docs/src/manual/derivatives.md @@ -15,6 +15,11 @@ Differential expand_derivatives ``` +!!! note + For symbolic differentiation, all registered functions in the symbolic expression + need a registered derivative. For more information, see the + [function registration](@ref function_registration) page. + ## High-Level Differentiation Functions The following functions are not exported and thus must be accessed in a namespaced diff --git a/docs/src/manual/functions.md b/docs/src/manual/functions.md index 12ec05189..ed9d9ac30 100644 --- a/docs/src/manual/functions.md +++ b/docs/src/manual/functions.md @@ -41,6 +41,34 @@ and pre-defines their derivatives. However, the user can utilize the [`@register_symbolic`](@ref) macro to add their function to allowed functions of the computation graph. +Additionally, [`@register_array_symbolic`](@ref) 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. + +## Defining Derivatives of Registered Functions + +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: + +```julia +function derivative(::typeof(min), args::NTuple{2,Any}, ::Val{1}) + x, y = args + IfElse.ifelse(x < y, one(x), zero(x)) +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 + ```@docs @register_symbolic +@register_array_symbolic ``` diff --git a/docs/src/manual/variables.md b/docs/src/manual/variables.md index f5a8962c9..3dd41fcff 100644 --- a/docs/src/manual/variables.md +++ b/docs/src/manual/variables.md @@ -10,7 +10,8 @@ written as `op1 ~ op2`, defines the symbolic equality between two operations. ## Types -`Sym`, `Term`, and `FnType` are from [SymbolicUtils.jl](https://juliasymbolics.github.io/SymbolicUtils.jl/api/). Note that in +`Sym`, `Term`, and `FnType` are from +[SymbolicUtils.jl](https://juliasymbolics.github.io/SymbolicUtils.jl/api/). 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)`. @@ -20,6 +21,8 @@ Instead, one needs to use `SymbolicUtils.istree` to check if `arguments` and ```@docs @variables +Symbolics.variable +Symbolics.variables Equation Base.:~(::Num, ::Num) ```