Skip to content

Commit b735c08

Browse files
authored
Merge pull request #397 from awadell1/feature/iterate
Iteration of Interpolation Knots
2 parents 318ebc8 + a88aa45 commit b735c08

File tree

6 files changed

+700
-3
lines changed

6 files changed

+700
-3
lines changed

docs/make.jl

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ pages=["Home" => "index.md",
88
"Interpolation algorithms" => "control.md",
99
"Extrapolation" => "extrapolation.md",
1010
"Convenience Constructors" => "convenience-construction.md",
11+
"Knot Iteration" => "iterate.md",
1112
"Developer documentation" => "devdocs.md",
1213
"Library" => "api.md"]
1314
)

docs/src/iterate.md

Lines changed: 256 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,256 @@
1+
# Knot Iteration
2+
3+
Given an `AbstractInterpolation` `itp` get an iterator over it's knots using
4+
`knots(itp)`
5+
6+
```julia
7+
using Interpolations
8+
itp = interpolate(rand(4), options...)
9+
kiter = knots(itp) # Iterator over knots
10+
collect(kiter) # Array of knots [1, 2, 3, 4]
11+
12+
```
13+
14+
For multiple dimensions, the iterator will return tuples of positions
15+
(ie. `(x, y, ...)`), with the first coordinate changing the fastest.
16+
17+
```jldoctest iterate-interpolate; setup = :(using Interpolations)
18+
julia> itp = interpolate(ones(3,3), BSpline(Linear()));
19+
20+
julia> kiter = knots(itp);
21+
22+
julia> collect(kiter)
23+
3×3 Array{Tuple{Int64,Int64},2}:
24+
(1, 1) (1, 2) (1, 3)
25+
(2, 1) (2, 2) (2, 3)
26+
(3, 1) (3, 2) (3, 3)
27+
```
28+
29+
The number of elements and size of the iterator can be found as shown:
30+
31+
```jldoctest iterate-interpolate; setup = :(using Interpolations)
32+
julia> length(kiter)
33+
9
34+
35+
julia> size(kiter)
36+
(3, 3)
37+
38+
```
39+
40+
41+
## Extrapolated Knots
42+
43+
Given an `AbstractExtrapolation` `etp`, `knots(etp)` will also iterate over the
44+
the knots with the following behavior.
45+
46+
- For `Throw`, `Flat`, `Line` iterate the knots once
47+
- For `Periodic` and `Reflect` generate an infinite sequence of knots starting
48+
at the first knot.
49+
50+
As `Periodic` and `Reflect` generate infinite sequences of knots, `length` and
51+
`size` are undefined. For `Throw`, `Flat`, `Line`, `length` and `size` behave as
52+
expected.
53+
54+
### Periodic
55+
56+
With Periodic boundary condition, knots repeat indefinitely with the first and
57+
last knot being co-located. (ie. in the example below `etp(2.0) = 1.0` not
58+
`4.0`).
59+
60+
```jldoctest periodic-demo; setup = :(using Interpolations)
61+
julia> x = [1.0, 1.5, 1.75, 2.0];
62+
63+
julia> etp = LinearInterpolation(x, x.^2, extrapolation_bc=Periodic());
64+
65+
julia> kiter = knots(etp);
66+
67+
julia> k = Iterators.take(kiter, 6) |> collect
68+
6-element Array{Float64,1}:
69+
1.0
70+
1.5
71+
1.75
72+
2.0
73+
2.5
74+
2.75
75+
76+
```
77+
78+
Extrapolating to the generated knots `etp.(k)`, confirms that the extrapolated
79+
knots do map back to the correct inbound knots (ie. `etp(k[1]) == etp(k[4])`).
80+
81+
```jldoctest periodic-demo
82+
julia> etp.(k)
83+
6-element Array{Float64,1}:
84+
1.0
85+
2.25
86+
3.0625
87+
1.0
88+
2.25
89+
3.0625
90+
91+
```
92+
93+
### Reflect
94+
95+
With the `Reflect` boundary condition knots repeat indefinitely, following the
96+
pattern shown below (Offset terms are not shown for brevity).
97+
98+
```
99+
k[1], k[2], ..., k[end-1], k[end], k[end+1], ... k[2], k[1], k[2], ...
100+
```
101+
102+
```jldoctest reflect-demo; setup = :(using Interpolations)
103+
julia> x = [1.0, 1.5, 1.75, 2.0];
104+
105+
julia> etp = LinearInterpolation(x, x.^2, extrapolation_bc=Reflect());
106+
107+
julia> kiter = knots(etp);
108+
109+
julia> k = Iterators.take(kiter, 6) |> collect
110+
6-element Array{Float64,1}:
111+
1.0
112+
1.5
113+
1.75
114+
2.0
115+
2.25
116+
2.5
117+
118+
```
119+
120+
Evaluating the extrapolation at `etp.(k)` confirms that the extrapolated knots
121+
correspond to the correct inbound knots.
122+
123+
```jldoctest reflect-demo
124+
julia> etp.(k)
125+
6-element Array{Float64,1}:
126+
1.0
127+
2.25
128+
3.0625
129+
4.0
130+
3.0625
131+
2.25
132+
133+
```
134+
135+
### Multiple Dimensions
136+
137+
As with an `AbstractInterpolation`, iterating over knots for a
138+
multi-dimensional extrapolation also supported.
139+
140+
```jldoctest; setup = :(using Interpolations)
141+
julia> x = [1.0, 1.5, 1.75, 2.0];
142+
143+
julia> etp = LinearInterpolation((x, x), x.*x');
144+
145+
julia> knots(etp) |> collect
146+
4×4 Array{Tuple{Float64,Float64},2}:
147+
(1.0, 1.0) (1.0, 1.5) (1.0, 1.75) (1.0, 2.0)
148+
(1.5, 1.0) (1.5, 1.5) (1.5, 1.75) (1.5, 2.0)
149+
(1.75, 1.0) (1.75, 1.5) (1.75, 1.75) (1.75, 2.0)
150+
(2.0, 1.0) (2.0, 1.5) (2.0, 1.75) (2.0, 2.0)
151+
152+
```
153+
154+
Because some boundary conditions generate an infinite sequences of knots,
155+
iteration over knots can end up "stuck" iterating along a single axis:
156+
157+
```jldoctest; setup = :(using Interpolations)
158+
julia> x = [1.0, 1.5, 1.75, 2.0];
159+
160+
julia> etp = LinearInterpolation((x, x), x.*x', extrapolation_bc=(Periodic(), Throw()));
161+
162+
julia> knots(etp) |> k -> Iterators.take(k, 6) |> collect
163+
6-element Array{Tuple{Float64,Float64},1}:
164+
(1.0, 1.0)
165+
(1.5, 1.0)
166+
(1.75, 1.0)
167+
(2.0, 1.0)
168+
(2.5, 1.0)
169+
(2.75, 1.0)
170+
171+
```
172+
173+
Rearranging the axes so non-repeating knots are first can address this issue:
174+
175+
```jldoctest; setup = :(using Interpolations)
176+
julia> x = [1.0, 1.5, 1.75, 2.0];
177+
178+
julia> etp = LinearInterpolation((x, x), x.*x', extrapolation_bc=(Throw(), Periodic()));
179+
180+
julia> knots(etp) |> k -> Iterators.take(k, 6) |> collect
181+
6-element Array{Tuple{Float64,Float64},1}:
182+
(1.0, 1.0)
183+
(1.5, 1.0)
184+
(1.75, 1.0)
185+
(2.0, 1.0)
186+
(1.0, 1.5)
187+
(1.5, 1.5)
188+
189+
```
190+
191+
### Directional Boundary Conditions
192+
193+
If the boundary conditions are directional, the forward boundary condition is
194+
used to determine if the iterator will generate an infinite sequence of knots.
195+
196+
For example the following extrapolation `etp`, will throw an error for values
197+
less than `1.0`, but will use `Periodic` extrapolation for values above `2.0`. As a
198+
result, the iterator will generate an infinite sequence of knots starting at `1.0`.
199+
200+
```jldoctest iterate-directional-unbounded; setup = :(using Interpolations)
201+
julia> x = [1.0, 1.2, 1.3, 2.0];
202+
203+
julia> etp = LinearInterpolation(x, x.^2, extrapolation_bc=((Throw(), Periodic()),));
204+
205+
julia> kiter = knots(etp);
206+
207+
julia> kiter |> k -> Iterators.take(k, 5) |> collect
208+
5-element Array{Float64,1}:
209+
1.0
210+
1.2
211+
1.3
212+
2.0
213+
2.2
214+
215+
```
216+
217+
We can also check if the iterator has a length using: `Base.IteratorSize`
218+
219+
```jldoctest iterate-directional-unbounded
220+
julia> Base.IteratorSize(kiter)
221+
Base.IsInfinite()
222+
223+
```
224+
225+
Swapping the boundary conditions, results in a finite sequence of knots from
226+
`1.0` to `2.0`.
227+
228+
```jldoctest iterate-directional-bounded; setup = :(using Interpolations)
229+
julia> x = [1.0, 1.2, 1.3, 2.0];
230+
231+
julia> etp = LinearInterpolation(x, x.^2, extrapolation_bc=((Periodic(), Throw()),));
232+
233+
julia> kiter = knots(etp);
234+
235+
julia> collect(kiter)
236+
4-element Array{Float64,1}:
237+
1.0
238+
1.2
239+
1.3
240+
2.0
241+
242+
```
243+
244+
As expected the iterator now has a defined length:
245+
246+
```jldoctest iterate-directional-bounded; setup = :(using Interpolations)
247+
julia> Base.IteratorSize(kiter)
248+
Base.HasLength()
249+
250+
julia> length(kiter)
251+
4
252+
253+
julia> size(kiter)
254+
(4,)
255+
256+
```

