Skip to content

Commit

Permalink
Add documentation for array symbolic registration
Browse files Browse the repository at this point in the history
  • Loading branch information
ChrisRackauckas committed Apr 15, 2024
1 parent 3a36298 commit 80c69ab
Showing 1 changed file with 68 additions and 0 deletions.
68 changes: 68 additions & 0 deletions docs/src/manual/functions.md
Original file line number Diff line number Diff line change
Expand Up @@ -66,9 +66,77 @@ is the partial derivative of the Julia `min(x,y)` function with respect to `x`.
is required in the derivative expression is defined. Note that you only need to define
the partial derivatives which are actually computed.

## Registration of Array Functions

Similar to scalar functions, array functions can be registered to define new primitives for
functions which either take in or return arrays. This is done by using the `@register_array_symbolic`
macro. It acts similarly to the scalar function registration but requires a calculation of the
input and output sizes. For example, let's assume we wanted to have a function that computes the
solution to `Ax = b`, i.e. a linear solve, using an SVD factorization. In Julia, the code for this
would be `svdsolve(A,b) = svd(A)\b`. We would create this function as follows:

```@example array_registration
using LinearAlgebra, Symbolics
svdsolve(A, b) = svd(A)\b
@register_array_symbolic svdsolve(A::AbstractMatrix, b::AbstractVector) begin
size = size(b)
eltype = promote_type(eltype(A), eltype(b))
end
```

Now using the function `svdsolve` with symbolic array variables will be kept lazy:

```@example array_registration
@variables A[1:3, 1:3] b[1:3]
svdsolve(A,b)
```

Note that at this time array derivatives cannot be defined.

## Registration API

```@docs
@register_symbolic
@register_array_symbolic
```

## Direct Registration API (Advanced, Experimental)

!!! warn

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

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

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

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

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

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

Additionally a symbolic name is required:

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

The derivative is defined similarly to the macro case:

```julia
function Symbolics.derivative(interp::AbstractInterpolation, args::NTuple{1, Any}, ::Val{1})
Symbolics.unwrap(derivative(interp, Symbolics.wrap(args[1])))
end
```

0 comments on commit 80c69ab

Please sign in to comment.