From b1a751f4b464ebf38d67ef044619a5f55d9e4274 Mon Sep 17 00:00:00 2001 From: Bart de Koning Date: Wed, 13 Nov 2024 18:32:51 +0100 Subject: [PATCH 01/14] QuadraticSpline POC --- src/DataInterpolations.jl | 24 +++++++++++++++-------- src/interpolation_caches.jl | 17 ++++++++++------ src/interpolation_methods.jl | 38 +++++++++++++++++++++++++++++++++--- src/interpolation_utils.jl | 6 ++++++ 4 files changed, 68 insertions(+), 17 deletions(-) diff --git a/src/DataInterpolations.jl b/src/DataInterpolations.jl index 393f1160..9e56531f 100644 --- a/src/DataInterpolations.jl +++ b/src/DataInterpolations.jl @@ -55,33 +55,39 @@ function (interp::AbstractInterpolation)(u::AbstractVector, t::AbstractVector) u end -const EXTRAPOLATION_ERROR = "Cannot extrapolate as `extrapolate` keyword passed was `false`" -struct ExtrapolationError <: Exception end -function Base.showerror(io::IO, e::ExtrapolationError) - print(io, EXTRAPOLATION_ERROR) +const DOWN_EXTRAPOLATION_ERROR = "Cannot extrapolate down as `extrapolation_down` keyword passed was `:none`" +struct DownExtrapolationError <: Exception end +function Base.showerror(io::IO, ::DownExtrapolationError) + print(io, DOWN_EXTRAPOLATION_ERROR) +end + +const UP_EXTRAPOLATION_ERROR = "Cannot extrapolate up as `extrapolation_up` keyword passed was `:none`" +struct UpExtrapolationError <: Exception end +function Base.showerror(io::IO, ::UpExtrapolationError) + print(io, UP_EXTRAPOLATION_ERROR) end const INTEGRAL_NOT_FOUND_ERROR = "Cannot integrate it analytically. Please use Numerical Integration methods." struct IntegralNotFoundError <: Exception end -function Base.showerror(io::IO, e::IntegralNotFoundError) +function Base.showerror(io::IO, ::IntegralNotFoundError) print(io, INTEGRAL_NOT_FOUND_ERROR) end const DERIVATIVE_NOT_FOUND_ERROR = "Derivatives greater than second order is not supported." struct DerivativeNotFoundError <: Exception end -function Base.showerror(io::IO, e::DerivativeNotFoundError) +function Base.showerror(io::IO, ::DerivativeNotFoundError) print(io, DERIVATIVE_NOT_FOUND_ERROR) end const INTEGRAL_INVERSE_NOT_FOUND_ERROR = "Cannot invert the integral analytically. Please use Numerical methods." struct IntegralInverseNotFoundError <: Exception end -function Base.showerror(io::IO, e::IntegralInverseNotFoundError) +function Base.showerror(io::IO, ::IntegralInverseNotFoundError) print(io, INTEGRAL_INVERSE_NOT_FOUND_ERROR) end const INTEGRAL_NOT_INVERTIBLE_ERROR = "The Interpolation is not positive everywhere so its integral is not invertible." struct IntegralNotInvertibleError <: Exception end -function Base.showerror(io::IO, e::IntegralNotInvertibleError) +function Base.showerror(io::IO, ::IntegralNotInvertibleError) print(io, INTEGRAL_NOT_INVERTIBLE_ERROR) end @@ -90,6 +96,8 @@ export LinearInterpolation, QuadraticInterpolation, LagrangeInterpolation, BSplineInterpolation, BSplineApprox, CubicHermiteSpline, PCHIPInterpolation, QuinticHermiteSpline, LinearInterpolationIntInv, ConstantInterpolationIntInv +const extrapolation_types::Vector{Symbol} = [:none, :constant, :linear, :extension] + # added for RegularizationSmooth, JJS 11/27/21 ### Regularization data smoothing and interpolation struct RegularizationSmooth{uType, tType, T, T2, N, ITP <: AbstractInterpolation{T, N}} <: diff --git a/src/interpolation_caches.jl b/src/interpolation_caches.jl index d0bfe26e..f8f65755 100644 --- a/src/interpolation_caches.jl +++ b/src/interpolation_caches.jl @@ -314,14 +314,18 @@ struct QuadraticSpline{uType, tType, IType, pType, kType, cType, scType, T, N} < k::kType # knot vector c::cType # B-spline control points sc::scType # Spline coefficients (preallocated memory) - extrapolate::Bool + extrapolation_down::Symbol + extrapolation_up::Symbol iguesser::Guesser{tType} cache_parameters::Bool linear_lookup::Bool function QuadraticSpline( - u, t, I, p, k, c, sc, extrapolate, cache_parameters, assume_linear_t) + u, t, I, p, k, c, sc, extrapolation_down, + extrapolation_up, cache_parameters, assume_linear_t) linear_lookup = seems_linear(assume_linear_t, t) N = get_output_dim(u) + validate_extrapolation(extrapolation_down) + validate_extrapolation(extrapolation_up) new{typeof(u), typeof(t), typeof(I), typeof(p.α), typeof(k), typeof(c), typeof(sc), eltype(u), N}(u, t, @@ -330,7 +334,8 @@ struct QuadraticSpline{uType, tType, IType, pType, kType, cType, scType, T, N} < k, c, sc, - extrapolate, + extrapolation_down, + extrapolation_up, Guesser(t), cache_parameters, linear_lookup @@ -339,7 +344,7 @@ struct QuadraticSpline{uType, tType, IType, pType, kType, cType, scType, T, N} < end function QuadraticSpline( - u::uType, t; extrapolate = false, + u::uType, t; extrapolation_down::Symbol = :none, extrapolation_up::Symbol = :none, cache_parameters = false, assume_linear_t = 1e-2) where {uType <: AbstractVector{<:Number}} u, t = munge_data(u, t) @@ -352,9 +357,9 @@ function QuadraticSpline( p = QuadraticSplineParameterCache(u, t, k, c, sc, cache_parameters) A = QuadraticSpline( - u, t, nothing, p, k, c, sc, extrapolate, cache_parameters, assume_linear_t) + u, t, nothing, p, k, c, sc, extrapolation_down, extrapolation_up, cache_parameters, assume_linear_t) I = cumulative_integral(A, cache_parameters) - QuadraticSpline(u, t, I, p, k, c, sc, extrapolate, cache_parameters, assume_linear_t) + QuadraticSpline(u, t, I, p, k, c, sc, extrapolation_down, extrapolation_up, cache_parameters, assume_linear_t) end function QuadraticSpline( diff --git a/src/interpolation_methods.jl b/src/interpolation_methods.jl index f47f9e3c..8dbf0f75 100644 --- a/src/interpolation_methods.jl +++ b/src/interpolation_methods.jl @@ -1,7 +1,39 @@ function _interpolate(A, t) - ((t < A.t[1] || t > A.t[end]) && !A.extrapolate) && - throw(ExtrapolationError()) - return _interpolate(A, t, A.iguesser) + if t < first(A.t) + _extrapolate_down(A, t) + elseif t > last(A.t) + _extrapolate_up(A, t) + else + _interpolate(A, t, A.iguesser) + end +end + +function _extrapolate_down(A, t) + (; extrapolation_down) = A + if extrapolation_down == :none + throw(ExtrapolationError(DOWN_EXTRAPOLATION_ERROR)) + elseif extrapolation_down == :constant + first(A.u) + elseif extrapolation_down == :linear + slope = derivative(A, first(A.t)) + first(A.u) + slope * (t - first(A.t)) + elseif extrapolation_down == :extension + _interpolate(A, t, A.iguesser) + end +end + +function _extrapolate_up(A, t) + (; extrapolation_up) = A + if extrapolation_up == :none + throw(ExtrapolationError(DOWN_EXTRAPOLATION_ERROR)) + elseif extrapolation_up == :constant + last(A.u) + elseif extrapolation_up == :linear + slope = derivative(A, last(A.t)) + last(A.u) + slope * (t - last(A.t)) + elseif extrapolation_up == :extension + _interpolate(A, t, A.iguesser) + end end # Linear Interpolation diff --git a/src/interpolation_utils.jl b/src/interpolation_utils.jl index 2d1c6432..77470423 100644 --- a/src/interpolation_utils.jl +++ b/src/interpolation_utils.jl @@ -248,6 +248,12 @@ function get_parameters(A::QuinticHermiteSpline, idx) end end +function validate_extrapolation(method::Symbol) + if method ∉ extrapolation_types + error("Invalid extrapolation method `$method` supplied, use one of $extrapolation_types.") + end +end + function du_PCHIP(u, t) h = diff(u) δ = h ./ diff(t) From 76859cdd610997c3701bf5d7c387772a0eee08bf Mon Sep 17 00:00:00 2001 From: Bart de Koning Date: Thu, 14 Nov 2024 07:04:38 +0100 Subject: [PATCH 02/14] nit --- src/interpolation_methods.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/interpolation_methods.jl b/src/interpolation_methods.jl index 8dbf0f75..9dc02b95 100644 --- a/src/interpolation_methods.jl +++ b/src/interpolation_methods.jl @@ -25,7 +25,7 @@ end function _extrapolate_up(A, t) (; extrapolation_up) = A if extrapolation_up == :none - throw(ExtrapolationError(DOWN_EXTRAPOLATION_ERROR)) + throw(ExtrapolationError(UP_EXTRAPOLATION_ERROR)) elseif extrapolation_up == :constant last(A.u) elseif extrapolation_up == :linear From 832ca31f0d71af829097fba0734dd31100fcb028 Mon Sep 17 00:00:00 2001 From: Bart de Koning Date: Thu, 14 Nov 2024 07:37:45 +0100 Subject: [PATCH 03/14] Use enumx for extrapolation types --- Project.toml | 2 ++ src/DataInterpolations.jl | 12 +++++++----- src/interpolation_caches.jl | 15 ++++++++------- src/interpolation_methods.jl | 16 ++++++++-------- src/interpolation_utils.jl | 6 ------ 5 files changed, 25 insertions(+), 26 deletions(-) diff --git a/Project.toml b/Project.toml index 80cd1153..48e92f6f 100644 --- a/Project.toml +++ b/Project.toml @@ -3,6 +3,7 @@ uuid = "82cc6244-b520-54b8-b5a6-8a565e85f1d0" version = "6.6.0" [deps] +EnumX = "4e289a0a-7415-4d19-859d-a7e5c4648b56" FindFirstFunctions = "64ca27bc-2ba2-4a57-88aa-44e436879224" ForwardDiff = "f6369f11-7733-5829-9624-2563aa707210" LinearAlgebra = "37e2e46d-f89d-539d-b4ee-838fcccc9c8e" @@ -27,6 +28,7 @@ DataInterpolationsSymbolicsExt = "Symbolics" Aqua = "0.8" BenchmarkTools = "1" ChainRulesCore = "1.24" +EnumX = "1.0.4" FindFirstFunctions = "1.3" FiniteDifferences = "0.12.31" ForwardDiff = "0.10.36" diff --git a/src/DataInterpolations.jl b/src/DataInterpolations.jl index 9e56531f..eca8f783 100644 --- a/src/DataInterpolations.jl +++ b/src/DataInterpolations.jl @@ -7,9 +7,12 @@ abstract type AbstractInterpolation{T, N} end using LinearAlgebra, RecipesBase using PrettyTables using ForwardDiff +using EnumX import FindFirstFunctions: searchsortedfirstcorrelated, searchsortedlastcorrelated, Guesser +@enumx ExtrapolationType none constant linear extension + include("parameter_caches.jl") include("interpolation_caches.jl") include("interpolation_utils.jl") @@ -55,13 +58,13 @@ function (interp::AbstractInterpolation)(u::AbstractVector, t::AbstractVector) u end -const DOWN_EXTRAPOLATION_ERROR = "Cannot extrapolate down as `extrapolation_down` keyword passed was `:none`" +const DOWN_EXTRAPOLATION_ERROR = "Cannot extrapolate down as `extrapolation_down` keyword passed was `none`" struct DownExtrapolationError <: Exception end function Base.showerror(io::IO, ::DownExtrapolationError) print(io, DOWN_EXTRAPOLATION_ERROR) end -const UP_EXTRAPOLATION_ERROR = "Cannot extrapolate up as `extrapolation_up` keyword passed was `:none`" +const UP_EXTRAPOLATION_ERROR = "Cannot extrapolate up as `extrapolation_up` keyword passed was `none`" struct UpExtrapolationError <: Exception end function Base.showerror(io::IO, ::UpExtrapolationError) print(io, UP_EXTRAPOLATION_ERROR) @@ -94,9 +97,8 @@ end export LinearInterpolation, QuadraticInterpolation, LagrangeInterpolation, AkimaInterpolation, ConstantInterpolation, QuadraticSpline, CubicSpline, BSplineInterpolation, BSplineApprox, CubicHermiteSpline, PCHIPInterpolation, - QuinticHermiteSpline, LinearInterpolationIntInv, ConstantInterpolationIntInv - -const extrapolation_types::Vector{Symbol} = [:none, :constant, :linear, :extension] + QuinticHermiteSpline, LinearInterpolationIntInv, ConstantInterpolationIntInv, + ExtrapolationType # added for RegularizationSmooth, JJS 11/27/21 ### Regularization data smoothing and interpolation diff --git a/src/interpolation_caches.jl b/src/interpolation_caches.jl index f8f65755..992ab265 100644 --- a/src/interpolation_caches.jl +++ b/src/interpolation_caches.jl @@ -314,8 +314,8 @@ struct QuadraticSpline{uType, tType, IType, pType, kType, cType, scType, T, N} < k::kType # knot vector c::cType # B-spline control points sc::scType # Spline coefficients (preallocated memory) - extrapolation_down::Symbol - extrapolation_up::Symbol + extrapolation_down::ExtrapolationType.T + extrapolation_up::ExtrapolationType.T iguesser::Guesser{tType} cache_parameters::Bool linear_lookup::Bool @@ -324,8 +324,6 @@ struct QuadraticSpline{uType, tType, IType, pType, kType, cType, scType, T, N} < extrapolation_up, cache_parameters, assume_linear_t) linear_lookup = seems_linear(assume_linear_t, t) N = get_output_dim(u) - validate_extrapolation(extrapolation_down) - validate_extrapolation(extrapolation_up) new{typeof(u), typeof(t), typeof(I), typeof(p.α), typeof(k), typeof(c), typeof(sc), eltype(u), N}(u, t, @@ -344,7 +342,8 @@ struct QuadraticSpline{uType, tType, IType, pType, kType, cType, scType, T, N} < end function QuadraticSpline( - u::uType, t; extrapolation_down::Symbol = :none, extrapolation_up::Symbol = :none, + u::uType, t; extrapolation_down::ExtrapolationType.T = ExtrapolationType.none, + extrapolation_up::ExtrapolationType.T = ExtrapolationType.none, cache_parameters = false, assume_linear_t = 1e-2) where {uType <: AbstractVector{<:Number}} u, t = munge_data(u, t) @@ -357,9 +356,11 @@ function QuadraticSpline( p = QuadraticSplineParameterCache(u, t, k, c, sc, cache_parameters) A = QuadraticSpline( - u, t, nothing, p, k, c, sc, extrapolation_down, extrapolation_up, cache_parameters, assume_linear_t) + u, t, nothing, p, k, c, sc, extrapolation_down, + extrapolation_up, cache_parameters, assume_linear_t) I = cumulative_integral(A, cache_parameters) - QuadraticSpline(u, t, I, p, k, c, sc, extrapolation_down, extrapolation_up, cache_parameters, assume_linear_t) + QuadraticSpline(u, t, I, p, k, c, sc, extrapolation_down, + extrapolation_up, cache_parameters, assume_linear_t) end function QuadraticSpline( diff --git a/src/interpolation_methods.jl b/src/interpolation_methods.jl index 9dc02b95..a7692434 100644 --- a/src/interpolation_methods.jl +++ b/src/interpolation_methods.jl @@ -10,28 +10,28 @@ end function _extrapolate_down(A, t) (; extrapolation_down) = A - if extrapolation_down == :none + if extrapolation_down == ExtrapolationType.none throw(ExtrapolationError(DOWN_EXTRAPOLATION_ERROR)) - elseif extrapolation_down == :constant + elseif extrapolation_down == ExtrapolationType.constant first(A.u) - elseif extrapolation_down == :linear + elseif extrapolation_down == ExtrapolationType.linear slope = derivative(A, first(A.t)) first(A.u) + slope * (t - first(A.t)) - elseif extrapolation_down == :extension + elseif extrapolation_down == ExtrapolationType.extension _interpolate(A, t, A.iguesser) end end function _extrapolate_up(A, t) (; extrapolation_up) = A - if extrapolation_up == :none + if extrapolation_up == ExtrapolationType.none throw(ExtrapolationError(UP_EXTRAPOLATION_ERROR)) - elseif extrapolation_up == :constant + elseif extrapolation_up == ExtrapolationType.constant last(A.u) - elseif extrapolation_up == :linear + elseif extrapolation_up == ExtrapolationType.linear slope = derivative(A, last(A.t)) last(A.u) + slope * (t - last(A.t)) - elseif extrapolation_up == :extension + elseif extrapolation_up == ExtrapolationType.extension _interpolate(A, t, A.iguesser) end end diff --git a/src/interpolation_utils.jl b/src/interpolation_utils.jl index 77470423..2d1c6432 100644 --- a/src/interpolation_utils.jl +++ b/src/interpolation_utils.jl @@ -248,12 +248,6 @@ function get_parameters(A::QuinticHermiteSpline, idx) end end -function validate_extrapolation(method::Symbol) - if method ∉ extrapolation_types - error("Invalid extrapolation method `$method` supplied, use one of $extrapolation_types.") - end -end - function du_PCHIP(u, t) h = diff(u) δ = h ./ diff(t) From ea91f76b4484eefd4777afb7a87881881dc223a2 Mon Sep 17 00:00:00 2001 From: Bart de Koning Date: Thu, 14 Nov 2024 10:57:36 +0100 Subject: [PATCH 04/14] Derivative extrapolation POC --- src/derivatives.jl | 47 ++++++++++++++++++++++++++++++------ src/interpolation_methods.jl | 4 +-- 2 files changed, 42 insertions(+), 9 deletions(-) diff --git a/src/derivatives.jl b/src/derivatives.jl index f7a2b0e1..8c9d6030 100644 --- a/src/derivatives.jl +++ b/src/derivatives.jl @@ -1,15 +1,48 @@ function derivative(A, t, order = 1) - ((t < A.t[1] || t > A.t[end]) && !A.extrapolate) && throw(ExtrapolationError()) - iguess = A.iguesser + (order ∉ (1, 2)) && throw(DerivativeNotFoundError()) + if t < first(A.t) + _extrapolate_derivative_down(A, t, order) + elseif t > last(A.t) + _extrapolate_derivative_up(A, t, order) + else + (order == 1) ? _derivative(A, t, A.iguesser) : + ForwardDiff.derivative(t -> begin + _derivative(A, t, iguess) + end, t) + end +end - return if order == 1 - _derivative(A, t, iguess) - elseif order == 2 +function _extrapolate_derivative_down(A, t, order) + (; extrapolation_down) = A + typed_zero = zero(one(A.u[1]) / one(A.t[1])) + if extrapolation_down == ExtrapolationType.none + throw(UpExtrapolationError()) + elseif extrapolation_down == ExtrapolationType.constant + typed_zero + elseif extrapolation_down == ExtrapolationType.linear + (order == 1) ? derivative(A, first(A.t)) : typed_zero + elseif extrapolation_down == ExtrapolationType.extension + (order == 1) ? _derivative(A, t, A.iguesser) : + ForwardDiff.derivative(t -> begin + _derivative(A, t, iguess) + end, t) + end +end + +function _extrapolate_derivative_up(A, t, order) + (; extrapolation_up) = A + typed_zero = zero(one(A.u[1]) / one(A.t[1])) + if extrapolation_up == ExtrapolationType.none + throw(DownExtrapolationError()) + elseif extrapolation_up == ExtrapolationType.constant + typed_zero + elseif extrapolation_up == ExtrapolationType.linear + (order == 1) ? derivative(A, last(A.t)) : typed_zero + elseif extrapolation_up == ExtrapolationType.extension + (order == 1) ? _derivative(A, t, A.iguesser) : ForwardDiff.derivative(t -> begin _derivative(A, t, iguess) end, t) - else - throw(DerivativeNotFoundError()) end end diff --git a/src/interpolation_methods.jl b/src/interpolation_methods.jl index a7692434..836f535e 100644 --- a/src/interpolation_methods.jl +++ b/src/interpolation_methods.jl @@ -11,7 +11,7 @@ end function _extrapolate_down(A, t) (; extrapolation_down) = A if extrapolation_down == ExtrapolationType.none - throw(ExtrapolationError(DOWN_EXTRAPOLATION_ERROR)) + throw(DownExtrapolationError()) elseif extrapolation_down == ExtrapolationType.constant first(A.u) elseif extrapolation_down == ExtrapolationType.linear @@ -25,7 +25,7 @@ end function _extrapolate_up(A, t) (; extrapolation_up) = A if extrapolation_up == ExtrapolationType.none - throw(ExtrapolationError(UP_EXTRAPOLATION_ERROR)) + throw(UpExtrapolationError()) elseif extrapolation_up == ExtrapolationType.constant last(A.u) elseif extrapolation_up == ExtrapolationType.linear From f0efd0eb56e4f7798d8a71c5d92f40b2af32372a Mon Sep 17 00:00:00 2001 From: Bart de Koning Date: Thu, 14 Nov 2024 12:50:08 +0100 Subject: [PATCH 05/14] Integral extrapolation POC --- src/derivatives.jl | 9 +++++--- src/integrals.jl | 55 ++++++++++++++++++++++++++++++++++++++-------- 2 files changed, 52 insertions(+), 12 deletions(-) diff --git a/src/derivatives.jl b/src/derivatives.jl index 8c9d6030..2035dec0 100644 --- a/src/derivatives.jl +++ b/src/derivatives.jl @@ -5,7 +5,8 @@ function derivative(A, t, order = 1) elseif t > last(A.t) _extrapolate_derivative_up(A, t, order) else - (order == 1) ? _derivative(A, t, A.iguesser) : + iguess = A.iguesser + (order == 1) ? _derivative(A, t, iguess) : ForwardDiff.derivative(t -> begin _derivative(A, t, iguess) end, t) @@ -22,7 +23,8 @@ function _extrapolate_derivative_down(A, t, order) elseif extrapolation_down == ExtrapolationType.linear (order == 1) ? derivative(A, first(A.t)) : typed_zero elseif extrapolation_down == ExtrapolationType.extension - (order == 1) ? _derivative(A, t, A.iguesser) : + iguess = A.iguesser + (order == 1) ? _derivative(A, t, iguess) : ForwardDiff.derivative(t -> begin _derivative(A, t, iguess) end, t) @@ -39,7 +41,8 @@ function _extrapolate_derivative_up(A, t, order) elseif extrapolation_up == ExtrapolationType.linear (order == 1) ? derivative(A, last(A.t)) : typed_zero elseif extrapolation_up == ExtrapolationType.extension - (order == 1) ? _derivative(A, t, A.iguesser) : + iguess = A.iguesser + (order == 1) ? _derivative(A, t, iguess) : ForwardDiff.derivative(t -> begin _derivative(A, t, iguess) end, t) diff --git a/src/integrals.jl b/src/integrals.jl index de1b4633..e1b041e9 100644 --- a/src/integrals.jl +++ b/src/integrals.jl @@ -1,11 +1,8 @@ function integral(A::AbstractInterpolation, t::Number) - ((t < A.t[1] || t > A.t[end]) && !A.extrapolate) && throw(ExtrapolationError()) - integral(A, A.t[1], t) + integral(A, first(A.t), t) end function integral(A::AbstractInterpolation, t1::Number, t2::Number) - ((t1 < A.t[1] || t1 > A.t[end]) && !A.extrapolate) && throw(ExtrapolationError()) - ((t2 < A.t[1] || t2 > A.t[end]) && !A.extrapolate) && throw(ExtrapolationError()) !hasfield(typeof(A), :I) && throw(IntegralNotFoundError()) # the index less than or equal to t1 idx1 = get_idx(A, t1, 0) @@ -17,10 +14,10 @@ function integral(A::AbstractInterpolation, t1::Number, t2::Number) return if t1 == t2 zero(total) else - total += _integral(A, idx1, A.t[idx1]) - total -= _integral(A, idx1, t1) - total += _integral(A, idx2, t2) - total -= _integral(A, idx2, A.t[idx2]) + total += __integral(A, idx1, A.t[idx1]) + total -= __integral(A, idx1, t1) + total += __integral(A, idx2, t2) + total -= __integral(A, idx2, A.t[idx2]) total end else @@ -28,12 +25,52 @@ function integral(A::AbstractInterpolation, t1::Number, t2::Number) for idx in idx1:idx2 lt1 = idx == idx1 ? t1 : A.t[idx] lt2 = idx == idx2 ? t2 : A.t[idx + 1] - total += _integral(A, idx, lt2) - _integral(A, idx, lt1) + total += __integral(A, idx, lt2) - __integral(A, idx, lt1) end total end end +function __integral(A::AbstractInterpolation, idx::Number, t::Number) + if t < first(A.t) + _extrapolate_integral_down(A, idx, t) + elseif t > last(A.t) + _extrapolate_integral_up(A, idx, t) + else + _integral(A, idx, t) + end +end + +function _extrapolate_integral_down(A, idx, t) + (; extrapolation_down) = A + if extrapolation_down == ExtrapolationType.none + throw(DownExtrapolationError()) + elseif extrapolation_down == ExtrapolationType.constant + first(A.u) * (t - first(A.t)) + elseif extrapolation_down == ExtrapolationType.linear + slope = derivative(A, first(A.t)) + Δt = t - first(A.t) + (first(A.u) + slope * Δt/2) * Δt + elseif extrapolation_down == ExtrapolationType.extension + _integral(A, idx, t) + end +end + +function _extrapolate_integral_up(A, idx, t) + (; extrapolation_up) = A + if extrapolation_up == ExtrapolationType.none + throw(UpExtrapolationError()) + elseif extrapolation_up == ExtrapolationType.constant + integral(A, A.t[end-1], A.t[end]) + last(A.u) * (t - last(A.t)) + elseif extrapolation_up == ExtrapolationType.linear + slope = derivative(A, last(A.t)) + Δt = t - last(A.t) + integral(A, A.t[end-1], A.t[end]) + (last(A.u) + slope * Δt/2) * Δt + elseif extrapolation_up == ExtrapolationType.extension + _integral(A, idx, t) + end +end + function _integral(A::LinearInterpolation{<:AbstractVector{<:Number}}, idx::Number, t::Number) From 154b755c502893156cf3899bc98161ba9f164427 Mon Sep 17 00:00:00 2001 From: Bart de Koning Date: Thu, 14 Nov 2024 19:18:36 +0100 Subject: [PATCH 06/14] Apply extrapolation options to all interpolation types --- src/derivatives.jl | 6 +- src/integrals.jl | 6 +- src/interpolation_caches.jl | 316 ++++++++++++++++++++++++----------- src/interpolation_methods.jl | 10 +- test/interface.jl | 6 +- test/interpolation_tests.jl | 202 ++++++++++++++-------- 6 files changed, 368 insertions(+), 178 deletions(-) diff --git a/src/derivatives.jl b/src/derivatives.jl index 2035dec0..6035a2eb 100644 --- a/src/derivatives.jl +++ b/src/derivatives.jl @@ -22,7 +22,8 @@ function _extrapolate_derivative_down(A, t, order) typed_zero elseif extrapolation_down == ExtrapolationType.linear (order == 1) ? derivative(A, first(A.t)) : typed_zero - elseif extrapolation_down == ExtrapolationType.extension + else + # extrapolation_down == ExtrapolationType.extension iguess = A.iguesser (order == 1) ? _derivative(A, t, iguess) : ForwardDiff.derivative(t -> begin @@ -40,7 +41,8 @@ function _extrapolate_derivative_up(A, t, order) typed_zero elseif extrapolation_up == ExtrapolationType.linear (order == 1) ? derivative(A, last(A.t)) : typed_zero - elseif extrapolation_up == ExtrapolationType.extension + else + # extrapolation_up == ExtrapolationType.extension iguess = A.iguesser (order == 1) ? _derivative(A, t, iguess) : ForwardDiff.derivative(t -> begin diff --git a/src/integrals.jl b/src/integrals.jl index e1b041e9..a411ccd8 100644 --- a/src/integrals.jl +++ b/src/integrals.jl @@ -50,7 +50,7 @@ function _extrapolate_integral_down(A, idx, t) elseif extrapolation_down == ExtrapolationType.linear slope = derivative(A, first(A.t)) Δt = t - first(A.t) - (first(A.u) + slope * Δt/2) * Δt + (first(A.u) + slope * Δt / 2) * Δt elseif extrapolation_down == ExtrapolationType.extension _integral(A, idx, t) end @@ -61,11 +61,11 @@ function _extrapolate_integral_up(A, idx, t) if extrapolation_up == ExtrapolationType.none throw(UpExtrapolationError()) elseif extrapolation_up == ExtrapolationType.constant - integral(A, A.t[end-1], A.t[end]) + last(A.u) * (t - last(A.t)) + integral(A, A.t[end - 1], A.t[end]) + last(A.u) * (t - last(A.t)) elseif extrapolation_up == ExtrapolationType.linear slope = derivative(A, last(A.t)) Δt = t - last(A.t) - integral(A, A.t[end-1], A.t[end]) + (last(A.u) + slope * Δt/2) * Δt + integral(A, A.t[end - 1], A.t[end]) + (last(A.u) + slope * Δt / 2) * Δt elseif extrapolation_up == ExtrapolationType.extension _integral(A, idx, t) end diff --git a/src/interpolation_caches.jl b/src/interpolation_caches.jl index 992ab265..bb52519c 100644 --- a/src/interpolation_caches.jl +++ b/src/interpolation_caches.jl @@ -1,5 +1,6 @@ """ - LinearInterpolation(u, t; extrapolate = false, cache_parameters = false) + LinearInterpolation(u, t; extrapolation_down::ExtrapolationType.T = ExtrapolationType.none, + extrapolation_up::ExtrapolationType.T = ExtrapolationType.none, cache_parameters = false) It is the method of interpolating between the data points using a linear polynomial. For any point, two data points one each side are chosen and connected with a line. Extrapolation extends the last linear polynomial on each side. @@ -11,7 +12,10 @@ Extrapolation extends the last linear polynomial on each side. ## Keyword Arguments - - `extrapolate`: boolean value to allow extrapolation. Defaults to `false`. + - `extrapolate_down`: The extrapolation type beyond the data in the negative `t` direction. + Defaults to `ExtrapolationType.none`. + - `extrapolate_up`: The extrapolation type beyond the data in the positive `t` direction. + Defaults to `ExtrapolationType.none`. - `cache_parameters`: precompute parameters at initialization for faster interpolation computations. Note: if activated, `u` and `t` should not be modified. Defaults to `false`. - `assume_linear_t`: boolean value to specify a faster index lookup behaviour for @@ -24,30 +28,37 @@ struct LinearInterpolation{uType, tType, IType, pType, T, N} <: AbstractInterpol t::tType I::IType p::LinearParameterCache{pType} - extrapolate::Bool + extrapolation_down::ExtrapolationType.T + extrapolation_up::ExtrapolationType.T iguesser::Guesser{tType} cache_parameters::Bool linear_lookup::Bool - function LinearInterpolation(u, t, I, p, extrapolate, cache_parameters, assume_linear_t) + function LinearInterpolation(u, t, I, p, extrapolation_down, extrapolation_up, + cache_parameters, assume_linear_t) linear_lookup = seems_linear(assume_linear_t, t) N = get_output_dim(u) new{typeof(u), typeof(t), typeof(I), typeof(p.slope), eltype(u), N}( - u, t, I, p, extrapolate, Guesser(t), cache_parameters, linear_lookup) + u, t, I, p, extrapolation_down, extrapolation_up, + Guesser(t), cache_parameters, linear_lookup) end end function LinearInterpolation( - u, t; extrapolate = false, cache_parameters = false, assume_linear_t = 1e-2) + u, t; extrapolation_down::ExtrapolationType.T = ExtrapolationType.none, + extrapolation_up::ExtrapolationType.T = ExtrapolationType.none, cache_parameters = false, assume_linear_t = 1e-2) u, t = munge_data(u, t) p = LinearParameterCache(u, t, cache_parameters) A = LinearInterpolation( - u, t, nothing, p, extrapolate, cache_parameters, assume_linear_t) + u, t, nothing, p, extrapolation_down, + extrapolation_up, cache_parameters, assume_linear_t) I = cumulative_integral(A, cache_parameters) - LinearInterpolation(u, t, I, p, extrapolate, cache_parameters, assume_linear_t) + LinearInterpolation( + u, t, I, p, extrapolation_down, extrapolation_up, cache_parameters, assume_linear_t) end """ - QuadraticInterpolation(u, t, mode = :Forward; extrapolate = false, cache_parameters = false) + QuadraticInterpolation(u, t, mode = :Forward; extrapolation_down::ExtrapolationType.T = ExtrapolationType.none, + extrapolation_up::ExtrapolationType.T = ExtrapolationType.none, cache_parameters = false) It is the method of interpolating between the data points using quadratic polynomials. For any point, three data points nearby are taken to fit a quadratic polynomial. Extrapolation extends the last quadratic polynomial on each side. @@ -60,7 +71,10 @@ Extrapolation extends the last quadratic polynomial on each side. ## Keyword Arguments - - `extrapolate`: boolean value to allow extrapolation. Defaults to `false`. + - `extrapolate_down`: The extrapolation type beyond the data in the negative `t` direction. + Defaults to `ExtrapolationType.none`. + - `extrapolate_up`: The extrapolation type beyond the data in the positive `t` direction. + Defaults to `ExtrapolationType.none`. - `cache_parameters`: precompute parameters at initialization for faster interpolation computations. Note: if activated, `u` and `t` should not be modified. Defaults to `false`. - `assume_linear_t`: boolean value to specify a faster index lookup behaviour for evenly-distributed abscissae. Alternatively, a numerical threshold may be specified @@ -74,30 +88,36 @@ struct QuadraticInterpolation{uType, tType, IType, pType, T, N} <: I::IType p::QuadraticParameterCache{pType} mode::Symbol - extrapolate::Bool + extrapolation_down::ExtrapolationType.T + extrapolation_up::ExtrapolationType.T iguesser::Guesser{tType} cache_parameters::Bool linear_lookup::Bool function QuadraticInterpolation( - u, t, I, p, mode, extrapolate, cache_parameters, assume_linear_t) + u, t, I, p, mode, extrapolation_down, + extrapolation_up, cache_parameters, assume_linear_t) mode ∈ (:Forward, :Backward) || error("mode should be :Forward or :Backward for QuadraticInterpolation") linear_lookup = seems_linear(assume_linear_t, t) N = get_output_dim(u) new{typeof(u), typeof(t), typeof(I), typeof(p.l₀), eltype(u), N}( - u, t, I, p, mode, extrapolate, Guesser(t), cache_parameters, linear_lookup) + u, t, I, p, mode, extrapolation_down, extrapolation_up, + Guesser(t), cache_parameters, linear_lookup) end end function QuadraticInterpolation( - u, t, mode; extrapolate = false, cache_parameters = false, assume_linear_t = 1e-2) + u, t, mode; extrapolation_down::ExtrapolationType.T = ExtrapolationType.none, + extrapolation_up::ExtrapolationType.T = ExtrapolationType.none, cache_parameters = false, assume_linear_t = 1e-2) u, t = munge_data(u, t) linear_lookup = seems_linear(assume_linear_t, t) p = QuadraticParameterCache(u, t, cache_parameters) A = QuadraticInterpolation( - u, t, nothing, p, mode, extrapolate, cache_parameters, linear_lookup) + u, t, nothing, p, mode, extrapolation_down, + extrapolation_up, cache_parameters, linear_lookup) I = cumulative_integral(A, cache_parameters) - QuadraticInterpolation(u, t, I, p, mode, extrapolate, cache_parameters, linear_lookup) + QuadraticInterpolation(u, t, I, p, mode, extrapolation_down, + extrapolation_up, cache_parameters, linear_lookup) end function QuadraticInterpolation(u, t; kwargs...) @@ -105,7 +125,8 @@ function QuadraticInterpolation(u, t; kwargs...) end """ - LagrangeInterpolation(u, t, n = length(t) - 1; extrapolate = false, safetycopy = true) + LagrangeInterpolation(u, t, n = length(t) - 1; extrapolation_down::ExtrapolationType.T = ExtrapolationType.none, + extrapolation_up::ExtrapolationType.T = ExtrapolationType.none, safetycopy = true) It is the method of interpolation using Lagrange polynomials of (k-1)th order passing through all the data points where k is the number of data points. @@ -117,7 +138,10 @@ It is the method of interpolation using Lagrange polynomials of (k-1)th order pa ## Keyword Arguments - - `extrapolate`: boolean value to allow extrapolation. Defaults to `false`. + - `extrapolate_down`: The extrapolation type beyond the data in the negative `t` direction. + Defaults to `ExtrapolationType.none`. + - `extrapolate_up`: The extrapolation type beyond the data in the positive `t` direction. + Defaults to `ExtrapolationType.none`. """ struct LagrangeInterpolation{uType, tType, T, bcacheType, N} <: AbstractInterpolation{T, N} @@ -126,9 +150,10 @@ struct LagrangeInterpolation{uType, tType, T, bcacheType, N} <: n::Int bcache::bcacheType idxs::Vector{Int} - extrapolate::Bool + extrapolation_down::ExtrapolationType.T + extrapolation_up::ExtrapolationType.T iguesser::Guesser{tType} - function LagrangeInterpolation(u, t, n, extrapolate) + function LagrangeInterpolation(u, t, n, extrapolation_down, extrapolation_up) bcache = zeros(eltype(u[1]), n + 1) idxs = zeros(Int, n + 1) fill!(bcache, NaN) @@ -138,23 +163,27 @@ struct LagrangeInterpolation{uType, tType, T, bcacheType, N} <: n, bcache, idxs, - extrapolate, + extrapolation_down, + extrapolation_up, Guesser(t) ) end end function LagrangeInterpolation( - u, t, n = length(t) - 1; extrapolate = false) + u, t, n = length(t) - 1; + extrapolation_down::ExtrapolationType.T = ExtrapolationType.none, + extrapolation_up::ExtrapolationType.T = ExtrapolationType.none) u, t = munge_data(u, t) if n != length(t) - 1 error("Currently only n=length(t) - 1 is supported") end - LagrangeInterpolation(u, t, n, extrapolate) + LagrangeInterpolation(u, t, n, extrapolation_down, extrapolation_up) end """ - AkimaInterpolation(u, t; extrapolate = false, cache_parameters = false) + AkimaInterpolation(u, t; extrapolation_down::ExtrapolationType.T = ExtrapolationType.none, + extrapolation_up::ExtrapolationType.T = ExtrapolationType.none, cache_parameters = false) It is a spline interpolation built from cubic polynomials. It forms a continuously differentiable function. For more details, refer: [https://en.wikipedia.org/wiki/Akima_spline](https://en.wikipedia.org/wiki/Akima_spline). Extrapolation extends the last cubic polynomial on each side. @@ -166,7 +195,10 @@ Extrapolation extends the last cubic polynomial on each side. ## Keyword Arguments - - `extrapolate`: boolean value to allow extrapolation. Defaults to `false`. + - `extrapolate_down`: The extrapolation type beyond the data in the negative `t` direction. + Defaults to `ExtrapolationType.none`. + - `extrapolate_up`: The extrapolation type beyond the data in the positive `t` direction. + Defaults to `ExtrapolationType.none`. - `cache_parameters`: precompute parameters at initialization for faster interpolation computations. Note: if activated, `u` and `t` should not be modified. Defaults to `false`. - `assume_linear_t`: boolean value to specify a faster index lookup behaviour for evenly-distributed abscissae. Alternatively, a numerical threshold may be specified @@ -181,12 +213,14 @@ struct AkimaInterpolation{uType, tType, IType, bType, cType, dType, T, N} <: b::bType c::cType d::dType - extrapolate::Bool + extrapolation_down::ExtrapolationType.T + extrapolation_up::ExtrapolationType.T iguesser::Guesser{tType} cache_parameters::Bool linear_lookup::Bool function AkimaInterpolation( - u, t, I, b, c, d, extrapolate, cache_parameters, assume_linear_t) + u, t, I, b, c, d, extrapolation_down, + extrapolation_up, cache_parameters, assume_linear_t) linear_lookup = seems_linear(assume_linear_t, t) N = get_output_dim(u) new{typeof(u), typeof(t), typeof(I), typeof(b), typeof(c), @@ -196,7 +230,8 @@ struct AkimaInterpolation{uType, tType, IType, bType, cType, dType, T, N} <: b, c, d, - extrapolate, + extrapolation_down, + extrapolation_up, Guesser(t), cache_parameters, linear_lookup @@ -205,7 +240,8 @@ struct AkimaInterpolation{uType, tType, IType, bType, cType, dType, T, N} <: end function AkimaInterpolation( - u, t; extrapolate = false, cache_parameters = false, assume_linear_t = 1e-2) + u, t; extrapolation_down::ExtrapolationType.T = ExtrapolationType.none, + extrapolation_up::ExtrapolationType.T = ExtrapolationType.none, cache_parameters = false, assume_linear_t = 1e-2) u, t = munge_data(u, t) linear_lookup = seems_linear(assume_linear_t, t) n = length(t) @@ -229,13 +265,16 @@ function AkimaInterpolation( d = (b[1:(end - 1)] .+ b[2:end] .- 2.0 .* m[3:(end - 2)]) ./ dt .^ 2 A = AkimaInterpolation( - u, t, nothing, b, c, d, extrapolate, cache_parameters, linear_lookup) + u, t, nothing, b, c, d, extrapolation_down, + extrapolation_up, cache_parameters, linear_lookup) I = cumulative_integral(A, cache_parameters) - AkimaInterpolation(u, t, I, b, c, d, extrapolate, cache_parameters, linear_lookup) + AkimaInterpolation(u, t, I, b, c, d, extrapolation_down, + extrapolation_up, cache_parameters, linear_lookup) end """ - ConstantInterpolation(u, t; dir = :left, extrapolate = false, cache_parameters = false) + ConstantInterpolation(u, t; dir = :left, extrapolation_down::ExtrapolationType.T = ExtrapolationType.none, + extrapolation_up::ExtrapolationType.T = ExtrapolationType.none, cache_parameters = false) It is the method of interpolating using a constant polynomial. For any point, two adjacent data points are found on either side (left and right). The value at that point depends on `dir`. If it is `:left`, then the value at the left point is chosen and if it is `:right`, the value at the right point is chosen. @@ -249,7 +288,10 @@ Extrapolation extends the last constant polynomial at the end points on each sid ## Keyword Arguments - `dir`: indicates which value should be used for interpolation (`:left` or `:right`). - - `extrapolate`: boolean value to allow extrapolation. Defaults to `false`. + - `extrapolate_down`: The extrapolation type beyond the data in the negative `t` direction. + Defaults to `ExtrapolationType.none`. + - `extrapolate_up`: The extrapolation type beyond the data in the positive `t` direction. + Defaults to `ExtrapolationType.none`. - `cache_parameters`: precompute parameters at initialization for faster interpolation computations. Note: if activated, `u` and `t` should not be modified. Defaults to `false`. - `assume_linear_t`: boolean value to specify a faster index lookup behaviour for evenly-distributed abscissae. Alternatively, a numerical threshold may be specified @@ -262,31 +304,38 @@ struct ConstantInterpolation{uType, tType, IType, T, N} <: AbstractInterpolation I::IType p::Nothing dir::Symbol # indicates if value to the $dir should be used for the interpolation - extrapolate::Bool + extrapolation_down::ExtrapolationType.T + extrapolation_up::ExtrapolationType.T iguesser::Guesser{tType} cache_parameters::Bool linear_lookup::Bool function ConstantInterpolation( - u, t, I, dir, extrapolate, cache_parameters, assume_linear_t) + u, t, I, dir, extrapolation_down, extrapolation_up, + cache_parameters, assume_linear_t) linear_lookup = seems_linear(assume_linear_t, t) N = get_output_dim(u) new{typeof(u), typeof(t), typeof(I), eltype(u), N}( - u, t, I, nothing, dir, extrapolate, Guesser(t), cache_parameters, linear_lookup) + u, t, I, nothing, dir, extrapolation_down, extrapolation_up, + Guesser(t), cache_parameters, linear_lookup) end end function ConstantInterpolation( - u, t; dir = :left, extrapolate = false, + u, t; dir = :left, extrapolation_down::ExtrapolationType.T = ExtrapolationType.none, + extrapolation_up::ExtrapolationType.T = ExtrapolationType.none, cache_parameters = false, assume_linear_t = 1e-2) u, t = munge_data(u, t) A = ConstantInterpolation( - u, t, nothing, dir, extrapolate, cache_parameters, assume_linear_t) + u, t, nothing, dir, extrapolation_down, + extrapolation_up, cache_parameters, assume_linear_t) I = cumulative_integral(A, cache_parameters) - ConstantInterpolation(u, t, I, dir, extrapolate, cache_parameters, assume_linear_t) + ConstantInterpolation(u, t, I, dir, extrapolation_down, extrapolation_up, + cache_parameters, assume_linear_t) end """ - QuadraticSpline(u, t; extrapolate = false, cache_parameters = false) + QuadraticSpline(u, t; extrapolation_down::ExtrapolationType.T = ExtrapolationType.none, + extrapolation_up::ExtrapolationType.T = ExtrapolationType.none, cache_parameters = false) It is a spline interpolation using piecewise quadratic polynomials between each pair of data points. Its first derivative is also continuous. Extrapolation extends the last quadratic polynomial on each side. @@ -298,7 +347,10 @@ Extrapolation extends the last quadratic polynomial on each side. ## Keyword Arguments - - `extrapolate`: boolean value to allow extrapolation. Defaults to `false`. + - `extrapolate_down`: The extrapolation type beyond the data in the negative `t` direction. + Defaults to `ExtrapolationType.none`. + - `extrapolate_up`: The extrapolation type beyond the data in the positive `t` direction. + Defaults to `ExtrapolationType.none`. - `cache_parameters`: precompute parameters at initialization for faster interpolation computations. Note: if activated, `u` and `t` should not be modified. Defaults to `false`. - `assume_linear_t`: boolean value to specify a faster index lookup behaviour for evenly-distributed abscissae. Alternatively, a numerical threshold may be specified @@ -364,7 +416,8 @@ function QuadraticSpline( end function QuadraticSpline( - u::uType, t; extrapolate = false, cache_parameters = false, + u::uType, t; extrapolation_down::ExtrapolationType.T = ExtrapolationType.none, + extrapolation_up::ExtrapolationType.T = ExtrapolationType.none, cache_parameters = false, assume_linear_t = 1e-2) where {uType <: AbstractVector} u, t = munge_data(u, t) @@ -387,13 +440,16 @@ function QuadraticSpline( p = QuadraticSplineParameterCache(u, t, k, c, sc, cache_parameters) A = QuadraticSpline( - u, t, nothing, p, k, c, sc, extrapolate, cache_parameters, assume_linear_t) + u, t, nothing, p, k, c, sc, extrapolation_down, + extrapolation_up, cache_parameters, assume_linear_t) I = cumulative_integral(A, cache_parameters) - QuadraticSpline(u, t, I, p, k, c, sc, extrapolate, cache_parameters, assume_linear_t) + QuadraticSpline(u, t, I, p, k, c, sc, extrapolation_down, + extrapolation_up, cache_parameters, assume_linear_t) end """ - CubicSpline(u, t; extrapolate = false, cache_parameters = false) + CubicSpline(u, t; extrapolation_down::ExtrapolationType.T = ExtrapolationType.none, + extrapolation_up::ExtrapolationType.T = ExtrapolationType.none, cache_parameters = false) It is a spline interpolation using piecewise cubic polynomials between each pair of data points. Its first and second derivative is also continuous. Second derivative on both ends are zero, which are also called "natural" boundary conditions. Extrapolation extends the last cubic polynomial on each side. @@ -405,7 +461,10 @@ Second derivative on both ends are zero, which are also called "natural" boundar ## Keyword Arguments - - `extrapolate`: boolean value to allow extrapolation. Defaults to `false`. + - `extrapolate_down`: The extrapolation type beyond the data in the negative `t` direction. + Defaults to `ExtrapolationType.none`. + - `extrapolate_up`: The extrapolation type beyond the data in the positive `t` direction. + Defaults to `ExtrapolationType.none`. - `cache_parameters`: precompute parameters at initialization for faster interpolation computations. Note: if activated, `u` and `t` should not be modified. Defaults to `false`. - `assume_linear_t`: boolean value to specify a faster index lookup behaviour for evenly-distributed abscissae. Alternatively, a numerical threshold may be specified @@ -420,11 +479,13 @@ struct CubicSpline{uType, tType, IType, pType, hType, zType, T, N} <: p::CubicSplineParameterCache{pType} h::hType z::zType - extrapolate::Bool + extrapolation_down::ExtrapolationType.T + extrapolation_up::ExtrapolationType.T iguesser::Guesser{tType} cache_parameters::Bool linear_lookup::Bool - function CubicSpline(u, t, I, p, h, z, extrapolate, cache_parameters, assume_linear_t) + function CubicSpline(u, t, I, p, h, z, extrapolation_down, + extrapolation_up, cache_parameters, assume_linear_t) linear_lookup = seems_linear(assume_linear_t, t) N = get_output_dim(u) new{typeof(u), typeof(t), typeof(I), typeof(p.c₁), @@ -435,7 +496,8 @@ struct CubicSpline{uType, tType, IType, pType, hType, zType, T, N} <: p, h, z, - extrapolate, + extrapolation_down, + extrapolation_up, Guesser(t), cache_parameters, linear_lookup @@ -445,7 +507,8 @@ end function CubicSpline(u::uType, t; - extrapolate = false, cache_parameters = false, + extrapolation_down::ExtrapolationType.T = ExtrapolationType.none, + extrapolation_up::ExtrapolationType.T = ExtrapolationType.none, cache_parameters = false, assume_linear_t = 1e-2) where {uType <: AbstractVector{<:Number}} u, t = munge_data(u, t) @@ -468,14 +531,17 @@ function CubicSpline(u::uType, linear_lookup = seems_linear(assume_linear_t, t) p = CubicSplineParameterCache(u, h, z, cache_parameters) A = CubicSpline( - u, t, nothing, p, h[1:(n + 1)], z, extrapolate, cache_parameters, linear_lookup) + u, t, nothing, p, h[1:(n + 1)], z, extrapolation_down, + extrapolation_up, cache_parameters, linear_lookup) I = cumulative_integral(A, cache_parameters) - CubicSpline(u, t, I, p, h[1:(n + 1)], z, extrapolate, cache_parameters, linear_lookup) + CubicSpline(u, t, I, p, h[1:(n + 1)], z, extrapolation_down, + extrapolation_up, cache_parameters, linear_lookup) end function CubicSpline(u::uType, t; - extrapolate = false, cache_parameters = false, + extrapolation_down::ExtrapolationType.T = ExtrapolationType.none, + extrapolation_up::ExtrapolationType.T = ExtrapolationType.none, cache_parameters = false, assume_linear_t = 1e-2) where {uType <: AbstractArray{T, N}} where {T, N} u, t = munge_data(u, t) @@ -502,13 +568,16 @@ function CubicSpline(u::uType, linear_lookup = seems_linear(assume_linear_t, t) p = CubicSplineParameterCache(u, h, z, cache_parameters) A = CubicSpline( - u, t, nothing, p, h[1:(n + 1)], z, extrapolate, cache_parameters, linear_lookup) + u, t, nothing, p, h[1:(n + 1)], z, extrapolation_down, + extrapolation_up, cache_parameters, linear_lookup) I = cumulative_integral(A, cache_parameters) - CubicSpline(u, t, I, p, h[1:(n + 1)], z, extrapolate, cache_parameters, linear_lookup) + CubicSpline(u, t, I, p, h[1:(n + 1)], z, extrapolation_down, + extrapolation_up, cache_parameters, linear_lookup) end function CubicSpline( - u::uType, t; extrapolate = false, cache_parameters = false, + u::uType, t; extrapolation_down::ExtrapolationType.T = ExtrapolationType.none, + extrapolation_up::ExtrapolationType.T = ExtrapolationType.none, cache_parameters = false, assume_linear_t = 1e-2) where {uType <: AbstractVector} u, t = munge_data(u, t) @@ -528,13 +597,16 @@ function CubicSpline( p = CubicSplineParameterCache(u, h, z, cache_parameters) A = CubicSpline( - u, t, nothing, p, h[1:(n + 1)], z, extrapolate, cache_parameters, assume_linear_t) + u, t, nothing, p, h[1:(n + 1)], z, extrapolation_down, + extrapolation_up, cache_parameters, assume_linear_t) I = cumulative_integral(A, cache_parameters) - CubicSpline(u, t, I, p, h[1:(n + 1)], z, extrapolate, cache_parameters, assume_linear_t) + CubicSpline(u, t, I, p, h[1:(n + 1)], z, extrapolation_down, + extrapolation_up, cache_parameters, assume_linear_t) end """ - BSplineInterpolation(u, t, d, pVecType, knotVecType; extrapolate = false, safetycopy = true) + BSplineInterpolation(u, t, d, pVecType, knotVecType; extrapolation_down::ExtrapolationType.T = ExtrapolationType.none, + extrapolation_up::ExtrapolationType.T = ExtrapolationType.none, safetycopy = true) It is a curve defined by the linear combination of `n` basis functions of degree `d` where `n` is the number of data points. For more information, refer [https://pages.mtu.edu/~shene/COURSES/cs3621/NOTES/spline/B-spline/bspline-curve.html](https://pages.mtu.edu/%7Eshene/COURSES/cs3621/NOTES/spline/B-spline/bspline-curve.html). Extrapolation is a constant polynomial of the end points on each side. @@ -549,7 +621,10 @@ Extrapolation is a constant polynomial of the end points on each side. ## Keyword Arguments - - `extrapolate`: boolean value to allow extrapolation. Defaults to `false`. + - `extrapolate_down`: The extrapolation type beyond the data in the negative `t` direction. + Defaults to `ExtrapolationType.none`. + - `extrapolate_up`: The extrapolation type beyond the data in the positive `t` direction. + Defaults to `ExtrapolationType.none`. - `assume_linear_t`: boolean value to specify a faster index lookup behaviour for evenly-distributed abscissae. Alternatively, a numerical threshold may be specified for a test based on the normalized standard deviation of the difference with respect @@ -566,7 +641,8 @@ struct BSplineInterpolation{uType, tType, pType, kType, cType, scType, T, N} <: sc::scType # Spline coefficients (preallocated memory) pVecType::Symbol knotVecType::Symbol - extrapolate::Bool + extrapolation_down::ExtrapolationType.T + extrapolation_up::ExtrapolationType.T iguesser::Guesser{tType} linear_lookup::Bool function BSplineInterpolation(u, @@ -578,7 +654,8 @@ struct BSplineInterpolation{uType, tType, pType, kType, cType, scType, T, N} <: sc, pVecType, knotVecType, - extrapolate, + extrapolation_down, + extrapolation_up, assume_linear_t) linear_lookup = seems_linear(assume_linear_t, t) N = get_output_dim(u) @@ -592,7 +669,8 @@ struct BSplineInterpolation{uType, tType, pType, kType, cType, scType, T, N} <: sc, pVecType, knotVecType, - extrapolate, + extrapolation_down, + extrapolation_up, Guesser(t), linear_lookup ) @@ -600,7 +678,9 @@ struct BSplineInterpolation{uType, tType, pType, kType, cType, scType, T, N} <: end function BSplineInterpolation( - u::AbstractVector, t, d, pVecType, knotVecType; extrapolate = false, assume_linear_t = 1e-2) + u::AbstractVector, t, d, pVecType, knotVecType; + extrapolation_down::ExtrapolationType.T = ExtrapolationType.none, + extrapolation_up::ExtrapolationType.T = ExtrapolationType.none, assume_linear_t = 1e-2) u, t = munge_data(u, t) n = length(t) n < d + 1 && error("BSplineInterpolation needs at least d + 1, i.e. $(d+1) points.") @@ -665,11 +745,14 @@ function BSplineInterpolation( c = vec(sc \ u[:, :]) sc = zeros(eltype(t), n) BSplineInterpolation( - u, t, d, p, k, c, sc, pVecType, knotVecType, extrapolate, assume_linear_t) + u, t, d, p, k, c, sc, pVecType, knotVecType, + extrapolation_down, extrapolation_up, assume_linear_t) end function BSplineInterpolation( - u::AbstractArray{T, N}, t, d, pVecType, knotVecType; extrapolate = false, + u::AbstractArray{T, N}, t, d, pVecType, knotVecType; + extrapolation_down::ExtrapolationType.T = ExtrapolationType.none, + extrapolation_up::ExtrapolationType.T = ExtrapolationType.none, assume_linear_t = 1e-2) where {T, N} u, t = munge_data(u, t) n = length(t) @@ -738,11 +821,13 @@ function BSplineInterpolation( c = reshape(c, size(u)...) sc = zeros(eltype(t), n) BSplineInterpolation( - u, t, d, p, k, c, sc, pVecType, knotVecType, extrapolate, assume_linear_t) + u, t, d, p, k, c, sc, pVecType, knotVecType, + extrapolation_down, extrapolation_up, assume_linear_t) end """ - BSplineApprox(u, t, d, h, pVecType, knotVecType; extrapolate = false) + BSplineApprox(u, t, d, h, pVecType, knotVecType; extrapolation_down::ExtrapolationType.T = ExtrapolationType.none, + extrapolation_up::ExtrapolationType.T = ExtrapolationType.none) It is a regression based B-spline. The argument choices are the same as the `BSplineInterpolation`, with the additional parameter `h < length(t)` which is the number of control points to use, with smaller `h` indicating more smoothing. For more information, refer [http://www.cad.zju.edu.cn/home/zhx/GM/009/00-bsia.pdf](http://www.cad.zju.edu.cn/home/zhx/GM/009/00-bsia.pdf). @@ -759,7 +844,10 @@ Extrapolation is a constant polynomial of the end points on each side. ## Keyword Arguments - - `extrapolate`: boolean value to allow extrapolation. Defaults to `false`. + - `extrapolate_down`: The extrapolation type beyond the data in the negative `t` direction. + Defaults to `ExtrapolationType.none`. + - `extrapolate_up`: The extrapolation type beyond the data in the positive `t` direction. + Defaults to `ExtrapolationType.none`. - `assume_linear_t`: boolean value to specify a faster index lookup behaviour for evenly-distributed abscissae. Alternatively, a numerical threshold may be specified for a test based on the normalized standard deviation of the difference with respect @@ -777,7 +865,8 @@ struct BSplineApprox{uType, tType, pType, kType, cType, scType, T, N} <: sc::scType # Spline coefficients (preallocated memory) pVecType::Symbol knotVecType::Symbol - extrapolate::Bool + extrapolation_down::ExtrapolationType.T + extrapolation_up::ExtrapolationType.T iguesser::Guesser{tType} linear_lookup::Bool function BSplineApprox(u, @@ -790,7 +879,8 @@ struct BSplineApprox{uType, tType, pType, kType, cType, scType, T, N} <: sc, pVecType, knotVecType, - extrapolate, + extrapolation_down, + extrapolation_up, assume_linear_t ) linear_lookup = seems_linear(assume_linear_t, t) @@ -806,7 +896,8 @@ struct BSplineApprox{uType, tType, pType, kType, cType, scType, T, N} <: sc, pVecType, knotVecType, - extrapolate, + extrapolation_down, + extrapolation_up, Guesser(t), linear_lookup ) @@ -814,7 +905,9 @@ struct BSplineApprox{uType, tType, pType, kType, cType, scType, T, N} <: end function BSplineApprox( - u::AbstractVector, t, d, h, pVecType, knotVecType; extrapolate = false, assume_linear_t = 1e-2) + u::AbstractVector, t, d, h, pVecType, knotVecType; + extrapolation_down::ExtrapolationType.T = ExtrapolationType.none, + extrapolation_up::ExtrapolationType.T = ExtrapolationType.none, assume_linear_t = 1e-2) u, t = munge_data(u, t) n = length(t) h < d + 1 && error("BSplineApprox needs at least d + 1, i.e. $(d+1) control points.") @@ -900,11 +993,14 @@ function BSplineApprox( c[2:(end - 1)] .= vec(P) sc = zeros(eltype(t), h) BSplineApprox( - u, t, d, h, p, k, c, sc, pVecType, knotVecType, extrapolate, assume_linear_t) + u, t, d, h, p, k, c, sc, pVecType, knotVecType, + extrapolation_down, extrapolation_up, assume_linear_t) end function BSplineApprox( - u::AbstractArray{T, N}, t, d, h, pVecType, knotVecType; extrapolate = false, + u::AbstractArray{T, N}, t, d, h, pVecType, knotVecType; + extrapolation_down::ExtrapolationType.T = ExtrapolationType.none, + extrapolation_up::ExtrapolationType.T = ExtrapolationType.none, assume_linear_t = 1e-2) where {T, N} u, t = munge_data(u, t) n = length(t) @@ -996,10 +1092,12 @@ function BSplineApprox( c[ax_u..., 2:(end - 1)] = P sc = zeros(eltype(t), h) BSplineApprox( - u, t, d, h, p, k, c, sc, pVecType, knotVecType, extrapolate, assume_linear_t) + u, t, d, h, p, k, c, sc, pVecType, knotVecType, + extrapolation_down, extrapolation_up, assume_linear_t) end """ - CubicHermiteSpline(du, u, t; extrapolate = false, cache_parameters = false) + CubicHermiteSpline(du, u, t; extrapolation_down::ExtrapolationType.T = ExtrapolationType.none, + extrapolation_up::ExtrapolationType.T = ExtrapolationType.none, cache_parameters = false) It is a Cubic Hermite interpolation, which is a piece-wise third degree polynomial such that the value and the first derivative are equal to given values in the data points. @@ -1011,7 +1109,10 @@ It is a Cubic Hermite interpolation, which is a piece-wise third degree polynomi ## Keyword Arguments - - `extrapolate`: boolean value to allow extrapolation. Defaults to `false`. + - `extrapolate_down`: The extrapolation type beyond the data in the negative `t` direction. + Defaults to `ExtrapolationType.none`. + - `extrapolate_up`: The extrapolation type beyond the data in the positive `t` direction. + Defaults to `ExtrapolationType.none`. - `cache_parameters`: precompute parameters at initialization for faster interpolation computations. Note: if activated, `u` and `t` should not be modified. Defaults to `false`. - `assume_linear_t`: boolean value to specify a faster index lookup behaviour for evenly-distributed abscissae. Alternatively, a numerical threshold may be specified @@ -1025,33 +1126,40 @@ struct CubicHermiteSpline{uType, tType, IType, duType, pType, T, N} <: t::tType I::IType p::CubicHermiteParameterCache{pType} - extrapolate::Bool + extrapolation_down::ExtrapolationType.T + extrapolation_up::ExtrapolationType.T iguesser::Guesser{tType} cache_parameters::Bool linear_lookup::Bool function CubicHermiteSpline( - du, u, t, I, p, extrapolate, cache_parameters, assume_linear_t) + du, u, t, I, p, extrapolation_down, extrapolation_up, + cache_parameters, assume_linear_t) linear_lookup = seems_linear(assume_linear_t, t) N = get_output_dim(u) new{typeof(u), typeof(t), typeof(I), typeof(du), typeof(p.c₁), eltype(u), N}( - du, u, t, I, p, extrapolate, Guesser(t), cache_parameters, linear_lookup) + du, u, t, I, p, extrapolation_down, extrapolation_up, + Guesser(t), cache_parameters, linear_lookup) end end function CubicHermiteSpline( - du, u, t; extrapolate = false, cache_parameters = false, assume_linear_t = 1e-2) + du, u, t; extrapolation_down::ExtrapolationType.T = ExtrapolationType.none, + extrapolation_up::ExtrapolationType.T = ExtrapolationType.none, cache_parameters = false, assume_linear_t = 1e-2) @assert length(u)==length(du) "Length of `u` is not equal to length of `du`." u, t = munge_data(u, t) linear_lookup = seems_linear(assume_linear_t, t) p = CubicHermiteParameterCache(du, u, t, cache_parameters) A = CubicHermiteSpline( - du, u, t, nothing, p, extrapolate, cache_parameters, linear_lookup) + du, u, t, nothing, p, extrapolation_down, + extrapolation_up, cache_parameters, linear_lookup) I = cumulative_integral(A, cache_parameters) - CubicHermiteSpline(du, u, t, I, p, extrapolate, cache_parameters, linear_lookup) + CubicHermiteSpline(du, u, t, I, p, extrapolation_down, + extrapolation_up, cache_parameters, linear_lookup) end """ - PCHIPInterpolation(u, t; extrapolate = false, safetycopy = true) + PCHIPInterpolation(u, t; extrapolation_down::ExtrapolationType.T = ExtrapolationType.none, + extrapolation_up::ExtrapolationType.T = ExtrapolationType.none, safetycopy = true) It is a PCHIP Interpolation, which is a type of [`CubicHermiteSpline`](@ref) where the derivative values `du` are derived from the input data in such a way that the interpolation never overshoots the data. See [here](https://www.mathworks.com/content/dam/mathworks/mathworks-dot-com/moler/interp.pdf), @@ -1064,7 +1172,10 @@ section 3.4 for more details. ## Keyword Arguments - - `extrapolate`: boolean value to allow extrapolation. Defaults to `false`. + - `extrapolate_down`: The extrapolation type beyond the data in the negative `t` direction. + Defaults to `ExtrapolationType.none`. + - `extrapolate_up`: The extrapolation type beyond the data in the positive `t` direction. + Defaults to `ExtrapolationType.none`. - `cache_parameters`: precompute parameters at initialization for faster interpolation computations. Note: if activated, `u` and `t` should not be modified. Defaults to `false`. - `assume_linear_t`: boolean value to specify a faster index lookup behaviour for evenly-distributed abscissae. Alternatively, a numerical threshold may be specified @@ -1072,14 +1183,17 @@ section 3.4 for more details. to the straight line (see [`looks_linear`](@ref)). Defaults to 1e-2. """ function PCHIPInterpolation( - u, t; extrapolate = false, cache_parameters = false, assume_linear_t = 1e-2) + u, t; extrapolation_down::ExtrapolationType.T = ExtrapolationType.none, + extrapolation_up::ExtrapolationType.T = ExtrapolationType.none, cache_parameters = false, assume_linear_t = 1e-2) u, t = munge_data(u, t) du = du_PCHIP(u, t) - CubicHermiteSpline(du, u, t; extrapolate, cache_parameters, assume_linear_t) + CubicHermiteSpline( + du, u, t; extrapolation_down, extrapolation_up, cache_parameters, assume_linear_t) end """ - QuinticHermiteSpline(ddu, du, u, t; extrapolate = false, safetycopy = true) + QuinticHermiteSpline(ddu, du, u, t; extrapolation_down::ExtrapolationType.T = ExtrapolationType.none, + extrapolation_up::ExtrapolationType.T = ExtrapolationType.none, safetycopy = true) It is a Quintic Hermite interpolation, which is a piece-wise fifth degree polynomial such that the value and the first and second derivative are equal to given values in the data points. @@ -1092,7 +1206,10 @@ It is a Quintic Hermite interpolation, which is a piece-wise fifth degree polyno ## Keyword Arguments - - `extrapolate`: boolean value to allow extrapolation. Defaults to `false`. + - `extrapolate_down`: The extrapolation type beyond the data in the negative `t` direction. + Defaults to `ExtrapolationType.none`. + - `extrapolate_up`: The extrapolation type beyond the data in the positive `t` direction. + Defaults to `ExtrapolationType.none`. - `cache_parameters`: precompute parameters at initialization for faster interpolation computations. Note: if activated, `u` and `t` should not be modified. Defaults to `false`. - `assume_linear_t`: boolean value to specify a faster index lookup behaviour for evenly-distributed abscissae. Alternatively, a numerical threshold may be specified @@ -1107,29 +1224,36 @@ struct QuinticHermiteSpline{uType, tType, IType, duType, dduType, pType, T, N} < t::tType I::IType p::QuinticHermiteParameterCache{pType} - extrapolate::Bool + extrapolation_down::ExtrapolationType.T + extrapolation_up::ExtrapolationType.T iguesser::Guesser{tType} cache_parameters::Bool linear_lookup::Bool function QuinticHermiteSpline( - ddu, du, u, t, I, p, extrapolate, cache_parameters, assume_linear_t) + ddu, du, u, t, I, p, extrapolation_down, + extrapolation_up, cache_parameters, assume_linear_t) linear_lookup = seems_linear(assume_linear_t, t) N = get_output_dim(u) new{typeof(u), typeof(t), typeof(I), typeof(du), typeof(ddu), typeof(p.c₁), eltype(u), N}( - ddu, du, u, t, I, p, extrapolate, Guesser(t), cache_parameters, linear_lookup) + ddu, du, u, t, I, p, extrapolation_down, extrapolation_up, + Guesser(t), cache_parameters, linear_lookup) end end -function QuinticHermiteSpline(ddu, du, u, t; extrapolate = false, +function QuinticHermiteSpline( + ddu, du, u, t; extrapolation_down::ExtrapolationType.T = ExtrapolationType.none, + extrapolation_up::ExtrapolationType.T = ExtrapolationType.none, cache_parameters = false, assume_linear_t = 1e-2) @assert length(u)==length(du)==length(ddu) "Length of `u` is not equal to length of `du` or `ddu`." u, t = munge_data(u, t) linear_lookup = seems_linear(assume_linear_t, t) p = QuinticHermiteParameterCache(ddu, du, u, t, cache_parameters) A = QuinticHermiteSpline( - ddu, du, u, t, nothing, p, extrapolate, cache_parameters, linear_lookup) + ddu, du, u, t, nothing, p, extrapolation_down, + extrapolation_up, cache_parameters, linear_lookup) I = cumulative_integral(A, cache_parameters) QuinticHermiteSpline( - ddu, du, u, t, I, p, extrapolate, cache_parameters, linear_lookup) + ddu, du, u, t, I, p, extrapolation_down, + extrapolation_up, cache_parameters, linear_lookup) end diff --git a/src/interpolation_methods.jl b/src/interpolation_methods.jl index 836f535e..0c070f18 100644 --- a/src/interpolation_methods.jl +++ b/src/interpolation_methods.jl @@ -13,11 +13,12 @@ function _extrapolate_down(A, t) if extrapolation_down == ExtrapolationType.none throw(DownExtrapolationError()) elseif extrapolation_down == ExtrapolationType.constant - first(A.u) + first(A.u) * one(t) elseif extrapolation_down == ExtrapolationType.linear slope = derivative(A, first(A.t)) first(A.u) + slope * (t - first(A.t)) - elseif extrapolation_down == ExtrapolationType.extension + else + # extrapolation_down == ExtrapolationType.extension _interpolate(A, t, A.iguesser) end end @@ -27,11 +28,12 @@ function _extrapolate_up(A, t) if extrapolation_up == ExtrapolationType.none throw(UpExtrapolationError()) elseif extrapolation_up == ExtrapolationType.constant - last(A.u) + last(A.u) * one(t) elseif extrapolation_up == ExtrapolationType.linear slope = derivative(A, last(A.t)) last(A.u) + slope * (t - last(A.t)) - elseif extrapolation_up == ExtrapolationType.extension + else + # extrapolation_up == ExtrapolationType.extension _interpolate(A, t, A.iguesser) end end diff --git a/test/interface.jl b/test/interface.jl index e7b2b81b..40fb25f6 100644 --- a/test/interface.jl +++ b/test/interface.jl @@ -18,8 +18,10 @@ end @testset "Symbolics" begin u = 2.0collect(1:10) t = 1.0collect(1:10) - A = LinearInterpolation(u, t; extrapolate = true) - B = LinearInterpolation(u .^ 2, t; extrapolate = true) + A = LinearInterpolation(u, t; extrapolation_up = ExtrapolationType.extension, + extrapolation_down = ExtrapolationType.extension) + B = LinearInterpolation(u .^ 2, t; extrapolation_up = ExtrapolationType.extension, + extrapolation_down = ExtrapolationType.extension) @variables t x(t) substitute(A(t), Dict(t => x)) t_val = 2.7 diff --git a/test/interpolation_tests.jl b/test/interpolation_tests.jl index bbeb72f4..832819f3 100644 --- a/test/interpolation_tests.jl +++ b/test/interpolation_tests.jl @@ -8,7 +8,8 @@ function test_interpolation_type(T) @test T <: DataInterpolations.AbstractInterpolation @test hasfield(T, :u) @test hasfield(T, :t) - @test hasfield(T, :extrapolate) + @test hasfield(T, :extrapolation_up) + @test hasfield(T, :extrapolation_down) @test hasfield(T, :iguesser) @test !isempty(methods(DataInterpolations._interpolate, (T, Any, Number))) @test !isempty(methods(DataInterpolations._integral, (T, Any, Number))) @@ -30,7 +31,8 @@ end for t in (1.0:10.0, 1.0collect(1:10)) u = 2.0collect(1:10) #t = 1.0collect(1:10) - A = LinearInterpolation(u, t; extrapolate = true) + A = LinearInterpolation(u, t; extrapolation_up = ExtrapolationType.extension, + extrapolation_down = ExtrapolationType.extension) for (_t, _u) in zip(t, u) @test A(_t) == _u @@ -40,7 +42,8 @@ end @test A(11) == 22 u = vcat(2.0collect(1:10)', 3.0collect(1:10)') - A = LinearInterpolation(u, t; extrapolate = true) + A = LinearInterpolation(u, t; extrapolation_up = ExtrapolationType.extension, + extrapolation_down = ExtrapolationType.extension) for (_t, _u) in zip(t, eachcol(u)) @test A(_t) == _u @@ -53,7 +56,8 @@ end y = 2:4 u_ = x' .* y u = [u_[:, i] for i in 1:size(u_, 2)] - A = LinearInterpolation(u, t; extrapolate = true) + A = LinearInterpolation(u, t; extrapolation_up = ExtrapolationType.extension, + extrapolation_down = ExtrapolationType.extension) @test A(0) == [0.0, 0.0, 0.0] @test A(5.5) == [11.0, 16.5, 22.0] @test A(11) == [22.0, 33.0, 44.0] @@ -64,7 +68,8 @@ end u_ = x' .* y u = [u_[:, i:(i + 1)] for i in 1:2:10] t = 1.0collect(2:2:10) - A = LinearInterpolation(u, t; extrapolate = true) + A = LinearInterpolation(u, t; extrapolation_up = ExtrapolationType.extension, + extrapolation_down = ExtrapolationType.extension) @test A(0) == [-2.0 0.0; -3.0 0.0; -4.0 0.0] @test A(3) == [4.0 6.0; 6.0 9.0; 8.0 12.0] @@ -74,7 +79,8 @@ end # with NaNs (#113) u = [NaN, 1.0, 2.0, 3.0] t = 1:4 - A = LinearInterpolation(u, t; extrapolate = true) + A = LinearInterpolation(u, t; extrapolation_up = ExtrapolationType.extension, + extrapolation_down = ExtrapolationType.extension) @test isnan(A(1.0)) @test A(2.0) == 1.0 @test A(2.5) == 1.5 @@ -82,7 +88,8 @@ end @test A(4.0) == 3.0 u = [0.0, NaN, 2.0, 3.0] - A = LinearInterpolation(u, t; extrapolate = true) + A = LinearInterpolation(u, t; extrapolation_up = ExtrapolationType.extension, + extrapolation_down = ExtrapolationType.extension) @test A(1.0) == 0.0 @test isnan(A(2.0)) @test isnan(A(2.5)) @@ -90,7 +97,8 @@ end @test A(4.0) == 3.0 u = [0.0, 1.0, NaN, 3.0] - A = LinearInterpolation(u, t; extrapolate = true) + A = LinearInterpolation(u, t; extrapolation_up = ExtrapolationType.extension, + extrapolation_down = ExtrapolationType.extension) @test A(1.0) == 0.0 @test A(2.0) == 1.0 @test isnan(A(2.5)) @@ -98,7 +106,8 @@ end @test A(4.0) == 3.0 u = [0.0, 1.0, 2.0, NaN] - A = LinearInterpolation(u, t; extrapolate = true) + A = LinearInterpolation(u, t; extrapolation_up = ExtrapolationType.extension, + extrapolation_down = ExtrapolationType.extension) @test A(1.0) == 0.0 @test A(2.0) == 1.0 @test A(3.0) == 2.0 @@ -108,16 +117,20 @@ end # Test type stability u = Float32.(1:5) t = Float32.(1:5) - A1 = LinearInterpolation(u, t; extrapolate = true) + A1 = LinearInterpolation(u, t; extrapolation_up = ExtrapolationType.extension, + extrapolation_down = ExtrapolationType.extension) u = 1:5 t = 1:5 - A2 = LinearInterpolation(u, t; extrapolate = true) + A2 = LinearInterpolation(u, t; extrapolation_up = ExtrapolationType.extension, + extrapolation_down = ExtrapolationType.extension) u = [1 // i for i in 1:5] t = (1:5) - A3 = LinearInterpolation(u, t; extrapolate = true) + A3 = LinearInterpolation(u, t; extrapolation_up = ExtrapolationType.extension, + extrapolation_down = ExtrapolationType.extension) u = [1 // i for i in 1:5] t = [1 // (6 - i) for i in 1:5] - A4 = LinearInterpolation(u, t; extrapolate = true) + A4 = LinearInterpolation(u, t; extrapolation_up = ExtrapolationType.extension, + extrapolation_down = ExtrapolationType.extension) F32 = Float32(1) F64 = Float64(1) @@ -126,7 +139,10 @@ end R32 = Int32(1) // Int32(1) R64 = 1 // 1 for A in Any[A1, A2, A3, A4] + @show A.u + @show A.t @test @inferred(A(F32)) === A(F32) + println("foo") @test @inferred(A(F64)) === A(F64) @test @inferred(A(I32)) === A(I32) @test @inferred(A(I64)) === A(I64) @@ -137,13 +153,15 @@ end # Nan time value: t = 0.0:3 # Floats u = [0, -2, -1, -2] - A = LinearInterpolation(u, t; extrapolate = true) + A = LinearInterpolation(u, t; extrapolation_up = ExtrapolationType.extension, + extrapolation_down = ExtrapolationType.extension) dA = t -> ForwardDiff.derivative(A, t) @test isnan(dA(NaN)) t = 0:3 # Integers u = [0, -2, -1, -2] - A = LinearInterpolation(u, t; extrapolate = true) + A = LinearInterpolation(u, t; extrapolation_up = ExtrapolationType.extension, + extrapolation_down = ExtrapolationType.extension) dA = t -> ForwardDiff.derivative(A, t) @test isnan(dA(NaN)) @@ -156,7 +174,8 @@ end # Test array-valued interpolation u = collect.(2.0collect(1:10)) t = 1.0collect(1:10) - A = LinearInterpolation(u, t; extrapolate = true) + A = LinearInterpolation(u, t; extrapolation_up = ExtrapolationType.extension, + extrapolation_down = ExtrapolationType.extension) @test A(0) == fill(0.0) @test A(5.5) == fill(11.0) @test A(11) == fill(22) @@ -171,13 +190,14 @@ end # Test extrapolation u = 2.0collect(1:10) t = 1.0collect(1:10) - A = LinearInterpolation(u, t; extrapolate = true) + A = LinearInterpolation(u, t; extrapolation_up = ExtrapolationType.extension, + extrapolation_down = ExtrapolationType.extension) @test A(-1.0) == -2.0 @test A(11.0) == 22.0 A = LinearInterpolation(u, t) - @test_throws DataInterpolations.ExtrapolationError A(-1.0) - @test_throws DataInterpolations.ExtrapolationError A(11.0) - @test_throws DataInterpolations.ExtrapolationError A([-1.0, 11.0]) + @test_throws DataInterpolations.DownExtrapolationError A(-1.0) + @test_throws DataInterpolations.UpExtrapolationError A(11.0) + @test_throws DataInterpolations.DownExtrapolationError A([-1.0, 11.0]) end @testset "Quadratic Interpolation" begin @@ -185,7 +205,8 @@ end u = [1.0, 4.0, 9.0, 16.0] t = [1.0, 2.0, 3.0, 4.0] - A = QuadraticInterpolation(u, t; extrapolate = true) + A = QuadraticInterpolation(u, t; extrapolation_up = ExtrapolationType.extension, + extrapolation_down = ExtrapolationType.extension) for (_t, _u) in zip(t, u) @test A(_t) == _u @@ -200,7 +221,9 @@ end # backward-looking interpolation u = [1.0, 4.0, 9.0, 16.0] t = [1.0, 2.0, 3.0, 4.0] - A = QuadraticInterpolation(u, t, :Backward; extrapolate = true) + A = QuadraticInterpolation( + u, t, :Backward; extrapolation_up = ExtrapolationType.extension, + extrapolation_down = ExtrapolationType.extension) for (_t, _u) in zip(t, u) @test A(_t) == _u @@ -238,7 +261,8 @@ end # Matrix interpolation test u = [1.0 4.0 9.0 16.0; 1.0 4.0 9.0 16.0] - A = QuadraticInterpolation(u, t; extrapolate = true) + A = QuadraticInterpolation(u, t; extrapolation_up = ExtrapolationType.extension, + extrapolation_down = ExtrapolationType.extension) for (_t, _u) in zip(t, eachcol(u)) @test A(_t) == _u @@ -251,7 +275,8 @@ end u_ = [1.0, 4.0, 9.0, 16.0]' .* ones(5) u = [u_[:, i] for i in 1:size(u_, 2)] - A = QuadraticInterpolation(u, t; extrapolate = true) + A = QuadraticInterpolation(u, t; extrapolation_up = ExtrapolationType.extension, + extrapolation_down = ExtrapolationType.extension) @test A(0) == zeros(5) @test A(1.5) == 2.25 * ones(5) @test A(2.5) == 6.25 * ones(5) @@ -259,7 +284,8 @@ end @test A(5.0) == 25.0 * ones(5) u = [repeat(u[i], 1, 3) for i in 1:4] - A = QuadraticInterpolation(u, t; extrapolate = true) + A = QuadraticInterpolation(u, t; extrapolation_up = ExtrapolationType.extension, + extrapolation_down = ExtrapolationType.extension) @test A(0) == zeros(5, 3) @test A(1.5) == 2.25 * ones(5, 3) @test A(2.5) == 6.25 * ones(5, 3) @@ -269,12 +295,13 @@ end # Test extrapolation u = [1.0, 4.5, 6.0, 2.0] t = [1.0, 2.0, 3.0, 4.0] - A = QuadraticInterpolation(u, t; extrapolate = true) + A = QuadraticInterpolation(u, t; extrapolation_up = ExtrapolationType.extension, + extrapolation_down = ExtrapolationType.extension) @test A(0.0) == -4.5 @test A(5.0) == -7.5 A = QuadraticInterpolation(u, t) - @test_throws DataInterpolations.ExtrapolationError A(0.0) - @test_throws DataInterpolations.ExtrapolationError A(5.0) + @test_throws DataInterpolations.DownExtrapolationError A(0.0) + @test_throws DataInterpolations.UpExtrapolationError A(5.0) end @testset "Lagrange Interpolation" begin @@ -329,12 +356,13 @@ end # Test extrapolation u = [1.0, 4.0, 9.0] t = [1.0, 2.0, 3.0] - A = LagrangeInterpolation(u, t; extrapolate = true) + A = LagrangeInterpolation(u, t; extrapolation_up = ExtrapolationType.extension, + extrapolation_down = ExtrapolationType.extension) @test A(0.0) == 0.0 @test A(4.0) == 16.0 A = LagrangeInterpolation(u, t) - @test_throws DataInterpolations.ExtrapolationError A(-1.0) - @test_throws DataInterpolations.ExtrapolationError A(4.0) + @test_throws DataInterpolations.DownExtrapolationError A(-1.0) + @test_throws DataInterpolations.UpExtrapolationError A(4.0) end @testset "Akima Interpolation" begin @@ -360,12 +388,13 @@ end test_cached_index(A) # Test extrapolation - A = AkimaInterpolation(u, t; extrapolate = true) + A = AkimaInterpolation(u, t; extrapolation_up = ExtrapolationType.extension, + extrapolation_down = ExtrapolationType.extension) @test A(-1.0) ≈ -5.0 @test A(11.0) ≈ -3.924742268041234 A = AkimaInterpolation(u, t) - @test_throws DataInterpolations.ExtrapolationError A(-1.0) - @test_throws DataInterpolations.ExtrapolationError A(11.0) + @test_throws DataInterpolations.UpExtrapolationError A(-1.0) + @test_throws DataInterpolations.DownExtrapolationError A(11.0) end @testset "ConstantInterpolation" begin @@ -374,7 +403,9 @@ end t = [1.0, 2.0, 3.0, 4.0] @testset "Vector case" for u in [[1.0, 2.0, 0.0, 1.0], ["B", "C", "A", "B"]] - A = ConstantInterpolation(u, t, dir = :right; extrapolate = true) + A = ConstantInterpolation( + u, t, dir = :right; extrapolation_up = ExtrapolationType.extension, + extrapolation_down = ExtrapolationType.extension) @test A(0.5) == u[1] @test A(1.0) == u[1] @test A(1.5) == u[2] @@ -386,7 +417,8 @@ end @test A(4.5) == u[1] test_cached_index(A) - A = ConstantInterpolation(u, t; extrapolate = true) # dir=:left is default + A = ConstantInterpolation(u, t; extrapolation_up = ExtrapolationType.extension, + extrapolation_down = ExtrapolationType.extension) # dir=:left is default @test A(0.5) == u[1] @test A(1.0) == u[1] @test A(1.5) == u[1] @@ -403,7 +435,9 @@ end [1.0 2.0 0.0 1.0; 1.0 2.0 0.0 1.0], ["B" "C" "A" "B"; "B" "C" "A" "B"] ] - A = ConstantInterpolation(u, t, dir = :right; extrapolate = true) + A = ConstantInterpolation( + u, t, dir = :right; extrapolation_up = ExtrapolationType.extension, + extrapolation_down = ExtrapolationType.extension) @test A(0.5) == u[:, 1] @test A(1.0) == u[:, 1] @test A(1.5) == u[:, 2] @@ -415,7 +449,8 @@ end @test A(4.5) == u[:, 1] test_cached_index(A) - A = ConstantInterpolation(u, t; extrapolate = true) # dir=:left is default + A = ConstantInterpolation(u, t; extrapolation_up = ExtrapolationType.extension, + extrapolation_down = ExtrapolationType.extension) # dir=:left is default @test A(0.5) == u[:, 1] @test A(1.0) == u[:, 1] @test A(1.5) == u[:, 1] @@ -431,7 +466,9 @@ end @testset "Vector of Vectors case" for u in [ [[1.0, 2.0], [0.0, 1.0], [1.0, 2.0], [0.0, 1.0]], [["B", "C"], ["A", "B"], ["B", "C"], ["A", "B"]]] - A = ConstantInterpolation(u, t, dir = :right; extrapolate = true) + A = ConstantInterpolation( + u, t, dir = :right; extrapolation_up = ExtrapolationType.extension, + extrapolation_down = ExtrapolationType.extension) @test A(0.5) == u[1] @test A(1.0) == u[1] @test A(1.5) == u[2] @@ -443,7 +480,8 @@ end @test A(4.5) == u[4] test_cached_index(A) - A = ConstantInterpolation(u, t; extrapolate = true) # dir=:left is default + A = ConstantInterpolation(u, t; extrapolation_up = ExtrapolationType.extension, + extrapolation_down = ExtrapolationType.extension) # dir=:left is default @test A(0.5) == u[1] @test A(1.0) == u[1] @test A(1.5) == u[1] @@ -459,7 +497,9 @@ end @testset "Vector of Matrices case" for u in [ [[1.0 2.0; 1.0 2.0], [0.0 1.0; 0.0 1.0], [1.0 2.0; 1.0 2.0], [0.0 1.0; 0.0 1.0]], [["B" "C"; "B" "C"], ["A" "B"; "A" "B"], ["B" "C"; "B" "C"], ["A" "B"; "A" "B"]]] - A = ConstantInterpolation(u, t, dir = :right; extrapolate = true) + A = ConstantInterpolation( + u, t, dir = :right; extrapolation_up = ExtrapolationType.extension, + extrapolation_down = ExtrapolationType.extension) @test A(0.5) == u[1] @test A(1.0) == u[1] @test A(1.5) == u[2] @@ -471,7 +511,8 @@ end @test A(4.5) == u[4] test_cached_index(A) - A = ConstantInterpolation(u, t; extrapolate = true) # dir=:left is default + A = ConstantInterpolation(u, t; extrapolation_up = ExtrapolationType.extension, + extrapolation_down = ExtrapolationType.extension) # dir=:left is default @test A(0.5) == u[1] @test A(1.0) == u[1] @test A(1.5) == u[1] @@ -486,17 +527,19 @@ end # Test extrapolation u = [1.0, 2.0, 0.0, 1.0] - A = ConstantInterpolation(u, t; extrapolate = true) + A = ConstantInterpolation(u, t; extrapolation_up = ExtrapolationType.extension, + extrapolation_down = ExtrapolationType.extension) @test A(-1.0) == 1.0 @test A(11.0) == 1.0 A = ConstantInterpolation(u, t) - @test_throws DataInterpolations.ExtrapolationError A(-1.0) - @test_throws DataInterpolations.ExtrapolationError A(11.0) + @test_throws DataInterpolations.DownExtrapolationError A(-1.0) + @test_throws DataInterpolations.UpExtrapolationError A(11.0) # Test extrapolation with infs with regularly spaced t u = [1.67e7, 1.6867e7, 1.7034e7, 1.7201e7, 1.7368e7] t = [0.0, 0.1, 0.2, 0.3, 0.4] - A = ConstantInterpolation(u, t; extrapolate = true) + A = ConstantInterpolation(u, t; extrapolation_up = ExtrapolationType.extension, + extrapolation_down = ExtrapolationType.extension) @test A(Inf) == last(u) @test A(-Inf) == first(u) end @@ -507,7 +550,8 @@ end u = [0.0, 1.0, 3.0] t = [-1.0, 0.0, 1.0] - A = QuadraticSpline(u, t; extrapolate = true) + A = QuadraticSpline(u, t; extrapolation_up = ExtrapolationType.extension, + extrapolation_down = ExtrapolationType.extension) # Solution P₁ = x -> 0.5 * (x + 1) * (x + 2) @@ -523,14 +567,16 @@ end u_ = [0.0, 1.0, 3.0]' .* ones(4) u = [u_[:, i] for i in 1:size(u_, 2)] - A = QuadraticSpline(u, t; extrapolate = true) + A = QuadraticSpline(u, t; extrapolation_up = ExtrapolationType.extension, + extrapolation_down = ExtrapolationType.extension) @test A(-2.0) == P₁(-2.0) * ones(4) @test A(-0.5) == P₁(-0.5) * ones(4) @test A(0.7) == P₁(0.7) * ones(4) @test A(2.0) == P₁(2.0) * ones(4) u = [repeat(u[i], 1, 3) for i in 1:3] - A = QuadraticSpline(u, t; extrapolate = true) + A = QuadraticSpline(u, t; extrapolation_up = ExtrapolationType.extension, + extrapolation_down = ExtrapolationType.extension) @test A(-2.0) == P₁(-2.0) * ones(4, 3) @test A(-0.5) == P₁(-0.5) * ones(4, 3) @test A(0.7) == P₁(0.7) * ones(4, 3) @@ -539,12 +585,13 @@ end # Test extrapolation u = [0.0, 1.0, 3.0] t = [-1.0, 0.0, 1.0] - A = QuadraticSpline(u, t; extrapolate = true) + A = QuadraticSpline(u, t; extrapolation_up = ExtrapolationType.extension, + extrapolation_down = ExtrapolationType.extension) @test A(-2.0) == 0.0 @test A(2.0) == 6.0 A = QuadraticSpline(u, t) - @test_throws DataInterpolations.ExtrapolationError A(-2.0) - @test_throws DataInterpolations.ExtrapolationError A(2.0) + @test_throws DataInterpolations.DownExtrapolationError A(-2.0) + @test_throws DataInterpolations.UpExtrapolationError A(2.0) end @testset "CubicSpline Interpolation" begin @@ -553,7 +600,8 @@ end u = [0.0, 1.0, 3.0] t = [-1.0, 0.0, 1.0] - A = CubicSpline(u, t; extrapolate = true) + A = CubicSpline(u, t; extrapolation_up = ExtrapolationType.extension, + extrapolation_down = ExtrapolationType.extension) test_cached_index(A) # Solution @@ -572,7 +620,8 @@ end u_ = [0.0, 1.0, 3.0]' .* ones(4) u = [u_[:, i] for i in 1:size(u_, 2)] - A = CubicSpline(u, t; extrapolate = true) + A = CubicSpline(u, t; extrapolation_up = ExtrapolationType.extension, + extrapolation_down = ExtrapolationType.extension) for x in (-1.5, -0.5, -0.7) @test A(x) ≈ P₁(x) * ones(4) end @@ -581,7 +630,8 @@ end end u = [repeat(u[i], 1, 3) for i in 1:3] - A = CubicSpline(u, t; extrapolate = true) + A = CubicSpline(u, t; extrapolation_up = ExtrapolationType.extension, + extrapolation_down = ExtrapolationType.extension) for x in (-1.5, -0.5, -0.7) @test A(x) ≈ P₁(x) * ones(4, 3) end @@ -592,12 +642,13 @@ end # Test extrapolation u = [0.0, 1.0, 3.0] t = [-1.0, 0.0, 1.0] - A = CubicSpline(u, t; extrapolate = true) + A = CubicSpline(u, t; extrapolation_up = ExtrapolationType.extension, + extrapolation_down = ExtrapolationType.extension) @test A(-2.0) ≈ -1.0 @test A(2.0) ≈ 5.0 A = CubicSpline(u, t) - @test_throws DataInterpolations.ExtrapolationError A(-2.0) - @test_throws DataInterpolations.ExtrapolationError A(2.0) + @test_throws DataInterpolations.DownExtrapolationError A(-2.0) + @test_throws DataInterpolations.UpExtrapolationError A(2.0) @testset "AbstractMatrix" begin t = 0.1:0.1:1.0 @@ -636,12 +687,14 @@ end test_cached_index(A) # Test extrapolation - A = BSplineInterpolation(u, t, 2, :Uniform, :Uniform; extrapolate = true) + A = BSplineInterpolation( + u, t, 2, :Uniform, :Uniform; extrapolation_up = ExtrapolationType.extension, + extrapolation_down = ExtrapolationType.extension) @test A(-1.0) == u[1] @test A(300.0) == u[end] A = BSplineInterpolation(u, t, 2, :Uniform, :Uniform) - @test_throws DataInterpolations.ExtrapolationError A(-1.0) - @test_throws DataInterpolations.ExtrapolationError A(300.0) + @test_throws DataInterpolations.DownExtrapolationError A(-1.0) + @test_throws DataInterpolations.UpExtrapolationError A(300.0) A = BSplineInterpolation(u, t, 2, :ArcLen, :Average) @@ -656,12 +709,14 @@ end @test_nowarn BSplineInterpolation(u[1:3], t[1:3], 2, :Uniform, :Uniform) # Test extrapolation - A = BSplineInterpolation(u, t, 2, :ArcLen, :Average; extrapolate = true) + A = BSplineInterpolation( + u, t, 2, :ArcLen, :Average; extrapolation_up = ExtrapolationType.extension, + extrapolation_down = ExtrapolationType.extension) @test A(-1.0) == u[1] @test A(300.0) == u[end] A = BSplineInterpolation(u, t, 2, :ArcLen, :Average) - @test_throws DataInterpolations.ExtrapolationError A(-1.0) - @test_throws DataInterpolations.ExtrapolationError A(300.0) + @test_throws DataInterpolations.DownExtrapolationError A(-1.0) + @test_throws DataInterpolations.UpExtrapolationError A(300.0) @testset "AbstractMatrix" begin t = 0.1:0.1:1.0 @@ -713,12 +768,14 @@ end @test_nowarn BSplineApprox(u, t, 2, 3, :Uniform, :Uniform) # Test extrapolation - A = BSplineApprox(u, t, 2, 4, :Uniform, :Uniform; extrapolate = true) + A = BSplineApprox( + u, t, 2, 4, :Uniform, :Uniform; extrapolation_up = ExtrapolationType.extension, + extrapolation_down = ExtrapolationType.extension) @test A(-1.0) == u[1] @test A(300.0) == u[end] A = BSplineApprox(u, t, 2, 4, :Uniform, :Uniform) - @test_throws DataInterpolations.ExtrapolationError A(-1.0) - @test_throws DataInterpolations.ExtrapolationError A(300.0) + @test_throws DataInterpolations.DownExtrapolationError A(-1.0) + @test_throws DataInterpolations.UpExtrapolationError A(300.0) @testset "AbstractMatrix" begin t = 0.1:0.1:1.0 @@ -759,7 +816,8 @@ end du = [-0.047, -0.058, 0.054, 0.012, -0.068, 0.0] u = [14.7, 11.51, 10.41, 14.95, 12.24, 11.22] t = [0.0, 62.25, 109.66, 162.66, 205.8, 252.3] - A = CubicHermiteSpline(du, u, t; extrapolate = true) + A = CubicHermiteSpline(du, u, t; extrapolation_up = ExtrapolationType.extension, + extrapolation_down = ExtrapolationType.extension) @test A.(t) ≈ u @test A(100.0)≈10.106770 rtol=1e-5 @test A(300.0)≈9.901542 rtol=1e-5 @@ -787,7 +845,8 @@ end du = [-0.047, -0.058, 0.054, 0.012, -0.068, 0.0] u = [14.7, 11.51, 10.41, 14.95, 12.24, 11.22] t = [0.0, 62.25, 109.66, 162.66, 205.8, 252.3] - A = QuinticHermiteSpline(ddu, du, u, t; extrapolate = true) + A = QuinticHermiteSpline(ddu, du, u, t; extrapolation_up = ExtrapolationType.extension, + extrapolation_down = ExtrapolationType.extension) @test A.(t) ≈ u @test A(100.0)≈10.107996 rtol=1e-5 @test A(300.0)≈11.364162 rtol=1e-5 @@ -818,10 +877,11 @@ end @test vs ≈ us # Test extrapolation - A = Curvefit(u, t, model, p0, LBFGS(); extrapolate = true) + A = Curvefit(u, t, model, p0, LBFGS(); extrapolation_up = ExtrapolationType.extension, + extrapolation_down = ExtrapolationType.extension) @test A(15.0) == model(15.0, A.pmin) A = Curvefit(u, t, model, p0, LBFGS()) - @test_throws DataInterpolations.ExtrapolationError A(15.0) + @test_throws DataInterpolations.UpExtrapolationError A(15.0) end @testset "Type of vector returned" begin From b1e4bb7cebf5fcb4f0737569ca8acbf2ea6aa693 Mon Sep 17 00:00:00 2001 From: Bart de Koning Date: Thu, 14 Nov 2024 21:41:32 +0100 Subject: [PATCH 07/14] Fix tests --- ...ataInterpolationsRegularizationToolsExt.jl | 70 +++++++++++++------ src/DataInterpolations.jl | 15 +++- src/derivatives.jl | 12 ++-- src/integral_inverses.jl | 10 +-- src/interpolation_methods.jl | 8 +-- test/derivative_tests.jl | 24 +++++-- test/integral_inverse_tests.jl | 3 +- test/integral_tests.jl | 11 +-- test/interpolation_tests.jl | 12 ++-- test/regularization.jl | 5 +- test/zygote_tests.jl | 8 ++- 11 files changed, 112 insertions(+), 66 deletions(-) diff --git a/ext/DataInterpolationsRegularizationToolsExt.jl b/ext/DataInterpolationsRegularizationToolsExt.jl index 732ea1bb..6856996d 100644 --- a/ext/DataInterpolationsRegularizationToolsExt.jl +++ b/ext/DataInterpolationsRegularizationToolsExt.jl @@ -69,13 +69,17 @@ A = RegularizationSmooth(u, t, t̂, wls, wr, d; λ = 1.0, alg = :gcv_svd) """ function RegularizationSmooth(u::AbstractVector, t::AbstractVector, t̂::AbstractVector, wls::AbstractVector, wr::AbstractVector, d::Int = 2; - λ::Real = 1.0, alg::Symbol = :gcv_svd, extrapolate::Bool = false) + λ::Real = 1.0, alg::Symbol = :gcv_svd, + extrapolation_down::ExtrapolationType.T = ExtrapolationType.none, + extrapolation_up::ExtrapolationType.T = ExtrapolationType.none) u, t = munge_data(u, t) M = _mapping_matrix(t̂, t) Wls½ = LA.diagm(sqrt.(wls)) Wr½ = LA.diagm(sqrt.(wr)) - û, λ, Aitp = _reg_smooth_solve(u, t̂, d, M, Wls½, Wr½, λ, alg, extrapolate) - RegularizationSmooth(u, û, t, t̂, wls, wr, d, λ, alg, Aitp, extrapolate) + û, λ, Aitp = _reg_smooth_solve( + u, t̂, d, M, Wls½, Wr½, λ, alg, extrapolation_down, extrapolation_up) + RegularizationSmooth( + u, û, t, t̂, wls, wr, d, λ, alg, Aitp, extrapolation_down, extrapolation_up) end """ Direct smoothing, no `t̂` or weights @@ -86,14 +90,16 @@ A = RegularizationSmooth(u, t, d; λ = 1.0, alg = :gcv_svd, extrapolate = false) """ function RegularizationSmooth(u::AbstractVector, t::AbstractVector, d::Int = 2; λ::Real = 1.0, - alg::Symbol = :gcv_svd, extrapolate::Bool = false) + alg::Symbol = :gcv_svd, extrapolation_down::ExtrapolationType.T = ExtrapolationType.none, + extrapolation_up::ExtrapolationType.T = ExtrapolationType.none) u, t = munge_data(u, t) t̂ = t N = length(t) M = Array{Float64}(LA.I, N, N) Wls½ = Array{Float64}(LA.I, N, N) Wr½ = Array{Float64}(LA.I, N - d, N - d) - û, λ, Aitp = _reg_smooth_solve(u, t̂, d, M, Wls½, Wr½, λ, alg, extrapolate) + û, λ, Aitp = _reg_smooth_solve( + u, t̂, d, M, Wls½, Wr½, λ, alg, extrapolation_down, extrapolation_up) RegularizationSmooth(u, û, t, @@ -104,7 +110,8 @@ function RegularizationSmooth(u::AbstractVector, t::AbstractVector, d::Int = 2; λ, alg, Aitp, - extrapolate) + extrapolation_down, + extrapolation_up) end """ `t̂` provided, no weights @@ -115,13 +122,15 @@ A = RegularizationSmooth(u, t, t̂, d; λ = 1.0, alg = :gcv_svd, extrapolate = f """ function RegularizationSmooth(u::AbstractVector, t::AbstractVector, t̂::AbstractVector, d::Int = 2; λ::Real = 1.0, alg::Symbol = :gcv_svd, - extrapolate::Bool = false) + extrapolation_down::ExtrapolationType.T = ExtrapolationType.none, + extrapolation_up::ExtrapolationType.T = ExtrapolationType.none) u, t = munge_data(u, t) N, N̂ = length(t), length(t̂) M = _mapping_matrix(t̂, t) Wls½ = Array{Float64}(LA.I, N, N) Wr½ = Array{Float64}(LA.I, N̂ - d, N̂ - d) - û, λ, Aitp = _reg_smooth_solve(u, t̂, d, M, Wls½, Wr½, λ, alg, extrapolate) + û, λ, Aitp = _reg_smooth_solve( + u, t̂, d, M, Wls½, Wr½, λ, alg, extrapolation_down, extrapolation_up) RegularizationSmooth(u, û, t, @@ -132,7 +141,8 @@ function RegularizationSmooth(u::AbstractVector, t::AbstractVector, t̂::Abstrac λ, alg, Aitp, - extrapolate) + extrapolation_down, + extrapolation_up) end """ `t̂` and `wls` provided @@ -143,13 +153,15 @@ A = RegularizationSmooth(u, t, t̂, wls, d; λ = 1.0, alg = :gcv_svd, extrapolat """ function RegularizationSmooth(u::AbstractVector, t::AbstractVector, t̂::AbstractVector, wls::AbstractVector, d::Int = 2; λ::Real = 1.0, - alg::Symbol = :gcv_svd, extrapolate::Bool = false) + alg::Symbol = :gcv_svd, extrapolation_down::ExtrapolationType.T = ExtrapolationType.none, + extrapolation_up::ExtrapolationType.T = ExtrapolationType.none) u, t = munge_data(u, t) N, N̂ = length(t), length(t̂) M = _mapping_matrix(t̂, t) Wls½ = LA.diagm(sqrt.(wls)) Wr½ = Array{Float64}(LA.I, N̂ - d, N̂ - d) - û, λ, Aitp = _reg_smooth_solve(u, t̂, d, M, Wls½, Wr½, λ, alg, extrapolate) + û, λ, Aitp = _reg_smooth_solve( + u, t̂, d, M, Wls½, Wr½, λ, alg, extrapolation_down, extrapolation_up) RegularizationSmooth(u, û, t, @@ -160,7 +172,8 @@ function RegularizationSmooth(u::AbstractVector, t::AbstractVector, t̂::Abstrac λ, alg, Aitp, - extrapolate) + extrapolation_down, + extrapolation_up) end """ `wls` provided, no `t̂` @@ -172,14 +185,16 @@ A = RegularizationSmooth( """ function RegularizationSmooth(u::AbstractVector, t::AbstractVector, t̂::Nothing, wls::AbstractVector, d::Int = 2; λ::Real = 1.0, - alg::Symbol = :gcv_svd, extrapolate::Bool = false) + alg::Symbol = :gcv_svd, extrapolation_down::ExtrapolationType.T = ExtrapolationType.none, + extrapolation_up::ExtrapolationType.T = ExtrapolationType.none) u, t = munge_data(u, t) t̂ = t N = length(t) M = Array{Float64}(LA.I, N, N) Wls½ = LA.diagm(sqrt.(wls)) Wr½ = Array{Float64}(LA.I, N - d, N - d) - û, λ, Aitp = _reg_smooth_solve(u, t̂, d, M, Wls½, Wr½, λ, alg, extrapolate) + û, λ, Aitp = _reg_smooth_solve( + u, t̂, d, M, Wls½, Wr½, λ, alg, extrapolation_down, extrapolation_up) RegularizationSmooth(u, û, t, @@ -190,7 +205,8 @@ function RegularizationSmooth(u::AbstractVector, t::AbstractVector, t̂::Nothing λ, alg, Aitp, - extrapolate) + extrapolation_down, + extrapolation_up) end """ `wls` and `wr` provided, no `t̂` @@ -202,14 +218,17 @@ A = RegularizationSmooth( """ function RegularizationSmooth(u::AbstractVector, t::AbstractVector, t̂::Nothing, wls::AbstractVector, wr::AbstractVector, d::Int = 2; - λ::Real = 1.0, alg::Symbol = :gcv_svd, extrapolate::Bool = false) + λ::Real = 1.0, alg::Symbol = :gcv_svd, + extrapolation_down::ExtrapolationType.T = ExtrapolationType.none, + extrapolation_up::ExtrapolationType.T = ExtrapolationType.none) u, t = munge_data(u, t) t̂ = t N = length(t) M = Array{Float64}(LA.I, N, N) Wls½ = LA.diagm(sqrt.(wls)) Wr½ = LA.diagm(sqrt.(wr)) - û, λ, Aitp = _reg_smooth_solve(u, t̂, d, M, Wls½, Wr½, λ, alg, extrapolate) + û, λ, Aitp = _reg_smooth_solve( + u, t̂, d, M, Wls½, Wr½, λ, alg, extrapolation_down, extrapolation_up) RegularizationSmooth(u, û, t, @@ -220,7 +239,8 @@ function RegularizationSmooth(u::AbstractVector, t::AbstractVector, t̂::Nothing λ, alg, Aitp, - extrapolate) + extrapolation_down, + extrapolation_up) end """ Keyword provided for `wls`, no `t̂` @@ -232,7 +252,8 @@ A = RegularizationSmooth( """ function RegularizationSmooth(u::AbstractVector, t::AbstractVector, t̂::Nothing, wls::Symbol, d::Int = 2; λ::Real = 1.0, alg::Symbol = :gcv_svd, - extrapolate::Bool = false) + extrapolation_down::ExtrapolationType.T = ExtrapolationType.none, + extrapolation_up::ExtrapolationType.T = ExtrapolationType.none) u, t = munge_data(u, t) t̂ = t N = length(t) @@ -240,7 +261,8 @@ function RegularizationSmooth(u::AbstractVector, t::AbstractVector, t̂::Nothing wls, wr = _weighting_by_kw(t, d, wls) Wls½ = LA.diagm(sqrt.(wls)) Wr½ = LA.diagm(sqrt.(wr)) - û, λ, Aitp = _reg_smooth_solve(u, t̂, d, M, Wls½, Wr½, λ, alg, extrapolate) + û, λ, Aitp = _reg_smooth_solve( + u, t̂, d, M, Wls½, Wr½, λ, alg, extrapolation_down, extrapolation_up) RegularizationSmooth(u, û, t, @@ -251,7 +273,8 @@ function RegularizationSmooth(u::AbstractVector, t::AbstractVector, t̂::Nothing λ, alg, Aitp, - extrapolate) + extrapolation_down, + extrapolation_up) end # """ t̂ provided and keyword for wls _TBD_ """ # function RegularizationSmooth(u::AbstractVector, t::AbstractVector, t̂::AbstractVector, @@ -262,7 +285,8 @@ Solve for the smoothed dependent variables and create spline interpolator """ function _reg_smooth_solve( u::AbstractVector, t̂::AbstractVector, d::Int, M::AbstractMatrix, - Wls½::AbstractMatrix, Wr½::AbstractMatrix, λ::Real, alg::Symbol, extrapolate::Bool) + Wls½::AbstractMatrix, Wr½::AbstractMatrix, λ::Real, alg::Symbol, + extrapolation_down::ExtrapolationType.T, extrapolation_up::ExtrapolationType.T) λ = float(λ) # `float` expected by RT D = _derivative_matrix(t̂, d) Ψ = RT.setupRegularizationProblem(Wls½ * M, Wr½ * D) @@ -279,7 +303,7 @@ function _reg_smooth_solve( û = result.x λ = result.λ end - Aitp = CubicSpline(û, t̂; extrapolate) + Aitp = CubicSpline(û, t̂; extrapolation_down, extrapolation_up) # It seems logical to use B-Spline of order d+1, but I am unsure if theory supports the # extra computational cost, JJS 12/25/21 #Aitp = BSplineInterpolation(û,t̂,d+1,:ArcLen,:Average) diff --git a/src/DataInterpolations.jl b/src/DataInterpolations.jl index eca8f783..700ffb98 100644 --- a/src/DataInterpolations.jl +++ b/src/DataInterpolations.jl @@ -58,6 +58,12 @@ function (interp::AbstractInterpolation)(u::AbstractVector, t::AbstractVector) u end +const EXTRAPOLATION_ERROR = "Cannot extrapolate as `extrapolate` keyword passed was `false`" +struct ExtrapolationError <: Exception end +function Base.showerror(io::IO, ::ExtrapolationError) + print(io, EXTRAPOLATION_ERROR) +end + const DOWN_EXTRAPOLATION_ERROR = "Cannot extrapolate down as `extrapolation_down` keyword passed was `none`" struct DownExtrapolationError <: Exception end function Base.showerror(io::IO, ::DownExtrapolationError) @@ -114,7 +120,8 @@ struct RegularizationSmooth{uType, tType, T, T2, N, ITP <: AbstractInterpolation λ::T2 # regularization parameter alg::Symbol # how to determine λ: `:fixed`, `:gcv_svd`, `:gcv_tr`, `L_curve` Aitp::ITP - extrapolate::Bool + extrapolation_down::ExtrapolationType.T + extrapolation_up::ExtrapolationType.T function RegularizationSmooth(u, û, t, @@ -125,7 +132,8 @@ struct RegularizationSmooth{uType, tType, T, T2, N, ITP <: AbstractInterpolation λ, alg, Aitp, - extrapolate) + extrapolation_down, + extrapolation_up) N = get_output_dim(u) new{typeof(u), typeof(t), eltype(u), typeof(λ), N, typeof(Aitp)}( u, @@ -138,7 +146,8 @@ struct RegularizationSmooth{uType, tType, T, T2, N, ITP <: AbstractInterpolation λ, alg, Aitp, - extrapolate) + extrapolation_down, + extrapolation_up) end end diff --git a/src/derivatives.jl b/src/derivatives.jl index 6035a2eb..6632c13f 100644 --- a/src/derivatives.jl +++ b/src/derivatives.jl @@ -15,9 +15,9 @@ end function _extrapolate_derivative_down(A, t, order) (; extrapolation_down) = A - typed_zero = zero(one(A.u[1]) / one(A.t[1])) + typed_zero = zero(first(A.u) / one(A.t[1])) if extrapolation_down == ExtrapolationType.none - throw(UpExtrapolationError()) + throw(DownExtrapolationError()) elseif extrapolation_down == ExtrapolationType.constant typed_zero elseif extrapolation_down == ExtrapolationType.linear @@ -34,9 +34,9 @@ end function _extrapolate_derivative_up(A, t, order) (; extrapolation_up) = A - typed_zero = zero(one(A.u[1]) / one(A.t[1])) + typed_zero = zero(first(A.u) / one(A.t[1])) if extrapolation_up == ExtrapolationType.none - throw(DownExtrapolationError()) + throw(UpExtrapolationError()) elseif extrapolation_up == ExtrapolationType.constant typed_zero elseif extrapolation_up == ExtrapolationType.linear @@ -67,7 +67,6 @@ function _derivative(A::QuadraticInterpolation, t::Number, iguess) end function _derivative(A::LagrangeInterpolation{<:AbstractVector}, t::Number) - ((t < A.t[1] || t > A.t[end]) && !A.extrapolate) && throw(ExtrapolationError()) der = zero(A.u[1]) for j in eachindex(A.t) tmp = zero(A.t[1]) @@ -101,7 +100,6 @@ function _derivative(A::LagrangeInterpolation{<:AbstractVector}, t::Number) end function _derivative(A::LagrangeInterpolation{<:AbstractMatrix}, t::Number) - ((t < A.t[1] || t > A.t[end]) && !A.extrapolate) && throw(ExtrapolationError()) der = zero(A.u[:, 1]) for j in eachindex(A.t) tmp = zero(A.t[1]) @@ -153,12 +151,10 @@ function _derivative(A::ConstantInterpolation, t::Number, iguess) end function _derivative(A::ConstantInterpolation{<:AbstractVector}, t::Number, iguess) - ((t < A.t[1] || t > A.t[end]) && !A.extrapolate) && throw(ExtrapolationError()) return isempty(searchsorted(A.t, t)) ? zero(A.u[1]) : eltype(A.u)(NaN) end function _derivative(A::ConstantInterpolation{<:AbstractMatrix}, t::Number, iguess) - ((t < A.t[1] || t > A.t[end]) && !A.extrapolate) && throw(ExtrapolationError()) return isempty(searchsorted(A.t, t)) ? zero(A.u[:, 1]) : eltype(A.u)(NaN) .* A.u[:, 1] end diff --git a/src/integral_inverses.jl b/src/integral_inverses.jl index 33621f1a..71313adb 100644 --- a/src/integral_inverses.jl +++ b/src/integral_inverses.jl @@ -37,13 +37,14 @@ struct LinearInterpolationIntInv{uType, tType, itpType, T, N} <: AbstractIntegralInverseInterpolation{T, N} u::uType t::tType - extrapolate::Bool + extrapolation_down::ExtrapolationType.T + extrapolation_up::ExtrapolationType.T iguesser::Guesser{tType} itp::itpType function LinearInterpolationIntInv(u, t, A) N = get_output_dim(u) new{typeof(u), typeof(t), typeof(A), eltype(u), N}( - u, t, A.extrapolate, Guesser(t), A) + u, t, A.extrapolation_down, A.extrapolation_up, Guesser(t), A) end end @@ -84,13 +85,14 @@ struct ConstantInterpolationIntInv{uType, tType, itpType, T, N} <: AbstractIntegralInverseInterpolation{T, N} u::uType t::tType - extrapolate::Bool + extrapolation_down::ExtrapolationType.T + extrapolation_up::ExtrapolationType.T iguesser::Guesser{tType} itp::itpType function ConstantInterpolationIntInv(u, t, A) N = get_output_dim(u) new{typeof(u), typeof(t), typeof(A), eltype(u), N}( - u, t, A.extrapolate, Guesser(t), A + u, t, A.extrapolation_down, A.extrapolation_up, Guesser(t), A ) end end diff --git a/src/interpolation_methods.jl b/src/interpolation_methods.jl index 0c070f18..209e2541 100644 --- a/src/interpolation_methods.jl +++ b/src/interpolation_methods.jl @@ -13,11 +13,11 @@ function _extrapolate_down(A, t) if extrapolation_down == ExtrapolationType.none throw(DownExtrapolationError()) elseif extrapolation_down == ExtrapolationType.constant - first(A.u) * one(t) + first(A.u) + zero(eltype(A.p.slope)) * t elseif extrapolation_down == ExtrapolationType.linear slope = derivative(A, first(A.t)) first(A.u) + slope * (t - first(A.t)) - else + else # extrapolation_down == ExtrapolationType.extension _interpolate(A, t, A.iguesser) end @@ -28,11 +28,11 @@ function _extrapolate_up(A, t) if extrapolation_up == ExtrapolationType.none throw(UpExtrapolationError()) elseif extrapolation_up == ExtrapolationType.constant - last(A.u) * one(t) + last(A.u) + zero(eltype(A.p.slope)) * t elseif extrapolation_up == ExtrapolationType.linear slope = derivative(A, last(A.t)) last(A.u) + slope * (t - last(A.t)) - else + else # extrapolation_up == ExtrapolationType.extension _interpolate(A, t, A.iguesser) end diff --git a/test/derivative_tests.jl b/test/derivative_tests.jl index 37595a4f..d81f346b 100644 --- a/test/derivative_tests.jl +++ b/test/derivative_tests.jl @@ -9,7 +9,11 @@ using Optim using ForwardDiff function test_derivatives(method; args = [], kwargs = [], name::String) - func = method(args...; kwargs..., extrapolate = true) + kwargs_extrapolation = (method == Curvefit) ? + [:extrapolate => true] : + [:extrapolation_up => ExtrapolationType.extension, + :extrapolation_down => ExtrapolationType.extension] + func = method(args...; kwargs..., kwargs_extrapolation...) (; t) = func trange = collect(range(minimum(t) - 5.0, maximum(t) + 5.0, step = 0.1)) trange_exclude = filter(x -> !in(x, t), trange) @@ -68,8 +72,13 @@ function test_derivatives(method; args = [], kwargs = [], name::String) @test_throws DataInterpolations.DerivativeNotFoundError derivative( func, t[1], 3) func = method(args...) - @test_throws DataInterpolations.ExtrapolationError derivative(func, t[1] - 1.0) - @test_throws DataInterpolations.ExtrapolationError derivative(func, t[end] + 1.0) + if method == Curvefit + @test_throws DataInterpolations.ExtrapolationError derivative(func, t[1] - 1.0) + @test_throws DataInterpolations.ExtrapolationError derivative(func, t[end] + 1.0) + else + @test_throws DataInterpolations.DownExtrapolationError derivative(func, t[1] - 1.0) + @test_throws DataInterpolations.UpExtrapolationError derivative(func, t[end] + 1.0) + end @test_throws DataInterpolations.DerivativeNotFoundError derivative( func, t[1], 3) end @@ -232,7 +241,8 @@ end t = [0.0, 62.25, 109.66, 162.66, 205.8, 252.3] test_derivatives(CubicHermiteSpline; args = [du, u, t], name = "Cubic Hermite Spline") - A = CubicHermiteSpline(du, u, t; extrapolate = true) + A = CubicHermiteSpline(du, u, t; extrapolation_up = ExtrapolationType.extension, + extrapolation_down = ExtrapolationType.extension) @test derivative.(Ref(A), t) ≈ du @test derivative(A, 100.0)≈0.0105409 rtol=1e-5 @test derivative(A, 300.0)≈-0.0806717 rtol=1e-5 @@ -245,7 +255,8 @@ end t = [0.0, 62.25, 109.66, 162.66, 205.8, 252.3] test_derivatives(QuinticHermiteSpline; args = [ddu, du, u, t], name = "Quintic Hermite Spline") - A = QuinticHermiteSpline(ddu, du, u, t; extrapolate = true) + A = QuinticHermiteSpline(ddu, du, u, t; extrapolation_up = ExtrapolationType.extension, + extrapolation_down = ExtrapolationType.extension) @test derivative.(Ref(A), t) ≈ du @test derivative.(Ref(A), t, 2) ≈ ddu @test derivative(A, 100.0)≈0.0103916 rtol=1e-5 @@ -324,7 +335,8 @@ end @testset "Jacobian tests" begin u = rand(5) t = 0:4 - interp = LinearInterpolation(u, t, extrapolate = true) + interp = LinearInterpolation(u, t, extrapolation_up = ExtrapolationType.extension, + extrapolation_down = ExtrapolationType.extension) grad1 = ForwardDiff.derivative(interp, 2.4) myvec = rand(20) .* 4.0 diff --git a/test/integral_inverse_tests.jl b/test/integral_inverse_tests.jl index fd83be67..0e9e4cf2 100644 --- a/test/integral_inverse_tests.jl +++ b/test/integral_inverse_tests.jl @@ -3,7 +3,8 @@ using DataInterpolations: integral, derivative, invert_integral using FiniteDifferences function test_integral_inverses(method; args = [], kwargs = []) - A = method(args...; kwargs..., extrapolate = true) + A = method(args...; kwargs..., extrapolation_down = ExtrapolationType.extension, + extrapolation_up = ExtrapolationType.extension) @test hasfield(typeof(A), :I) A_intinv = invert_integral(A) @test A_intinv isa DataInterpolations.AbstractIntegralInverseInterpolation diff --git a/test/integral_tests.jl b/test/integral_tests.jl index 3d59a6c0..142e0d1f 100644 --- a/test/integral_tests.jl +++ b/test/integral_tests.jl @@ -6,7 +6,8 @@ using RegularizationTools using StableRNGs function test_integral(method; args = [], kwargs = [], name::String) - func = method(args...; kwargs..., extrapolate = true) + func = method(args...; kwargs..., extrapolation_up = ExtrapolationType.extension, + extrapolation_down = ExtrapolationType.extension) (; t) = func t1 = minimum(t) t2 = maximum(t) @@ -48,10 +49,10 @@ function test_integral(method; args = [], kwargs = [], name::String) @test isapprox(qint, aint, atol = 1e-6, rtol = 1e-8) end func = method(args...; kwargs...) - @test_throws DataInterpolations.ExtrapolationError integral(func, t[1] - 1.0) - @test_throws DataInterpolations.ExtrapolationError integral(func, t[end] + 1.0) - @test_throws DataInterpolations.ExtrapolationError integral(func, t[1] - 1.0, t[2]) - @test_throws DataInterpolations.ExtrapolationError integral(func, t[1], t[end] + 1.0) + @test_throws DataInterpolations.DownExtrapolationError integral(func, t[1] - 1.0) + @test_throws DataInterpolations.UpExtrapolationError integral(func, t[end] + 1.0) + @test_throws DataInterpolations.DownExtrapolationError integral(func, t[1] - 1.0, t[2]) + @test_throws DataInterpolations.UpExtrapolationError integral(func, t[1], t[end] + 1.0) end @testset "LinearInterpolation" begin diff --git a/test/interpolation_tests.jl b/test/interpolation_tests.jl index 832819f3..041507f7 100644 --- a/test/interpolation_tests.jl +++ b/test/interpolation_tests.jl @@ -139,10 +139,7 @@ end R32 = Int32(1) // Int32(1) R64 = 1 // 1 for A in Any[A1, A2, A3, A4] - @show A.u - @show A.t @test @inferred(A(F32)) === A(F32) - println("foo") @test @inferred(A(F64)) === A(F64) @test @inferred(A(I32)) === A(I32) @test @inferred(A(I64)) === A(I64) @@ -393,8 +390,8 @@ end @test A(-1.0) ≈ -5.0 @test A(11.0) ≈ -3.924742268041234 A = AkimaInterpolation(u, t) - @test_throws DataInterpolations.UpExtrapolationError A(-1.0) - @test_throws DataInterpolations.DownExtrapolationError A(11.0) + @test_throws DataInterpolations.DownExtrapolationError A(-1.0) + @test_throws DataInterpolations.UpExtrapolationError A(11.0) end @testset "ConstantInterpolation" begin @@ -877,11 +874,10 @@ end @test vs ≈ us # Test extrapolation - A = Curvefit(u, t, model, p0, LBFGS(); extrapolation_up = ExtrapolationType.extension, - extrapolation_down = ExtrapolationType.extension) + A = Curvefit(u, t, model, p0, LBFGS(); extrapolate = true) @test A(15.0) == model(15.0, A.pmin) A = Curvefit(u, t, model, p0, LBFGS()) - @test_throws DataInterpolations.UpExtrapolationError A(15.0) + @test_throws DataInterpolations.ExtrapolationError A(15.0) end @testset "Type of vector returned" begin diff --git a/test/regularization.jl b/test/regularization.jl index fcf7580d..3e6579cf 100644 --- a/test/regularization.jl +++ b/test/regularization.jl @@ -180,10 +180,11 @@ end end @testset "Extrapolation" begin - A = RegularizationSmooth(uₒ, tₒ; alg = :fixed, extrapolate = true) + A = RegularizationSmooth( + uₒ, tₒ; alg = :fixed, extrapolation_up = ExtrapolationType.extension) @test A(10.0) == A.Aitp(10.0) A = RegularizationSmooth(uₒ, tₒ; alg = :fixed) - @test_throws DataInterpolations.ExtrapolationError A(10.0) + @test_throws DataInterpolations.UpExtrapolationError A(10.0) end @testset "Type inference" begin diff --git a/test/zygote_tests.jl b/test/zygote_tests.jl index 7af735af..6476ee0f 100644 --- a/test/zygote_tests.jl +++ b/test/zygote_tests.jl @@ -3,7 +3,9 @@ using ForwardDiff using Zygote function test_zygote(method, u, t; args = [], args_after = [], kwargs = [], name::String) - func = method(args..., u, t, args_after...; kwargs..., extrapolate = true) + func = method(args..., u, t, args_after...; kwargs..., + extrapolation_up = ExtrapolationType.extension, + extrapolation_down = ExtrapolationType.extension) trange = collect(range(minimum(t) - 5.0, maximum(t) + 5.0, step = 0.1)) trange_exclude = filter(x -> !in(x, t), trange) @testset "$name, derivatives w.r.t. input" begin @@ -19,7 +21,9 @@ function test_zygote(method, u, t; args = [], args_after = [], kwargs = [], name [LagrangeInterpolation, BSplineInterpolation, BSplineApprox, QuadraticSpline] @testset "$name, derivatives w.r.t. u" begin function f(u) - A = method(args..., u, t, args_after...; kwargs..., extrapolate = true) + A = method(args..., u, t, args_after...; kwargs..., + extrapolation_up = ExtrapolationType.extension, + extrapolation_down = ExtrapolationType.extension) out = if u isa AbstractVector{<:Real} zero(eltype(u)) elseif u isa AbstractMatrix From 3b3b6267a7afb55b608b054daea84ef313f38866 Mon Sep 17 00:00:00 2001 From: Bart de Koning Date: Fri, 15 Nov 2024 09:51:13 +0100 Subject: [PATCH 08/14] Add docs page --- docs/make.jl | 3 +- docs/src/extrapolation_methods.md | 56 +++++++++++++++++++++++++++++++ docs/src/interface.md | 4 +-- src/interpolation_methods.jl | 6 ++-- 4 files changed, 64 insertions(+), 5 deletions(-) create mode 100644 docs/src/extrapolation_methods.md diff --git a/docs/make.jl b/docs/make.jl index 6438f482..a6b062b5 100644 --- a/docs/make.jl +++ b/docs/make.jl @@ -12,7 +12,8 @@ makedocs(modules = [DataInterpolations], linkcheck = true, format = Documenter.HTML(assets = ["assets/favicon.ico"], canonical = "https://docs.sciml.ai/DataInterpolations/stable/"), - pages = ["index.md", "Methods" => "methods.md", + pages = ["index.md", "Interpolation methods" => "methods.md", + "Extrapolation methods" => "extrapolation_methods.md", "Interface" => "interface.md", "Using with Symbolics/ModelingToolkit" => "symbolics.md", "Manual" => "manual.md", "Inverting Integrals" => "inverting_integrals.md"]) diff --git a/docs/src/extrapolation_methods.md b/docs/src/extrapolation_methods.md new file mode 100644 index 00000000..ad431027 --- /dev/null +++ b/docs/src/extrapolation_methods.md @@ -0,0 +1,56 @@ +# Extrapolation methods + +We will use the following interpolation to demonstrate the various extrapolation methods. + +```@example tutorial +using DataInterpolations, Plots + +u = [0.86, 0.65, 0.44, 0.76, 0.73] +t = [0.0022, 0.68, 1.41, 2.22, 2.46] +t_eval_down = range(-1, first(t), length = 25) +t_eval_up = range(last(t), 3.5, length = 25) +A = QuadraticSpline(u, t) +plot(A) +``` + +Extrapolation behavior can be set for `t` beyond the data in the negative and positive direction separately with the `extrapolation_down` and `extrapolation_up` keywords of the interpolation constructors respectively. + +## `ExtrapolationType.none` + +This extrapolation type will throw an error when the input `t` is beyond the data in the specified direction. + +## `ExtrapolationType.constant` + +This extrapolation type extends the interpolation with the boundary values of the data `u`. + +```@example tutorial +A = QuadraticSpline(u, t; extrapolation_down = ExtrapolationType.constant, + extrapolation_up = ExtrapolationType.constant) +plot(A) +plot!(t_eval_down, A.(t_eval_down); label = "extrapolation down") +plot!(t_eval_up, A.(t_eval_up); label = "extrapolation up") +``` + +## `ExtrapolationType.linear` + +This extrapolation type extends the interpolation with a linear continuation of the interpolation, making it $C^1$ smooth at the data boundaries. + +```@example tutorial +A = QuadraticSpline(u, t; extrapolation_down = ExtrapolationType.linear, + extrapolation_up = ExtrapolationType.linear) +plot(A) +plot!(t_eval_down, A.(t_eval_down); label = "extrapolation down") +plot!(t_eval_up, A.(t_eval_up); label = "extrapolation up") +``` + +## `ExtrapolationType.extension` + +This extrapolation type extends the interpolation with a continuation of the expression for the interpolation at the boundary intervals for maximum smoothness. + +```@example tutorial +A = QuadraticSpline(u, t; extrapolation_down = ExtrapolationType.extension, + extrapolation_up = ExtrapolationType.extension) +plot(A) +plot!(t_eval_down, A.(t_eval_down); label = "extrapolation down") +plot!(t_eval_up, A.(t_eval_up); label = "extrapolation up") +``` diff --git a/docs/src/interface.md b/docs/src/interface.md index cfc9ed0b..05dd0760 100644 --- a/docs/src/interface.md +++ b/docs/src/interface.md @@ -17,7 +17,7 @@ t = [0.0, 62.25, 109.66, 162.66, 205.8, 252.3] All interpolation methods return an object from which we can compute the value of the dependent variable at any time point. -We will use the `CubicSpline` method for demonstration, but the API is the same for all the methods. We can also pass the `extrapolate = true` keyword if we want to allow the interpolation to go beyond the range of the timepoints. The default value is `extrapolate = false`. +We will use the `CubicSpline` method for demonstration, but the API is the same for all the methods. We can also pass the `extrapolation_up = ExtrapolationType.extension` keyword if we want to allow the interpolation to go beyond the range of the timepoints in the positive `t` direction. The default value is `extrapolation_up = ExtrapolationType.none`. For more information on extrapolation see [Extrapolation methods](extrapolation_methods.md). ```@example interface A1 = CubicSpline(u, t) @@ -25,7 +25,7 @@ A1 = CubicSpline(u, t) # For interpolation do, A(t) A1(100.0) -A2 = CubicSpline(u, t; extrapolate = true) +A2 = CubicSpline(u, t; extrapolation_up = ExtrapolationType.extension) # Extrapolation A2(300.0) diff --git a/src/interpolation_methods.jl b/src/interpolation_methods.jl index 209e2541..d7d4dce0 100644 --- a/src/interpolation_methods.jl +++ b/src/interpolation_methods.jl @@ -13,7 +13,8 @@ function _extrapolate_down(A, t) if extrapolation_down == ExtrapolationType.none throw(DownExtrapolationError()) elseif extrapolation_down == ExtrapolationType.constant - first(A.u) + zero(eltype(A.p.slope)) * t + slope = derivative(A, first(A.t)) + first(A.u) + slope * zero(t) elseif extrapolation_down == ExtrapolationType.linear slope = derivative(A, first(A.t)) first(A.u) + slope * (t - first(A.t)) @@ -28,7 +29,8 @@ function _extrapolate_up(A, t) if extrapolation_up == ExtrapolationType.none throw(UpExtrapolationError()) elseif extrapolation_up == ExtrapolationType.constant - last(A.u) + zero(eltype(A.p.slope)) * t + slope = derivative(A, last(A.t)) + last(A.u) + slope * zero(t) elseif extrapolation_up == ExtrapolationType.linear slope = derivative(A, last(A.t)) last(A.u) + slope * (t - last(A.t)) From c6ab856aaa9b9855381ecd7ec73e86ddadbe8f07 Mon Sep 17 00:00:00 2001 From: Bart de Koning Date: Sat, 16 Nov 2024 10:00:51 +0100 Subject: [PATCH 09/14] Add some tests, refactor most of integration --- src/integrals.jl | 143 ++++++++++++++++++++---------------- src/interpolation_utils.jl | 14 +++- test/extrapolation_tests.jl | 84 +++++++++++++++++++++ test/runtests.jl | 1 + 4 files changed, 176 insertions(+), 66 deletions(-) create mode 100644 test/extrapolation_tests.jl diff --git a/src/integrals.jl b/src/integrals.jl index a411ccd8..6c0f97d6 100644 --- a/src/integrals.jl +++ b/src/integrals.jl @@ -4,89 +4,112 @@ end function integral(A::AbstractInterpolation, t1::Number, t2::Number) !hasfield(typeof(A), :I) && throw(IntegralNotFoundError()) + + if t1 == t2 + # If the integration interval is trivial then the result is 0 + return zero(eltype(A.I)) + elseif t1 > t2 + # Make sure that t1 < t2 + return -integral(A, t2, t1) + end + # the index less than or equal to t1 idx1 = get_idx(A, t1, 0) # the index less than t2 idx2 = get_idx(A, t2, 0; idx_shift = -1, side = :first) - if A.cache_parameters - total = A.I[idx2] - A.I[idx1] - return if t1 == t2 - zero(total) - else - total += __integral(A, idx1, A.t[idx1]) - total -= __integral(A, idx1, t1) - total += __integral(A, idx2, t2) - total -= __integral(A, idx2, A.t[idx2]) - total + total = zero(eltype(A.I)) + + # Lower potentially incomplete interval + if t1 < first(A.t) + + if t2 < first(A.t) + # If interval is entirely below data + return _extrapolate_integral_down(A, t2) - extrapolate_integral_down(A.t1) end + + idx1 -= 1 # Make sure lowest complete interval is included + total += _extrapolate_integral_down(A, t1) else - total = zero(eltype(A.u)) - for idx in idx1:idx2 - lt1 = idx == idx1 ? t1 : A.t[idx] - lt2 = idx == idx2 ? t2 : A.t[idx + 1] - total += __integral(A, idx, lt2) - __integral(A, idx, lt1) + total += _integral(A, idx1, t1, A.t[idx1 + 1]) + end + + # Upper potentially incomplete interval + if t2 > last(A.t) + + if t1 > last(A.t) + # If interval is entirely above data + return _extrapolate_integral_up(A, t2) - extrapolate_integral_up(A.t, t1) end - total + + idx2 += 1 # Make sure highest complete interval is included + total += _extrapolate_integral_up(A, t2) + else + total += _integral(A, idx2, A.t[idx2], t2) end -end -function __integral(A::AbstractInterpolation, idx::Number, t::Number) - if t < first(A.t) - _extrapolate_integral_down(A, idx, t) - elseif t > last(A.t) - _extrapolate_integral_up(A, idx, t) + if idx1 == idx2 + return _integral(A, idx1, t1, t2) + end + + # Complete intervals + if A.cache_parameters + total += A.I[idx2] - A.I[idx1 + 1] else - _integral(A, idx, t) + for idx in (idx1 + 1):(idx2 - 1) + total += _integral(A, idx, A.t[idx], A.t[idx + 1]) + end end + + return total end -function _extrapolate_integral_down(A, idx, t) +function _extrapolate_integral_down(A, t) (; extrapolation_down) = A if extrapolation_down == ExtrapolationType.none throw(DownExtrapolationError()) elseif extrapolation_down == ExtrapolationType.constant - first(A.u) * (t - first(A.t)) + first(A.u) * (first(A.t) - t) elseif extrapolation_down == ExtrapolationType.linear slope = derivative(A, first(A.t)) - Δt = t - first(A.t) - (first(A.u) + slope * Δt / 2) * Δt + Δt = first(A.t) - t + (first(A.u) - slope * Δt / 2) * Δt elseif extrapolation_down == ExtrapolationType.extension - _integral(A, idx, t) + _integral(A, 1, t, first(A.t)) end end -function _extrapolate_integral_up(A, idx, t) +function _extrapolate_integral_up(A, t) (; extrapolation_up) = A if extrapolation_up == ExtrapolationType.none throw(UpExtrapolationError()) elseif extrapolation_up == ExtrapolationType.constant - integral(A, A.t[end - 1], A.t[end]) + last(A.u) * (t - last(A.t)) + last(A.u) * (t - last(A.t)) elseif extrapolation_up == ExtrapolationType.linear slope = derivative(A, last(A.t)) Δt = t - last(A.t) - integral(A, A.t[end - 1], A.t[end]) + (last(A.u) + slope * Δt / 2) * Δt + (last(A.u) + slope * Δt / 2) * Δt elseif extrapolation_up == ExtrapolationType.extension - _integral(A, idx, t) + _integral(A, length(A.t) - 1, last(A.t), t) end end function _integral(A::LinearInterpolation{<:AbstractVector{<:Number}}, - idx::Number, - t::Number) - Δt = t - A.t[idx] + idx::Number, t1::Number, t2::Number) slope = get_parameters(A, idx) - Δt * (A.u[idx] + slope * Δt / 2) + u_mean = A.u[idx] + slope * ((t1 + t2)/2 - A.t[idx]) + u_mean * (t2 - t1) end function _integral( - A::ConstantInterpolation{<:AbstractVector{<:Number}}, idx::Number, t::Number) + A::ConstantInterpolation{<:AbstractVector{<:Number}}, idx::Number, t1::Number, t2::Number) + Δt = t2 - t1 if A.dir === :left # :left means that value to the left is used for interpolation - return A.u[idx] * t + return A.u[idx] * Δt else # :right means that value to the right is used for interpolation - return A.u[idx + 1] * t + return A.u[idx + 1] * Δt end end @@ -107,30 +130,27 @@ function _integral(A::QuadraticInterpolation{<:AbstractVector{<:Number}}, return Iu₀ + Iu₁ + Iu₂ end -function _integral(A::QuadraticSpline{<:AbstractVector{<:Number}}, idx::Number, t::Number) +function _integral(A::QuadraticSpline{<:AbstractVector{<:Number}}, idx::Number, t1::Number, t2::Number) α, β = get_parameters(A, idx) uᵢ = A.u[idx] - Δt = t - A.t[idx] - Δt_full = A.t[idx + 1] - A.t[idx] - Δt * (α * Δt^2 / (3Δt_full^2) + β * Δt / (2Δt_full) + uᵢ) + tᵢ = A.t[idx] + t1_rel = t1 - tᵢ + t2_rel = t2 - tᵢ + Δt = t2 - t1 + Δt * (α * (t2_rel^2 + t1_rel * t2_rel + t1_rel^2) / 3 + β * (t2_rel + t1_rel) / 2 + uᵢ) end -function _integral(A::CubicSpline{<:AbstractVector{<:Number}}, idx::Number, t::Number) - Δt₁sq = (t - A.t[idx])^2 / 2 - Δt₂sq = (A.t[idx + 1] - t)^2 / 2 - II = (-A.z[idx] * Δt₂sq^2 + A.z[idx + 1] * Δt₁sq^2) / (6A.h[idx + 1]) +function _integral(A::CubicSpline{<:AbstractVector{<:Number}}, idx::Number, t1::Number, t2::Number) + tᵢ = A.t[idx] + tᵢ₊₁ = A.t[idx + 1] c₁, c₂ = get_parameters(A, idx) - IC = c₁ * Δt₁sq - ID = -c₂ * Δt₂sq - II + IC + ID + integrate_cubic_polynomial(t1, t2, tᵢ, 0, c₁, 0, A.z[idx + 1] / (6A.h[idx + 1])) + + integrate_cubic_polynomial(t1, t2, tᵢ₊₁, 0, -c₂, 0, -A.z[idx] / (6A.h[idx + 1])) end function _integral(A::AkimaInterpolation{<:AbstractVector{<:Number}}, - idx::Number, - t::Number) - t1 = A.t[idx] - A.u[idx] * (t - t1) + A.b[idx] * ((t - t1)^2 / 2) + A.c[idx] * ((t - t1)^3 / 3) + - A.d[idx] * ((t - t1)^4 / 4) + idx::Number, t1::Number, t2::Number) + integrate_cubic_polynomial(t1, t2, A.t[idx], A.u[idx], A.b[idx], A.c[idx], A.d[idx]) end _integral(A::LagrangeInterpolation, idx::Number, t::Number) = throw(IntegralNotFoundError()) @@ -139,15 +159,12 @@ _integral(A::BSplineApprox, idx::Number, t::Number) = throw(IntegralNotFoundErro # Cubic Hermite Spline function _integral( - A::CubicHermiteSpline{<:AbstractVector{<:Number}}, idx::Number, t::Number) - Δt₀ = t - A.t[idx] - Δt₁ = t - A.t[idx + 1] - out = Δt₀ * (A.u[idx] + Δt₀ * A.du[idx] / 2) + A::CubicHermiteSpline{<:AbstractVector{<:Number}}, idx::Number, t1::Number, t2::Number) c₁, c₂ = get_parameters(A, idx) - p = c₁ + Δt₁ * c₂ - dp = c₂ - out += Δt₀^3 / 3 * (p - dp * Δt₀ / 4) - out + tᵢ = A.t[idx] + tᵢ₊₁ = A.t[idx + 1] + c = c₁ - c₂ * (tᵢ₊₁ - tᵢ) + integrate_cubic_polynomial(t1, t2, tᵢ, A.u[idx], A.du[idx], c, c₂) end # Quintic Hermite Spline diff --git a/src/interpolation_utils.jl b/src/interpolation_utils.jl index 2d1c6432..eac4862e 100644 --- a/src/interpolation_utils.jl +++ b/src/interpolation_utils.jl @@ -190,9 +190,8 @@ function get_idx(A::AbstractInterpolation, t, iguess::Union{<:Integer, Guesser}; end function cumulative_integral(A, cache_parameters) - if cache_parameters && hasmethod(_integral, Tuple{typeof(A), Number, Number}) - integral_values = [_integral(A, idx, A.t[idx + 1]) - _integral(A, idx, A.t[idx]) - for idx in 1:(length(A.t) - 1)] + if cache_parameters && hasmethod(_integral, Tuple{typeof(A), Number, Number, Number}) + integral_values = _integral.(Ref(A), 1:(length(A.t) - 1), A.t[1:end-1], A.t[2:end]) pushfirst!(integral_values, zero(first(integral_values))) cumsum(integral_values) else @@ -282,3 +281,12 @@ function du_PCHIP(u, t) return _du.(eachindex(t)) end + +function integrate_cubic_polynomial(t1, t2, offset, a, b, c, d) + t1_rel = t1 - offset + t2_rel = t2 - offset + t_sum = t1_rel + t2_rel + t_sq_sum = t1_rel^2 + t2_rel^2 + Δt = t2 - t1 + Δt * (a + t_sum * (b / 2 + d * t_sq_sum / 4) + c * (t_sq_sum + t1_rel * t2_rel) / 3) +end \ No newline at end of file diff --git a/test/extrapolation_tests.jl b/test/extrapolation_tests.jl new file mode 100644 index 00000000..a93ef127 --- /dev/null +++ b/test/extrapolation_tests.jl @@ -0,0 +1,84 @@ +using DataInterpolations + +@testset "Linear Interpolation" begin + u = [1.0, 2.0] + t = [1.0, 2.0] + + A = LinearInterpolation(u, t; extrapolation_down = ExtrapolationType.constant) + t_eval = 0.0 + @test A(t_eval) == 1.0 + @test DataInterpolations.derivative(A, t_eval) == 0.0 + @test DataInterpolations.derivative(A, t_eval, 2) == 0.0 + @test DataInterpolations.integral(A, t_eval) == -1.0 + t_eval = 3.0 + @test_throws DataInterpolations.UpExtrapolationError A(t_eval) + @test_throws DataInterpolations.UpExtrapolationError DataInterpolations.derivative(A, t_eval) + @test_throws DataInterpolations.UpExtrapolationError DataInterpolations.derivative(A, t_eval, 2) + @test_throws DataInterpolations.UpExtrapolationError DataInterpolations.integral(A, t_eval) + + A = LinearInterpolation(u, t; extrapolation_up = ExtrapolationType.constant) + t_eval = 3.0 + @test A(t_eval) == 2.0 + @test DataInterpolations.derivative(A, t_eval) == 0.0 + @test DataInterpolations.derivative(A, t_eval, 2) == 0.0 + @test DataInterpolations.integral(A, t_eval) == 3.5 + t_eval = 0.0 + @test_throws DataInterpolations.DownExtrapolationError A(t_eval) + @test_throws DataInterpolations.DownExtrapolationError DataInterpolations.derivative(A, t_eval) + @test_throws DataInterpolations.DownExtrapolationError DataInterpolations.derivative(A, t_eval, 2) + @test_throws DataInterpolations.DownExtrapolationError DataInterpolations.integral(A, t_eval) + + for extrapolation_type in [ExtrapolationType.linear, ExtrapolationType.extension] + A = LinearInterpolation(u, t; extrapolation_down = extrapolation_type) + t_eval = 0.0 + @test A(t_eval) == 0.0 + @test DataInterpolations.derivative(A, t_eval) == 1.0 + @test DataInterpolations.derivative(A, t_eval, 2) == 0.0 + @test DataInterpolations.integral(A, t_eval) == -0.5 + t_eval = 3.0 + @test_throws DataInterpolations.UpExtrapolationError A(t_eval) + @test_throws DataInterpolations.UpExtrapolationError DataInterpolations.derivative(A, t_eval) + @test_throws DataInterpolations.UpExtrapolationError DataInterpolations.derivative(A, t_eval, 2) + @test_throws DataInterpolations.UpExtrapolationError DataInterpolations.integral(A, t_eval) + + A = LinearInterpolation(u, t; extrapolation_up = extrapolation_type) + t_eval = 3.0 + @test A(t_eval) == 3.0 + @test DataInterpolations.derivative(A, t_eval) == 1.0 + @test DataInterpolations.derivative(A, t_eval, 2) == 0.0 + @test DataInterpolations.integral(A, t_eval) == 4.0 + t_eval = 0.0 + @test_throws DataInterpolations.DownExtrapolationError A(t_eval) + @test_throws DataInterpolations.DownExtrapolationError DataInterpolations.derivative(A, t_eval) + @test_throws DataInterpolations.DownExtrapolationError DataInterpolations.derivative(A, t_eval, 2) + @test_throws DataInterpolations.DownExtrapolationError DataInterpolations.integral(A, t_eval) + end +end + +@testset "Quadratic Interpolation" begin + u = [1.0, 3.0, 2.0] + t = 1:3 + f = t -> (-3t^2 + 13t - 8)/2 + + A = QuadraticInterpolation(u, t; extrapolation_down = ExtrapolationType.constant) + t_eval = 0.0 + @test A(t_eval) ≈ 1.0 + @test DataInterpolations.derivative(A, t_eval) == 0.0 + @test DataInterpolations.derivative(A, t_eval, 2) == 0.0 + @test DataInterpolations.integral(A, t_eval) ≈ -1.0 + t_eval = 4.0 + @test_throws DataInterpolations.UpExtrapolationError DataInterpolations.derivative(A, t_eval) + @test_throws DataInterpolations.UpExtrapolationError DataInterpolations.derivative(A, t_eval, 2) + @test_throws DataInterpolations.UpExtrapolationError DataInterpolations.integral(A, t_eval) + + A = QuadraticInterpolation(u, t; extrapolation_up = ExtrapolationType.constant) + t_eval = 4.0 + @test A(t_eval) ≈ 2.0 + @test DataInterpolations.derivative(A, t_eval) == 0.0 + @test DataInterpolations.derivative(A, t_eval, 2) == 0.0 + @test DataInterpolations.integral(A, t[end], t_eval) ≈ 2.0 + t_eval = 0.0 + @test_throws DataInterpolations.DownExtrapolationError DataInterpolations.derivative(A, t_eval) + @test_throws DataInterpolations.DownExtrapolationError DataInterpolations.derivative(A, t_eval, 2) + @test_throws DataInterpolations.DownExtrapolationError DataInterpolations.integral(A, t_eval) +end \ No newline at end of file diff --git a/test/runtests.jl b/test/runtests.jl index 80080a75..774ad497 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -7,6 +7,7 @@ using SafeTestsets @safetestset "Derivative Tests" include("derivative_tests.jl") @safetestset "Integral Tests" include("integral_tests.jl") @safetestset "Integral Inverse Tests" include("integral_inverse_tests.jl") +@safetestset "Extrapolation" include("extrapolation_tests.jl") @safetestset "Online Tests" include("online_tests.jl") @safetestset "Regularization Smoothing" include("regularization.jl") @safetestset "Show methods" include("show.jl") From a562bf7105a3287b99785e3f0cb9a193a40c85aa Mon Sep 17 00:00:00 2001 From: Bart de Koning Date: Sun, 17 Nov 2024 17:00:38 +0100 Subject: [PATCH 10/14] Complete tests --- test/extrapolation_tests.jl | 130 ++++++++++++++++++------------------ 1 file changed, 65 insertions(+), 65 deletions(-) diff --git a/test/extrapolation_tests.jl b/test/extrapolation_tests.jl index f282fa10..5c934d83 100644 --- a/test/extrapolation_tests.jl +++ b/test/extrapolation_tests.jl @@ -1,40 +1,45 @@ using DataInterpolations +function test_extrapolation_errors(method, u, t) + A = method(u, t) + @test A.extrapolation_up == ExtrapolationType.none + @test A.extrapolation_down == ExtrapolationType.none + for (error_type, t_eval) in zip( + (DataInterpolations.DownExtrapolationError, + DataInterpolations.UpExtrapolationError), + (first(t) - 1, last(t) + 1)) + @test_throws error_type A(t_eval) + @test_throws error_type DataInterpolations.derivative( + A, t_eval) + @test_throws error_type DataInterpolations.derivative( + A, t_eval, 2) + @test_throws error_type DataInterpolations.integral( + A, t_eval) + end +end + +function test_constant_extrapolation(method, u, t) + A = method(u, t; extrapolation_down = ExtrapolationType.constant, + extrapolation_up = ExtrapolationType.constant) + t_lower = first(t) - 1 + t_upper = last(t) + 1 + @test A(t_lower) == first(u) + @test A(t_upper) == last(u) + @test DataInterpolations.derivative(A, t_lower) == 0 + @test DataInterpolations.derivative(A, t_upper) == 0 + @test DataInterpolations.integral(A, t_lower, first(t)) ≈ first(u) * (first(t) - t_lower) + @test DataInterpolations.integral(A, last(t), t_upper) ≈ last(u) * (t_upper - last(t)) +end + @testset "Linear Interpolation" begin u = [1.0, 2.0] t = [1.0, 2.0] - A = LinearInterpolation(u, t; extrapolation_down = ExtrapolationType.constant) - t_eval = 0.0 - @test A(t_eval) == 1.0 - @test DataInterpolations.derivative(A, t_eval) == 0.0 - @test DataInterpolations.derivative(A, t_eval, 2) == 0.0 - @test DataInterpolations.integral(A, t_eval) == -1.0 - t_eval = 3.0 - @test_throws DataInterpolations.UpExtrapolationError A(t_eval) - @test_throws DataInterpolations.UpExtrapolationError DataInterpolations.derivative( - A, t_eval) - @test_throws DataInterpolations.UpExtrapolationError DataInterpolations.derivative( - A, t_eval, 2) - @test_throws DataInterpolations.UpExtrapolationError DataInterpolations.integral( - A, t_eval) - - A = LinearInterpolation(u, t; extrapolation_up = ExtrapolationType.constant) - t_eval = 3.0 - @test A(t_eval) == 2.0 - @test DataInterpolations.derivative(A, t_eval) == 0.0 - @test DataInterpolations.derivative(A, t_eval, 2) == 0.0 - @test DataInterpolations.integral(A, t_eval) == 3.5 - t_eval = 0.0 - @test_throws DataInterpolations.DownExtrapolationError A(t_eval) - @test_throws DataInterpolations.DownExtrapolationError DataInterpolations.derivative( - A, t_eval) - @test_throws DataInterpolations.DownExtrapolationError DataInterpolations.derivative( - A, t_eval, 2) - @test_throws DataInterpolations.DownExtrapolationError DataInterpolations.integral( - A, t_eval) + test_extrapolation_errors(LinearInterpolation, u, t) + test_constant_extrapolation(LinearInterpolation, u, t) for extrapolation_type in [ExtrapolationType.linear, ExtrapolationType.extension] + # Down extrapolation A = LinearInterpolation(u, t; extrapolation_down = extrapolation_type) t_eval = 0.0 @test A(t_eval) == 0.0 @@ -42,14 +47,8 @@ using DataInterpolations @test DataInterpolations.derivative(A, t_eval, 2) == 0.0 @test DataInterpolations.integral(A, t_eval) == -0.5 t_eval = 3.0 - @test_throws DataInterpolations.UpExtrapolationError A(t_eval) - @test_throws DataInterpolations.UpExtrapolationError DataInterpolations.derivative( - A, t_eval) - @test_throws DataInterpolations.UpExtrapolationError DataInterpolations.derivative( - A, t_eval, 2) - @test_throws DataInterpolations.UpExtrapolationError DataInterpolations.integral( - A, t_eval) + # Up extrapolation A = LinearInterpolation(u, t; extrapolation_up = extrapolation_type) t_eval = 3.0 @test A(t_eval) == 3.0 @@ -57,46 +56,47 @@ using DataInterpolations @test DataInterpolations.derivative(A, t_eval, 2) == 0.0 @test DataInterpolations.integral(A, t_eval) == 4.0 t_eval = 0.0 - @test_throws DataInterpolations.DownExtrapolationError A(t_eval) - @test_throws DataInterpolations.DownExtrapolationError DataInterpolations.derivative( - A, t_eval) - @test_throws DataInterpolations.DownExtrapolationError DataInterpolations.derivative( - A, t_eval, 2) - @test_throws DataInterpolations.DownExtrapolationError DataInterpolations.integral( - A, t_eval) end end @testset "Quadratic Interpolation" begin u = [1.0, 3.0, 2.0] t = 1:3 - f = t -> (-3t^2 + 13t - 8) / 2 - A = QuadraticInterpolation(u, t; extrapolation_down = ExtrapolationType.constant) + test_extrapolation_errors(QuadraticInterpolation, u, t) + test_constant_extrapolation(LinearInterpolation, u, t) + + # Linear down extrapolation + A = QuadraticInterpolation(u, t; extrapolation_down = ExtrapolationType.linear) t_eval = 0.0 - @test A(t_eval) ≈ 1.0 - @test DataInterpolations.derivative(A, t_eval) == 0.0 + @test A(t_eval) ≈ -2.5 + @test DataInterpolations.derivative(A, t_eval) == 3.5 @test DataInterpolations.derivative(A, t_eval, 2) == 0.0 - @test DataInterpolations.integral(A, t_eval) ≈ -1.0 - t_eval = 4.0 - @test_throws DataInterpolations.UpExtrapolationError DataInterpolations.derivative( - A, t_eval) - @test_throws DataInterpolations.UpExtrapolationError DataInterpolations.derivative( - A, t_eval, 2) - @test_throws DataInterpolations.UpExtrapolationError DataInterpolations.integral( - A, t_eval) + @test DataInterpolations.integral(A, t_eval) ≈ 0.75 - A = QuadraticInterpolation(u, t; extrapolation_up = ExtrapolationType.constant) + # Linear up extrapolation + A = QuadraticInterpolation(u, t; extrapolation_up = ExtrapolationType.linear) t_eval = 4.0 - @test A(t_eval) ≈ 2.0 - @test DataInterpolations.derivative(A, t_eval) == 0.0 + @test A(t_eval) ≈ -0.5 + @test DataInterpolations.derivative(A, t_eval) == -2.5 @test DataInterpolations.derivative(A, t_eval, 2) == 0.0 - @test DataInterpolations.integral(A, t[end], t_eval) ≈ 2.0 + @test DataInterpolations.integral(A, t[end], t_eval) ≈ 0.75 + + # Extension down extrapolation + f = t -> (-3t^2 + 13t - 8) / 2 + df = t -> (-6t + 13) / 2 + A = QuadraticInterpolation(u, t; extrapolation_down = ExtrapolationType.extension) t_eval = 0.0 - @test_throws DataInterpolations.DownExtrapolationError DataInterpolations.derivative( - A, t_eval) - @test_throws DataInterpolations.DownExtrapolationError DataInterpolations.derivative( - A, t_eval, 2) - @test_throws DataInterpolations.DownExtrapolationError DataInterpolations.integral( - A, t_eval) + @test A(t_eval) ≈ -4.0 + @test DataInterpolations.derivative(A, t_eval) == df(t_eval) + @test DataInterpolations.derivative(A, t_eval, 2) == -3 + @test DataInterpolations.integral(A, t_eval) ≈ 1.25 + + # Extension up extrapolation + A = QuadraticInterpolation(u, t; extrapolation_up = ExtrapolationType.extension) + t_eval = 4.0 + @test A(t_eval) ≈ -2.0 + @test DataInterpolations.derivative(A, t_eval) == df(t_eval) + @test DataInterpolations.derivative(A, t_eval, 2) == -3 + @test DataInterpolations.integral(A, t_eval) ≈ 5.25 end From d145c4b4a7e6c2faafc54c19740f53592a99a5ee Mon Sep 17 00:00:00 2001 From: Bart de Koning Date: Sun, 17 Nov 2024 17:02:31 +0100 Subject: [PATCH 11/14] Formatting --- test/extrapolation_tests.jl | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/test/extrapolation_tests.jl b/test/extrapolation_tests.jl index 5c934d83..e87fd340 100644 --- a/test/extrapolation_tests.jl +++ b/test/extrapolation_tests.jl @@ -22,12 +22,13 @@ function test_constant_extrapolation(method, u, t) A = method(u, t; extrapolation_down = ExtrapolationType.constant, extrapolation_up = ExtrapolationType.constant) t_lower = first(t) - 1 - t_upper = last(t) + 1 + t_upper = last(t) + 1 @test A(t_lower) == first(u) @test A(t_upper) == last(u) @test DataInterpolations.derivative(A, t_lower) == 0 @test DataInterpolations.derivative(A, t_upper) == 0 - @test DataInterpolations.integral(A, t_lower, first(t)) ≈ first(u) * (first(t) - t_lower) + @test DataInterpolations.integral(A, t_lower, first(t)) ≈ + first(u) * (first(t) - t_lower) @test DataInterpolations.integral(A, last(t), t_upper) ≈ last(u) * (t_upper - last(t)) end From 3c54932735dbae3d9c99de8163ab89cc0c72cad2 Mon Sep 17 00:00:00 2001 From: Bart de Koning Date: Tue, 19 Nov 2024 19:09:18 +0100 Subject: [PATCH 12/14] Comments adressed --- docs/src/extrapolation_methods.md | 11 +- docs/src/interface.md | 4 +- ...ataInterpolationsRegularizationToolsExt.jl | 72 +-- src/DataInterpolations.jl | 16 +- src/derivatives.jl | 20 +- src/integral_inverses.jl | 12 +- src/integrals.jl | 20 +- src/interpolation_caches.jl | 510 ++++++++++-------- src/interpolation_methods.jl | 20 +- src/interpolation_utils.jl | 8 + test/derivative_tests.jl | 13 +- test/extrapolation_tests.jl | 20 +- test/integral_inverse_tests.jl | 3 +- test/integral_tests.jl | 4 +- test/interface.jl | 6 +- test/interpolation_tests.jl | 145 ++--- test/regularization.jl | 2 +- test/zygote_tests.jl | 6 +- 18 files changed, 461 insertions(+), 431 deletions(-) diff --git a/docs/src/extrapolation_methods.md b/docs/src/extrapolation_methods.md index ad431027..5b6286ba 100644 --- a/docs/src/extrapolation_methods.md +++ b/docs/src/extrapolation_methods.md @@ -13,7 +13,7 @@ A = QuadraticSpline(u, t) plot(A) ``` -Extrapolation behavior can be set for `t` beyond the data in the negative and positive direction separately with the `extrapolation_down` and `extrapolation_up` keywords of the interpolation constructors respectively. +Extrapolation behavior can be set left and right of the data simultaneously with the `extension` keyword, or left and right separately with the `extension_left` and `extension_right` keywords respectively. ## `ExtrapolationType.none` @@ -24,8 +24,7 @@ This extrapolation type will throw an error when the input `t` is beyond the dat This extrapolation type extends the interpolation with the boundary values of the data `u`. ```@example tutorial -A = QuadraticSpline(u, t; extrapolation_down = ExtrapolationType.constant, - extrapolation_up = ExtrapolationType.constant) +A = QuadraticSpline(u, t; extrapolation = ExtrapolationType.constant) plot(A) plot!(t_eval_down, A.(t_eval_down); label = "extrapolation down") plot!(t_eval_up, A.(t_eval_up); label = "extrapolation up") @@ -36,8 +35,7 @@ plot!(t_eval_up, A.(t_eval_up); label = "extrapolation up") This extrapolation type extends the interpolation with a linear continuation of the interpolation, making it $C^1$ smooth at the data boundaries. ```@example tutorial -A = QuadraticSpline(u, t; extrapolation_down = ExtrapolationType.linear, - extrapolation_up = ExtrapolationType.linear) +A = QuadraticSpline(u, t; extrapolation = ExtrapolationType.linear) plot(A) plot!(t_eval_down, A.(t_eval_down); label = "extrapolation down") plot!(t_eval_up, A.(t_eval_up); label = "extrapolation up") @@ -48,8 +46,7 @@ plot!(t_eval_up, A.(t_eval_up); label = "extrapolation up") This extrapolation type extends the interpolation with a continuation of the expression for the interpolation at the boundary intervals for maximum smoothness. ```@example tutorial -A = QuadraticSpline(u, t; extrapolation_down = ExtrapolationType.extension, - extrapolation_up = ExtrapolationType.extension) +A = QuadraticSpline(u, t; extrapolation = ExtrapolationType.extension) plot(A) plot!(t_eval_down, A.(t_eval_down); label = "extrapolation down") plot!(t_eval_up, A.(t_eval_up); label = "extrapolation up") diff --git a/docs/src/interface.md b/docs/src/interface.md index 05dd0760..21bfd27a 100644 --- a/docs/src/interface.md +++ b/docs/src/interface.md @@ -17,7 +17,7 @@ t = [0.0, 62.25, 109.66, 162.66, 205.8, 252.3] All interpolation methods return an object from which we can compute the value of the dependent variable at any time point. -We will use the `CubicSpline` method for demonstration, but the API is the same for all the methods. We can also pass the `extrapolation_up = ExtrapolationType.extension` keyword if we want to allow the interpolation to go beyond the range of the timepoints in the positive `t` direction. The default value is `extrapolation_up = ExtrapolationType.none`. For more information on extrapolation see [Extrapolation methods](extrapolation_methods.md). +We will use the `CubicSpline` method for demonstration, but the API is the same for all the methods. We can also pass the `extrapolation = ExtrapolationType.extension` keyword if we want to allow the interpolation to go beyond the range of the timepoints in the positive `t` direction. The default value is `extrapolation = ExtrapolationType.none`. For more information on extrapolation see [Extrapolation methods](extrapolation_methods.md). ```@example interface A1 = CubicSpline(u, t) @@ -25,7 +25,7 @@ A1 = CubicSpline(u, t) # For interpolation do, A(t) A1(100.0) -A2 = CubicSpline(u, t; extrapolation_up = ExtrapolationType.extension) +A2 = CubicSpline(u, t; extrapolation = ExtrapolationType.extension) # Extrapolation A2(300.0) diff --git a/ext/DataInterpolationsRegularizationToolsExt.jl b/ext/DataInterpolationsRegularizationToolsExt.jl index 6856996d..84693c49 100644 --- a/ext/DataInterpolationsRegularizationToolsExt.jl +++ b/ext/DataInterpolationsRegularizationToolsExt.jl @@ -70,16 +70,16 @@ A = RegularizationSmooth(u, t, t̂, wls, wr, d; λ = 1.0, alg = :gcv_svd) function RegularizationSmooth(u::AbstractVector, t::AbstractVector, t̂::AbstractVector, wls::AbstractVector, wr::AbstractVector, d::Int = 2; λ::Real = 1.0, alg::Symbol = :gcv_svd, - extrapolation_down::ExtrapolationType.T = ExtrapolationType.none, - extrapolation_up::ExtrapolationType.T = ExtrapolationType.none) + extrapolation_left::ExtrapolationType.T = ExtrapolationType.none, + extrapolation_right::ExtrapolationType.T = ExtrapolationType.none) u, t = munge_data(u, t) M = _mapping_matrix(t̂, t) Wls½ = LA.diagm(sqrt.(wls)) Wr½ = LA.diagm(sqrt.(wr)) û, λ, Aitp = _reg_smooth_solve( - u, t̂, d, M, Wls½, Wr½, λ, alg, extrapolation_down, extrapolation_up) + u, t̂, d, M, Wls½, Wr½, λ, alg, extrapolation_left, extrapolation_right) RegularizationSmooth( - u, û, t, t̂, wls, wr, d, λ, alg, Aitp, extrapolation_down, extrapolation_up) + u, û, t, t̂, wls, wr, d, λ, alg, Aitp, extrapolation_left, extrapolation_right) end """ Direct smoothing, no `t̂` or weights @@ -90,8 +90,8 @@ A = RegularizationSmooth(u, t, d; λ = 1.0, alg = :gcv_svd, extrapolate = false) """ function RegularizationSmooth(u::AbstractVector, t::AbstractVector, d::Int = 2; λ::Real = 1.0, - alg::Symbol = :gcv_svd, extrapolation_down::ExtrapolationType.T = ExtrapolationType.none, - extrapolation_up::ExtrapolationType.T = ExtrapolationType.none) + alg::Symbol = :gcv_svd, extrapolation_left::ExtrapolationType.T = ExtrapolationType.none, + extrapolation_right::ExtrapolationType.T = ExtrapolationType.none) u, t = munge_data(u, t) t̂ = t N = length(t) @@ -99,7 +99,7 @@ function RegularizationSmooth(u::AbstractVector, t::AbstractVector, d::Int = 2; Wls½ = Array{Float64}(LA.I, N, N) Wr½ = Array{Float64}(LA.I, N - d, N - d) û, λ, Aitp = _reg_smooth_solve( - u, t̂, d, M, Wls½, Wr½, λ, alg, extrapolation_down, extrapolation_up) + u, t̂, d, M, Wls½, Wr½, λ, alg, extrapolation_left, extrapolation_right) RegularizationSmooth(u, û, t, @@ -110,8 +110,8 @@ function RegularizationSmooth(u::AbstractVector, t::AbstractVector, d::Int = 2; λ, alg, Aitp, - extrapolation_down, - extrapolation_up) + extrapolation_left, + extrapolation_right) end """ `t̂` provided, no weights @@ -122,15 +122,15 @@ A = RegularizationSmooth(u, t, t̂, d; λ = 1.0, alg = :gcv_svd, extrapolate = f """ function RegularizationSmooth(u::AbstractVector, t::AbstractVector, t̂::AbstractVector, d::Int = 2; λ::Real = 1.0, alg::Symbol = :gcv_svd, - extrapolation_down::ExtrapolationType.T = ExtrapolationType.none, - extrapolation_up::ExtrapolationType.T = ExtrapolationType.none) + extrapolation_left::ExtrapolationType.T = ExtrapolationType.none, + extrapolation_right::ExtrapolationType.T = ExtrapolationType.none) u, t = munge_data(u, t) N, N̂ = length(t), length(t̂) M = _mapping_matrix(t̂, t) Wls½ = Array{Float64}(LA.I, N, N) Wr½ = Array{Float64}(LA.I, N̂ - d, N̂ - d) û, λ, Aitp = _reg_smooth_solve( - u, t̂, d, M, Wls½, Wr½, λ, alg, extrapolation_down, extrapolation_up) + u, t̂, d, M, Wls½, Wr½, λ, alg, extrapolation_left, extrapolation_right) RegularizationSmooth(u, û, t, @@ -141,8 +141,8 @@ function RegularizationSmooth(u::AbstractVector, t::AbstractVector, t̂::Abstrac λ, alg, Aitp, - extrapolation_down, - extrapolation_up) + extrapolation_left, + extrapolation_right) end """ `t̂` and `wls` provided @@ -153,15 +153,15 @@ A = RegularizationSmooth(u, t, t̂, wls, d; λ = 1.0, alg = :gcv_svd, extrapolat """ function RegularizationSmooth(u::AbstractVector, t::AbstractVector, t̂::AbstractVector, wls::AbstractVector, d::Int = 2; λ::Real = 1.0, - alg::Symbol = :gcv_svd, extrapolation_down::ExtrapolationType.T = ExtrapolationType.none, - extrapolation_up::ExtrapolationType.T = ExtrapolationType.none) + alg::Symbol = :gcv_svd, extrapolation_left::ExtrapolationType.T = ExtrapolationType.none, + extrapolation_right::ExtrapolationType.T = ExtrapolationType.none) u, t = munge_data(u, t) N, N̂ = length(t), length(t̂) M = _mapping_matrix(t̂, t) Wls½ = LA.diagm(sqrt.(wls)) Wr½ = Array{Float64}(LA.I, N̂ - d, N̂ - d) û, λ, Aitp = _reg_smooth_solve( - u, t̂, d, M, Wls½, Wr½, λ, alg, extrapolation_down, extrapolation_up) + u, t̂, d, M, Wls½, Wr½, λ, alg, extrapolation_left, extrapolation_right) RegularizationSmooth(u, û, t, @@ -172,8 +172,8 @@ function RegularizationSmooth(u::AbstractVector, t::AbstractVector, t̂::Abstrac λ, alg, Aitp, - extrapolation_down, - extrapolation_up) + extrapolation_left, + extrapolation_right) end """ `wls` provided, no `t̂` @@ -185,8 +185,8 @@ A = RegularizationSmooth( """ function RegularizationSmooth(u::AbstractVector, t::AbstractVector, t̂::Nothing, wls::AbstractVector, d::Int = 2; λ::Real = 1.0, - alg::Symbol = :gcv_svd, extrapolation_down::ExtrapolationType.T = ExtrapolationType.none, - extrapolation_up::ExtrapolationType.T = ExtrapolationType.none) + alg::Symbol = :gcv_svd, extrapolation_left::ExtrapolationType.T = ExtrapolationType.none, + extrapolation_right::ExtrapolationType.T = ExtrapolationType.none) u, t = munge_data(u, t) t̂ = t N = length(t) @@ -194,7 +194,7 @@ function RegularizationSmooth(u::AbstractVector, t::AbstractVector, t̂::Nothing Wls½ = LA.diagm(sqrt.(wls)) Wr½ = Array{Float64}(LA.I, N - d, N - d) û, λ, Aitp = _reg_smooth_solve( - u, t̂, d, M, Wls½, Wr½, λ, alg, extrapolation_down, extrapolation_up) + u, t̂, d, M, Wls½, Wr½, λ, alg, extrapolation_left, extrapolation_right) RegularizationSmooth(u, û, t, @@ -205,8 +205,8 @@ function RegularizationSmooth(u::AbstractVector, t::AbstractVector, t̂::Nothing λ, alg, Aitp, - extrapolation_down, - extrapolation_up) + extrapolation_left, + extrapolation_right) end """ `wls` and `wr` provided, no `t̂` @@ -219,8 +219,8 @@ A = RegularizationSmooth( function RegularizationSmooth(u::AbstractVector, t::AbstractVector, t̂::Nothing, wls::AbstractVector, wr::AbstractVector, d::Int = 2; λ::Real = 1.0, alg::Symbol = :gcv_svd, - extrapolation_down::ExtrapolationType.T = ExtrapolationType.none, - extrapolation_up::ExtrapolationType.T = ExtrapolationType.none) + extrapolation_left::ExtrapolationType.T = ExtrapolationType.none, + extrapolation_right::ExtrapolationType.T = ExtrapolationType.none) u, t = munge_data(u, t) t̂ = t N = length(t) @@ -228,7 +228,7 @@ function RegularizationSmooth(u::AbstractVector, t::AbstractVector, t̂::Nothing Wls½ = LA.diagm(sqrt.(wls)) Wr½ = LA.diagm(sqrt.(wr)) û, λ, Aitp = _reg_smooth_solve( - u, t̂, d, M, Wls½, Wr½, λ, alg, extrapolation_down, extrapolation_up) + u, t̂, d, M, Wls½, Wr½, λ, alg, extrapolation_left, extrapolation_right) RegularizationSmooth(u, û, t, @@ -239,8 +239,8 @@ function RegularizationSmooth(u::AbstractVector, t::AbstractVector, t̂::Nothing λ, alg, Aitp, - extrapolation_down, - extrapolation_up) + extrapolation_left, + extrapolation_right) end """ Keyword provided for `wls`, no `t̂` @@ -252,8 +252,8 @@ A = RegularizationSmooth( """ function RegularizationSmooth(u::AbstractVector, t::AbstractVector, t̂::Nothing, wls::Symbol, d::Int = 2; λ::Real = 1.0, alg::Symbol = :gcv_svd, - extrapolation_down::ExtrapolationType.T = ExtrapolationType.none, - extrapolation_up::ExtrapolationType.T = ExtrapolationType.none) + extrapolation_left::ExtrapolationType.T = ExtrapolationType.none, + extrapolation_right::ExtrapolationType.T = ExtrapolationType.none) u, t = munge_data(u, t) t̂ = t N = length(t) @@ -262,7 +262,7 @@ function RegularizationSmooth(u::AbstractVector, t::AbstractVector, t̂::Nothing Wls½ = LA.diagm(sqrt.(wls)) Wr½ = LA.diagm(sqrt.(wr)) û, λ, Aitp = _reg_smooth_solve( - u, t̂, d, M, Wls½, Wr½, λ, alg, extrapolation_down, extrapolation_up) + u, t̂, d, M, Wls½, Wr½, λ, alg, extrapolation_left, extrapolation_right) RegularizationSmooth(u, û, t, @@ -273,8 +273,8 @@ function RegularizationSmooth(u::AbstractVector, t::AbstractVector, t̂::Nothing λ, alg, Aitp, - extrapolation_down, - extrapolation_up) + extrapolation_left, + extrapolation_right) end # """ t̂ provided and keyword for wls _TBD_ """ # function RegularizationSmooth(u::AbstractVector, t::AbstractVector, t̂::AbstractVector, @@ -286,7 +286,7 @@ Solve for the smoothed dependent variables and create spline interpolator function _reg_smooth_solve( u::AbstractVector, t̂::AbstractVector, d::Int, M::AbstractMatrix, Wls½::AbstractMatrix, Wr½::AbstractMatrix, λ::Real, alg::Symbol, - extrapolation_down::ExtrapolationType.T, extrapolation_up::ExtrapolationType.T) + extrapolation_left::ExtrapolationType.T, extrapolation_right::ExtrapolationType.T) λ = float(λ) # `float` expected by RT D = _derivative_matrix(t̂, d) Ψ = RT.setupRegularizationProblem(Wls½ * M, Wr½ * D) @@ -303,7 +303,7 @@ function _reg_smooth_solve( û = result.x λ = result.λ end - Aitp = CubicSpline(û, t̂; extrapolation_down, extrapolation_up) + Aitp = CubicSpline(û, t̂; extrapolation_left, extrapolation_right) # It seems logical to use B-Spline of order d+1, but I am unsure if theory supports the # extra computational cost, JJS 12/25/21 #Aitp = BSplineInterpolation(û,t̂,d+1,:ArcLen,:Average) diff --git a/src/DataInterpolations.jl b/src/DataInterpolations.jl index 700ffb98..ee15a452 100644 --- a/src/DataInterpolations.jl +++ b/src/DataInterpolations.jl @@ -64,13 +64,13 @@ function Base.showerror(io::IO, ::ExtrapolationError) print(io, EXTRAPOLATION_ERROR) end -const DOWN_EXTRAPOLATION_ERROR = "Cannot extrapolate down as `extrapolation_down` keyword passed was `none`" +const DOWN_EXTRAPOLATION_ERROR = "Cannot extrapolate down as `extrapolation_left` keyword passed was `none`" struct DownExtrapolationError <: Exception end function Base.showerror(io::IO, ::DownExtrapolationError) print(io, DOWN_EXTRAPOLATION_ERROR) end -const UP_EXTRAPOLATION_ERROR = "Cannot extrapolate up as `extrapolation_up` keyword passed was `none`" +const UP_EXTRAPOLATION_ERROR = "Cannot extrapolate up as `extrapolation_right` keyword passed was `none`" struct UpExtrapolationError <: Exception end function Base.showerror(io::IO, ::UpExtrapolationError) print(io, UP_EXTRAPOLATION_ERROR) @@ -120,8 +120,8 @@ struct RegularizationSmooth{uType, tType, T, T2, N, ITP <: AbstractInterpolation λ::T2 # regularization parameter alg::Symbol # how to determine λ: `:fixed`, `:gcv_svd`, `:gcv_tr`, `L_curve` Aitp::ITP - extrapolation_down::ExtrapolationType.T - extrapolation_up::ExtrapolationType.T + extrapolation_left::ExtrapolationType.T + extrapolation_right::ExtrapolationType.T function RegularizationSmooth(u, û, t, @@ -132,8 +132,8 @@ struct RegularizationSmooth{uType, tType, T, T2, N, ITP <: AbstractInterpolation λ, alg, Aitp, - extrapolation_down, - extrapolation_up) + extrapolation_left, + extrapolation_right) N = get_output_dim(u) new{typeof(u), typeof(t), eltype(u), typeof(λ), N, typeof(Aitp)}( u, @@ -146,8 +146,8 @@ struct RegularizationSmooth{uType, tType, T, T2, N, ITP <: AbstractInterpolation λ, alg, Aitp, - extrapolation_down, - extrapolation_up) + extrapolation_left, + extrapolation_right) end end diff --git a/src/derivatives.jl b/src/derivatives.jl index e43fe2c4..6b1f02e6 100644 --- a/src/derivatives.jl +++ b/src/derivatives.jl @@ -14,16 +14,16 @@ function derivative(A, t, order = 1) end function _extrapolate_derivative_down(A, t, order) - (; extrapolation_down) = A + (; extrapolation_left) = A typed_zero = zero(first(A.u) / one(A.t[1])) - if extrapolation_down == ExtrapolationType.none + if extrapolation_left == ExtrapolationType.none throw(DownExtrapolationError()) - elseif extrapolation_down == ExtrapolationType.constant + elseif extrapolation_left == ExtrapolationType.constant typed_zero - elseif extrapolation_down == ExtrapolationType.linear + elseif extrapolation_left == ExtrapolationType.linear (order == 1) ? derivative(A, first(A.t)) : typed_zero else - # extrapolation_down == ExtrapolationType.extension + # extrapolation_left == ExtrapolationType.extension iguess = A.iguesser (order == 1) ? _derivative(A, t, iguess) : ForwardDiff.derivative(t -> begin @@ -33,16 +33,16 @@ function _extrapolate_derivative_down(A, t, order) end function _extrapolate_derivative_up(A, t, order) - (; extrapolation_up) = A + (; extrapolation_right) = A typed_zero = zero(first(A.u) / one(A.t[1])) - if extrapolation_up == ExtrapolationType.none + if extrapolation_right == ExtrapolationType.none throw(UpExtrapolationError()) - elseif extrapolation_up == ExtrapolationType.constant + elseif extrapolation_right == ExtrapolationType.constant typed_zero - elseif extrapolation_up == ExtrapolationType.linear + elseif extrapolation_right == ExtrapolationType.linear (order == 1) ? derivative(A, last(A.t)) : typed_zero else - # extrapolation_up == ExtrapolationType.extension + # extrapolation_right == ExtrapolationType.extension iguess = A.iguesser (order == 1) ? _derivative(A, t, iguess) : ForwardDiff.derivative(t -> begin diff --git a/src/integral_inverses.jl b/src/integral_inverses.jl index ec3bf9c3..577b98e3 100644 --- a/src/integral_inverses.jl +++ b/src/integral_inverses.jl @@ -37,14 +37,14 @@ struct LinearInterpolationIntInv{uType, tType, itpType, T, N} <: AbstractIntegralInverseInterpolation{T, N} u::uType t::tType - extrapolation_down::ExtrapolationType.T - extrapolation_up::ExtrapolationType.T + extrapolation_left::ExtrapolationType.T + extrapolation_right::ExtrapolationType.T iguesser::Guesser{tType} itp::itpType function LinearInterpolationIntInv(u, t, A) N = get_output_dim(u) new{typeof(u), typeof(t), typeof(A), eltype(u), N}( - u, t, A.extrapolation_down, A.extrapolation_up, Guesser(t), A) + u, t, A.extrapolation_left, A.extrapolation_right, Guesser(t), A) end end @@ -89,14 +89,14 @@ struct ConstantInterpolationIntInv{uType, tType, itpType, T, N} <: AbstractIntegralInverseInterpolation{T, N} u::uType t::tType - extrapolation_down::ExtrapolationType.T - extrapolation_up::ExtrapolationType.T + extrapolation_left::ExtrapolationType.T + extrapolation_right::ExtrapolationType.T iguesser::Guesser{tType} itp::itpType function ConstantInterpolationIntInv(u, t, A) N = get_output_dim(u) new{typeof(u), typeof(t), typeof(A), eltype(u), N}( - u, t, A.extrapolation_down, A.extrapolation_up, Guesser(t), A + u, t, A.extrapolation_left, A.extrapolation_right, Guesser(t), A ) end end diff --git a/src/integrals.jl b/src/integrals.jl index 4261ffe5..d24a79a8 100644 --- a/src/integrals.jl +++ b/src/integrals.jl @@ -63,31 +63,31 @@ function integral(A::AbstractInterpolation, t1::Number, t2::Number) end function _extrapolate_integral_down(A, t) - (; extrapolation_down) = A - if extrapolation_down == ExtrapolationType.none + (; extrapolation_left) = A + if extrapolation_left == ExtrapolationType.none throw(DownExtrapolationError()) - elseif extrapolation_down == ExtrapolationType.constant + elseif extrapolation_left == ExtrapolationType.constant first(A.u) * (first(A.t) - t) - elseif extrapolation_down == ExtrapolationType.linear + elseif extrapolation_left == ExtrapolationType.linear slope = derivative(A, first(A.t)) Δt = first(A.t) - t (first(A.u) - slope * Δt / 2) * Δt - elseif extrapolation_down == ExtrapolationType.extension + elseif extrapolation_left == ExtrapolationType.extension _integral(A, 1, t, first(A.t)) end end function _extrapolate_integral_up(A, t) - (; extrapolation_up) = A - if extrapolation_up == ExtrapolationType.none + (; extrapolation_right) = A + if extrapolation_right == ExtrapolationType.none throw(UpExtrapolationError()) - elseif extrapolation_up == ExtrapolationType.constant + elseif extrapolation_right == ExtrapolationType.constant last(A.u) * (t - last(A.t)) - elseif extrapolation_up == ExtrapolationType.linear + elseif extrapolation_right == ExtrapolationType.linear slope = derivative(A, last(A.t)) Δt = t - last(A.t) (last(A.u) + slope * Δt / 2) * Δt - elseif extrapolation_up == ExtrapolationType.extension + elseif extrapolation_right == ExtrapolationType.extension _integral(A, length(A.t) - 1, last(A.t), t) end end diff --git a/src/interpolation_caches.jl b/src/interpolation_caches.jl index 2c01aed6..4cc2670b 100644 --- a/src/interpolation_caches.jl +++ b/src/interpolation_caches.jl @@ -1,6 +1,6 @@ """ - LinearInterpolation(u, t; extrapolation_down::ExtrapolationType.T = ExtrapolationType.none, - extrapolation_up::ExtrapolationType.T = ExtrapolationType.none, cache_parameters = false) + LinearInterpolation(u, t; extrapolation_left::ExtrapolationType.T = ExtrapolationType.none, + extrapolation_right::ExtrapolationType.T = ExtrapolationType.none, cache_parameters = false) It is the method of interpolating between the data points using a linear polynomial. For any point, two data points one each side are chosen and connected with a line. Extrapolation extends the last linear polynomial on each side. @@ -12,13 +12,16 @@ Extrapolation extends the last linear polynomial on each side. ## Keyword Arguments - - `extrapolate_down`: The extrapolation type beyond the data in the negative `t` direction. - Defaults to `ExtrapolationType.none`. - - `extrapolate_up`: The extrapolation type beyond the data in the positive `t` direction. - Defaults to `ExtrapolationType.none`. + - `extrapolation`: The extrapolation type applied left and right of the data. Possible options + are `ExtrapolationType.none` (default), `ExtrapolationType.constant`, + `ExtrapolationType.linear` and `ExtrapolationType.extension`. + - `extrapolation_left`: The extrapolation type applied left of the data. See `extrapolation` for + the possible options. + - `extrapolation_right`: The extrapolation type applied right of the data. See `extrapolation` for + the possible options. - `cache_parameters`: precompute parameters at initialization for faster interpolation computations. Note: if activated, `u` and `t` should not be modified. Defaults to `false`. - - `assume_linear_t`: boolean value to specify a faster index lookup behaviour for + - `assume_linear_t`: boolean value to specify a faster index lookup behavior for evenly-distributed abscissae. Alternatively, a numerical threshold may be specified for a test based on the normalized standard deviation of the difference with respect to the straight line (see [`looks_linear`](@ref)). Defaults to 1e-2. @@ -28,37 +31,41 @@ struct LinearInterpolation{uType, tType, IType, pType, T, N} <: AbstractInterpol t::tType I::IType p::LinearParameterCache{pType} - extrapolation_down::ExtrapolationType.T - extrapolation_up::ExtrapolationType.T + extrapolation_left::ExtrapolationType.T + extrapolation_right::ExtrapolationType.T iguesser::Guesser{tType} cache_parameters::Bool linear_lookup::Bool - function LinearInterpolation(u, t, I, p, extrapolation_down, extrapolation_up, + function LinearInterpolation(u, t, I, p, extrapolation_left, extrapolation_right, cache_parameters, assume_linear_t) linear_lookup = seems_linear(assume_linear_t, t) N = get_output_dim(u) new{typeof(u), typeof(t), typeof(I), typeof(p.slope), eltype(u), N}( - u, t, I, p, extrapolation_down, extrapolation_up, + u, t, I, p, extrapolation_left, extrapolation_right, Guesser(t), cache_parameters, linear_lookup) end end function LinearInterpolation( - u, t; extrapolation_down::ExtrapolationType.T = ExtrapolationType.none, - extrapolation_up::ExtrapolationType.T = ExtrapolationType.none, cache_parameters = false, assume_linear_t = 1e-2) + u, t; extrapolation::ExtrapolationType.T = ExtrapolationType.none, + extrapolation_left::ExtrapolationType.T = ExtrapolationType.none, + extrapolation_right::ExtrapolationType.T = ExtrapolationType.none, cache_parameters = false, assume_linear_t = 1e-2) + extrapolation_left, extrapolation_right = munge_extrapolation( + extrapolation, extrapolation_left, extrapolation_right) u, t = munge_data(u, t) p = LinearParameterCache(u, t, cache_parameters) A = LinearInterpolation( - u, t, nothing, p, extrapolation_down, - extrapolation_up, cache_parameters, assume_linear_t) + u, t, nothing, p, extrapolation_left, + extrapolation_right, cache_parameters, assume_linear_t) I = cumulative_integral(A, cache_parameters) LinearInterpolation( - u, t, I, p, extrapolation_down, extrapolation_up, cache_parameters, assume_linear_t) + u, t, I, p, extrapolation_left, extrapolation_right, + cache_parameters, assume_linear_t) end """ - QuadraticInterpolation(u, t, mode = :Forward; extrapolation_down::ExtrapolationType.T = ExtrapolationType.none, - extrapolation_up::ExtrapolationType.T = ExtrapolationType.none, cache_parameters = false) + QuadraticInterpolation(u, t, mode = :Forward; extrapolation_left::ExtrapolationType.T = ExtrapolationType.none, + extrapolation_right::ExtrapolationType.T = ExtrapolationType.none, cache_parameters = false) It is the method of interpolating between the data points using quadratic polynomials. For any point, three data points nearby are taken to fit a quadratic polynomial. Extrapolation extends the last quadratic polynomial on each side. @@ -71,10 +78,13 @@ Extrapolation extends the last quadratic polynomial on each side. ## Keyword Arguments - - `extrapolate_down`: The extrapolation type beyond the data in the negative `t` direction. - Defaults to `ExtrapolationType.none`. - - `extrapolate_up`: The extrapolation type beyond the data in the positive `t` direction. - Defaults to `ExtrapolationType.none`. + - `extrapolation`: The extrapolation type applied left and right of the data. Possible options + are `ExtrapolationType.none` (default), `ExtrapolationType.constant`, + `ExtrapolationType.linear` and `ExtrapolationType.extension`. + - `extrapolation_left`: The extrapolation type applied left of the data. See `extrapolation` for + the possible options. + - `extrapolation_right`: The extrapolation type applied right of the data. See `extrapolation` for + the possible options. - `cache_parameters`: precompute parameters at initialization for faster interpolation computations. Note: if activated, `u` and `t` should not be modified. Defaults to `false`. - `assume_linear_t`: boolean value to specify a faster index lookup behaviour for evenly-distributed abscissae. Alternatively, a numerical threshold may be specified @@ -88,36 +98,39 @@ struct QuadraticInterpolation{uType, tType, IType, pType, T, N} <: I::IType p::QuadraticParameterCache{pType} mode::Symbol - extrapolation_down::ExtrapolationType.T - extrapolation_up::ExtrapolationType.T + extrapolation_left::ExtrapolationType.T + extrapolation_right::ExtrapolationType.T iguesser::Guesser{tType} cache_parameters::Bool linear_lookup::Bool function QuadraticInterpolation( - u, t, I, p, mode, extrapolation_down, - extrapolation_up, cache_parameters, assume_linear_t) + u, t, I, p, mode, extrapolation_left, + extrapolation_right, cache_parameters, assume_linear_t) mode ∈ (:Forward, :Backward) || error("mode should be :Forward or :Backward for QuadraticInterpolation") linear_lookup = seems_linear(assume_linear_t, t) N = get_output_dim(u) new{typeof(u), typeof(t), typeof(I), typeof(p.α), eltype(u), N}( - u, t, I, p, mode, extrapolation_down, extrapolation_up, + u, t, I, p, mode, extrapolation_left, extrapolation_right, Guesser(t), cache_parameters, linear_lookup) end end function QuadraticInterpolation( - u, t, mode; extrapolation_down::ExtrapolationType.T = ExtrapolationType.none, - extrapolation_up::ExtrapolationType.T = ExtrapolationType.none, cache_parameters = false, assume_linear_t = 1e-2) + u, t, mode; extrapolation::ExtrapolationType.T = ExtrapolationType.none, + extrapolation_left::ExtrapolationType.T = ExtrapolationType.none, + extrapolation_right::ExtrapolationType.T = ExtrapolationType.none, cache_parameters = false, assume_linear_t = 1e-2) + extrapolation_left, extrapolation_right = munge_extrapolation( + extrapolation, extrapolation_left, extrapolation_right) u, t = munge_data(u, t) linear_lookup = seems_linear(assume_linear_t, t) p = QuadraticParameterCache(u, t, cache_parameters, mode) A = QuadraticInterpolation( - u, t, nothing, p, mode, extrapolation_down, - extrapolation_up, cache_parameters, linear_lookup) + u, t, nothing, p, mode, extrapolation_left, + extrapolation_right, cache_parameters, linear_lookup) I = cumulative_integral(A, cache_parameters) - QuadraticInterpolation(u, t, I, p, mode, extrapolation_down, - extrapolation_up, cache_parameters, linear_lookup) + QuadraticInterpolation(u, t, I, p, mode, extrapolation_left, + extrapolation_right, cache_parameters, linear_lookup) end function QuadraticInterpolation(u, t; kwargs...) @@ -125,8 +138,8 @@ function QuadraticInterpolation(u, t; kwargs...) end """ - LagrangeInterpolation(u, t, n = length(t) - 1; extrapolation_down::ExtrapolationType.T = ExtrapolationType.none, - extrapolation_up::ExtrapolationType.T = ExtrapolationType.none, safetycopy = true) + LagrangeInterpolation(u, t, n = length(t) - 1; extrapolation_left::ExtrapolationType.T = ExtrapolationType.none, + extrapolation_right::ExtrapolationType.T = ExtrapolationType.none, safetycopy = true) It is the method of interpolation using Lagrange polynomials of (k-1)th order passing through all the data points where k is the number of data points. @@ -138,10 +151,13 @@ It is the method of interpolation using Lagrange polynomials of (k-1)th order pa ## Keyword Arguments - - `extrapolate_down`: The extrapolation type beyond the data in the negative `t` direction. - Defaults to `ExtrapolationType.none`. - - `extrapolate_up`: The extrapolation type beyond the data in the positive `t` direction. - Defaults to `ExtrapolationType.none`. + - `extrapolation`: The extrapolation type applied left and right of the data. Possible options + are `ExtrapolationType.none` (default), `ExtrapolationType.constant`, + `ExtrapolationType.linear` and `ExtrapolationType.extension`. + - `extrapolation_left`: The extrapolation type applied left of the data. See `extrapolation` for + the possible options. + - `extrapolation_right`: The extrapolation type applied right of the data. See `extrapolation` for + the possible options. """ struct LagrangeInterpolation{uType, tType, T, bcacheType, N} <: AbstractInterpolation{T, N} @@ -150,10 +166,10 @@ struct LagrangeInterpolation{uType, tType, T, bcacheType, N} <: n::Int bcache::bcacheType idxs::Vector{Int} - extrapolation_down::ExtrapolationType.T - extrapolation_up::ExtrapolationType.T + extrapolation_left::ExtrapolationType.T + extrapolation_right::ExtrapolationType.T iguesser::Guesser{tType} - function LagrangeInterpolation(u, t, n, extrapolation_down, extrapolation_up) + function LagrangeInterpolation(u, t, n, extrapolation_left, extrapolation_right) bcache = zeros(eltype(u[1]), n + 1) idxs = zeros(Int, n + 1) fill!(bcache, NaN) @@ -163,8 +179,8 @@ struct LagrangeInterpolation{uType, tType, T, bcacheType, N} <: n, bcache, idxs, - extrapolation_down, - extrapolation_up, + extrapolation_left, + extrapolation_right, Guesser(t) ) end @@ -172,18 +188,21 @@ end function LagrangeInterpolation( u, t, n = length(t) - 1; - extrapolation_down::ExtrapolationType.T = ExtrapolationType.none, - extrapolation_up::ExtrapolationType.T = ExtrapolationType.none) + extrapolation::ExtrapolationType.T = ExtrapolationType.none, + extrapolation_left::ExtrapolationType.T = ExtrapolationType.none, + extrapolation_right::ExtrapolationType.T = ExtrapolationType.none) + extrapolation_left, extrapolation_right = munge_extrapolation( + extrapolation, extrapolation_left, extrapolation_right) u, t = munge_data(u, t) if n != length(t) - 1 error("Currently only n=length(t) - 1 is supported") end - LagrangeInterpolation(u, t, n, extrapolation_down, extrapolation_up) + LagrangeInterpolation(u, t, n, extrapolation_left, extrapolation_right) end """ - AkimaInterpolation(u, t; extrapolation_down::ExtrapolationType.T = ExtrapolationType.none, - extrapolation_up::ExtrapolationType.T = ExtrapolationType.none, cache_parameters = false) + AkimaInterpolation(u, t; extrapolation_left::ExtrapolationType.T = ExtrapolationType.none, + extrapolation_right::ExtrapolationType.T = ExtrapolationType.none, cache_parameters = false) It is a spline interpolation built from cubic polynomials. It forms a continuously differentiable function. For more details, refer: [https://en.wikipedia.org/wiki/Akima_spline](https://en.wikipedia.org/wiki/Akima_spline). Extrapolation extends the last cubic polynomial on each side. @@ -195,10 +214,13 @@ Extrapolation extends the last cubic polynomial on each side. ## Keyword Arguments - - `extrapolate_down`: The extrapolation type beyond the data in the negative `t` direction. - Defaults to `ExtrapolationType.none`. - - `extrapolate_up`: The extrapolation type beyond the data in the positive `t` direction. - Defaults to `ExtrapolationType.none`. + - `extrapolation`: The extrapolation type applied left and right of the data. Possible options + are `ExtrapolationType.none` (default), `ExtrapolationType.constant`, + `ExtrapolationType.linear` and `ExtrapolationType.extension`. + - `extrapolation_left`: The extrapolation type applied left of the data. See `extrapolation` for + the possible options. + - `extrapolation_right`: The extrapolation type applied right of the data. See `extrapolation` for + the possible options. - `cache_parameters`: precompute parameters at initialization for faster interpolation computations. Note: if activated, `u` and `t` should not be modified. Defaults to `false`. - `assume_linear_t`: boolean value to specify a faster index lookup behaviour for evenly-distributed abscissae. Alternatively, a numerical threshold may be specified @@ -213,14 +235,14 @@ struct AkimaInterpolation{uType, tType, IType, bType, cType, dType, T, N} <: b::bType c::cType d::dType - extrapolation_down::ExtrapolationType.T - extrapolation_up::ExtrapolationType.T + extrapolation_left::ExtrapolationType.T + extrapolation_right::ExtrapolationType.T iguesser::Guesser{tType} cache_parameters::Bool linear_lookup::Bool function AkimaInterpolation( - u, t, I, b, c, d, extrapolation_down, - extrapolation_up, cache_parameters, assume_linear_t) + u, t, I, b, c, d, extrapolation_left, + extrapolation_right, cache_parameters, assume_linear_t) linear_lookup = seems_linear(assume_linear_t, t) N = get_output_dim(u) new{typeof(u), typeof(t), typeof(I), typeof(b), typeof(c), @@ -230,8 +252,8 @@ struct AkimaInterpolation{uType, tType, IType, bType, cType, dType, T, N} <: b, c, d, - extrapolation_down, - extrapolation_up, + extrapolation_left, + extrapolation_right, Guesser(t), cache_parameters, linear_lookup @@ -240,8 +262,11 @@ struct AkimaInterpolation{uType, tType, IType, bType, cType, dType, T, N} <: end function AkimaInterpolation( - u, t; extrapolation_down::ExtrapolationType.T = ExtrapolationType.none, - extrapolation_up::ExtrapolationType.T = ExtrapolationType.none, cache_parameters = false, assume_linear_t = 1e-2) + u, t; extrapolation::ExtrapolationType.T = ExtrapolationType.none, + extrapolation_left::ExtrapolationType.T = ExtrapolationType.none, + extrapolation_right::ExtrapolationType.T = ExtrapolationType.none, cache_parameters = false, assume_linear_t = 1e-2) + extrapolation_left, extrapolation_right = munge_extrapolation( + extrapolation, extrapolation_left, extrapolation_right) u, t = munge_data(u, t) linear_lookup = seems_linear(assume_linear_t, t) n = length(t) @@ -265,16 +290,16 @@ function AkimaInterpolation( d = (b[1:(end - 1)] .+ b[2:end] .- 2.0 .* m[3:(end - 2)]) ./ dt .^ 2 A = AkimaInterpolation( - u, t, nothing, b, c, d, extrapolation_down, - extrapolation_up, cache_parameters, linear_lookup) + u, t, nothing, b, c, d, extrapolation_left, + extrapolation_right, cache_parameters, linear_lookup) I = cumulative_integral(A, cache_parameters) - AkimaInterpolation(u, t, I, b, c, d, extrapolation_down, - extrapolation_up, cache_parameters, linear_lookup) + AkimaInterpolation(u, t, I, b, c, d, extrapolation_left, + extrapolation_right, cache_parameters, linear_lookup) end """ - ConstantInterpolation(u, t; dir = :left, extrapolation_down::ExtrapolationType.T = ExtrapolationType.none, - extrapolation_up::ExtrapolationType.T = ExtrapolationType.none, cache_parameters = false) + ConstantInterpolation(u, t; dir = :left, extrapolation_left::ExtrapolationType.T = ExtrapolationType.none, + extrapolation_right::ExtrapolationType.T = ExtrapolationType.none, cache_parameters = false) It is the method of interpolating using a constant polynomial. For any point, two adjacent data points are found on either side (left and right). The value at that point depends on `dir`. If it is `:left`, then the value at the left point is chosen and if it is `:right`, the value at the right point is chosen. @@ -288,10 +313,13 @@ Extrapolation extends the last constant polynomial at the end points on each sid ## Keyword Arguments - `dir`: indicates which value should be used for interpolation (`:left` or `:right`). - - `extrapolate_down`: The extrapolation type beyond the data in the negative `t` direction. - Defaults to `ExtrapolationType.none`. - - `extrapolate_up`: The extrapolation type beyond the data in the positive `t` direction. - Defaults to `ExtrapolationType.none`. + - `extrapolation`: The extrapolation type applied left and right of the data. Possible options + are `ExtrapolationType.none` (default), `ExtrapolationType.constant`, + `ExtrapolationType.linear` and `ExtrapolationType.extension`. + - `extrapolation_left`: The extrapolation type applied left of the data. See `extrapolation` for + the possible options. + - `extrapolation_right`: The extrapolation type applied right of the data. See `extrapolation` for + the possible options. - `cache_parameters`: precompute parameters at initialization for faster interpolation computations. Note: if activated, `u` and `t` should not be modified. Defaults to `false`. - `assume_linear_t`: boolean value to specify a faster index lookup behaviour for evenly-distributed abscissae. Alternatively, a numerical threshold may be specified @@ -304,38 +332,41 @@ struct ConstantInterpolation{uType, tType, IType, T, N} <: AbstractInterpolation I::IType p::Nothing dir::Symbol # indicates if value to the $dir should be used for the interpolation - extrapolation_down::ExtrapolationType.T - extrapolation_up::ExtrapolationType.T + extrapolation_left::ExtrapolationType.T + extrapolation_right::ExtrapolationType.T iguesser::Guesser{tType} cache_parameters::Bool linear_lookup::Bool function ConstantInterpolation( - u, t, I, dir, extrapolation_down, extrapolation_up, + u, t, I, dir, extrapolation_left, extrapolation_right, cache_parameters, assume_linear_t) linear_lookup = seems_linear(assume_linear_t, t) N = get_output_dim(u) new{typeof(u), typeof(t), typeof(I), eltype(u), N}( - u, t, I, nothing, dir, extrapolation_down, extrapolation_up, + u, t, I, nothing, dir, extrapolation_left, extrapolation_right, Guesser(t), cache_parameters, linear_lookup) end end function ConstantInterpolation( - u, t; dir = :left, extrapolation_down::ExtrapolationType.T = ExtrapolationType.none, - extrapolation_up::ExtrapolationType.T = ExtrapolationType.none, + u, t; dir = :left, extrapolation::ExtrapolationType.T = ExtrapolationType.none, + extrapolation_left::ExtrapolationType.T = ExtrapolationType.none, + extrapolation_right::ExtrapolationType.T = ExtrapolationType.none, cache_parameters = false, assume_linear_t = 1e-2) + extrapolation_left, extrapolation_right = munge_extrapolation( + extrapolation, extrapolation_left, extrapolation_right) u, t = munge_data(u, t) A = ConstantInterpolation( - u, t, nothing, dir, extrapolation_down, - extrapolation_up, cache_parameters, assume_linear_t) + u, t, nothing, dir, extrapolation_left, + extrapolation_right, cache_parameters, assume_linear_t) I = cumulative_integral(A, cache_parameters) - ConstantInterpolation(u, t, I, dir, extrapolation_down, extrapolation_up, + ConstantInterpolation(u, t, I, dir, extrapolation_left, extrapolation_right, cache_parameters, assume_linear_t) end """ - QuadraticSpline(u, t; extrapolation_down::ExtrapolationType.T = ExtrapolationType.none, - extrapolation_up::ExtrapolationType.T = ExtrapolationType.none, cache_parameters = false) + QuadraticSpline(u, t; extrapolation_left::ExtrapolationType.T = ExtrapolationType.none, + extrapolation_right::ExtrapolationType.T = ExtrapolationType.none, cache_parameters = false) It is a spline interpolation using piecewise quadratic polynomials between each pair of data points. Its first derivative is also continuous. Extrapolation extends the last quadratic polynomial on each side. @@ -347,10 +378,13 @@ Extrapolation extends the last quadratic polynomial on each side. ## Keyword Arguments - - `extrapolate_down`: The extrapolation type beyond the data in the negative `t` direction. - Defaults to `ExtrapolationType.none`. - - `extrapolate_up`: The extrapolation type beyond the data in the positive `t` direction. - Defaults to `ExtrapolationType.none`. + - `extrapolation`: The extrapolation type applied left and right of the data. Possible options + are `ExtrapolationType.none` (default), `ExtrapolationType.constant`, + `ExtrapolationType.linear` and `ExtrapolationType.extension`. + - `extrapolation_left`: The extrapolation type applied left of the data. See `extrapolation` for + the possible options. + - `extrapolation_right`: The extrapolation type applied right of the data. See `extrapolation` for + the possible options. - `cache_parameters`: precompute parameters at initialization for faster interpolation computations. Note: if activated, `u` and `t` should not be modified. Defaults to `false`. - `assume_linear_t`: boolean value to specify a faster index lookup behaviour for evenly-distributed abscissae. Alternatively, a numerical threshold may be specified @@ -366,14 +400,14 @@ struct QuadraticSpline{uType, tType, IType, pType, kType, cType, scType, T, N} < k::kType # knot vector c::cType # B-spline control points sc::scType # Spline coefficients (preallocated memory) - extrapolation_down::ExtrapolationType.T - extrapolation_up::ExtrapolationType.T + extrapolation_left::ExtrapolationType.T + extrapolation_right::ExtrapolationType.T iguesser::Guesser{tType} cache_parameters::Bool linear_lookup::Bool function QuadraticSpline( - u, t, I, p, k, c, sc, extrapolation_down, - extrapolation_up, cache_parameters, assume_linear_t) + u, t, I, p, k, c, sc, extrapolation_left, + extrapolation_right, cache_parameters, assume_linear_t) linear_lookup = seems_linear(assume_linear_t, t) N = get_output_dim(u) new{typeof(u), typeof(t), typeof(I), typeof(p.α), typeof(k), @@ -384,8 +418,8 @@ struct QuadraticSpline{uType, tType, IType, pType, kType, cType, scType, T, N} < k, c, sc, - extrapolation_down, - extrapolation_up, + extrapolation_left, + extrapolation_right, Guesser(t), cache_parameters, linear_lookup @@ -394,10 +428,13 @@ struct QuadraticSpline{uType, tType, IType, pType, kType, cType, scType, T, N} < end function QuadraticSpline( - u::uType, t; extrapolation_down::ExtrapolationType.T = ExtrapolationType.none, - extrapolation_up::ExtrapolationType.T = ExtrapolationType.none, + u::uType, t; extrapolation::ExtrapolationType.T = ExtrapolationType.none, + extrapolation_left::ExtrapolationType.T = ExtrapolationType.none, + extrapolation_right::ExtrapolationType.T = ExtrapolationType.none, cache_parameters = false, assume_linear_t = 1e-2) where {uType <: AbstractVector{<:Number}} + extrapolation_left, extrapolation_right = munge_extrapolation( + extrapolation, extrapolation_left, extrapolation_right) u, t = munge_data(u, t) n = length(t) @@ -408,18 +445,21 @@ function QuadraticSpline( p = QuadraticSplineParameterCache(u, t, k, c, sc, cache_parameters) A = QuadraticSpline( - u, t, nothing, p, k, c, sc, extrapolation_down, - extrapolation_up, cache_parameters, assume_linear_t) + u, t, nothing, p, k, c, sc, extrapolation_left, + extrapolation_right, cache_parameters, assume_linear_t) I = cumulative_integral(A, cache_parameters) - QuadraticSpline(u, t, I, p, k, c, sc, extrapolation_down, - extrapolation_up, cache_parameters, assume_linear_t) + QuadraticSpline(u, t, I, p, k, c, sc, extrapolation_left, + extrapolation_right, cache_parameters, assume_linear_t) end function QuadraticSpline( - u::uType, t; extrapolation_down::ExtrapolationType.T = ExtrapolationType.none, - extrapolation_up::ExtrapolationType.T = ExtrapolationType.none, cache_parameters = false, + u::uType, t; extrapolation::ExtrapolationType.T = ExtrapolationType.none, + extrapolation_left::ExtrapolationType.T = ExtrapolationType.none, + extrapolation_right::ExtrapolationType.T = ExtrapolationType.none, cache_parameters = false, assume_linear_t = 1e-2) where {uType <: AbstractVector} + extrapolation_left, extrapolation_right = munge_extrapolation( + extrapolation, extrapolation_left, extrapolation_right) u, t = munge_data(u, t) n = length(t) @@ -440,16 +480,16 @@ function QuadraticSpline( p = QuadraticSplineParameterCache(u, t, k, c, sc, cache_parameters) A = QuadraticSpline( - u, t, nothing, p, k, c, sc, extrapolation_down, - extrapolation_up, cache_parameters, assume_linear_t) + u, t, nothing, p, k, c, sc, extrapolation_left, + extrapolation_right, cache_parameters, assume_linear_t) I = cumulative_integral(A, cache_parameters) - QuadraticSpline(u, t, I, p, k, c, sc, extrapolation_down, - extrapolation_up, cache_parameters, assume_linear_t) + QuadraticSpline(u, t, I, p, k, c, sc, extrapolation_left, + extrapolation_right, cache_parameters, assume_linear_t) end """ - CubicSpline(u, t; extrapolation_down::ExtrapolationType.T = ExtrapolationType.none, - extrapolation_up::ExtrapolationType.T = ExtrapolationType.none, cache_parameters = false) + CubicSpline(u, t; extrapolation_left::ExtrapolationType.T = ExtrapolationType.none, + extrapolation_right::ExtrapolationType.T = ExtrapolationType.none, cache_parameters = false) It is a spline interpolation using piecewise cubic polynomials between each pair of data points. Its first and second derivative is also continuous. Second derivative on both ends are zero, which are also called "natural" boundary conditions. Extrapolation extends the last cubic polynomial on each side. @@ -461,10 +501,13 @@ Second derivative on both ends are zero, which are also called "natural" boundar ## Keyword Arguments - - `extrapolate_down`: The extrapolation type beyond the data in the negative `t` direction. - Defaults to `ExtrapolationType.none`. - - `extrapolate_up`: The extrapolation type beyond the data in the positive `t` direction. - Defaults to `ExtrapolationType.none`. + - `extrapolation`: The extrapolation type applied left and right of the data. Possible options + are `ExtrapolationType.none` (default), `ExtrapolationType.constant`, + `ExtrapolationType.linear` and `ExtrapolationType.extension`. + - `extrapolation_left`: The extrapolation type applied left of the data. See `extrapolation` for + the possible options. + - `extrapolation_right`: The extrapolation type applied right of the data. See `extrapolation` for + the possible options. - `cache_parameters`: precompute parameters at initialization for faster interpolation computations. Note: if activated, `u` and `t` should not be modified. Defaults to `false`. - `assume_linear_t`: boolean value to specify a faster index lookup behaviour for evenly-distributed abscissae. Alternatively, a numerical threshold may be specified @@ -479,13 +522,13 @@ struct CubicSpline{uType, tType, IType, pType, hType, zType, T, N} <: p::CubicSplineParameterCache{pType} h::hType z::zType - extrapolation_down::ExtrapolationType.T - extrapolation_up::ExtrapolationType.T + extrapolation_left::ExtrapolationType.T + extrapolation_right::ExtrapolationType.T iguesser::Guesser{tType} cache_parameters::Bool linear_lookup::Bool - function CubicSpline(u, t, I, p, h, z, extrapolation_down, - extrapolation_up, cache_parameters, assume_linear_t) + function CubicSpline(u, t, I, p, h, z, extrapolation_left, + extrapolation_right, cache_parameters, assume_linear_t) linear_lookup = seems_linear(assume_linear_t, t) N = get_output_dim(u) new{typeof(u), typeof(t), typeof(I), typeof(p.c₁), @@ -496,8 +539,8 @@ struct CubicSpline{uType, tType, IType, pType, hType, zType, T, N} <: p, h, z, - extrapolation_down, - extrapolation_up, + extrapolation_left, + extrapolation_right, Guesser(t), cache_parameters, linear_lookup @@ -506,11 +549,13 @@ struct CubicSpline{uType, tType, IType, pType, hType, zType, T, N} <: end function CubicSpline(u::uType, - t; - extrapolation_down::ExtrapolationType.T = ExtrapolationType.none, - extrapolation_up::ExtrapolationType.T = ExtrapolationType.none, cache_parameters = false, + t; extrapolation::ExtrapolationType.T = ExtrapolationType.none, + extrapolation_left::ExtrapolationType.T = ExtrapolationType.none, + extrapolation_right::ExtrapolationType.T = ExtrapolationType.none, cache_parameters = false, assume_linear_t = 1e-2) where {uType <: AbstractVector{<:Number}} + extrapolation_left, extrapolation_right = munge_extrapolation( + extrapolation, extrapolation_left, extrapolation_right) u, t = munge_data(u, t) n = length(t) - 1 h = vcat(0, map(k -> t[k + 1] - t[k], 1:(length(t) - 1)), 0) @@ -531,19 +576,21 @@ function CubicSpline(u::uType, linear_lookup = seems_linear(assume_linear_t, t) p = CubicSplineParameterCache(u, h, z, cache_parameters) A = CubicSpline( - u, t, nothing, p, h[1:(n + 1)], z, extrapolation_down, - extrapolation_up, cache_parameters, linear_lookup) + u, t, nothing, p, h[1:(n + 1)], z, extrapolation_left, + extrapolation_right, cache_parameters, linear_lookup) I = cumulative_integral(A, cache_parameters) - CubicSpline(u, t, I, p, h[1:(n + 1)], z, extrapolation_down, - extrapolation_up, cache_parameters, linear_lookup) + CubicSpline(u, t, I, p, h[1:(n + 1)], z, extrapolation_left, + extrapolation_right, cache_parameters, linear_lookup) end function CubicSpline(u::uType, t; - extrapolation_down::ExtrapolationType.T = ExtrapolationType.none, - extrapolation_up::ExtrapolationType.T = ExtrapolationType.none, cache_parameters = false, + extrapolation::ExtrapolationType.T = ExtrapolationType.none, extrapolation_left::ExtrapolationType.T = ExtrapolationType.none, + extrapolation_right::ExtrapolationType.T = ExtrapolationType.none, cache_parameters = false, assume_linear_t = 1e-2) where {uType <: AbstractArray{T, N}} where {T, N} + extrapolation_left, extrapolation_right = munge_extrapolation( + extrapolation, extrapolation_left, extrapolation_right) u, t = munge_data(u, t) n = length(t) - 1 h = vcat(0, map(k -> t[k + 1] - t[k], 1:(length(t) - 1)), 0) @@ -568,18 +615,21 @@ function CubicSpline(u::uType, linear_lookup = seems_linear(assume_linear_t, t) p = CubicSplineParameterCache(u, h, z, cache_parameters) A = CubicSpline( - u, t, nothing, p, h[1:(n + 1)], z, extrapolation_down, - extrapolation_up, cache_parameters, linear_lookup) + u, t, nothing, p, h[1:(n + 1)], z, extrapolation_left, + extrapolation_right, cache_parameters, linear_lookup) I = cumulative_integral(A, cache_parameters) - CubicSpline(u, t, I, p, h[1:(n + 1)], z, extrapolation_down, - extrapolation_up, cache_parameters, linear_lookup) + CubicSpline(u, t, I, p, h[1:(n + 1)], z, extrapolation_left, + extrapolation_right, cache_parameters, linear_lookup) end function CubicSpline( - u::uType, t; extrapolation_down::ExtrapolationType.T = ExtrapolationType.none, - extrapolation_up::ExtrapolationType.T = ExtrapolationType.none, cache_parameters = false, + u::uType, t; extrapolation::ExtrapolationType.T = ExtrapolationType.none, + extrapolation_left::ExtrapolationType.T = ExtrapolationType.none, + extrapolation_right::ExtrapolationType.T = ExtrapolationType.none, cache_parameters = false, assume_linear_t = 1e-2) where {uType <: AbstractVector} + extrapolation_left, extrapolation_right = munge_extrapolation( + extrapolation, extrapolation_left, extrapolation_right) u, t = munge_data(u, t) n = length(t) - 1 h = vcat(0, map(k -> t[k + 1] - t[k], 1:(length(t) - 1)), 0) @@ -597,16 +647,16 @@ function CubicSpline( p = CubicSplineParameterCache(u, h, z, cache_parameters) A = CubicSpline( - u, t, nothing, p, h[1:(n + 1)], z, extrapolation_down, - extrapolation_up, cache_parameters, assume_linear_t) + u, t, nothing, p, h[1:(n + 1)], z, extrapolation_left, + extrapolation_right, cache_parameters, assume_linear_t) I = cumulative_integral(A, cache_parameters) - CubicSpline(u, t, I, p, h[1:(n + 1)], z, extrapolation_down, - extrapolation_up, cache_parameters, assume_linear_t) + CubicSpline(u, t, I, p, h[1:(n + 1)], z, extrapolation_left, + extrapolation_right, cache_parameters, assume_linear_t) end """ - BSplineInterpolation(u, t, d, pVecType, knotVecType; extrapolation_down::ExtrapolationType.T = ExtrapolationType.none, - extrapolation_up::ExtrapolationType.T = ExtrapolationType.none, safetycopy = true) + BSplineInterpolation(u, t, d, pVecType, knotVecType; extrapolation_left::ExtrapolationType.T = ExtrapolationType.none, + extrapolation_right::ExtrapolationType.T = ExtrapolationType.none, safetycopy = true) It is a curve defined by the linear combination of `n` basis functions of degree `d` where `n` is the number of data points. For more information, refer [https://pages.mtu.edu/~shene/COURSES/cs3621/NOTES/spline/B-spline/bspline-curve.html](https://pages.mtu.edu/%7Eshene/COURSES/cs3621/NOTES/spline/B-spline/bspline-curve.html). Extrapolation is a constant polynomial of the end points on each side. @@ -621,10 +671,13 @@ Extrapolation is a constant polynomial of the end points on each side. ## Keyword Arguments - - `extrapolate_down`: The extrapolation type beyond the data in the negative `t` direction. - Defaults to `ExtrapolationType.none`. - - `extrapolate_up`: The extrapolation type beyond the data in the positive `t` direction. - Defaults to `ExtrapolationType.none`. + - `extrapolation`: The extrapolation type applied left and right of the data. Possible options + are `ExtrapolationType.none` (default), `ExtrapolationType.constant`, + `ExtrapolationType.linear` and `ExtrapolationType.extension`. + - `extrapolation_left`: The extrapolation type applied left of the data. See `extrapolation` for + the possible options. + - `extrapolation_right`: The extrapolation type applied right of the data. See `extrapolation` for + the possible options. - `assume_linear_t`: boolean value to specify a faster index lookup behaviour for evenly-distributed abscissae. Alternatively, a numerical threshold may be specified for a test based on the normalized standard deviation of the difference with respect @@ -641,8 +694,8 @@ struct BSplineInterpolation{uType, tType, pType, kType, cType, scType, T, N} <: sc::scType # Spline coefficients (preallocated memory) pVecType::Symbol knotVecType::Symbol - extrapolation_down::ExtrapolationType.T - extrapolation_up::ExtrapolationType.T + extrapolation_left::ExtrapolationType.T + extrapolation_right::ExtrapolationType.T iguesser::Guesser{tType} linear_lookup::Bool function BSplineInterpolation(u, @@ -654,8 +707,8 @@ struct BSplineInterpolation{uType, tType, pType, kType, cType, scType, T, N} <: sc, pVecType, knotVecType, - extrapolation_down, - extrapolation_up, + extrapolation_left, + extrapolation_right, assume_linear_t) linear_lookup = seems_linear(assume_linear_t, t) N = get_output_dim(u) @@ -669,8 +722,8 @@ struct BSplineInterpolation{uType, tType, pType, kType, cType, scType, T, N} <: sc, pVecType, knotVecType, - extrapolation_down, - extrapolation_up, + extrapolation_left, + extrapolation_right, Guesser(t), linear_lookup ) @@ -679,8 +732,11 @@ end function BSplineInterpolation( u::AbstractVector, t, d, pVecType, knotVecType; - extrapolation_down::ExtrapolationType.T = ExtrapolationType.none, - extrapolation_up::ExtrapolationType.T = ExtrapolationType.none, assume_linear_t = 1e-2) + extrapolation::ExtrapolationType.T = ExtrapolationType.none, + extrapolation_left::ExtrapolationType.T = ExtrapolationType.none, + extrapolation_right::ExtrapolationType.T = ExtrapolationType.none, assume_linear_t = 1e-2) + extrapolation_left, extrapolation_right = munge_extrapolation( + extrapolation, extrapolation_left, extrapolation_right) u, t = munge_data(u, t) n = length(t) n < d + 1 && error("BSplineInterpolation needs at least d + 1, i.e. $(d+1) points.") @@ -746,14 +802,17 @@ function BSplineInterpolation( sc = zeros(eltype(t), n) BSplineInterpolation( u, t, d, p, k, c, sc, pVecType, knotVecType, - extrapolation_down, extrapolation_up, assume_linear_t) + extrapolation_left, extrapolation_right, assume_linear_t) end function BSplineInterpolation( u::AbstractArray{T, N}, t, d, pVecType, knotVecType; - extrapolation_down::ExtrapolationType.T = ExtrapolationType.none, - extrapolation_up::ExtrapolationType.T = ExtrapolationType.none, + extrapolation::ExtrapolationType.T = ExtrapolationType.none, + extrapolation_left::ExtrapolationType.T = ExtrapolationType.none, + extrapolation_right::ExtrapolationType.T = ExtrapolationType.none, assume_linear_t = 1e-2) where {T, N} + extrapolation_left, extrapolation_right = munge_extrapolation( + extrapolation, extrapolation_left, extrapolation_right) u, t = munge_data(u, t) n = length(t) n < d + 1 && error("BSplineInterpolation needs at least d + 1, i.e. $(d+1) points.") @@ -822,12 +881,12 @@ function BSplineInterpolation( sc = zeros(eltype(t), n) BSplineInterpolation( u, t, d, p, k, c, sc, pVecType, knotVecType, - extrapolation_down, extrapolation_up, assume_linear_t) + extrapolation_left, extrapolation_right, assume_linear_t) end """ - BSplineApprox(u, t, d, h, pVecType, knotVecType; extrapolation_down::ExtrapolationType.T = ExtrapolationType.none, - extrapolation_up::ExtrapolationType.T = ExtrapolationType.none) + BSplineApprox(u, t, d, h, pVecType, knotVecType; extrapolation_left::ExtrapolationType.T = ExtrapolationType.none, + extrapolation_right::ExtrapolationType.T = ExtrapolationType.none) It is a regression based B-spline. The argument choices are the same as the `BSplineInterpolation`, with the additional parameter `h < length(t)` which is the number of control points to use, with smaller `h` indicating more smoothing. For more information, refer [http://www.cad.zju.edu.cn/home/zhx/GM/009/00-bsia.pdf](http://www.cad.zju.edu.cn/home/zhx/GM/009/00-bsia.pdf). @@ -844,10 +903,13 @@ Extrapolation is a constant polynomial of the end points on each side. ## Keyword Arguments - - `extrapolate_down`: The extrapolation type beyond the data in the negative `t` direction. - Defaults to `ExtrapolationType.none`. - - `extrapolate_up`: The extrapolation type beyond the data in the positive `t` direction. - Defaults to `ExtrapolationType.none`. + - `extrapolation`: The extrapolation type applied left and right of the data. Possible options + are `ExtrapolationType.none` (default), `ExtrapolationType.constant`, + `ExtrapolationType.linear` and `ExtrapolationType.extension`. + - `extrapolation_left`: The extrapolation type applied left of the data. See `extrapolation` for + the possible options. + - `extrapolation_right`: The extrapolation type applied right of the data. See `extrapolation` for + the possible options. - `assume_linear_t`: boolean value to specify a faster index lookup behaviour for evenly-distributed abscissae. Alternatively, a numerical threshold may be specified for a test based on the normalized standard deviation of the difference with respect @@ -865,8 +927,8 @@ struct BSplineApprox{uType, tType, pType, kType, cType, scType, T, N} <: sc::scType # Spline coefficients (preallocated memory) pVecType::Symbol knotVecType::Symbol - extrapolation_down::ExtrapolationType.T - extrapolation_up::ExtrapolationType.T + extrapolation_left::ExtrapolationType.T + extrapolation_right::ExtrapolationType.T iguesser::Guesser{tType} linear_lookup::Bool function BSplineApprox(u, @@ -879,8 +941,8 @@ struct BSplineApprox{uType, tType, pType, kType, cType, scType, T, N} <: sc, pVecType, knotVecType, - extrapolation_down, - extrapolation_up, + extrapolation_left, + extrapolation_right, assume_linear_t ) linear_lookup = seems_linear(assume_linear_t, t) @@ -896,8 +958,8 @@ struct BSplineApprox{uType, tType, pType, kType, cType, scType, T, N} <: sc, pVecType, knotVecType, - extrapolation_down, - extrapolation_up, + extrapolation_left, + extrapolation_right, Guesser(t), linear_lookup ) @@ -906,8 +968,11 @@ end function BSplineApprox( u::AbstractVector, t, d, h, pVecType, knotVecType; - extrapolation_down::ExtrapolationType.T = ExtrapolationType.none, - extrapolation_up::ExtrapolationType.T = ExtrapolationType.none, assume_linear_t = 1e-2) + extrapolation::ExtrapolationType.T = ExtrapolationType.none, + extrapolation_left::ExtrapolationType.T = ExtrapolationType.none, + extrapolation_right::ExtrapolationType.T = ExtrapolationType.none, assume_linear_t = 1e-2) + extrapolation_left, extrapolation_right = munge_extrapolation( + extrapolation, extrapolation_left, extrapolation_right) u, t = munge_data(u, t) n = length(t) h < d + 1 && error("BSplineApprox needs at least d + 1, i.e. $(d+1) control points.") @@ -994,14 +1059,17 @@ function BSplineApprox( sc = zeros(eltype(t), h) BSplineApprox( u, t, d, h, p, k, c, sc, pVecType, knotVecType, - extrapolation_down, extrapolation_up, assume_linear_t) + extrapolation_left, extrapolation_right, assume_linear_t) end function BSplineApprox( u::AbstractArray{T, N}, t, d, h, pVecType, knotVecType; - extrapolation_down::ExtrapolationType.T = ExtrapolationType.none, - extrapolation_up::ExtrapolationType.T = ExtrapolationType.none, + extrapolation::ExtrapolationType.T = ExtrapolationType.none, + extrapolation_left::ExtrapolationType.T = ExtrapolationType.none, + extrapolation_right::ExtrapolationType.T = ExtrapolationType.none, assume_linear_t = 1e-2) where {T, N} + extrapolation_left, extrapolation_right = munge_extrapolation( + extrapolation, extrapolation_left, extrapolation_right) u, t = munge_data(u, t) n = length(t) h < d + 1 && error("BSplineApprox needs at least d + 1, i.e. $(d+1) control points.") @@ -1093,11 +1161,11 @@ function BSplineApprox( sc = zeros(eltype(t), h) BSplineApprox( u, t, d, h, p, k, c, sc, pVecType, knotVecType, - extrapolation_down, extrapolation_up, assume_linear_t) + extrapolation_left, extrapolation_right, assume_linear_t) end """ - CubicHermiteSpline(du, u, t; extrapolation_down::ExtrapolationType.T = ExtrapolationType.none, - extrapolation_up::ExtrapolationType.T = ExtrapolationType.none, cache_parameters = false) + CubicHermiteSpline(du, u, t; extrapolation_left::ExtrapolationType.T = ExtrapolationType.none, + extrapolation_right::ExtrapolationType.T = ExtrapolationType.none, cache_parameters = false) It is a Cubic Hermite interpolation, which is a piece-wise third degree polynomial such that the value and the first derivative are equal to given values in the data points. @@ -1109,10 +1177,13 @@ It is a Cubic Hermite interpolation, which is a piece-wise third degree polynomi ## Keyword Arguments - - `extrapolate_down`: The extrapolation type beyond the data in the negative `t` direction. - Defaults to `ExtrapolationType.none`. - - `extrapolate_up`: The extrapolation type beyond the data in the positive `t` direction. - Defaults to `ExtrapolationType.none`. + - `extrapolation`: The extrapolation type applied left and right of the data. Possible options + are `ExtrapolationType.none` (default), `ExtrapolationType.constant`, + `ExtrapolationType.linear` and `ExtrapolationType.extension`. + - `extrapolation_left`: The extrapolation type applied left of the data. See `extrapolation` for + the possible options. + - `extrapolation_right`: The extrapolation type applied right of the data. See `extrapolation` for + the possible options. - `cache_parameters`: precompute parameters at initialization for faster interpolation computations. Note: if activated, `u` and `t` should not be modified. Defaults to `false`. - `assume_linear_t`: boolean value to specify a faster index lookup behaviour for evenly-distributed abscissae. Alternatively, a numerical threshold may be specified @@ -1126,40 +1197,43 @@ struct CubicHermiteSpline{uType, tType, IType, duType, pType, T, N} <: t::tType I::IType p::CubicHermiteParameterCache{pType} - extrapolation_down::ExtrapolationType.T - extrapolation_up::ExtrapolationType.T + extrapolation_left::ExtrapolationType.T + extrapolation_right::ExtrapolationType.T iguesser::Guesser{tType} cache_parameters::Bool linear_lookup::Bool function CubicHermiteSpline( - du, u, t, I, p, extrapolation_down, extrapolation_up, + du, u, t, I, p, extrapolation_left, extrapolation_right, cache_parameters, assume_linear_t) linear_lookup = seems_linear(assume_linear_t, t) N = get_output_dim(u) new{typeof(u), typeof(t), typeof(I), typeof(du), typeof(p.c₁), eltype(u), N}( - du, u, t, I, p, extrapolation_down, extrapolation_up, + du, u, t, I, p, extrapolation_left, extrapolation_right, Guesser(t), cache_parameters, linear_lookup) end end function CubicHermiteSpline( - du, u, t; extrapolation_down::ExtrapolationType.T = ExtrapolationType.none, - extrapolation_up::ExtrapolationType.T = ExtrapolationType.none, cache_parameters = false, assume_linear_t = 1e-2) + du, u, t; extrapolation::ExtrapolationType.T = ExtrapolationType.none, + extrapolation_left::ExtrapolationType.T = ExtrapolationType.none, + extrapolation_right::ExtrapolationType.T = ExtrapolationType.none, cache_parameters = false, assume_linear_t = 1e-2) @assert length(u)==length(du) "Length of `u` is not equal to length of `du`." + extrapolation_left, extrapolation_right = munge_extrapolation( + extrapolation, extrapolation_left, extrapolation_right) u, t = munge_data(u, t) linear_lookup = seems_linear(assume_linear_t, t) p = CubicHermiteParameterCache(du, u, t, cache_parameters) A = CubicHermiteSpline( - du, u, t, nothing, p, extrapolation_down, - extrapolation_up, cache_parameters, linear_lookup) + du, u, t, nothing, p, extrapolation_left, + extrapolation_right, cache_parameters, linear_lookup) I = cumulative_integral(A, cache_parameters) - CubicHermiteSpline(du, u, t, I, p, extrapolation_down, - extrapolation_up, cache_parameters, linear_lookup) + CubicHermiteSpline(du, u, t, I, p, extrapolation_left, + extrapolation_right, cache_parameters, linear_lookup) end """ - PCHIPInterpolation(u, t; extrapolation_down::ExtrapolationType.T = ExtrapolationType.none, - extrapolation_up::ExtrapolationType.T = ExtrapolationType.none, safetycopy = true) + PCHIPInterpolation(u, t; extrapolation_left::ExtrapolationType.T = ExtrapolationType.none, + extrapolation_right::ExtrapolationType.T = ExtrapolationType.none, safetycopy = true) It is a PCHIP Interpolation, which is a type of [`CubicHermiteSpline`](@ref) where the derivative values `du` are derived from the input data in such a way that the interpolation never overshoots the data. See [here](https://www.mathworks.com/content/dam/mathworks/mathworks-dot-com/moler/interp.pdf), @@ -1172,28 +1246,28 @@ section 3.4 for more details. ## Keyword Arguments - - `extrapolate_down`: The extrapolation type beyond the data in the negative `t` direction. - Defaults to `ExtrapolationType.none`. - - `extrapolate_up`: The extrapolation type beyond the data in the positive `t` direction. - Defaults to `ExtrapolationType.none`. + - `extrapolation`: The extrapolation type applied left and right of the data. Possible options + are `ExtrapolationType.none` (default), `ExtrapolationType.constant`, + `ExtrapolationType.linear` and `ExtrapolationType.extension`. + - `extrapolation_left`: The extrapolation type applied left of the data. See `extrapolation` for + the possible options. + - `extrapolation_right`: The extrapolation type applied right of the data. See `extrapolation` for + the possible options. - `cache_parameters`: precompute parameters at initialization for faster interpolation computations. Note: if activated, `u` and `t` should not be modified. Defaults to `false`. - `assume_linear_t`: boolean value to specify a faster index lookup behaviour for evenly-distributed abscissae. Alternatively, a numerical threshold may be specified for a test based on the normalized standard deviation of the difference with respect to the straight line (see [`looks_linear`](@ref)). Defaults to 1e-2. """ -function PCHIPInterpolation( - u, t; extrapolation_down::ExtrapolationType.T = ExtrapolationType.none, - extrapolation_up::ExtrapolationType.T = ExtrapolationType.none, cache_parameters = false, assume_linear_t = 1e-2) +function PCHIPInterpolation(u, t; kwargs...) u, t = munge_data(u, t) du = du_PCHIP(u, t) - CubicHermiteSpline( - du, u, t; extrapolation_down, extrapolation_up, cache_parameters, assume_linear_t) + CubicHermiteSpline(du, u, t; kwargs...) end """ - QuinticHermiteSpline(ddu, du, u, t; extrapolation_down::ExtrapolationType.T = ExtrapolationType.none, - extrapolation_up::ExtrapolationType.T = ExtrapolationType.none, safetycopy = true) + QuinticHermiteSpline(ddu, du, u, t; extrapolation_left::ExtrapolationType.T = ExtrapolationType.none, + extrapolation_right::ExtrapolationType.T = ExtrapolationType.none, safetycopy = true) It is a Quintic Hermite interpolation, which is a piece-wise fifth degree polynomial such that the value and the first and second derivative are equal to given values in the data points. @@ -1206,10 +1280,13 @@ It is a Quintic Hermite interpolation, which is a piece-wise fifth degree polyno ## Keyword Arguments - - `extrapolate_down`: The extrapolation type beyond the data in the negative `t` direction. - Defaults to `ExtrapolationType.none`. - - `extrapolate_up`: The extrapolation type beyond the data in the positive `t` direction. - Defaults to `ExtrapolationType.none`. + - `extrapolation`: The extrapolation type applied left and right of the data. Possible options + are `ExtrapolationType.none` (default), `ExtrapolationType.constant`, + `ExtrapolationType.linear` and `ExtrapolationType.extension`. + - `extrapolation_left`: The extrapolation type applied left of the data. See `extrapolation` for + the possible options. + - `extrapolation_right`: The extrapolation type applied right of the data. See `extrapolation` for + the possible options. - `cache_parameters`: precompute parameters at initialization for faster interpolation computations. Note: if activated, `u` and `t` should not be modified. Defaults to `false`. - `assume_linear_t`: boolean value to specify a faster index lookup behaviour for evenly-distributed abscissae. Alternatively, a numerical threshold may be specified @@ -1224,36 +1301,39 @@ struct QuinticHermiteSpline{uType, tType, IType, duType, dduType, pType, T, N} < t::tType I::IType p::QuinticHermiteParameterCache{pType} - extrapolation_down::ExtrapolationType.T - extrapolation_up::ExtrapolationType.T + extrapolation_left::ExtrapolationType.T + extrapolation_right::ExtrapolationType.T iguesser::Guesser{tType} cache_parameters::Bool linear_lookup::Bool function QuinticHermiteSpline( - ddu, du, u, t, I, p, extrapolation_down, - extrapolation_up, cache_parameters, assume_linear_t) + ddu, du, u, t, I, p, extrapolation_left, + extrapolation_right, cache_parameters, assume_linear_t) linear_lookup = seems_linear(assume_linear_t, t) N = get_output_dim(u) new{typeof(u), typeof(t), typeof(I), typeof(du), typeof(ddu), typeof(p.c₁), eltype(u), N}( - ddu, du, u, t, I, p, extrapolation_down, extrapolation_up, + ddu, du, u, t, I, p, extrapolation_left, extrapolation_right, Guesser(t), cache_parameters, linear_lookup) end end function QuinticHermiteSpline( - ddu, du, u, t; extrapolation_down::ExtrapolationType.T = ExtrapolationType.none, - extrapolation_up::ExtrapolationType.T = ExtrapolationType.none, + ddu, du, u, t; extrapolation::ExtrapolationType.T = ExtrapolationType.none, + extrapolation_left::ExtrapolationType.T = ExtrapolationType.none, + extrapolation_right::ExtrapolationType.T = ExtrapolationType.none, cache_parameters = false, assume_linear_t = 1e-2) @assert length(u)==length(du)==length(ddu) "Length of `u` is not equal to length of `du` or `ddu`." + extrapolation_left, extrapolation_right = munge_extrapolation( + extrapolation, extrapolation_left, extrapolation_right) u, t = munge_data(u, t) linear_lookup = seems_linear(assume_linear_t, t) p = QuinticHermiteParameterCache(ddu, du, u, t, cache_parameters) A = QuinticHermiteSpline( - ddu, du, u, t, nothing, p, extrapolation_down, - extrapolation_up, cache_parameters, linear_lookup) + ddu, du, u, t, nothing, p, extrapolation_left, + extrapolation_right, cache_parameters, linear_lookup) I = cumulative_integral(A, cache_parameters) QuinticHermiteSpline( - ddu, du, u, t, I, p, extrapolation_down, - extrapolation_up, cache_parameters, linear_lookup) + ddu, du, u, t, I, p, extrapolation_left, + extrapolation_right, cache_parameters, linear_lookup) end diff --git a/src/interpolation_methods.jl b/src/interpolation_methods.jl index 74494ff5..a691fbb2 100644 --- a/src/interpolation_methods.jl +++ b/src/interpolation_methods.jl @@ -9,33 +9,33 @@ function _interpolate(A, t) end function _extrapolate_down(A, t) - (; extrapolation_down) = A - if extrapolation_down == ExtrapolationType.none + (; extrapolation_left) = A + if extrapolation_left == ExtrapolationType.none throw(DownExtrapolationError()) - elseif extrapolation_down == ExtrapolationType.constant + elseif extrapolation_left == ExtrapolationType.constant slope = derivative(A, first(A.t)) first(A.u) + slope * zero(t) - elseif extrapolation_down == ExtrapolationType.linear + elseif extrapolation_left == ExtrapolationType.linear slope = derivative(A, first(A.t)) first(A.u) + slope * (t - first(A.t)) else - # extrapolation_down == ExtrapolationType.extension + # extrapolation_left == ExtrapolationType.extension _interpolate(A, t, A.iguesser) end end function _extrapolate_up(A, t) - (; extrapolation_up) = A - if extrapolation_up == ExtrapolationType.none + (; extrapolation_right) = A + if extrapolation_right == ExtrapolationType.none throw(UpExtrapolationError()) - elseif extrapolation_up == ExtrapolationType.constant + elseif extrapolation_right == ExtrapolationType.constant slope = derivative(A, last(A.t)) last(A.u) + slope * zero(t) - elseif extrapolation_up == ExtrapolationType.linear + elseif extrapolation_right == ExtrapolationType.linear slope = derivative(A, last(A.t)) last(A.u) + slope * (t - last(A.t)) else - # extrapolation_up == ExtrapolationType.extension + # extrapolation_right == ExtrapolationType.extension _interpolate(A, t, A.iguesser) end end diff --git a/src/interpolation_utils.jl b/src/interpolation_utils.jl index 0f922a93..f12bfa07 100644 --- a/src/interpolation_utils.jl +++ b/src/interpolation_utils.jl @@ -303,3 +303,11 @@ function integrate_quintic_polynomial(t1, t2, offset, a, b, c, d, e, f) cube_diff_factor * (c / 3 + f * t_cb_sum / 6)) + e * (t2_rel^5 - t1_rel^5) / 5 end + +function munge_extrapolation(extrapolation, extrapolation_left, extrapolation_right) + if extrapolation == ExtrapolationType.none + extrapolation_left, extrapolation_right + else + extrapolation, extrapolation + end +end diff --git a/test/derivative_tests.jl b/test/derivative_tests.jl index d81f346b..df96b0fb 100644 --- a/test/derivative_tests.jl +++ b/test/derivative_tests.jl @@ -11,8 +11,8 @@ using ForwardDiff function test_derivatives(method; args = [], kwargs = [], name::String) kwargs_extrapolation = (method == Curvefit) ? [:extrapolate => true] : - [:extrapolation_up => ExtrapolationType.extension, - :extrapolation_down => ExtrapolationType.extension] + [:extrapolation_right => ExtrapolationType.extension, + :extrapolation_left => ExtrapolationType.extension] func = method(args...; kwargs..., kwargs_extrapolation...) (; t) = func trange = collect(range(minimum(t) - 5.0, maximum(t) + 5.0, step = 0.1)) @@ -241,8 +241,7 @@ end t = [0.0, 62.25, 109.66, 162.66, 205.8, 252.3] test_derivatives(CubicHermiteSpline; args = [du, u, t], name = "Cubic Hermite Spline") - A = CubicHermiteSpline(du, u, t; extrapolation_up = ExtrapolationType.extension, - extrapolation_down = ExtrapolationType.extension) + A = CubicHermiteSpline(du, u, t; extrapolation = ExtrapolationType.extension) @test derivative.(Ref(A), t) ≈ du @test derivative(A, 100.0)≈0.0105409 rtol=1e-5 @test derivative(A, 300.0)≈-0.0806717 rtol=1e-5 @@ -255,8 +254,7 @@ end t = [0.0, 62.25, 109.66, 162.66, 205.8, 252.3] test_derivatives(QuinticHermiteSpline; args = [ddu, du, u, t], name = "Quintic Hermite Spline") - A = QuinticHermiteSpline(ddu, du, u, t; extrapolation_up = ExtrapolationType.extension, - extrapolation_down = ExtrapolationType.extension) + A = QuinticHermiteSpline(ddu, du, u, t; extrapolation = ExtrapolationType.extension) @test derivative.(Ref(A), t) ≈ du @test derivative.(Ref(A), t, 2) ≈ ddu @test derivative(A, 100.0)≈0.0103916 rtol=1e-5 @@ -335,8 +333,7 @@ end @testset "Jacobian tests" begin u = rand(5) t = 0:4 - interp = LinearInterpolation(u, t, extrapolation_up = ExtrapolationType.extension, - extrapolation_down = ExtrapolationType.extension) + interp = LinearInterpolation(u, t, extrapolation = ExtrapolationType.extension) grad1 = ForwardDiff.derivative(interp, 2.4) myvec = rand(20) .* 4.0 diff --git a/test/extrapolation_tests.jl b/test/extrapolation_tests.jl index e87fd340..9ba616f4 100644 --- a/test/extrapolation_tests.jl +++ b/test/extrapolation_tests.jl @@ -2,8 +2,8 @@ using DataInterpolations function test_extrapolation_errors(method, u, t) A = method(u, t) - @test A.extrapolation_up == ExtrapolationType.none - @test A.extrapolation_down == ExtrapolationType.none + @test A.extrapolation_right == ExtrapolationType.none + @test A.extrapolation_left == ExtrapolationType.none for (error_type, t_eval) in zip( (DataInterpolations.DownExtrapolationError, DataInterpolations.UpExtrapolationError), @@ -19,8 +19,8 @@ function test_extrapolation_errors(method, u, t) end function test_constant_extrapolation(method, u, t) - A = method(u, t; extrapolation_down = ExtrapolationType.constant, - extrapolation_up = ExtrapolationType.constant) + A = method(u, t; extrapolation_left = ExtrapolationType.constant, + extrapolation_right = ExtrapolationType.constant) t_lower = first(t) - 1 t_upper = last(t) + 1 @test A(t_lower) == first(u) @@ -41,7 +41,7 @@ end for extrapolation_type in [ExtrapolationType.linear, ExtrapolationType.extension] # Down extrapolation - A = LinearInterpolation(u, t; extrapolation_down = extrapolation_type) + A = LinearInterpolation(u, t; extrapolation_left = extrapolation_type) t_eval = 0.0 @test A(t_eval) == 0.0 @test DataInterpolations.derivative(A, t_eval) == 1.0 @@ -50,7 +50,7 @@ end t_eval = 3.0 # Up extrapolation - A = LinearInterpolation(u, t; extrapolation_up = extrapolation_type) + A = LinearInterpolation(u, t; extrapolation_right = extrapolation_type) t_eval = 3.0 @test A(t_eval) == 3.0 @test DataInterpolations.derivative(A, t_eval) == 1.0 @@ -68,7 +68,7 @@ end test_constant_extrapolation(LinearInterpolation, u, t) # Linear down extrapolation - A = QuadraticInterpolation(u, t; extrapolation_down = ExtrapolationType.linear) + A = QuadraticInterpolation(u, t; extrapolation_left = ExtrapolationType.linear) t_eval = 0.0 @test A(t_eval) ≈ -2.5 @test DataInterpolations.derivative(A, t_eval) == 3.5 @@ -76,7 +76,7 @@ end @test DataInterpolations.integral(A, t_eval) ≈ 0.75 # Linear up extrapolation - A = QuadraticInterpolation(u, t; extrapolation_up = ExtrapolationType.linear) + A = QuadraticInterpolation(u, t; extrapolation_right = ExtrapolationType.linear) t_eval = 4.0 @test A(t_eval) ≈ -0.5 @test DataInterpolations.derivative(A, t_eval) == -2.5 @@ -86,7 +86,7 @@ end # Extension down extrapolation f = t -> (-3t^2 + 13t - 8) / 2 df = t -> (-6t + 13) / 2 - A = QuadraticInterpolation(u, t; extrapolation_down = ExtrapolationType.extension) + A = QuadraticInterpolation(u, t; extrapolation_left = ExtrapolationType.extension) t_eval = 0.0 @test A(t_eval) ≈ -4.0 @test DataInterpolations.derivative(A, t_eval) == df(t_eval) @@ -94,7 +94,7 @@ end @test DataInterpolations.integral(A, t_eval) ≈ 1.25 # Extension up extrapolation - A = QuadraticInterpolation(u, t; extrapolation_up = ExtrapolationType.extension) + A = QuadraticInterpolation(u, t; extrapolation_right = ExtrapolationType.extension) t_eval = 4.0 @test A(t_eval) ≈ -2.0 @test DataInterpolations.derivative(A, t_eval) == df(t_eval) diff --git a/test/integral_inverse_tests.jl b/test/integral_inverse_tests.jl index 0e9e4cf2..9ba0b158 100644 --- a/test/integral_inverse_tests.jl +++ b/test/integral_inverse_tests.jl @@ -3,8 +3,7 @@ using DataInterpolations: integral, derivative, invert_integral using FiniteDifferences function test_integral_inverses(method; args = [], kwargs = []) - A = method(args...; kwargs..., extrapolation_down = ExtrapolationType.extension, - extrapolation_up = ExtrapolationType.extension) + A = method(args...; kwargs..., extrapolation = ExtrapolationType.extension) @test hasfield(typeof(A), :I) A_intinv = invert_integral(A) @test A_intinv isa DataInterpolations.AbstractIntegralInverseInterpolation diff --git a/test/integral_tests.jl b/test/integral_tests.jl index 142e0d1f..c37d9d8d 100644 --- a/test/integral_tests.jl +++ b/test/integral_tests.jl @@ -6,8 +6,8 @@ using RegularizationTools using StableRNGs function test_integral(method; args = [], kwargs = [], name::String) - func = method(args...; kwargs..., extrapolation_up = ExtrapolationType.extension, - extrapolation_down = ExtrapolationType.extension) + func = method(args...; kwargs..., extrapolation_left = ExtrapolationType.extension, + extrapolation_right = ExtrapolationType.extension) (; t) = func t1 = minimum(t) t2 = maximum(t) diff --git a/test/interface.jl b/test/interface.jl index 40fb25f6..29f6864d 100644 --- a/test/interface.jl +++ b/test/interface.jl @@ -18,10 +18,8 @@ end @testset "Symbolics" begin u = 2.0collect(1:10) t = 1.0collect(1:10) - A = LinearInterpolation(u, t; extrapolation_up = ExtrapolationType.extension, - extrapolation_down = ExtrapolationType.extension) - B = LinearInterpolation(u .^ 2, t; extrapolation_up = ExtrapolationType.extension, - extrapolation_down = ExtrapolationType.extension) + A = LinearInterpolation(u, t; extrapolation = ExtrapolationType.extension) + B = LinearInterpolation(u .^ 2, t; extrapolation = ExtrapolationType.extension) @variables t x(t) substitute(A(t), Dict(t => x)) t_val = 2.7 diff --git a/test/interpolation_tests.jl b/test/interpolation_tests.jl index 14bc93b8..3b374c00 100644 --- a/test/interpolation_tests.jl +++ b/test/interpolation_tests.jl @@ -8,8 +8,8 @@ function test_interpolation_type(T) @test T <: DataInterpolations.AbstractInterpolation @test hasfield(T, :u) @test hasfield(T, :t) - @test hasfield(T, :extrapolation_up) - @test hasfield(T, :extrapolation_down) + @test hasfield(T, :extrapolation_right) + @test hasfield(T, :extrapolation_left) @test hasfield(T, :iguesser) @test !isempty(methods(DataInterpolations._interpolate, (T, Any, Number))) @test !isempty(methods(DataInterpolations._integral, (T, Number, Number, Number))) @@ -31,8 +31,7 @@ end for t in (1.0:10.0, 1.0collect(1:10)) u = 2.0collect(1:10) #t = 1.0collect(1:10) - A = LinearInterpolation(u, t; extrapolation_up = ExtrapolationType.extension, - extrapolation_down = ExtrapolationType.extension) + A = LinearInterpolation(u, t; extrapolation = ExtrapolationType.extension) for (_t, _u) in zip(t, u) @test A(_t) == _u @@ -42,8 +41,7 @@ end @test A(11) == 22 u = vcat(2.0collect(1:10)', 3.0collect(1:10)') - A = LinearInterpolation(u, t; extrapolation_up = ExtrapolationType.extension, - extrapolation_down = ExtrapolationType.extension) + A = LinearInterpolation(u, t; extrapolation = ExtrapolationType.extension) for (_t, _u) in zip(t, eachcol(u)) @test A(_t) == _u @@ -56,8 +54,7 @@ end y = 2:4 u_ = x' .* y u = [u_[:, i] for i in 1:size(u_, 2)] - A = LinearInterpolation(u, t; extrapolation_up = ExtrapolationType.extension, - extrapolation_down = ExtrapolationType.extension) + A = LinearInterpolation(u, t; extrapolation = ExtrapolationType.extension) @test A(0) == [0.0, 0.0, 0.0] @test A(5.5) == [11.0, 16.5, 22.0] @test A(11) == [22.0, 33.0, 44.0] @@ -68,8 +65,7 @@ end u_ = x' .* y u = [u_[:, i:(i + 1)] for i in 1:2:10] t = 1.0collect(2:2:10) - A = LinearInterpolation(u, t; extrapolation_up = ExtrapolationType.extension, - extrapolation_down = ExtrapolationType.extension) + A = LinearInterpolation(u, t; extrapolation = ExtrapolationType.extension) @test A(0) == [-2.0 0.0; -3.0 0.0; -4.0 0.0] @test A(3) == [4.0 6.0; 6.0 9.0; 8.0 12.0] @@ -79,8 +75,7 @@ end # with NaNs (#113) u = [NaN, 1.0, 2.0, 3.0] t = 1:4 - A = LinearInterpolation(u, t; extrapolation_up = ExtrapolationType.extension, - extrapolation_down = ExtrapolationType.extension) + A = LinearInterpolation(u, t; extrapolation = ExtrapolationType.extension) @test isnan(A(1.0)) @test A(2.0) == 1.0 @test A(2.5) == 1.5 @@ -88,8 +83,7 @@ end @test A(4.0) == 3.0 u = [0.0, NaN, 2.0, 3.0] - A = LinearInterpolation(u, t; extrapolation_up = ExtrapolationType.extension, - extrapolation_down = ExtrapolationType.extension) + A = LinearInterpolation(u, t; extrapolation = ExtrapolationType.extension) @test A(1.0) == 0.0 @test isnan(A(2.0)) @test isnan(A(2.5)) @@ -97,8 +91,7 @@ end @test A(4.0) == 3.0 u = [0.0, 1.0, NaN, 3.0] - A = LinearInterpolation(u, t; extrapolation_up = ExtrapolationType.extension, - extrapolation_down = ExtrapolationType.extension) + A = LinearInterpolation(u, t; extrapolation = ExtrapolationType.extension) @test A(1.0) == 0.0 @test A(2.0) == 1.0 @test isnan(A(2.5)) @@ -106,8 +99,7 @@ end @test A(4.0) == 3.0 u = [0.0, 1.0, 2.0, NaN] - A = LinearInterpolation(u, t; extrapolation_up = ExtrapolationType.extension, - extrapolation_down = ExtrapolationType.extension) + A = LinearInterpolation(u, t; extrapolation = ExtrapolationType.extension) @test A(1.0) == 0.0 @test A(2.0) == 1.0 @test A(3.0) == 2.0 @@ -117,20 +109,16 @@ end # Test type stability u = Float32.(1:5) t = Float32.(1:5) - A1 = LinearInterpolation(u, t; extrapolation_up = ExtrapolationType.extension, - extrapolation_down = ExtrapolationType.extension) + A1 = LinearInterpolation(u, t; extrapolation = ExtrapolationType.extension) u = 1:5 t = 1:5 - A2 = LinearInterpolation(u, t; extrapolation_up = ExtrapolationType.extension, - extrapolation_down = ExtrapolationType.extension) + A2 = LinearInterpolation(u, t; extrapolation = ExtrapolationType.extension) u = [1 // i for i in 1:5] t = (1:5) - A3 = LinearInterpolation(u, t; extrapolation_up = ExtrapolationType.extension, - extrapolation_down = ExtrapolationType.extension) + A3 = LinearInterpolation(u, t; extrapolation = ExtrapolationType.extension) u = [1 // i for i in 1:5] t = [1 // (6 - i) for i in 1:5] - A4 = LinearInterpolation(u, t; extrapolation_up = ExtrapolationType.extension, - extrapolation_down = ExtrapolationType.extension) + A4 = LinearInterpolation(u, t; extrapolation = ExtrapolationType.extension) F32 = Float32(1) F64 = Float64(1) @@ -150,15 +138,13 @@ end # Nan time value: t = 0.0:3 # Floats u = [0, -2, -1, -2] - A = LinearInterpolation(u, t; extrapolation_up = ExtrapolationType.extension, - extrapolation_down = ExtrapolationType.extension) + A = LinearInterpolation(u, t; extrapolation = ExtrapolationType.extension) dA = t -> ForwardDiff.derivative(A, t) @test isnan(dA(NaN)) t = 0:3 # Integers u = [0, -2, -1, -2] - A = LinearInterpolation(u, t; extrapolation_up = ExtrapolationType.extension, - extrapolation_down = ExtrapolationType.extension) + A = LinearInterpolation(u, t; extrapolation = ExtrapolationType.extension) dA = t -> ForwardDiff.derivative(A, t) @test isnan(dA(NaN)) @@ -171,8 +157,7 @@ end # Test array-valued interpolation u = collect.(2.0collect(1:10)) t = 1.0collect(1:10) - A = LinearInterpolation(u, t; extrapolation_up = ExtrapolationType.extension, - extrapolation_down = ExtrapolationType.extension) + A = LinearInterpolation(u, t; extrapolation = ExtrapolationType.extension) @test A(0) == fill(0.0) @test A(5.5) == fill(11.0) @test A(11) == fill(22) @@ -187,8 +172,7 @@ end # Test extrapolation u = 2.0collect(1:10) t = 1.0collect(1:10) - A = LinearInterpolation(u, t; extrapolation_up = ExtrapolationType.extension, - extrapolation_down = ExtrapolationType.extension) + A = LinearInterpolation(u, t; extrapolation = ExtrapolationType.extension) @test A(-1.0) == -2.0 @test A(11.0) == 22.0 A = LinearInterpolation(u, t) @@ -202,8 +186,7 @@ end u = [1.0, 4.0, 9.0, 16.0] t = [1.0, 2.0, 3.0, 4.0] - A = QuadraticInterpolation(u, t; extrapolation_up = ExtrapolationType.extension, - extrapolation_down = ExtrapolationType.extension) + A = QuadraticInterpolation(u, t; extrapolation = ExtrapolationType.extension) for (_t, _u) in zip(t, u) @test A(_t) == _u @@ -219,8 +202,7 @@ end u = [1.0, 4.0, 9.0, 16.0] t = [1.0, 2.0, 3.0, 4.0] A = QuadraticInterpolation( - u, t, :Backward; extrapolation_up = ExtrapolationType.extension, - extrapolation_down = ExtrapolationType.extension) + u, t, :Backward; extrapolation = ExtrapolationType.extension) for (_t, _u) in zip(t, u) @test A(_t) == _u @@ -258,8 +240,7 @@ end # Matrix interpolation test u = [1.0 4.0 9.0 16.0; 1.0 4.0 9.0 16.0] - A = QuadraticInterpolation(u, t; extrapolation_up = ExtrapolationType.extension, - extrapolation_down = ExtrapolationType.extension) + A = QuadraticInterpolation(u, t; extrapolation = ExtrapolationType.extension) for (_t, _u) in zip(t, eachcol(u)) @test A(_t) == _u @@ -272,8 +253,7 @@ end u_ = [1.0, 4.0, 9.0, 16.0]' .* ones(5) u = [u_[:, i] for i in 1:size(u_, 2)] - A = QuadraticInterpolation(u, t; extrapolation_up = ExtrapolationType.extension, - extrapolation_down = ExtrapolationType.extension) + A = QuadraticInterpolation(u, t; extrapolation = ExtrapolationType.extension) @test A(0) == zeros(5) @test A(1.5) == 2.25 * ones(5) @test A(2.5) == 6.25 * ones(5) @@ -281,8 +261,7 @@ end @test A(5.0) == 25.0 * ones(5) u = [repeat(u[i], 1, 3) for i in 1:4] - A = QuadraticInterpolation(u, t; extrapolation_up = ExtrapolationType.extension, - extrapolation_down = ExtrapolationType.extension) + A = QuadraticInterpolation(u, t; extrapolation = ExtrapolationType.extension) @test A(0) == zeros(5, 3) @test A(1.5) == 2.25 * ones(5, 3) @test A(2.5) == 6.25 * ones(5, 3) @@ -292,8 +271,7 @@ end # Test extrapolation u = [1.0, 4.5, 6.0, 2.0] t = [1.0, 2.0, 3.0, 4.0] - A = QuadraticInterpolation(u, t; extrapolation_up = ExtrapolationType.extension, - extrapolation_down = ExtrapolationType.extension) + A = QuadraticInterpolation(u, t; extrapolation = ExtrapolationType.extension) @test A(0.0) == -4.5 @test A(5.0) == -7.5 A = QuadraticInterpolation(u, t) @@ -353,8 +331,7 @@ end # Test extrapolation u = [1.0, 4.0, 9.0] t = [1.0, 2.0, 3.0] - A = LagrangeInterpolation(u, t; extrapolation_up = ExtrapolationType.extension, - extrapolation_down = ExtrapolationType.extension) + A = LagrangeInterpolation(u, t; extrapolation = ExtrapolationType.extension) @test A(0.0) == 0.0 @test A(4.0) == 16.0 A = LagrangeInterpolation(u, t) @@ -385,8 +362,7 @@ end test_cached_index(A) # Test extrapolation - A = AkimaInterpolation(u, t; extrapolation_up = ExtrapolationType.extension, - extrapolation_down = ExtrapolationType.extension) + A = AkimaInterpolation(u, t; extrapolation = ExtrapolationType.extension) @test A(-1.0) ≈ -5.0 @test A(11.0) ≈ -3.924742268041234 A = AkimaInterpolation(u, t) @@ -401,8 +377,7 @@ end @testset "Vector case" for u in [[1.0, 2.0, 0.0, 1.0], ["B", "C", "A", "B"]] A = ConstantInterpolation( - u, t, dir = :right; extrapolation_up = ExtrapolationType.extension, - extrapolation_down = ExtrapolationType.extension) + u, t, dir = :right; extrapolation = ExtrapolationType.extension) @test A(0.5) == u[1] @test A(1.0) == u[1] @test A(1.5) == u[2] @@ -414,8 +389,7 @@ end @test A(4.5) == u[1] test_cached_index(A) - A = ConstantInterpolation(u, t; extrapolation_up = ExtrapolationType.extension, - extrapolation_down = ExtrapolationType.extension) # dir=:left is default + A = ConstantInterpolation(u, t; extrapolation = ExtrapolationType.extension) # dir=:left is default @test A(0.5) == u[1] @test A(1.0) == u[1] @test A(1.5) == u[1] @@ -433,8 +407,7 @@ end ["B" "C" "A" "B"; "B" "C" "A" "B"] ] A = ConstantInterpolation( - u, t, dir = :right; extrapolation_up = ExtrapolationType.extension, - extrapolation_down = ExtrapolationType.extension) + u, t, dir = :right; extrapolation = ExtrapolationType.extension) @test A(0.5) == u[:, 1] @test A(1.0) == u[:, 1] @test A(1.5) == u[:, 2] @@ -446,8 +419,7 @@ end @test A(4.5) == u[:, 1] test_cached_index(A) - A = ConstantInterpolation(u, t; extrapolation_up = ExtrapolationType.extension, - extrapolation_down = ExtrapolationType.extension) # dir=:left is default + A = ConstantInterpolation(u, t; extrapolation = ExtrapolationType.extension) # dir=:left is default @test A(0.5) == u[:, 1] @test A(1.0) == u[:, 1] @test A(1.5) == u[:, 1] @@ -464,8 +436,7 @@ end [[1.0, 2.0], [0.0, 1.0], [1.0, 2.0], [0.0, 1.0]], [["B", "C"], ["A", "B"], ["B", "C"], ["A", "B"]]] A = ConstantInterpolation( - u, t, dir = :right; extrapolation_up = ExtrapolationType.extension, - extrapolation_down = ExtrapolationType.extension) + u, t, dir = :right; extrapolation = ExtrapolationType.extension) @test A(0.5) == u[1] @test A(1.0) == u[1] @test A(1.5) == u[2] @@ -477,8 +448,7 @@ end @test A(4.5) == u[4] test_cached_index(A) - A = ConstantInterpolation(u, t; extrapolation_up = ExtrapolationType.extension, - extrapolation_down = ExtrapolationType.extension) # dir=:left is default + A = ConstantInterpolation(u, t; extrapolation = ExtrapolationType.extension) # dir=:left is default @test A(0.5) == u[1] @test A(1.0) == u[1] @test A(1.5) == u[1] @@ -495,8 +465,7 @@ end [[1.0 2.0; 1.0 2.0], [0.0 1.0; 0.0 1.0], [1.0 2.0; 1.0 2.0], [0.0 1.0; 0.0 1.0]], [["B" "C"; "B" "C"], ["A" "B"; "A" "B"], ["B" "C"; "B" "C"], ["A" "B"; "A" "B"]]] A = ConstantInterpolation( - u, t, dir = :right; extrapolation_up = ExtrapolationType.extension, - extrapolation_down = ExtrapolationType.extension) + u, t, dir = :right; extrapolation = ExtrapolationType.extension) @test A(0.5) == u[1] @test A(1.0) == u[1] @test A(1.5) == u[2] @@ -508,8 +477,7 @@ end @test A(4.5) == u[4] test_cached_index(A) - A = ConstantInterpolation(u, t; extrapolation_up = ExtrapolationType.extension, - extrapolation_down = ExtrapolationType.extension) # dir=:left is default + A = ConstantInterpolation(u, t; extrapolation = ExtrapolationType.extension) # dir=:left is default @test A(0.5) == u[1] @test A(1.0) == u[1] @test A(1.5) == u[1] @@ -524,8 +492,7 @@ end # Test extrapolation u = [1.0, 2.0, 0.0, 1.0] - A = ConstantInterpolation(u, t; extrapolation_up = ExtrapolationType.extension, - extrapolation_down = ExtrapolationType.extension) + A = ConstantInterpolation(u, t; extrapolation = ExtrapolationType.extension) @test A(-1.0) == 1.0 @test A(11.0) == 1.0 A = ConstantInterpolation(u, t) @@ -535,8 +502,7 @@ end # Test extrapolation with infs with regularly spaced t u = [1.67e7, 1.6867e7, 1.7034e7, 1.7201e7, 1.7368e7] t = [0.0, 0.1, 0.2, 0.3, 0.4] - A = ConstantInterpolation(u, t; extrapolation_up = ExtrapolationType.extension, - extrapolation_down = ExtrapolationType.extension) + A = ConstantInterpolation(u, t; extrapolation = ExtrapolationType.extension) @test A(Inf) == last(u) @test A(-Inf) == first(u) end @@ -547,8 +513,7 @@ end u = [0.0, 1.0, 3.0] t = [-1.0, 0.0, 1.0] - A = QuadraticSpline(u, t; extrapolation_up = ExtrapolationType.extension, - extrapolation_down = ExtrapolationType.extension) + A = QuadraticSpline(u, t; extrapolation = ExtrapolationType.extension) # Solution P₁ = x -> 0.5 * (x + 1) * (x + 2) @@ -564,16 +529,14 @@ end u_ = [0.0, 1.0, 3.0]' .* ones(4) u = [u_[:, i] for i in 1:size(u_, 2)] - A = QuadraticSpline(u, t; extrapolation_up = ExtrapolationType.extension, - extrapolation_down = ExtrapolationType.extension) + A = QuadraticSpline(u, t; extrapolation = ExtrapolationType.extension) @test A(-2.0) == P₁(-2.0) * ones(4) @test A(-0.5) == P₁(-0.5) * ones(4) @test A(0.7) == P₁(0.7) * ones(4) @test A(2.0) == P₁(2.0) * ones(4) u = [repeat(u[i], 1, 3) for i in 1:3] - A = QuadraticSpline(u, t; extrapolation_up = ExtrapolationType.extension, - extrapolation_down = ExtrapolationType.extension) + A = QuadraticSpline(u, t; extrapolation = ExtrapolationType.extension) @test A(-2.0) == P₁(-2.0) * ones(4, 3) @test A(-0.5) == P₁(-0.5) * ones(4, 3) @test A(0.7) == P₁(0.7) * ones(4, 3) @@ -582,8 +545,7 @@ end # Test extrapolation u = [0.0, 1.0, 3.0] t = [-1.0, 0.0, 1.0] - A = QuadraticSpline(u, t; extrapolation_up = ExtrapolationType.extension, - extrapolation_down = ExtrapolationType.extension) + A = QuadraticSpline(u, t; extrapolation = ExtrapolationType.extension) @test A(-2.0) == 0.0 @test A(2.0) == 6.0 A = QuadraticSpline(u, t) @@ -597,8 +559,7 @@ end u = [0.0, 1.0, 3.0] t = [-1.0, 0.0, 1.0] - A = CubicSpline(u, t; extrapolation_up = ExtrapolationType.extension, - extrapolation_down = ExtrapolationType.extension) + A = CubicSpline(u, t; extrapolation = ExtrapolationType.extension) test_cached_index(A) # Solution @@ -617,8 +578,7 @@ end u_ = [0.0, 1.0, 3.0]' .* ones(4) u = [u_[:, i] for i in 1:size(u_, 2)] - A = CubicSpline(u, t; extrapolation_up = ExtrapolationType.extension, - extrapolation_down = ExtrapolationType.extension) + A = CubicSpline(u, t; extrapolation = ExtrapolationType.extension) for x in (-1.5, -0.5, -0.7) @test A(x) ≈ P₁(x) * ones(4) end @@ -627,8 +587,7 @@ end end u = [repeat(u[i], 1, 3) for i in 1:3] - A = CubicSpline(u, t; extrapolation_up = ExtrapolationType.extension, - extrapolation_down = ExtrapolationType.extension) + A = CubicSpline(u, t; extrapolation = ExtrapolationType.extension) for x in (-1.5, -0.5, -0.7) @test A(x) ≈ P₁(x) * ones(4, 3) end @@ -639,8 +598,7 @@ end # Test extrapolation u = [0.0, 1.0, 3.0] t = [-1.0, 0.0, 1.0] - A = CubicSpline(u, t; extrapolation_up = ExtrapolationType.extension, - extrapolation_down = ExtrapolationType.extension) + A = CubicSpline(u, t; extrapolation = ExtrapolationType.extension) @test A(-2.0) ≈ -1.0 @test A(2.0) ≈ 5.0 A = CubicSpline(u, t) @@ -685,8 +643,7 @@ end # Test extrapolation A = BSplineInterpolation( - u, t, 2, :Uniform, :Uniform; extrapolation_up = ExtrapolationType.extension, - extrapolation_down = ExtrapolationType.extension) + u, t, 2, :Uniform, :Uniform; extrapolation = ExtrapolationType.extension) @test A(-1.0) == u[1] @test A(300.0) == u[end] A = BSplineInterpolation(u, t, 2, :Uniform, :Uniform) @@ -707,8 +664,7 @@ end # Test extrapolation A = BSplineInterpolation( - u, t, 2, :ArcLen, :Average; extrapolation_up = ExtrapolationType.extension, - extrapolation_down = ExtrapolationType.extension) + u, t, 2, :ArcLen, :Average; extrapolation = ExtrapolationType.extension) @test A(-1.0) == u[1] @test A(300.0) == u[end] A = BSplineInterpolation(u, t, 2, :ArcLen, :Average) @@ -766,8 +722,7 @@ end # Test extrapolation A = BSplineApprox( - u, t, 2, 4, :Uniform, :Uniform; extrapolation_up = ExtrapolationType.extension, - extrapolation_down = ExtrapolationType.extension) + u, t, 2, 4, :Uniform, :Uniform; extrapolation = ExtrapolationType.extension) @test A(-1.0) == u[1] @test A(300.0) == u[end] A = BSplineApprox(u, t, 2, 4, :Uniform, :Uniform) @@ -813,8 +768,7 @@ end du = [-0.047, -0.058, 0.054, 0.012, -0.068, 0.0] u = [14.7, 11.51, 10.41, 14.95, 12.24, 11.22] t = [0.0, 62.25, 109.66, 162.66, 205.8, 252.3] - A = CubicHermiteSpline(du, u, t; extrapolation_up = ExtrapolationType.extension, - extrapolation_down = ExtrapolationType.extension) + A = CubicHermiteSpline(du, u, t; extrapolation = ExtrapolationType.extension) @test A.(t) ≈ u @test A(100.0)≈10.106770 rtol=1e-5 @test A(300.0)≈9.901542 rtol=1e-5 @@ -842,8 +796,7 @@ end du = [-0.047, -0.058, 0.054, 0.012, -0.068, 0.0] u = [14.7, 11.51, 10.41, 14.95, 12.24, 11.22] t = [0.0, 62.25, 109.66, 162.66, 205.8, 252.3] - A = QuinticHermiteSpline(ddu, du, u, t; extrapolation_up = ExtrapolationType.extension, - extrapolation_down = ExtrapolationType.extension) + A = QuinticHermiteSpline(ddu, du, u, t; extrapolation = ExtrapolationType.extension) @test A.(t) ≈ u @test A(100.0)≈10.107996 rtol=1e-5 @test A(300.0)≈11.364162 rtol=1e-5 diff --git a/test/regularization.jl b/test/regularization.jl index 3e6579cf..6d850475 100644 --- a/test/regularization.jl +++ b/test/regularization.jl @@ -181,7 +181,7 @@ end @testset "Extrapolation" begin A = RegularizationSmooth( - uₒ, tₒ; alg = :fixed, extrapolation_up = ExtrapolationType.extension) + uₒ, tₒ; alg = :fixed, extrapolation_right = ExtrapolationType.extension) @test A(10.0) == A.Aitp(10.0) A = RegularizationSmooth(uₒ, tₒ; alg = :fixed) @test_throws DataInterpolations.UpExtrapolationError A(10.0) diff --git a/test/zygote_tests.jl b/test/zygote_tests.jl index 6476ee0f..d8d178df 100644 --- a/test/zygote_tests.jl +++ b/test/zygote_tests.jl @@ -4,8 +4,7 @@ using Zygote function test_zygote(method, u, t; args = [], args_after = [], kwargs = [], name::String) func = method(args..., u, t, args_after...; kwargs..., - extrapolation_up = ExtrapolationType.extension, - extrapolation_down = ExtrapolationType.extension) + extrapolation = ExtrapolationType.extension) trange = collect(range(minimum(t) - 5.0, maximum(t) + 5.0, step = 0.1)) trange_exclude = filter(x -> !in(x, t), trange) @testset "$name, derivatives w.r.t. input" begin @@ -22,8 +21,7 @@ function test_zygote(method, u, t; args = [], args_after = [], kwargs = [], name @testset "$name, derivatives w.r.t. u" begin function f(u) A = method(args..., u, t, args_after...; kwargs..., - extrapolation_up = ExtrapolationType.extension, - extrapolation_down = ExtrapolationType.extension) + extrapolation = ExtrapolationType.extension) out = if u isa AbstractVector{<:Real} zero(eltype(u)) elseif u isa AbstractMatrix From c1f6f7e622f16cb82de113a82242f566a2c18a28 Mon Sep 17 00:00:00 2001 From: Bart de Koning Date: Thu, 21 Nov 2024 12:00:03 +0100 Subject: [PATCH 13/14] Comments adressed --- docs/src/extrapolation_methods.md | 14 +++++- src/DataInterpolations.jl | 16 +++--- src/derivatives.jl | 4 +- src/integrals.jl | 4 +- src/interpolation_caches.jl | 84 ++++++++++++++++--------------- src/interpolation_methods.jl | 4 +- test/derivative_tests.jl | 5 +- test/extrapolation_tests.jl | 4 +- test/integral_tests.jl | 9 ++-- test/interpolation_tests.jl | 42 ++++++++-------- test/regularization.jl | 2 +- 11 files changed, 102 insertions(+), 86 deletions(-) diff --git a/docs/src/extrapolation_methods.md b/docs/src/extrapolation_methods.md index 5b6286ba..09f8f0ae 100644 --- a/docs/src/extrapolation_methods.md +++ b/docs/src/extrapolation_methods.md @@ -13,7 +13,7 @@ A = QuadraticSpline(u, t) plot(A) ``` -Extrapolation behavior can be set left and right of the data simultaneously with the `extension` keyword, or left and right separately with the `extension_left` and `extension_right` keywords respectively. +Extrapolation behavior can be set left and right of the data simultaneously with the `extension` keyword, or left and right separately with the `extrapolation_left` and `extrapolation_right` keywords respectively. ## `ExtrapolationType.none` @@ -51,3 +51,15 @@ plot(A) plot!(t_eval_down, A.(t_eval_down); label = "extrapolation down") plot!(t_eval_up, A.(t_eval_up); label = "extrapolation up") ``` + +## Mixed extrapolation + +You can also have different extrapolation types left and right of the data. + +```@example tutorial +A = QuadraticSpline(u, t; extrapolation_left = ExtrapolationType.constant, + extrapolation_right = ExtrapolationType.linear) +plot(A) +plot!(t_eval_down, A.(t_eval_down); label = "extrapolation down") +plot!(t_eval_up, A.(t_eval_up); label = "extrapolation up") +``` diff --git a/src/DataInterpolations.jl b/src/DataInterpolations.jl index ee15a452..962b21a9 100644 --- a/src/DataInterpolations.jl +++ b/src/DataInterpolations.jl @@ -64,16 +64,16 @@ function Base.showerror(io::IO, ::ExtrapolationError) print(io, EXTRAPOLATION_ERROR) end -const DOWN_EXTRAPOLATION_ERROR = "Cannot extrapolate down as `extrapolation_left` keyword passed was `none`" -struct DownExtrapolationError <: Exception end -function Base.showerror(io::IO, ::DownExtrapolationError) - print(io, DOWN_EXTRAPOLATION_ERROR) +const LEFT_EXTRAPOLATION_ERROR = "Cannot extrapolate for t < first(A.t) as the `extrapolation_left` kwarg passed was `ExtrapolationType.none`" +struct LeftExtrapolationError <: Exception end +function Base.showerror(io::IO, ::LeftExtrapolationError) + print(io, LEFT_EXTRAPOLATION_ERROR) end -const UP_EXTRAPOLATION_ERROR = "Cannot extrapolate up as `extrapolation_right` keyword passed was `none`" -struct UpExtrapolationError <: Exception end -function Base.showerror(io::IO, ::UpExtrapolationError) - print(io, UP_EXTRAPOLATION_ERROR) +const RIGHT_EXTRAPOLATION_ERROR = "Cannot extrapolate for t > last(A.t) as the `extrapolation_tight` kwarg passed was `ExtrapolationType.none`" +struct RightExtrapolationError <: Exception end +function Base.showerror(io::IO, ::RightExtrapolationError) + print(io, RIGHT_EXTRAPOLATION_ERROR) end const INTEGRAL_NOT_FOUND_ERROR = "Cannot integrate it analytically. Please use Numerical Integration methods." diff --git a/src/derivatives.jl b/src/derivatives.jl index 6b1f02e6..da007025 100644 --- a/src/derivatives.jl +++ b/src/derivatives.jl @@ -17,7 +17,7 @@ function _extrapolate_derivative_down(A, t, order) (; extrapolation_left) = A typed_zero = zero(first(A.u) / one(A.t[1])) if extrapolation_left == ExtrapolationType.none - throw(DownExtrapolationError()) + throw(LeftExtrapolationError()) elseif extrapolation_left == ExtrapolationType.constant typed_zero elseif extrapolation_left == ExtrapolationType.linear @@ -36,7 +36,7 @@ function _extrapolate_derivative_up(A, t, order) (; extrapolation_right) = A typed_zero = zero(first(A.u) / one(A.t[1])) if extrapolation_right == ExtrapolationType.none - throw(UpExtrapolationError()) + throw(RightExtrapolationError()) elseif extrapolation_right == ExtrapolationType.constant typed_zero elseif extrapolation_right == ExtrapolationType.linear diff --git a/src/integrals.jl b/src/integrals.jl index d24a79a8..af2eb3bd 100644 --- a/src/integrals.jl +++ b/src/integrals.jl @@ -65,7 +65,7 @@ end function _extrapolate_integral_down(A, t) (; extrapolation_left) = A if extrapolation_left == ExtrapolationType.none - throw(DownExtrapolationError()) + throw(LeftExtrapolationError()) elseif extrapolation_left == ExtrapolationType.constant first(A.u) * (first(A.t) - t) elseif extrapolation_left == ExtrapolationType.linear @@ -80,7 +80,7 @@ end function _extrapolate_integral_up(A, t) (; extrapolation_right) = A if extrapolation_right == ExtrapolationType.none - throw(UpExtrapolationError()) + throw(RightExtrapolationError()) elseif extrapolation_right == ExtrapolationType.constant last(A.u) * (t - last(A.t)) elseif extrapolation_right == ExtrapolationType.linear diff --git a/src/interpolation_caches.jl b/src/interpolation_caches.jl index 4cc2670b..faeec06a 100644 --- a/src/interpolation_caches.jl +++ b/src/interpolation_caches.jl @@ -1,6 +1,7 @@ """ - LinearInterpolation(u, t; extrapolation_left::ExtrapolationType.T = ExtrapolationType.none, - extrapolation_right::ExtrapolationType.T = ExtrapolationType.none, cache_parameters = false) + LinearInterpolation(u, t; extrapolation_left::ExtrapolationType.T = ExtrapolationType.none, + extrapolation::ExtrapolationType.T = ExtrapolationType.none, extrapolation_right::ExtrapolationType.T = ExtrapolationType.none, + cache_parameters = false) It is the method of interpolating between the data points using a linear polynomial. For any point, two data points one each side are chosen and connected with a line. Extrapolation extends the last linear polynomial on each side. @@ -16,9 +17,9 @@ Extrapolation extends the last linear polynomial on each side. are `ExtrapolationType.none` (default), `ExtrapolationType.constant`, `ExtrapolationType.linear` and `ExtrapolationType.extension`. - `extrapolation_left`: The extrapolation type applied left of the data. See `extrapolation` for - the possible options. + the possible options. This keyword is ignored if `extrapolation != Extrapolation.none`. - `extrapolation_right`: The extrapolation type applied right of the data. See `extrapolation` for - the possible options. + the possible options. This keyword is ignored if `extrapolation != Extrapolation.none`. - `cache_parameters`: precompute parameters at initialization for faster interpolation computations. Note: if activated, `u` and `t` should not be modified. Defaults to `false`. - `assume_linear_t`: boolean value to specify a faster index lookup behavior for @@ -65,7 +66,8 @@ end """ QuadraticInterpolation(u, t, mode = :Forward; extrapolation_left::ExtrapolationType.T = ExtrapolationType.none, - extrapolation_right::ExtrapolationType.T = ExtrapolationType.none, cache_parameters = false) + extrapolation::ExtrapolationType.T = ExtrapolationType.none, extrapolation_right::ExtrapolationType.T = ExtrapolationType.none, + cache_parameters = false) It is the method of interpolating between the data points using quadratic polynomials. For any point, three data points nearby are taken to fit a quadratic polynomial. Extrapolation extends the last quadratic polynomial on each side. @@ -82,9 +84,9 @@ Extrapolation extends the last quadratic polynomial on each side. are `ExtrapolationType.none` (default), `ExtrapolationType.constant`, `ExtrapolationType.linear` and `ExtrapolationType.extension`. - `extrapolation_left`: The extrapolation type applied left of the data. See `extrapolation` for - the possible options. + the possible options. This keyword is ignored if `extrapolation != Extrapolation.none`. - `extrapolation_right`: The extrapolation type applied right of the data. See `extrapolation` for - the possible options. + the possible options. This keyword is ignored if `extrapolation != Extrapolation.none`. - `cache_parameters`: precompute parameters at initialization for faster interpolation computations. Note: if activated, `u` and `t` should not be modified. Defaults to `false`. - `assume_linear_t`: boolean value to specify a faster index lookup behaviour for evenly-distributed abscissae. Alternatively, a numerical threshold may be specified @@ -138,8 +140,8 @@ function QuadraticInterpolation(u, t; kwargs...) end """ - LagrangeInterpolation(u, t, n = length(t) - 1; extrapolation_left::ExtrapolationType.T = ExtrapolationType.none, - extrapolation_right::ExtrapolationType.T = ExtrapolationType.none, safetycopy = true) + LagrangeInterpolation(u, t, n = length(t) - 1; extrapolation::ExtrapolationType.T = ExtrapolationType.none, + extrapolation_left::ExtrapolationType.T = ExtrapolationType.none, extrapolation_right::ExtrapolationType.T = ExtrapolationType.none) It is the method of interpolation using Lagrange polynomials of (k-1)th order passing through all the data points where k is the number of data points. @@ -155,9 +157,9 @@ It is the method of interpolation using Lagrange polynomials of (k-1)th order pa are `ExtrapolationType.none` (default), `ExtrapolationType.constant`, `ExtrapolationType.linear` and `ExtrapolationType.extension`. - `extrapolation_left`: The extrapolation type applied left of the data. See `extrapolation` for - the possible options. + the possible options. This keyword is ignored if `extrapolation != Extrapolation.none`. - `extrapolation_right`: The extrapolation type applied right of the data. See `extrapolation` for - the possible options. + the possible options. This keyword is ignored if `extrapolation != Extrapolation.none`. """ struct LagrangeInterpolation{uType, tType, T, bcacheType, N} <: AbstractInterpolation{T, N} @@ -201,7 +203,7 @@ function LagrangeInterpolation( end """ - AkimaInterpolation(u, t; extrapolation_left::ExtrapolationType.T = ExtrapolationType.none, + AkimaInterpolation(u, t; extrapolation::ExtrapolationType.T = ExtrapolationType.none, extrapolation_left::ExtrapolationType.T = ExtrapolationType.none, extrapolation_right::ExtrapolationType.T = ExtrapolationType.none, cache_parameters = false) It is a spline interpolation built from cubic polynomials. It forms a continuously differentiable function. For more details, refer: [https://en.wikipedia.org/wiki/Akima_spline](https://en.wikipedia.org/wiki/Akima_spline). @@ -218,9 +220,9 @@ Extrapolation extends the last cubic polynomial on each side. are `ExtrapolationType.none` (default), `ExtrapolationType.constant`, `ExtrapolationType.linear` and `ExtrapolationType.extension`. - `extrapolation_left`: The extrapolation type applied left of the data. See `extrapolation` for - the possible options. + the possible options. This keyword is ignored if `extrapolation != Extrapolation.none`. - `extrapolation_right`: The extrapolation type applied right of the data. See `extrapolation` for - the possible options. + the possible options. This keyword is ignored if `extrapolation != Extrapolation.none`. - `cache_parameters`: precompute parameters at initialization for faster interpolation computations. Note: if activated, `u` and `t` should not be modified. Defaults to `false`. - `assume_linear_t`: boolean value to specify a faster index lookup behaviour for evenly-distributed abscissae. Alternatively, a numerical threshold may be specified @@ -298,7 +300,7 @@ function AkimaInterpolation( end """ - ConstantInterpolation(u, t; dir = :left, extrapolation_left::ExtrapolationType.T = ExtrapolationType.none, + ConstantInterpolation(u, t; dir = :left, extrapolation::ExtrapolationType.T = ExtrapolationType.none, extrapolation_left::ExtrapolationType.T = ExtrapolationType.none, extrapolation_right::ExtrapolationType.T = ExtrapolationType.none, cache_parameters = false) It is the method of interpolating using a constant polynomial. For any point, two adjacent data points are found on either side (left and right). The value at that point depends on `dir`. @@ -317,9 +319,9 @@ Extrapolation extends the last constant polynomial at the end points on each sid are `ExtrapolationType.none` (default), `ExtrapolationType.constant`, `ExtrapolationType.linear` and `ExtrapolationType.extension`. - `extrapolation_left`: The extrapolation type applied left of the data. See `extrapolation` for - the possible options. + the possible options. This keyword is ignored if `extrapolation != Extrapolation.none`. - `extrapolation_right`: The extrapolation type applied right of the data. See `extrapolation` for - the possible options. + the possible options. This keyword is ignored if `extrapolation != Extrapolation.none`. - `cache_parameters`: precompute parameters at initialization for faster interpolation computations. Note: if activated, `u` and `t` should not be modified. Defaults to `false`. - `assume_linear_t`: boolean value to specify a faster index lookup behaviour for evenly-distributed abscissae. Alternatively, a numerical threshold may be specified @@ -365,7 +367,7 @@ function ConstantInterpolation( end """ - QuadraticSpline(u, t; extrapolation_left::ExtrapolationType.T = ExtrapolationType.none, + QuadraticSpline(u, t; extrapolation::ExtrapolationType.T = ExtrapolationType.none, extrapolation_left::ExtrapolationType.T = ExtrapolationType.none, extrapolation_right::ExtrapolationType.T = ExtrapolationType.none, cache_parameters = false) It is a spline interpolation using piecewise quadratic polynomials between each pair of data points. Its first derivative is also continuous. @@ -382,9 +384,9 @@ Extrapolation extends the last quadratic polynomial on each side. are `ExtrapolationType.none` (default), `ExtrapolationType.constant`, `ExtrapolationType.linear` and `ExtrapolationType.extension`. - `extrapolation_left`: The extrapolation type applied left of the data. See `extrapolation` for - the possible options. + the possible options. This keyword is ignored if `extrapolation != Extrapolation.none`. - `extrapolation_right`: The extrapolation type applied right of the data. See `extrapolation` for - the possible options. + the possible options. This keyword is ignored if `extrapolation != Extrapolation.none`. - `cache_parameters`: precompute parameters at initialization for faster interpolation computations. Note: if activated, `u` and `t` should not be modified. Defaults to `false`. - `assume_linear_t`: boolean value to specify a faster index lookup behaviour for evenly-distributed abscissae. Alternatively, a numerical threshold may be specified @@ -488,7 +490,7 @@ function QuadraticSpline( end """ - CubicSpline(u, t; extrapolation_left::ExtrapolationType.T = ExtrapolationType.none, + CubicSpline(u, t; extrapolation::ExtrapolationType.T = ExtrapolationType.none, extrapolation_left::ExtrapolationType.T = ExtrapolationType.none, extrapolation_right::ExtrapolationType.T = ExtrapolationType.none, cache_parameters = false) It is a spline interpolation using piecewise cubic polynomials between each pair of data points. Its first and second derivative is also continuous. @@ -505,9 +507,9 @@ Second derivative on both ends are zero, which are also called "natural" boundar are `ExtrapolationType.none` (default), `ExtrapolationType.constant`, `ExtrapolationType.linear` and `ExtrapolationType.extension`. - `extrapolation_left`: The extrapolation type applied left of the data. See `extrapolation` for - the possible options. + the possible options. This keyword is ignored if `extrapolation != Extrapolation.none`. - `extrapolation_right`: The extrapolation type applied right of the data. See `extrapolation` for - the possible options. + the possible options. This keyword is ignored if `extrapolation != Extrapolation.none`. - `cache_parameters`: precompute parameters at initialization for faster interpolation computations. Note: if activated, `u` and `t` should not be modified. Defaults to `false`. - `assume_linear_t`: boolean value to specify a faster index lookup behaviour for evenly-distributed abscissae. Alternatively, a numerical threshold may be specified @@ -655,8 +657,8 @@ function CubicSpline( end """ - BSplineInterpolation(u, t, d, pVecType, knotVecType; extrapolation_left::ExtrapolationType.T = ExtrapolationType.none, - extrapolation_right::ExtrapolationType.T = ExtrapolationType.none, safetycopy = true) + BSplineInterpolation(u, t, d, pVecType, knotVecType; extrapolation::ExtrapolationType.T = ExtrapolationType.none, extrapolation_left::ExtrapolationType.T = ExtrapolationType.none, + extrapolation_right::ExtrapolationType.T = ExtrapolationType.none) It is a curve defined by the linear combination of `n` basis functions of degree `d` where `n` is the number of data points. For more information, refer [https://pages.mtu.edu/~shene/COURSES/cs3621/NOTES/spline/B-spline/bspline-curve.html](https://pages.mtu.edu/%7Eshene/COURSES/cs3621/NOTES/spline/B-spline/bspline-curve.html). Extrapolation is a constant polynomial of the end points on each side. @@ -675,10 +677,10 @@ Extrapolation is a constant polynomial of the end points on each side. are `ExtrapolationType.none` (default), `ExtrapolationType.constant`, `ExtrapolationType.linear` and `ExtrapolationType.extension`. - `extrapolation_left`: The extrapolation type applied left of the data. See `extrapolation` for - the possible options. + the possible options. This keyword is ignored if `extrapolation != Extrapolation.none`. - `extrapolation_right`: The extrapolation type applied right of the data. See `extrapolation` for - the possible options. - - `assume_linear_t`: boolean value to specify a faster index lookup behaviour for + the possible options. This keyword is ignored if `extrapolation != Extrapolation.none`. + - `assume_linear_t`: boolean value to specify a faster index lookup behavior for evenly-distributed abscissae. Alternatively, a numerical threshold may be specified for a test based on the normalized standard deviation of the difference with respect to the straight line (see [`looks_linear`](@ref)). Defaults to 1e-2. @@ -885,7 +887,7 @@ function BSplineInterpolation( end """ - BSplineApprox(u, t, d, h, pVecType, knotVecType; extrapolation_left::ExtrapolationType.T = ExtrapolationType.none, + BSplineApprox(u, t, d, h, pVecType, knotVecType; extrapolation::ExtrapolationType.T = ExtrapolationType.none, extrapolation_left::ExtrapolationType.T = ExtrapolationType.none, extrapolation_right::ExtrapolationType.T = ExtrapolationType.none) It is a regression based B-spline. The argument choices are the same as the `BSplineInterpolation`, with the additional parameter `h < length(t)` which is the number of control points to use, with smaller `h` indicating more smoothing. @@ -907,9 +909,9 @@ Extrapolation is a constant polynomial of the end points on each side. are `ExtrapolationType.none` (default), `ExtrapolationType.constant`, `ExtrapolationType.linear` and `ExtrapolationType.extension`. - `extrapolation_left`: The extrapolation type applied left of the data. See `extrapolation` for - the possible options. + the possible options. This keyword is ignored if `extrapolation != Extrapolation.none`. - `extrapolation_right`: The extrapolation type applied right of the data. See `extrapolation` for - the possible options. + the possible options. This keyword is ignored if `extrapolation != Extrapolation.none`. - `assume_linear_t`: boolean value to specify a faster index lookup behaviour for evenly-distributed abscissae. Alternatively, a numerical threshold may be specified for a test based on the normalized standard deviation of the difference with respect @@ -1164,7 +1166,7 @@ function BSplineApprox( extrapolation_left, extrapolation_right, assume_linear_t) end """ - CubicHermiteSpline(du, u, t; extrapolation_left::ExtrapolationType.T = ExtrapolationType.none, + CubicHermiteSpline(du, u, t; extrapolation::ExtrapolationType.T = ExtrapolationType.none, extrapolation_left::ExtrapolationType.T = ExtrapolationType.none, extrapolation_right::ExtrapolationType.T = ExtrapolationType.none, cache_parameters = false) It is a Cubic Hermite interpolation, which is a piece-wise third degree polynomial such that the value and the first derivative are equal to given values in the data points. @@ -1181,9 +1183,9 @@ It is a Cubic Hermite interpolation, which is a piece-wise third degree polynomi are `ExtrapolationType.none` (default), `ExtrapolationType.constant`, `ExtrapolationType.linear` and `ExtrapolationType.extension`. - `extrapolation_left`: The extrapolation type applied left of the data. See `extrapolation` for - the possible options. + the possible options. This keyword is ignored if `extrapolation != Extrapolation.none`. - `extrapolation_right`: The extrapolation type applied right of the data. See `extrapolation` for - the possible options. + the possible options. This keyword is ignored if `extrapolation != Extrapolation.none`. - `cache_parameters`: precompute parameters at initialization for faster interpolation computations. Note: if activated, `u` and `t` should not be modified. Defaults to `false`. - `assume_linear_t`: boolean value to specify a faster index lookup behaviour for evenly-distributed abscissae. Alternatively, a numerical threshold may be specified @@ -1232,8 +1234,8 @@ function CubicHermiteSpline( end """ - PCHIPInterpolation(u, t; extrapolation_left::ExtrapolationType.T = ExtrapolationType.none, - extrapolation_right::ExtrapolationType.T = ExtrapolationType.none, safetycopy = true) + PCHIPInterpolation(u, t; extrapolation::ExtrapolationType.T = ExtrapolationType.none, extrapolation_left::ExtrapolationType.T = ExtrapolationType.none, + extrapolation_right::ExtrapolationType.T = ExtrapolationType.none) It is a PCHIP Interpolation, which is a type of [`CubicHermiteSpline`](@ref) where the derivative values `du` are derived from the input data in such a way that the interpolation never overshoots the data. See [here](https://www.mathworks.com/content/dam/mathworks/mathworks-dot-com/moler/interp.pdf), @@ -1250,9 +1252,9 @@ section 3.4 for more details. are `ExtrapolationType.none` (default), `ExtrapolationType.constant`, `ExtrapolationType.linear` and `ExtrapolationType.extension`. - `extrapolation_left`: The extrapolation type applied left of the data. See `extrapolation` for - the possible options. + the possible options. This keyword is ignored if `extrapolation != Extrapolation.none`. - `extrapolation_right`: The extrapolation type applied right of the data. See `extrapolation` for - the possible options. + the possible options. This keyword is ignored if `extrapolation != Extrapolation.none`. - `cache_parameters`: precompute parameters at initialization for faster interpolation computations. Note: if activated, `u` and `t` should not be modified. Defaults to `false`. - `assume_linear_t`: boolean value to specify a faster index lookup behaviour for evenly-distributed abscissae. Alternatively, a numerical threshold may be specified @@ -1267,7 +1269,7 @@ end """ QuinticHermiteSpline(ddu, du, u, t; extrapolation_left::ExtrapolationType.T = ExtrapolationType.none, - extrapolation_right::ExtrapolationType.T = ExtrapolationType.none, safetycopy = true) + extrapolation_right::ExtrapolationType.T = ExtrapolationType.none) It is a Quintic Hermite interpolation, which is a piece-wise fifth degree polynomial such that the value and the first and second derivative are equal to given values in the data points. @@ -1284,9 +1286,9 @@ It is a Quintic Hermite interpolation, which is a piece-wise fifth degree polyno are `ExtrapolationType.none` (default), `ExtrapolationType.constant`, `ExtrapolationType.linear` and `ExtrapolationType.extension`. - `extrapolation_left`: The extrapolation type applied left of the data. See `extrapolation` for - the possible options. + the possible options. This keyword is ignored if `extrapolation != Extrapolation.none`. - `extrapolation_right`: The extrapolation type applied right of the data. See `extrapolation` for - the possible options. + the possible options. This keyword is ignored if `extrapolation != Extrapolation.none`. - `cache_parameters`: precompute parameters at initialization for faster interpolation computations. Note: if activated, `u` and `t` should not be modified. Defaults to `false`. - `assume_linear_t`: boolean value to specify a faster index lookup behaviour for evenly-distributed abscissae. Alternatively, a numerical threshold may be specified diff --git a/src/interpolation_methods.jl b/src/interpolation_methods.jl index a691fbb2..a781acf3 100644 --- a/src/interpolation_methods.jl +++ b/src/interpolation_methods.jl @@ -11,7 +11,7 @@ end function _extrapolate_down(A, t) (; extrapolation_left) = A if extrapolation_left == ExtrapolationType.none - throw(DownExtrapolationError()) + throw(LeftExtrapolationError()) elseif extrapolation_left == ExtrapolationType.constant slope = derivative(A, first(A.t)) first(A.u) + slope * zero(t) @@ -27,7 +27,7 @@ end function _extrapolate_up(A, t) (; extrapolation_right) = A if extrapolation_right == ExtrapolationType.none - throw(UpExtrapolationError()) + throw(RightExtrapolationError()) elseif extrapolation_right == ExtrapolationType.constant slope = derivative(A, last(A.t)) last(A.u) + slope * zero(t) diff --git a/test/derivative_tests.jl b/test/derivative_tests.jl index df96b0fb..e9e33328 100644 --- a/test/derivative_tests.jl +++ b/test/derivative_tests.jl @@ -76,8 +76,9 @@ function test_derivatives(method; args = [], kwargs = [], name::String) @test_throws DataInterpolations.ExtrapolationError derivative(func, t[1] - 1.0) @test_throws DataInterpolations.ExtrapolationError derivative(func, t[end] + 1.0) else - @test_throws DataInterpolations.DownExtrapolationError derivative(func, t[1] - 1.0) - @test_throws DataInterpolations.UpExtrapolationError derivative(func, t[end] + 1.0) + @test_throws DataInterpolations.LeftExtrapolationError derivative(func, t[1] - 1.0) + @test_throws DataInterpolations.RightExtrapolationError derivative( + func, t[end] + 1.0) end @test_throws DataInterpolations.DerivativeNotFoundError derivative( func, t[1], 3) diff --git a/test/extrapolation_tests.jl b/test/extrapolation_tests.jl index 9ba616f4..7fa75f06 100644 --- a/test/extrapolation_tests.jl +++ b/test/extrapolation_tests.jl @@ -5,8 +5,8 @@ function test_extrapolation_errors(method, u, t) @test A.extrapolation_right == ExtrapolationType.none @test A.extrapolation_left == ExtrapolationType.none for (error_type, t_eval) in zip( - (DataInterpolations.DownExtrapolationError, - DataInterpolations.UpExtrapolationError), + (DataInterpolations.LeftExtrapolationError, + DataInterpolations.RightExtrapolationError), (first(t) - 1, last(t) + 1)) @test_throws error_type A(t_eval) @test_throws error_type DataInterpolations.derivative( diff --git a/test/integral_tests.jl b/test/integral_tests.jl index c37d9d8d..09ba655b 100644 --- a/test/integral_tests.jl +++ b/test/integral_tests.jl @@ -49,10 +49,11 @@ function test_integral(method; args = [], kwargs = [], name::String) @test isapprox(qint, aint, atol = 1e-6, rtol = 1e-8) end func = method(args...; kwargs...) - @test_throws DataInterpolations.DownExtrapolationError integral(func, t[1] - 1.0) - @test_throws DataInterpolations.UpExtrapolationError integral(func, t[end] + 1.0) - @test_throws DataInterpolations.DownExtrapolationError integral(func, t[1] - 1.0, t[2]) - @test_throws DataInterpolations.UpExtrapolationError integral(func, t[1], t[end] + 1.0) + @test_throws DataInterpolations.LeftExtrapolationError integral(func, t[1] - 1.0) + @test_throws DataInterpolations.RightExtrapolationError integral(func, t[end] + 1.0) + @test_throws DataInterpolations.LeftExtrapolationError integral(func, t[1] - 1.0, t[2]) + @test_throws DataInterpolations.RightExtrapolationError integral( + func, t[1], t[end] + 1.0) end @testset "LinearInterpolation" begin diff --git a/test/interpolation_tests.jl b/test/interpolation_tests.jl index 3b374c00..83a0bef1 100644 --- a/test/interpolation_tests.jl +++ b/test/interpolation_tests.jl @@ -176,9 +176,9 @@ end @test A(-1.0) == -2.0 @test A(11.0) == 22.0 A = LinearInterpolation(u, t) - @test_throws DataInterpolations.DownExtrapolationError A(-1.0) - @test_throws DataInterpolations.UpExtrapolationError A(11.0) - @test_throws DataInterpolations.DownExtrapolationError A([-1.0, 11.0]) + @test_throws DataInterpolations.LeftExtrapolationError A(-1.0) + @test_throws DataInterpolations.RightExtrapolationError A(11.0) + @test_throws DataInterpolations.LeftExtrapolationError A([-1.0, 11.0]) end @testset "Quadratic Interpolation" begin @@ -275,8 +275,8 @@ end @test A(0.0) == -4.5 @test A(5.0) == -7.5 A = QuadraticInterpolation(u, t) - @test_throws DataInterpolations.DownExtrapolationError A(0.0) - @test_throws DataInterpolations.UpExtrapolationError A(5.0) + @test_throws DataInterpolations.LeftExtrapolationError A(0.0) + @test_throws DataInterpolations.RightExtrapolationError A(5.0) end @testset "Lagrange Interpolation" begin @@ -335,8 +335,8 @@ end @test A(0.0) == 0.0 @test A(4.0) == 16.0 A = LagrangeInterpolation(u, t) - @test_throws DataInterpolations.DownExtrapolationError A(-1.0) - @test_throws DataInterpolations.UpExtrapolationError A(4.0) + @test_throws DataInterpolations.LeftExtrapolationError A(-1.0) + @test_throws DataInterpolations.RightExtrapolationError A(4.0) end @testset "Akima Interpolation" begin @@ -366,8 +366,8 @@ end @test A(-1.0) ≈ -5.0 @test A(11.0) ≈ -3.924742268041234 A = AkimaInterpolation(u, t) - @test_throws DataInterpolations.DownExtrapolationError A(-1.0) - @test_throws DataInterpolations.UpExtrapolationError A(11.0) + @test_throws DataInterpolations.LeftExtrapolationError A(-1.0) + @test_throws DataInterpolations.RightExtrapolationError A(11.0) end @testset "ConstantInterpolation" begin @@ -496,8 +496,8 @@ end @test A(-1.0) == 1.0 @test A(11.0) == 1.0 A = ConstantInterpolation(u, t) - @test_throws DataInterpolations.DownExtrapolationError A(-1.0) - @test_throws DataInterpolations.UpExtrapolationError A(11.0) + @test_throws DataInterpolations.LeftExtrapolationError A(-1.0) + @test_throws DataInterpolations.RightExtrapolationError A(11.0) # Test extrapolation with infs with regularly spaced t u = [1.67e7, 1.6867e7, 1.7034e7, 1.7201e7, 1.7368e7] @@ -549,8 +549,8 @@ end @test A(-2.0) == 0.0 @test A(2.0) == 6.0 A = QuadraticSpline(u, t) - @test_throws DataInterpolations.DownExtrapolationError A(-2.0) - @test_throws DataInterpolations.UpExtrapolationError A(2.0) + @test_throws DataInterpolations.LeftExtrapolationError A(-2.0) + @test_throws DataInterpolations.RightExtrapolationError A(2.0) end @testset "CubicSpline Interpolation" begin @@ -602,8 +602,8 @@ end @test A(-2.0) ≈ -1.0 @test A(2.0) ≈ 5.0 A = CubicSpline(u, t) - @test_throws DataInterpolations.DownExtrapolationError A(-2.0) - @test_throws DataInterpolations.UpExtrapolationError A(2.0) + @test_throws DataInterpolations.LeftExtrapolationError A(-2.0) + @test_throws DataInterpolations.RightExtrapolationError A(2.0) @testset "AbstractMatrix" begin t = 0.1:0.1:1.0 @@ -647,8 +647,8 @@ end @test A(-1.0) == u[1] @test A(300.0) == u[end] A = BSplineInterpolation(u, t, 2, :Uniform, :Uniform) - @test_throws DataInterpolations.DownExtrapolationError A(-1.0) - @test_throws DataInterpolations.UpExtrapolationError A(300.0) + @test_throws DataInterpolations.LeftExtrapolationError A(-1.0) + @test_throws DataInterpolations.RightExtrapolationError A(300.0) A = BSplineInterpolation(u, t, 2, :ArcLen, :Average) @@ -668,8 +668,8 @@ end @test A(-1.0) == u[1] @test A(300.0) == u[end] A = BSplineInterpolation(u, t, 2, :ArcLen, :Average) - @test_throws DataInterpolations.DownExtrapolationError A(-1.0) - @test_throws DataInterpolations.UpExtrapolationError A(300.0) + @test_throws DataInterpolations.LeftExtrapolationError A(-1.0) + @test_throws DataInterpolations.RightExtrapolationError A(300.0) @testset "AbstractMatrix" begin t = 0.1:0.1:1.0 @@ -726,8 +726,8 @@ end @test A(-1.0) == u[1] @test A(300.0) == u[end] A = BSplineApprox(u, t, 2, 4, :Uniform, :Uniform) - @test_throws DataInterpolations.DownExtrapolationError A(-1.0) - @test_throws DataInterpolations.UpExtrapolationError A(300.0) + @test_throws DataInterpolations.LeftExtrapolationError A(-1.0) + @test_throws DataInterpolations.RightExtrapolationError A(300.0) @testset "AbstractMatrix" begin t = 0.1:0.1:1.0 diff --git a/test/regularization.jl b/test/regularization.jl index 6d850475..a3f4ef1c 100644 --- a/test/regularization.jl +++ b/test/regularization.jl @@ -184,7 +184,7 @@ end uₒ, tₒ; alg = :fixed, extrapolation_right = ExtrapolationType.extension) @test A(10.0) == A.Aitp(10.0) A = RegularizationSmooth(uₒ, tₒ; alg = :fixed) - @test_throws DataInterpolations.UpExtrapolationError A(10.0) + @test_throws DataInterpolations.RightExtrapolationError A(10.0) end @testset "Type inference" begin From 2af470598a44daa752c57205d94a956e298ef710 Mon Sep 17 00:00:00 2001 From: Bart de Koning Date: Sun, 24 Nov 2024 16:11:59 +0100 Subject: [PATCH 14/14] comments addressed --- docs/src/extrapolation_methods.md | 20 ++++++++++---------- src/derivatives.jl | 18 ++++++++---------- src/integrals.jl | 12 ++++++------ src/interpolation_methods.jl | 8 ++++---- test/extrapolation_tests.jl | 12 ++++++------ 5 files changed, 34 insertions(+), 36 deletions(-) diff --git a/docs/src/extrapolation_methods.md b/docs/src/extrapolation_methods.md index 09f8f0ae..c442a7f9 100644 --- a/docs/src/extrapolation_methods.md +++ b/docs/src/extrapolation_methods.md @@ -7,8 +7,8 @@ using DataInterpolations, Plots u = [0.86, 0.65, 0.44, 0.76, 0.73] t = [0.0022, 0.68, 1.41, 2.22, 2.46] -t_eval_down = range(-1, first(t), length = 25) -t_eval_up = range(last(t), 3.5, length = 25) +t_eval_left = range(-1, first(t), length = 25) +t_eval_right = range(last(t), 3.5, length = 25) A = QuadraticSpline(u, t) plot(A) ``` @@ -26,8 +26,8 @@ This extrapolation type extends the interpolation with the boundary values of th ```@example tutorial A = QuadraticSpline(u, t; extrapolation = ExtrapolationType.constant) plot(A) -plot!(t_eval_down, A.(t_eval_down); label = "extrapolation down") -plot!(t_eval_up, A.(t_eval_up); label = "extrapolation up") +plot!(t_eval_left, A.(t_eval_left); label = "extrapolation left") +plot!(t_eval_right, A.(t_eval_right); label = "extrapolation right") ``` ## `ExtrapolationType.linear` @@ -37,8 +37,8 @@ This extrapolation type extends the interpolation with a linear continuation of ```@example tutorial A = QuadraticSpline(u, t; extrapolation = ExtrapolationType.linear) plot(A) -plot!(t_eval_down, A.(t_eval_down); label = "extrapolation down") -plot!(t_eval_up, A.(t_eval_up); label = "extrapolation up") +plot!(t_eval_left, A.(t_eval_left); label = "extrapolation left") +plot!(t_eval_right, A.(t_eval_right); label = "extrapolation right") ``` ## `ExtrapolationType.extension` @@ -48,8 +48,8 @@ This extrapolation type extends the interpolation with a continuation of the exp ```@example tutorial A = QuadraticSpline(u, t; extrapolation = ExtrapolationType.extension) plot(A) -plot!(t_eval_down, A.(t_eval_down); label = "extrapolation down") -plot!(t_eval_up, A.(t_eval_up); label = "extrapolation up") +plot!(t_eval_left, A.(t_eval_left); label = "extrapolation left") +plot!(t_eval_right, A.(t_eval_right); label = "extrapolation right") ``` ## Mixed extrapolation @@ -60,6 +60,6 @@ You can also have different extrapolation types left and right of the data. A = QuadraticSpline(u, t; extrapolation_left = ExtrapolationType.constant, extrapolation_right = ExtrapolationType.linear) plot(A) -plot!(t_eval_down, A.(t_eval_down); label = "extrapolation down") -plot!(t_eval_up, A.(t_eval_up); label = "extrapolation up") +plot!(t_eval_left, A.(t_eval_left); label = "extrapolation left") +plot!(t_eval_right, A.(t_eval_right); label = "extrapolation right") ``` diff --git a/src/derivatives.jl b/src/derivatives.jl index da007025..30994075 100644 --- a/src/derivatives.jl +++ b/src/derivatives.jl @@ -1,9 +1,9 @@ function derivative(A, t, order = 1) (order ∉ (1, 2)) && throw(DerivativeNotFoundError()) if t < first(A.t) - _extrapolate_derivative_down(A, t, order) + _extrapolate_derivative_left(A, t, order) elseif t > last(A.t) - _extrapolate_derivative_up(A, t, order) + _extrapolate_derivative_right(A, t, order) else iguess = A.iguesser (order == 1) ? _derivative(A, t, iguess) : @@ -13,15 +13,14 @@ function derivative(A, t, order = 1) end end -function _extrapolate_derivative_down(A, t, order) +function _extrapolate_derivative_left(A, t, order) (; extrapolation_left) = A - typed_zero = zero(first(A.u) / one(A.t[1])) if extrapolation_left == ExtrapolationType.none throw(LeftExtrapolationError()) elseif extrapolation_left == ExtrapolationType.constant - typed_zero + zero(first(A.u) / one(A.t[1])) elseif extrapolation_left == ExtrapolationType.linear - (order == 1) ? derivative(A, first(A.t)) : typed_zero + (order == 1) ? derivative(A, first(A.t)) : zero(first(A.u) / one(A.t[1])) else # extrapolation_left == ExtrapolationType.extension iguess = A.iguesser @@ -32,15 +31,14 @@ function _extrapolate_derivative_down(A, t, order) end end -function _extrapolate_derivative_up(A, t, order) +function _extrapolate_derivative_right(A, t, order) (; extrapolation_right) = A - typed_zero = zero(first(A.u) / one(A.t[1])) if extrapolation_right == ExtrapolationType.none throw(RightExtrapolationError()) elseif extrapolation_right == ExtrapolationType.constant - typed_zero + zero(first(A.u) / one(A.t[1])) elseif extrapolation_right == ExtrapolationType.linear - (order == 1) ? derivative(A, last(A.t)) : typed_zero + (order == 1) ? derivative(A, last(A.t)) : zero(first(A.u) / one(A.t[1])) else # extrapolation_right == ExtrapolationType.extension iguess = A.iguesser diff --git a/src/integrals.jl b/src/integrals.jl index af2eb3bd..1da09b83 100644 --- a/src/integrals.jl +++ b/src/integrals.jl @@ -24,11 +24,11 @@ function integral(A::AbstractInterpolation, t1::Number, t2::Number) if t1 < first(A.t) if t2 < first(A.t) # If interval is entirely below data - return _extrapolate_integral_down(A, t2) - extrapolate_integral_down(A.t1) + return _extrapolate_integral_left(A, t2) - extrapolate_integral_left(A.t1) end idx1 -= 1 # Make sure lowest complete interval is included - total += _extrapolate_integral_down(A, t1) + total += _extrapolate_integral_left(A, t1) else total += _integral(A, idx1, t1, A.t[idx1 + 1]) end @@ -37,11 +37,11 @@ function integral(A::AbstractInterpolation, t1::Number, t2::Number) if t2 > last(A.t) if t1 > last(A.t) # If interval is entirely above data - return _extrapolate_integral_up(A, t2) - extrapolate_integral_up(A.t, t1) + return _extrapolate_integral_right(A, t2) - extrapolate_integral_right(A.t, t1) end idx2 += 1 # Make sure highest complete interval is included - total += _extrapolate_integral_up(A, t2) + total += _extrapolate_integral_right(A, t2) else total += _integral(A, idx2, A.t[idx2], t2) end @@ -62,7 +62,7 @@ function integral(A::AbstractInterpolation, t1::Number, t2::Number) return total end -function _extrapolate_integral_down(A, t) +function _extrapolate_integral_left(A, t) (; extrapolation_left) = A if extrapolation_left == ExtrapolationType.none throw(LeftExtrapolationError()) @@ -77,7 +77,7 @@ function _extrapolate_integral_down(A, t) end end -function _extrapolate_integral_up(A, t) +function _extrapolate_integral_right(A, t) (; extrapolation_right) = A if extrapolation_right == ExtrapolationType.none throw(RightExtrapolationError()) diff --git a/src/interpolation_methods.jl b/src/interpolation_methods.jl index a781acf3..266c80c0 100644 --- a/src/interpolation_methods.jl +++ b/src/interpolation_methods.jl @@ -1,14 +1,14 @@ function _interpolate(A, t) if t < first(A.t) - _extrapolate_down(A, t) + _extrapolate_left(A, t) elseif t > last(A.t) - _extrapolate_up(A, t) + _extrapolate_right(A, t) else _interpolate(A, t, A.iguesser) end end -function _extrapolate_down(A, t) +function _extrapolate_left(A, t) (; extrapolation_left) = A if extrapolation_left == ExtrapolationType.none throw(LeftExtrapolationError()) @@ -24,7 +24,7 @@ function _extrapolate_down(A, t) end end -function _extrapolate_up(A, t) +function _extrapolate_right(A, t) (; extrapolation_right) = A if extrapolation_right == ExtrapolationType.none throw(RightExtrapolationError()) diff --git a/test/extrapolation_tests.jl b/test/extrapolation_tests.jl index 7fa75f06..0429c540 100644 --- a/test/extrapolation_tests.jl +++ b/test/extrapolation_tests.jl @@ -40,7 +40,7 @@ end test_constant_extrapolation(LinearInterpolation, u, t) for extrapolation_type in [ExtrapolationType.linear, ExtrapolationType.extension] - # Down extrapolation + # Left extrapolation A = LinearInterpolation(u, t; extrapolation_left = extrapolation_type) t_eval = 0.0 @test A(t_eval) == 0.0 @@ -49,7 +49,7 @@ end @test DataInterpolations.integral(A, t_eval) == -0.5 t_eval = 3.0 - # Up extrapolation + # Right extrapolation A = LinearInterpolation(u, t; extrapolation_right = extrapolation_type) t_eval = 3.0 @test A(t_eval) == 3.0 @@ -67,7 +67,7 @@ end test_extrapolation_errors(QuadraticInterpolation, u, t) test_constant_extrapolation(LinearInterpolation, u, t) - # Linear down extrapolation + # Linear left extrapolation A = QuadraticInterpolation(u, t; extrapolation_left = ExtrapolationType.linear) t_eval = 0.0 @test A(t_eval) ≈ -2.5 @@ -75,7 +75,7 @@ end @test DataInterpolations.derivative(A, t_eval, 2) == 0.0 @test DataInterpolations.integral(A, t_eval) ≈ 0.75 - # Linear up extrapolation + # Linear right extrapolation A = QuadraticInterpolation(u, t; extrapolation_right = ExtrapolationType.linear) t_eval = 4.0 @test A(t_eval) ≈ -0.5 @@ -83,7 +83,7 @@ end @test DataInterpolations.derivative(A, t_eval, 2) == 0.0 @test DataInterpolations.integral(A, t[end], t_eval) ≈ 0.75 - # Extension down extrapolation + # Extension left extrapolation f = t -> (-3t^2 + 13t - 8) / 2 df = t -> (-6t + 13) / 2 A = QuadraticInterpolation(u, t; extrapolation_left = ExtrapolationType.extension) @@ -93,7 +93,7 @@ end @test DataInterpolations.derivative(A, t_eval, 2) == -3 @test DataInterpolations.integral(A, t_eval) ≈ 1.25 - # Extension up extrapolation + # Extension right extrapolation A = QuadraticInterpolation(u, t; extrapolation_right = ExtrapolationType.extension) t_eval = 4.0 @test A(t_eval) ≈ -2.0