Skip to content

Commit c3d299e

Browse files
Add SmoothedConstantInterpolation (#367)
* POC * Add docstring * derivatives * Add to docs * integrals * Add tests * Merge branch 'master' into SmoothedConstantInterpolation * Formatting * Make periodic extrapolation smooth * Fix tests * Actually fix all tests * Actually Actually fix all tests * Actually actually actually fix all tests * Address comments * Merge cleanup * Add get_transition_ts * Formatting * Update extrapolation behavior * Formatting * Fix extrapolation derivatives * Nit * Use get_transition_ts in derivative testing * Formatting * Fix derivatives and integrals * Clarify when the last u is ignored * Format * Format with JuliaFormatter v1
1 parent 5f029a7 commit c3d299e

16 files changed

+449
-49
lines changed

README.md

+1
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,7 @@ corresponding to `(u,t)` pairs.
4848

4949
- `ConstantInterpolation(u,t)` - A piecewise constant interpolation.
5050

51+
- `SmoothedConstantInterpolation(u,t)` - An integral preserving continuously differentiable approximation of constant interpolation.
5152
- `LinearInterpolation(u,t)` - A linear interpolation.
5253
- `QuadraticInterpolation(u,t)` - A quadratic interpolation.
5354
- `LagrangeInterpolation(u,t,n)` - A Lagrange interpolation of order `n`.

docs/src/index.md

+1
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ corresponding to `(u,t)` pairs.
1818

1919
- `ConstantInterpolation(u,t)` - A piecewise constant interpolation.
2020

21+
- `SmoothedConstantInterpolation(u,t)` - An integral preserving continuously differentiable approximation of constant interpolation.
2122
- `LinearInterpolation(u,t)` - A linear interpolation.
2223
- `QuadraticInterpolation(u,t)` - A quadratic interpolation.
2324
- `LagrangeInterpolation(u,t,n)` - A Lagrange interpolation of order `n`.

docs/src/manual.md

+1
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ QuadraticInterpolation
66
LagrangeInterpolation
77
AkimaInterpolation
88
ConstantInterpolation
9+
SmoothedConstantInterpolation
910
QuadraticSpline
1011
CubicSpline
1112
BSplineInterpolation

docs/src/methods.md

+18
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,24 @@ A = ConstantInterpolation(u, t, dir = :right)
7777
plot(A)
7878
```
7979

80+
## Smoothed Constant Interpolation
81+
82+
This function is much like the constant interpolation above, but the transition
83+
between consecutive values is smoothed out so that the function is continuously
84+
differentiable. The smoothing is done in such a way that the integral of this function
85+
is never much off from the same integral of constant interpolation without smoothing (because of the symmetry of the smoothing sections).
86+
The maximum smoothing distance in the `t` direction from the data points can be set
87+
with the keyword argument `d_max`.
88+
89+
```@example tutorial
90+
A = ConstantInterpolation(u, t)
91+
plot(A)
92+
A = SmoothedConstantInterpolation(u, t; d_max = 10)
93+
plot!(A)
94+
```
95+
96+
Note that `u[end]` is ignored, except when using extrapolation types `Constant` or `Extension`.
97+
8098
## Quadratic Spline
8199

82100
This is the quadratic spline. It is a continuously differentiable interpolation

ext/DataInterpolationsRegularizationToolsExt.jl

+28-14
Original file line numberDiff line numberDiff line change
@@ -74,13 +74,15 @@ function RegularizationSmooth(u::AbstractVector, t::AbstractVector, t̂::Abstrac
7474
extrapolation_left::ExtrapolationType.T = ExtrapolationType.None,
7575
extrapolation_right::ExtrapolationType.T = ExtrapolationType.None,
7676
cache_parameters::Bool = false)
77-
extrapolation_left, extrapolation_right = munge_extrapolation(
77+
extrapolation_left,
78+
extrapolation_right = munge_extrapolation(
7879
extrapolation, extrapolation_left, extrapolation_right)
7980
u, t = munge_data(u, t; check_sorted = t̂, sorted_arg_name = ("third", ""))
8081
M = _mapping_matrix(t̂, t)
8182
Wls½ = LA.diagm(sqrt.(wls))
8283
Wr½ = LA.diagm(sqrt.(wr))
83-
û, λ, Aitp = _reg_smooth_solve(
84+
û, λ,
85+
Aitp = _reg_smooth_solve(
8486
u, t̂, d, M, Wls½, Wr½, λ, alg, extrapolation_left,
8587
extrapolation_right, cache_parameters)
8688
RegularizationSmooth(
@@ -100,15 +102,17 @@ function RegularizationSmooth(u::AbstractVector, t::AbstractVector, d::Int = 2;
100102
extrapolation_left::ExtrapolationType.T = ExtrapolationType.None,
101103
extrapolation_right::ExtrapolationType.T = ExtrapolationType.None,
102104
cache_parameters::Bool = false)
103-
extrapolation_left, extrapolation_right = munge_extrapolation(
105+
extrapolation_left,
106+
extrapolation_right = munge_extrapolation(
104107
extrapolation, extrapolation_left, extrapolation_right)
105108
u, t = munge_data(u, t)
106109
= t
107110
N = length(t)
108111
M = Array{Float64}(LA.I, N, N)
109112
Wls½ = Array{Float64}(LA.I, N, N)
110113
Wr½ = Array{Float64}(LA.I, N - d, N - d)
111-
û, λ, Aitp = _reg_smooth_solve(
114+
û, λ,
115+
Aitp = _reg_smooth_solve(
112116
u, t̂, d, M, Wls½, Wr½, λ, alg, extrapolation_left,
113117
extrapolation_right, cache_parameters)
114118
RegularizationSmooth(u,
@@ -137,14 +141,16 @@ function RegularizationSmooth(u::AbstractVector, t::AbstractVector, t̂::Abstrac
137141
extrapolation_left::ExtrapolationType.T = ExtrapolationType.None,
138142
extrapolation_right::ExtrapolationType.T = ExtrapolationType.None,
139143
cache_parameters::Bool = false)
140-
extrapolation_left, extrapolation_right = munge_extrapolation(
144+
extrapolation_left,
145+
extrapolation_right = munge_extrapolation(
141146
extrapolation, extrapolation_left, extrapolation_right)
142147
u, t = munge_data(u, t; check_sorted = t̂, sorted_arg_name = ("third", ""))
143148
N, N̂ = length(t), length(t̂)
144149
M = _mapping_matrix(t̂, t)
145150
Wls½ = Array{Float64}(LA.I, N, N)
146151
Wr½ = Array{Float64}(LA.I, N̂ - d, N̂ - d)
147-
û, λ, Aitp = _reg_smooth_solve(
152+
û, λ,
153+
Aitp = _reg_smooth_solve(
148154
u, t̂, d, M, Wls½, Wr½, λ, alg, extrapolation_left,
149155
extrapolation_right, cache_parameters)
150156
RegularizationSmooth(u,
@@ -174,14 +180,16 @@ function RegularizationSmooth(u::AbstractVector, t::AbstractVector, t̂::Abstrac
174180
extrapolation_left::ExtrapolationType.T = ExtrapolationType.None,
175181
extrapolation_right::ExtrapolationType.T = ExtrapolationType.None,
176182
cache_parameters::Bool = false)
177-
extrapolation_left, extrapolation_right = munge_extrapolation(
183+
extrapolation_left,
184+
extrapolation_right = munge_extrapolation(
178185
extrapolation, extrapolation_left, extrapolation_right)
179186
u, t = munge_data(u, t; check_sorted = t̂, sorted_arg_name = ("third", ""))
180187
N, N̂ = length(t), length(t̂)
181188
M = _mapping_matrix(t̂, t)
182189
Wls½ = LA.diagm(sqrt.(wls))
183190
Wr½ = Array{Float64}(LA.I, N̂ - d, N̂ - d)
184-
û, λ, Aitp = _reg_smooth_solve(
191+
û, λ,
192+
Aitp = _reg_smooth_solve(
185193
u, t̂, d, M, Wls½, Wr½, λ, alg, extrapolation_left,
186194
extrapolation_right, cache_parameters)
187195
RegularizationSmooth(u,
@@ -212,15 +220,17 @@ function RegularizationSmooth(u::AbstractVector, t::AbstractVector, t̂::Nothing
212220
extrapolation_left::ExtrapolationType.T = ExtrapolationType.None,
213221
extrapolation_right::ExtrapolationType.T = ExtrapolationType.None,
214222
cache_parameters::Bool = false)
215-
extrapolation_left, extrapolation_right = munge_extrapolation(
223+
extrapolation_left,
224+
extrapolation_right = munge_extrapolation(
216225
extrapolation, extrapolation_left, extrapolation_right)
217226
u, t = munge_data(u, t)
218227
= t
219228
N = length(t)
220229
M = Array{Float64}(LA.I, N, N)
221230
Wls½ = LA.diagm(sqrt.(wls))
222231
Wr½ = Array{Float64}(LA.I, N - d, N - d)
223-
û, λ, Aitp = _reg_smooth_solve(
232+
û, λ,
233+
Aitp = _reg_smooth_solve(
224234
u, t̂, d, M, Wls½, Wr½, λ, alg, extrapolation_left,
225235
extrapolation_right, cache_parameters)
226236
RegularizationSmooth(u,
@@ -251,15 +261,17 @@ function RegularizationSmooth(u::AbstractVector, t::AbstractVector, t̂::Nothing
251261
extrapolation_left::ExtrapolationType.T = ExtrapolationType.None,
252262
extrapolation_right::ExtrapolationType.T = ExtrapolationType.None,
253263
cache_parameters::Bool = false)
254-
extrapolation_left, extrapolation_right = munge_extrapolation(
264+
extrapolation_left,
265+
extrapolation_right = munge_extrapolation(
255266
extrapolation, extrapolation_left, extrapolation_right)
256267
u, t = munge_data(u, t)
257268
= t
258269
N = length(t)
259270
M = Array{Float64}(LA.I, N, N)
260271
Wls½ = LA.diagm(sqrt.(wls))
261272
Wr½ = LA.diagm(sqrt.(wr))
262-
û, λ, Aitp = _reg_smooth_solve(
273+
û, λ,
274+
Aitp = _reg_smooth_solve(
263275
u, t̂, d, M, Wls½, Wr½, λ, alg, extrapolation_left,
264276
extrapolation_right, cache_parameters)
265277
RegularizationSmooth(u,
@@ -289,7 +301,8 @@ function RegularizationSmooth(u::AbstractVector, t::AbstractVector, t̂::Nothing
289301
extrapolation_left::ExtrapolationType.T = ExtrapolationType.None,
290302
extrapolation_right::ExtrapolationType.T = ExtrapolationType.None,
291303
cache_parameters::Bool = false)
292-
extrapolation_left, extrapolation_right = munge_extrapolation(
304+
extrapolation_left,
305+
extrapolation_right = munge_extrapolation(
293306
extrapolation, extrapolation_left, extrapolation_right)
294307
u, t = munge_data(u, t)
295308
= t
@@ -298,7 +311,8 @@ function RegularizationSmooth(u::AbstractVector, t::AbstractVector, t̂::Nothing
298311
wls, wr = _weighting_by_kw(t, d, wls)
299312
Wls½ = LA.diagm(sqrt.(wls))
300313
Wr½ = LA.diagm(sqrt.(wr))
301-
û, λ, Aitp = _reg_smooth_solve(
314+
û, λ,
315+
Aitp = _reg_smooth_solve(
302316
u, t̂, d, M, Wls½, Wr½, λ, alg, extrapolation_left,
303317
extrapolation_right, cache_parameters)
304318
RegularizationSmooth(u,

src/DataInterpolations.jl

+4-3
Original file line numberDiff line numberDiff line change
@@ -119,9 +119,10 @@ _output_size(u::AbstractVector) = size(first(u))
119119
_output_size(u::AbstractArray) = Base.front(size(u))
120120

121121
export LinearInterpolation, QuadraticInterpolation, LagrangeInterpolation,
122-
AkimaInterpolation, ConstantInterpolation, QuadraticSpline, CubicSpline,
123-
BSplineInterpolation, BSplineApprox, CubicHermiteSpline, PCHIPInterpolation,
124-
QuinticHermiteSpline, SmoothArcLengthInterpolation, LinearInterpolationIntInv,
122+
AkimaInterpolation, ConstantInterpolation, SmoothedConstantInterpolation,
123+
QuadraticSpline, CubicSpline, BSplineInterpolation, BSplineApprox,
124+
CubicHermiteSpline, PCHIPInterpolation, QuinticHermiteSpline,
125+
SmoothArcLengthInterpolation, LinearInterpolationIntInv,
125126
ConstantInterpolationIntInv, ExtrapolationType
126127
export output_dim, output_size
127128

src/derivatives.jl

+31
Original file line numberDiff line numberDiff line change
@@ -74,12 +74,43 @@ function _extrapolate_derivative_right(A, t, order)
7474
end
7575
end
7676

77+
function _extrapolate_derivative_right(A::SmoothedConstantInterpolation, t, order)
78+
if A.extrapolation_right == ExtrapolationType.None
79+
throw(RightExtrapolationError())
80+
elseif A.extrapolation_right in (
81+
ExtrapolationType.Constant, ExtrapolationType.Extension)
82+
d = min(A.t[end] - A.t[end - 1], 2A.d_max) / 2
83+
if A.t[end] + d < t
84+
zero(eltype(A.u))
85+
else
86+
c = (A.u[end] - A.u[end - 1]) / 2
87+
-c * (2((t - A.t[end]) / d) - 2) / d
88+
end
89+
90+
else
91+
_extrapolate_other(A, t, A.extrapolation_right)
92+
end
93+
end
94+
7795
function _derivative(A::LinearInterpolation, t::Number, iguess)
7896
idx = get_idx(A, t, iguess; idx_shift = -1, ub_shift = -1, side = :first)
7997
slope = get_parameters(A, idx)
8098
slope
8199
end
82100

101+
function _derivative(A::SmoothedConstantInterpolation{<:AbstractVector}, t::Number, iguess)
102+
idx = get_idx(A, t, iguess)
103+
d_lower, d_upper, c_lower, c_upper = get_parameters(A, idx)
104+
105+
if (t - A.t[idx]) < d_lower
106+
-2c_lower * ((t - A.t[idx]) / d_lower - 1) / d_lower
107+
elseif (A.t[idx + 1] - t) < d_upper
108+
2c_upper * (1 - (A.t[idx + 1] - t) / d_upper) / d_upper
109+
else
110+
zero(c_upper / oneunit(t))
111+
end
112+
end
113+
83114
function _derivative(A::QuadraticInterpolation, t::Number, iguess)
84115
idx = get_idx(A, t, iguess)
85116
Δt = t - A.t[idx]

src/integrals.jl

+76
Original file line numberDiff line numberDiff line change
@@ -135,6 +135,50 @@ function _extrapolate_integral_right(A, t)
135135
end
136136
end
137137

138+
function _extrapolate_integral_right(A::SmoothedConstantInterpolation, t)
139+
(; extrapolation_right) = A
140+
if extrapolation_right == ExtrapolationType.None
141+
throw(RightExtrapolationError())
142+
elseif A.extrapolation_right in (
143+
ExtrapolationType.Constant, ExtrapolationType.Extension)
144+
d = min(A.t[end] - A.t[end - 1], 2A.d_max) / 2
145+
c = (A.u[end] - A.u[end - 1]) / 2
146+
147+
Δt_transition = min(t - A.t[end], d)
148+
Δt_constant = max(0, t - A.t[end] - d)
149+
out = Δt_transition * A.u[end - 1] -
150+
c *
151+
(((Δt_transition / d)^3) / (3 / d) - ((Δt_transition^2) / d) -
152+
Δt_transition) +
153+
Δt_constant * A.u[end]
154+
155+
elseif extrapolation_right == ExtrapolationType.Linear
156+
slope = derivative(A, last(A.t))
157+
Δt = t - last(A.t)
158+
(last(A.u) + slope * Δt / 2) * Δt
159+
_extrapolate_other(A, t, A.extrapolation_right)
160+
elseif extrapolation_right == ExtrapolationType.Periodic
161+
t_, n = transformation_periodic(A, t)
162+
out = integral(A, first(A.t), t_)
163+
if !iszero(n)
164+
out += n * integral(A, first(A.t), last(A.t))
165+
end
166+
out
167+
else
168+
# extrapolation_right == ExtrapolationType.Reflective
169+
t_, n = transformation_reflective(A, t)
170+
out = if iseven(n)
171+
integral(A, t_, last(A.t))
172+
else
173+
integral(A, t_)
174+
end
175+
if !iszero(n)
176+
out += n * integral(A, first(A.t), last(A.t))
177+
end
178+
out
179+
end
180+
end
181+
138182
function _integral(A::LinearInterpolation{<:AbstractVector{<:Number}},
139183
idx::Number, t1::Number, t2::Number)
140184
slope = get_parameters(A, idx)
@@ -154,6 +198,38 @@ function _integral(
154198
end
155199
end
156200

201+
function _integral(A::SmoothedConstantInterpolation{<:AbstractVector},
202+
idx::Number, t1::Number, t2::Number)
203+
d_lower, d_upper, c_lower, c_upper = get_parameters(A, idx)
204+
205+
bound_lower = A.t[idx] + d_lower
206+
bound_upper = A.t[idx + 1] - d_upper
207+
208+
out = A.u[idx] * (t2 - t1)
209+
210+
# Fix extrapolation behavior as constant for now
211+
if t1 <= first(A.t)
212+
t1 = first(A.t)
213+
elseif t2 >= last(A.t)
214+
t2 = last(A.t)
215+
end
216+
217+
if t1 < bound_lower
218+
t2_ = min(t2, bound_lower)
219+
out -= c_lower * d_lower *
220+
(((t2_ - A.t[idx]) / d_lower - 1)^3 - ((t1 - A.t[idx]) / d_lower - 1)^3) / 3
221+
end
222+
223+
if t2 > bound_upper
224+
t1_ = max(t1, bound_upper)
225+
out += c_upper * d_upper *
226+
((1 - (A.t[idx + 1] - t2) / d_upper)^3 -
227+
(1 - (A.t[idx + 1] - t1_) / d_upper)^3) / 3
228+
end
229+
230+
out
231+
end
232+
157233
function _integral(A::QuadraticInterpolation{<:AbstractVector{<:Number}},
158234
idx::Number, t1::Number, t2::Number)
159235
α, β = get_parameters(A, idx)

0 commit comments

Comments
 (0)