|
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