@@ -127,6 +127,80 @@ count_interp_dims(it::Type{IT}, n) where IT<:Tuple{Vararg{InterpolationType,N}}
127
127
_count_interp_dims (c + count_interp_dims (IT1), args... )
128
128
_count_interp_dims (c) = c
129
129
130
+
131
+ """
132
+ wi = WeightedIndex(indexes, weights)
133
+
134
+ Construct a weighted index `wi`, which can be thought of as a generalization of an
135
+ ordinary array index to the context of interpolation.
136
+ For an ordinary vector `a`, `a[i]` extracts the element at index `i`.
137
+ When interpolating, one is typically interested in a range of indexes and the output is
138
+ some weighted combination of array values at these indexes.
139
+ For example, for linear interpolation between `i` and `i+1` we have
140
+
141
+ ret = (1-f)*a[i] + f*a[i]
142
+
143
+ This can be represented `a[wi]`, where
144
+
145
+ wi = WeightedIndex(i:i+1, (1-f, f))
146
+
147
+ i.e.,
148
+
149
+ ret = sum(a[indexes] .* weights)
150
+
151
+ Linear interpolation thus constructs weighted indices using a 2-tuple for `weights` and
152
+ a length-2 `indexes` range.
153
+ Higher-order interpolation would involve more positions and weights (e.g., 3-tuples for
154
+ quadratic interpolation, 4-tuples for cubic).
155
+
156
+ In multiple dimensions, separable interpolation schemes are implemented in terms
157
+ of multiple weighted indices, accessing `A[wi1, wi2, ...]` where each `wi` is the
158
+ `WeightedIndex` along the corresponding dimension.
159
+
160
+ For value interpolation, `weights` will typically sum to 1.
161
+ However, for gradient and Hessian computation this will not necessarily be true.
162
+ For example, the gradient of one-dimensional linear interpolation can be represented as
163
+
164
+ gwi = WeightedIndex(i:i+1, (-1, 1))
165
+ g1 = a[gwi]
166
+
167
+ For a three-dimensional array `A`, one might compute `∂A/∂x₂` (the second component
168
+ of the gradient) as `A[wi1, gwi2, wi3]`, where `wi1` and `wi3` are "value" weights
169
+ and `gwi2` "gradient" weights.
170
+
171
+ `indexes` may be supplied as a range or as a tuple of the same length as `weights`.
172
+ The latter is applicable, e.g., for periodic boundary conditions.
173
+ """
174
+ abstract type WeightedIndex{L,W} end
175
+
176
+ # Type to use when array locations are adjacent. This may offer more opportunities
177
+ # for compiler optimizations (e.g., SIMD).
178
+ struct WeightedAdjIndex{L,W} <: WeightedIndex{L,W}
179
+ istart:: Int
180
+ weights:: NTuple{L,W}
181
+ end
182
+ # Type to use with non-adjacent locations. E.g., periodic boundary conditions.
183
+ struct WeightedArbIndex{L,W} <: WeightedIndex{L,W}
184
+ indexes:: NTuple{L,Int}
185
+ weights:: NTuple{L,W}
186
+ end
187
+
188
+ function WeightedIndex (indexes:: AbstractUnitRange{<:Integer} , weights:: NTuple{L,Any} ) where L
189
+ @noinline mismatch (indexes, weights) = throw (ArgumentError (" the length of indexes must match weights, got $indexes vs $weights " ))
190
+ length (indexes) == L || mismatch (indexes, weights)
191
+ WeightedAdjIndex (first (indexes), promote (weights... ))
192
+ end
193
+ WeightedIndex (istart:: Integer , weights:: NTuple{L,Any} ) where L =
194
+ WeightedAdjIndex (istart, promote (weights... ))
195
+ WeightedIndex (indexes:: NTuple{L,Integer} , weights:: NTuple{L,Any} ) where L =
196
+ WeightedArbIndex (indexes, promote (weights... ))
197
+
198
+ weights (wi:: WeightedIndex ) = wi. weights
199
+ indexes (wi:: WeightedAdjIndex ) = wi. istart
200
+ indexes (wi:: WeightedArbIndex ) = wi. indexes
201
+
202
+
203
+
130
204
"""
131
205
w = value_weights(degree, δx)
132
206
0 commit comments