Skip to content

Commit f7a39df

Browse files
update docs
1 parent 7451c30 commit f7a39df

File tree

9 files changed

+209
-206
lines changed

9 files changed

+209
-206
lines changed

docs/src/index.md

Lines changed: 14 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ Let `M`, `D`, `F` be matrix-based, diagonal-matrix-based, and function-based
3232

3333
```julia
3434
N = 4
35-
f = (u, p, t) -> u .* u
35+
f = (v, u, p, t) -> u .* v
3636

3737
M = MatrixOperator(rand(N, N))
3838
D = DiagonalOperator(rand(N))
@@ -56,10 +56,11 @@ p = nothing # parameter struct
5656
t = 0.0 # time
5757

5858
u = rand(N)
59-
v = L1(u, p, t) # == L1 * u
59+
v = rand(N)
60+
w = L1(v, u, p, t) # == L1 * v
6061

61-
u_kron = rand(N^3)
62-
v_kron = L3(u_kron, p, t) # == L3 * u_kron
62+
v_kron = rand(N^3)
63+
w_kron = L3(v_kron, u, p, t) # == L3 * v_kron
6364
```
6465

6566
For mutating operator evaluations, call `cache_operator` to generate an
@@ -73,21 +74,17 @@ L2 = cache_operator(L2, u)
7374
L4 = cache_operator(L4, u)
7475