src/Interpolations.jl

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,9 @@ export
2424
Throw,
2525

2626
LinearInterpolation,
27-
CubicSplineInterpolation
27+
CubicSplineInterpolation,
28+
29+
knots
2830

2931
# see the following files for further exports:
3032
# b-splines/b-splines.jl
@@ -35,8 +37,9 @@ export
3537
using LinearAlgebra, SparseArrays
3638
using StaticArrays, WoodburyMatrices, Ratios, AxisAlgorithms, OffsetArrays
3739

38-
using Base: @propagate_inbounds
39-
import Base: convert, size, axes, promote_rule, ndims, eltype, checkbounds, axes1
40+
using Base: @propagate_inbounds, HasEltype, HasLength, IsInfinite, SizeUnknown
41+
import Base: convert, size, axes, promote_rule, ndims, eltype, checkbounds, axes1,
42+
iterate, length, IteratorEltype, IteratorSize
4043

4144
abstract type Flag end
4245
abstract type InterpolationType <: Flag end
@@ -474,5 +477,6 @@ include("io.jl")
474477
include("convenience-constructors.jl")
475478
include("deprecations.jl")
476479
include("lanczos/lanczos.jl")
480+
include("iterate.jl")
477481

478482
end # module

0 commit comments

Comments
 (0)