|
1 | 1 | # The `AbstractSciMLOperator` Interface
|
2 | 2 |
|
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 |
150 | 5 | ```
|
151 | 6 |
|
152 | 7 | ## Interface API Reference
|
@@ -219,6 +74,6 @@ update_coefficients!(γ, nothing, nothing, nothing; my_special_scaling = 7.0)
|
219 | 74 | @show γ * [2.0]
|
220 | 75 |
|
221 | 76 | # Use operator application form
|
222 |
| -@show γ([2.0], nothing, nothing; my_special_scaling = 5.0) |
| 77 | +@show γ([2.0], nothing, nothing, nothing; my_special_scaling = 5.0) |
223 | 78 | nothing # hide
|
224 | 79 | ```
|
0 commit comments