7576
# allocation-free evaluation
76-
L2(v, u, p, t) # == mul!(v, L2, u)
77-
L4(v, u, p, t, α, β) # == mul!(v, L4, u, α, β)
77+
L2(w, v, u, p, t) # == mul!(w, L2, v)
78+
L4(w, v, u, p, t, α, β) # == mul!(w, L4, v, α, β)
7879
```
7980

80-
The calling signature `L(u, p, t)`, for out-of-place evaluations, is
81-
equivalent to `L * u`, and the in-place evaluation `L(v, u, p, t, args...)`
82-
is equivalent to `LinearAlgebra.mul!(v, L, u, args...)`, where the arguments
83-
`p, t` are passed to `L` to update its state. More details are provided
84-
in the operator update section below. While overloads to `Base.*`
85-
and `LinearAlgebra.mul!` are available, where a `SciMLOperator` behaves
86-
like an `AbstractMatrix`, we recommend sticking with the
87-
`L(u, p, t)`, `L(v, u, p, t)`, `L(v, u, p, t, α, β)` calling signatures
88-
as the latter internally update the operator state.
89-
90-
The `(u, p, t)` calling signature is standardized over the `SciML`
81+
The calling signature `L(v, u, p, t)`, for out-of-place evaluations, is
82+
equivalent to `L * v`, and the in-place evaluation `L(w, v, u, p, t, args...)`
83+
is equivalent to `LinearAlgebra.mul!(w, L, v, args...)`, where the arguments
84+
`u, p, t` are passed to `L` to update its state. More details are provided
85+
in the operator update section below.
86+
87+
The `(v, u, p, t)` calling signature is standardized over the `SciML`
9188
ecosystem and is flexible enough to support use cases such as time-evolution
9289
in ODEs, as well as sensitivity computation with respect to the parameter
9390
object `p`.

docs/src/interface.md

Lines changed: 2 additions & 147 deletions
Original file line numberDiff line numberDiff line change
@@ -1,152 +1,7 @@
11
# The `AbstractSciMLOperator` Interface
22

3-
## Formal Properties of SciMLOperators
4-
5-
These are the formal properties that an `AbstractSciMLOperator` should obey
6-
for it to work in the solvers.
7-
8-
1. An `AbstractSciMLOperator` represents a linear or nonlinear operator, with input/output
9-
being `AbstractArray`s. Specifically, a SciMLOperator, `L`, of size `(M, N)` accepts an
10-
input argument `u` with leading length `N`, i.e. `size(u, 1) == N`, and returns an
11-
`AbstractArray` of the same dimension with leading length `M`, i.e. `size(L * u, 1) == M`.
12-
2. SciMLOperators can be applied to an `AbstractArray` via overloaded `Base.*`, or
13-
the in-place `LinearAlgebra.mul!`. Additionally, operators are allowed to be time,
14-
or parameter dependent. The state of a SciMLOperator can be updated by calling
15-
the mutating function `update_coefficients!(L, u, p, t)` where `p` represents
16-
parameters, and `t`, time. Calling a SciMLOperator as `L(du, u, p, t)` or out-of-place
17-
`L(u, p, t)` will automatically update the state of `L` before applying it to `u`.
18-
`L(u, p, t)` is the same operation as `L(u, p, t) * u`.
19-
3. To support the update functionality, we have lazily implemented a comprehensive operator
20-
algebra. That means a user can add, subtract, scale, compose and invert SciMLOperators,
21-
and the state of the resultant operator would be updated as expected upon calling
22-
`L(du, u, p, t)` or `L(u, p, t)` so long as an update function is provided for the
23-
component operators.
24-
25-
## Overloaded Traits
26-
27-
Thanks to overloads defined for evaluation methods and traits in
28-
`Base`, `LinearAlgebra`, the behavior of a `SciMLOperator` is
29-
indistinguishable from an `AbstractMatrix`. These operators can be
30-
passed to linear solver packages, and even to ordinary differential
31-
equation solvers. The list of overloads to the `AbstractMatrix`
32-
interface includes, but is not limited to, the following:
33-
34-
- `Base: size, zero, one, +, -, *, /, \, ∘, inv, adjoint, transpose, convert`
35-
- `LinearAlgebra: mul!, ldiv!, lmul!, rmul!, factorize, issymmetric, ishermitian, isposdef`
36-
- `SparseArrays: sparse, issparse`
37-
38-
## Multidimensional arrays and batching
39-
40-
SciMLOperator can also be applied to `AbstractMatrix` subtypes where
41-
operator-evaluation is done column-wise.
42-
43-
```julia
44-
K = 10
45-
u_mat = rand(N, K)
46-
47-
v_mat = F(u_mat, p, t) # == mul!(v_mat, F, u_mat)
48-
size(v_mat) == (N, K) # true
49-
```
50-
51-
`L#` can also be applied to `AbstractArray`s that are not
52-
`AbstractVecOrMat`s so long as their size in the first dimension is appropriate
53-
for matrix-multiplication. Internally, `SciMLOperator`s reshapes an
54-
`N`-dimensional array to an `AbstractMatrix`, and applies the operator via
55-
matrix-multiplication.
56-
57-
## Operator update
58-
59-
This package can also be used to write time-dependent, and
60-
parameter-dependent operators, whose state can be updated per
61-
a user-defined function.
62-
The updates can be done in-place, i.e. by mutating the object,
63-
or out-of-place, i.e. in a non-mutating, `Zygote`-compatible way.
64-
65-
For example,
66-
67-
```julia
68-
u = rand(N)
69-
p = rand(N)
70-
t = rand()
71-
72-
# out-of-place update
73-
mat_update_func = (A, u, p, t) -> t * (p * p')
74-
sca_update_func = (a, u, p, t) -> t * sum(p)
75-
76-
M = MatrixOperator(zero(N, N); update_func = mat_update_func)
77-
α = ScalarOperator(zero(Float64); update_func = sca_update_func)
78-
79-
L = α * M
80-
L = cache_operator(L, u)
81-
82-
# L is initialized with zero state
83-
L * u == zeros(N) # true
84-
85-
# update operator state with `(u, p, t)`
86-
L = update_coefficients(L, u, p, t)
87-
# and multiply
88-
L * u != zeros(N) # true
89-
90-
# updates state and evaluates L at (u, p, t)
91-
L(u, p, t) != zeros(N) # true
92-
```
93-
94-
The out-of-place evaluation function `L(u, p, t)` calls
95-
`update_coefficients` under the hood, which recursively calls
96-
the `update_func` for each component `SciMLOperator`.
97-
Therefore, the out-of-place evaluation function is equivalent to
98-
calling `update_coefficients` followed by `Base.*`. Notice that
99-
the out-of-place evaluation does not return the updated operator.
100-
101-
On the other hand, the in-place evaluation function, `L(v, u, p, t)`,
102-
mutates `L`, and is equivalent to calling `update_coefficients!`
103-
followed by `mul!`. The in-place update behavior works the same way,
104-
with a few `<!>`s appended here and there. For example,
105-
106-
```julia
107-
v = rand(N)
108-
u = rand(N)
109-
p = rand(N)
110-
t = rand()
111-
112-
# in-place update
113-
_A = rand(N, N)
114-
_d = rand(N)
115-
mat_update_func! = (A, u, p, t) -> (copy!(A, _A); lmul!(t, A); nothing)
116-
diag_update_func! = (diag, u, p, t) -> copy!(diag, N)
117-
118-
M = MatrixOperator(zero(N, N); update_func! = mat_update_func!)
119-
D = DiagonalOperator(zero(N); update_func! = diag_update_func!)
120-
121-
L = D * M
122-
L = cache_operator(L, u)
123-
124-
# L is initialized with zero state
125-
L * u == zeros(N) # true
126-
127-
# update L in-place
128-
update_coefficients!(L, u, p, t)
129-
# and multiply
130-
mul!(v, u, p, t) != zero(N) # true
131-
132-
# updates L in-place, and evaluates at (u, p, t)
133-
L(v, u, p, t) != zero(N) # true
134-
```
135-
136-
The update behavior makes this package flexible enough to be used
137-
in `OrdinaryDiffEq`. As the parameter object `p` is often reserved
138-
for sensitivity computation via automatic-differentiation, a user may
139-
prefer to pass in state information via other arguments. For that
140-
reason, we allow update functions with arbitrary keyword arguments.
141-
142-
```julia
143-
mat_update_func = (A, u, p, t; scale = 0.0) -> scale * (p * p')
144-
145-
M = MatrixOperator(zero(N, N); update_func = mat_update_func,
146-
accepted_kwargs = (:state,))
147-
148-
M(u, p, t) == zeros(N) # true
149-
M(u, p, t; scale = 1.0) != zero(N)
3+
```@docs
4+
SciMLOperators.AbstractSciMLOperator
1505
```
1516

1527
## Interface API Reference

docs/src/sciml.md

Lines changed: 17 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -19,22 +19,32 @@ Munthe-Kaas methods require defining operators of the form ``u' = A(u) u``.
1919
Thus, the operators need some form of time and state dependence, which the
2020
solvers can update and query when they are non-constant
2121
(`update_coefficients!`). Additionally, the operators need the ability to
22-
act like “normal” functions for equation solvers. For example, if `A(u,p,t)`
23-
has the same operation as `update_coefficients(A, u, p, t); A * u`, then `A`
22+
act like “normal” functions for equation solvers. For example, if `A(v,u,p,t)`
23+
has the same operation as `update_coefficients(A, u, p, t); A * v`, then `A`
2424
can be used in any place where a differential equation definition
25-
`f(u, p, t)` is used without requiring the user or solver to do any extra
26-
work. Thus while previous good efforts for matrix-free operators have existed
25+
`(u,p,t) -> A(u, u, p, t)` is used without requiring the user or solver to do any extra
26+
work.
27+
28+
Another example is state-dependent mass matrices. `M(u,p,t)*u' = f(u,p,t)`.
29+
When solving such an equation, the solver must understand how to "update M"
30+
during operations, and thus the ability to update the state of `M` is a required
31+
function in the interface. This is also required for the definition of Jacobians
32+
`J(u,p,t)` in order to be properly used with Krylov methods inside of ODE solves
33+
without reconstructing the matrix-free operator at each step.
34+
35+
Thus while previous good efforts for matrix-free operators have existed
2736
in the Julia ecosystem, such as
2837
[LinearMaps.jl](https://github.com/JuliaLinearAlgebra/LinearMaps.jl), those
2938
operator interfaces lack these aspects to actually be fully seamless
3039
with downstream equation solvers. This necessitates the definition and use of
3140
an extended operator interface with all of these properties, hence the
3241
`AbstractSciMLOperator` interface.
3342

34-
Some packages providing similar functionality are
43+
!!! warn
3544

36-
- [LinearMaps.jl](https://github.com/JuliaLinearAlgebra/LinearMaps.jl)
37-
- [`DiffEqOperators.jl`](https://github.com/SciML/DiffEqOperators.jl/tree/master) (deprecated)
45+
This means that LinearMaps.jl is fundamentally lacking and is incompatible
46+
with many of the tools in the SciML ecosystem, except for the specific cases
47+
where the matrix-free operator is a constant!
3848

3949
## Interoperability and extended Julia ecosystem
4050

docs/src/tutorials/fftw.md

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -23,11 +23,11 @@ k = rfftfreq(n, 2π * n / L) |> Array
2323
m = length(k)
2424
P = plan_rfft(x)
2525
26-
fwd(u, p, t) = P * u
27-
bwd(u, p, t) = P \ u
26+
fwd(v, u, p, t) = P * v
27+
bwd(v, u, p, t) = P \ v
2828
29-
fwd(du, u, p, t) = mul!(du, P, u)
30-
bwd(du, u, p, t) = ldiv!(du, P, u)
29+
fwd(w, v, u, p, t) = mul!(w, P, v)
30+
bwd(w, v, u, p, t) = ldiv!(w, P, v)
3131
3232
F = FunctionOperator(fwd, x, im * k;
3333
T = ComplexF64, op_adjoint = bwd,
@@ -79,15 +79,15 @@ P = plan_rfft(x)
7979

8080
Now we are ready to define our wrapper for the FFT object. To `FunctionOperator`, we
8181
pass the in-place forward application of the transform,
82-
`(du,u,p,t) -> mul!(du, transform, u)`, its inverse application,
83-
`(du,u,p,t) -> ldiv!(du, transform, u)`, as well as input and output prototype vectors.
82+
`(w,v,u,p,t) -> mul!(w, transform, v)`, its inverse application,
83+
`(w,v,u,p,t) -> ldiv!(w, transform, v)`, as well as input and output prototype vectors.
8484

8585
```@example fft_explanation
86-
fwd(u, p, t) = P * u
87-
bwd(u, p, t) = P \ u
86+
fwd(v, u, p, t) = P * v
87+
bwd(v, u, p, t) = P \ v
8888
89-
fwd(du, u, p, t) = mul!(du, P, u)
90-
bwd(du, u, p, t) = ldiv!(du, P, u)
89+
fwd(w, v, u, p, t) = mul!(w, P, v)
90+
bwd(w, v, p, t) = ldiv!(w, P, v)
9191
F = FunctionOperator(fwd, x, im * k;
9292
T = ComplexF64, op_adjoint = bwd,
9393
op_inverse = bwd,

0 commit comments

Comments
 (0)