-
Notifications
You must be signed in to change notification settings - Fork 113
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Monotonic interpolations #238
Changes from 2 commits
c3d3d0b
5854513
28ad194
1bfd8d2
9d65102
9c687b4
c3763e3
89dec3b
a19eff7
514185a
a2144cf
e6a38cb
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -60,8 +60,8 @@ Cubic cardinal splines, uses `tension` parameter which must be between [0,1] | |
Cubin cardinal splines can overshoot for non-monotonic data | ||
(increasing tension reduces overshoot). | ||
""" | ||
struct CardinalMonotonicInterpolation{T<:Number} <: MonotonicInterpolationType | ||
tension :: T # must be in [0, 1] | ||
struct CardinalMonotonicInterpolation{TTension<:Number} <: MonotonicInterpolationType | ||
tension :: TTension # must be in [0, 1] | ||
end | ||
|
||
""" | ||
|
@@ -107,12 +107,12 @@ end | |
Monotonic interpolation up to third order represented by type, knots and | ||
coefficients. | ||
""" | ||
struct MonotonicInterpolation{T, TCoeffs, Tel, Type<:MonotonicInterpolationType, | ||
TKnots<:AbstractVector{<:Number}, AType <: AbstractArray{Tel,1}} <: AbstractInterpolation{T,1,DimSpec{Type}} | ||
struct MonotonicInterpolation{T, TCoeffs, TEl, TInterpolationType<:MonotonicInterpolationType, | ||
TKnots<:AbstractVector{<:Number}, TACoeff <: AbstractArray{TEl,1}} <: AbstractInterpolation{T,1,DimSpec{TInterpolationType}} | ||
|
||
it::Type | ||
it::TInterpolationType | ||
knots::TKnots | ||
A::AType # constant parts of piecewise polynomials | ||
A::TACoeff # constant parts of piecewise polynomials | ||
m::Vector{TCoeffs} # coefficients of linear parts of piecewise polynomials | ||
c::Vector{TCoeffs} # coefficients of quadratic parts of piecewise polynomials | ||
d::Vector{TCoeffs} # coefficients of cubic parts of piecewise polynomials | ||
|
@@ -122,10 +122,10 @@ end | |
size(A::MonotonicInterpolation) = size(A.knots) | ||
axes(A::MonotonicInterpolation) = axes(A.knots) | ||
|
||
function MonotonicInterpolation(::Type{TWeights}, it::IType, knots::TKnots, A::AbstractArray{Tel,1}, | ||
m::Vector{TCoeffs}, c::Vector{TCoeffs}, d::Vector{TCoeffs}) where {TWeights, TCoeffs, Tel, IType<:MonotonicInterpolationType, TKnots<:AbstractVector{<:Number}} | ||
function MonotonicInterpolation(::Type{TWeights}, it::TInterpolationType, knots::TKnots, A::AbstractArray{TEl,1}, | ||
m::Vector{TCoeffs}, c::Vector{TCoeffs}, d::Vector{TCoeffs}) where {TWeights, TCoeffs, TEl, TInterpolationType<:MonotonicInterpolationType, TKnots<:AbstractVector{<:Number}} | ||
|
||
isconcretetype(IType) || error("The spline type must be a leaf type (was $IType)") | ||
isconcretetype(TInterpolationType) || error("The spline type must be a leaf type (was $TInterpolationType)") | ||
isconcretetype(TCoeffs) || warn("For performance reasons, consider using an array of a concrete type (eltype(A) == $(eltype(A)))") | ||
|
||
check_monotonic(knots, A) | ||
|
@@ -137,11 +137,11 @@ function MonotonicInterpolation(::Type{TWeights}, it::IType, knots::TKnots, A::A | |
T = typeof(cZero * first(A)) | ||
end | ||
|
||
MonotonicInterpolation{T, TCoeffs, Tel, IType, TKnots, typeof(A)}(it, knots, A, m, c, d) | ||
MonotonicInterpolation{T, TCoeffs, TEl, TInterpolationType, TKnots, typeof(A)}(it, knots, A, m, c, d) | ||
end | ||
|
||
function interpolate(::Type{TWeights}, ::Type{TCoeffs}, knots::TKnots, | ||
A::AbstractArray{Tel,1}, it::IT) where {TWeights,TCoeffs,Tel,TKnots<:AbstractVector{<:Number},IT<:MonotonicInterpolationType} | ||
A::AbstractArray{TEl,1}, it::TInterpolationType) where {TWeights,TCoeffs,TEl,TKnots<:AbstractVector{<:Number},TInterpolationType<:MonotonicInterpolationType} | ||
|
||
check_monotonic(knots, A) | ||
|
||
|
@@ -151,7 +151,7 @@ function interpolate(::Type{TWeights}, ::Type{TCoeffs}, knots::TKnots, | |
c = Vector{TCoeffs}(undef, n-1) | ||
d = Vector{TCoeffs}(undef, n-1) | ||
for k ∈ 1:n-1 | ||
if IT == LinearMonotonicInterpolation | ||
if TInterpolationType == LinearMonotonicInterpolation | ||
c[k] = d[k] = zero(TCoeffs) | ||
else | ||
xdiff = knots[k+1] - knots[k] | ||
|
@@ -163,8 +163,8 @@ function interpolate(::Type{TWeights}, ::Type{TCoeffs}, knots::TKnots, | |
MonotonicInterpolation(TWeights, it, knots, A, m, c, d) | ||
end | ||
|
||
function interpolate(knots::AbstractVector{<:Number}, A::AbstractArray{Tel,1}, | ||
it::IT) where {Tel,IT<:MonotonicInterpolationType} | ||
function interpolate(knots::AbstractVector{<:Number}, A::AbstractArray{TEl,1}, | ||
it::TInterpolationType) where {TEl,TInterpolationType<:MonotonicInterpolationType} | ||
|
||
interpolate(tweight(A), tcoef(A), knots, A, it) | ||
end | ||
|
@@ -180,7 +180,12 @@ function (itp::MonotonicInterpolation)(x::Number) | |
return itp.A[k] + itp.m[k]*xdiff + itp.c[k]*xdiff*xdiff + itp.d[k]*xdiff*xdiff*xdiff | ||
end | ||
|
||
function derivative(itp::MonotonicInterpolation, x::Number) | ||
function gradient(itp::MonotonicInterpolation, x::AbstractArray{<:Number, 1}) | ||
length(x) == 1 || error("Given vector x ($x) should have exactly one element") | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. With There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This check isn't needed with There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yeah; for monotonic interpolations it doesn't matter very much. My main concern here is that I want the interface to match It might be that There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yes, I understand, but There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Hum. I'll ping in @timholy here... |
||
return SVector(gradient1(itp, x[1])) | ||
end | ||
|
||
function gradient1(itp::MonotonicInterpolation, x::Number) | ||
k = searchsortedfirst(itp.knots, x) | ||
if k > 1 | ||
k -= 1 | ||
|
@@ -189,13 +194,27 @@ function derivative(itp::MonotonicInterpolation, x::Number) | |
return itp.m[k] + 2*itp.c[k]*xdiff + 3*itp.d[k]*xdiff*xdiff | ||
end | ||
|
||
function hessian(itp::MonotonicInterpolation, x::AbstractArray{<:Number, 1}) | ||
length(x) == 1 || error("Given vector x ($x) should have exactly one element") | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Same thing here: There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. No |
||
return SVector(hessian1(itp, x[1])) | ||
end | ||
|
||
function hessian1(itp::MonotonicInterpolation, x::Number) | ||
k = searchsortedfirst(itp.knots, x) | ||
if k > 1 | ||
k -= 1 | ||
end | ||
xdiff = x - itp.knots[k] | ||
return 2*itp.c[k] + 6*itp.d[k]*xdiff | ||
end | ||
|
||
@inline function check_monotonic(knots, A) | ||
axes(knots) == axes(A) || throw(DimensionMismatch("knot vector must have the same axes as the corresponding array")) | ||
issorted(knots) || error("knot-vector must be sorted in increasing order") | ||
end | ||
|
||
function calcTangents(::Type{TCoeffs}, x::AbstractVector{<:Number}, | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. These methods could do with some more documentation, that explains the mathematical reasoning behind the algorithm (or at least refers to resources that explain it). The Fritsch & Carlson version has good docs, but the other ones have none... There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This code was taken almost straight from the implementation provided by niclasmattsson in discussion for issue #105 . I haven't really looked into the details except for the interpretation of coefficients |
||
y::AbstractVector{Tel}, method::LinearMonotonicInterpolation) where {TCoeffs, Tel} | ||
y::AbstractVector{TEl}, method::LinearMonotonicInterpolation) where {TCoeffs, TEl} | ||
|
||
n = length(x) | ||
Δ = Vector{TCoeffs}(undef, n-1) | ||
|
@@ -210,7 +229,7 @@ function calcTangents(::Type{TCoeffs}, x::AbstractVector{<:Number}, | |
end | ||
|
||
function calcTangents(::Type{TCoeffs}, x::AbstractVector{<:Number}, | ||
y::AbstractVector{Tel}, method::FiniteDifferenceMonotonicInterpolation) where {TCoeffs, Tel} | ||
y::AbstractVector{TEl}, method::FiniteDifferenceMonotonicInterpolation) where {TCoeffs, TEl} | ||
|
||
n = length(x) | ||
Δ = Vector{TCoeffs}(undef, n-1) | ||
|
@@ -229,7 +248,7 @@ function calcTangents(::Type{TCoeffs}, x::AbstractVector{<:Number}, | |
end | ||
|
||
function calcTangents(::Type{TCoeffs}, x::AbstractVector{<:Number}, | ||
y::AbstractVector{Tel}, method::CardinalMonotonicInterpolation{T}) where {T, TCoeffs, Tel} | ||
y::AbstractVector{TEl}, method::CardinalMonotonicInterpolation{TTension}) where {TTension, TCoeffs, TEl} | ||
|
||
n = length(x) | ||
Δ = Vector{TCoeffs}(undef, n-1) | ||
|
@@ -240,15 +259,15 @@ function calcTangents(::Type{TCoeffs}, x::AbstractVector{<:Number}, | |
if k == 1 # left endpoint | ||
m[k] = Δk | ||
else | ||
m[k] = (oneunit(T) - method.tension) * (y[k+1] - y[k-1]) / (x[k+1] - x[k-1]) | ||
m[k] = (oneunit(TTension) - method.tension) * (y[k+1] - y[k-1]) / (x[k+1] - x[k-1]) | ||
end | ||
end | ||
m[n] = Δ[n-1] | ||
return (m, Δ) | ||
end | ||
|
||
function calcTangents(::Type{TCoeffs}, x::AbstractVector{<:Number}, | ||
y::AbstractVector{Tel}, method::FritschCarlsonMonotonicInterpolation) where {TCoeffs, Tel} | ||
y::AbstractVector{TEl}, method::FritschCarlsonMonotonicInterpolation) where {TCoeffs, TEl} | ||
|
||
n = length(x) | ||
Δ = Vector{TCoeffs}(undef, n-1) | ||
|
@@ -298,7 +317,7 @@ function calcTangents(::Type{TCoeffs}, x::AbstractVector{<:Number}, | |
end | ||
|
||
function calcTangents(::Type{TCoeffs}, x::AbstractVector{<:Number}, | ||
y :: AbstractVector{Tel}, method :: FritschButlandMonotonicInterpolation) where {TCoeffs, Tel} | ||
y :: AbstractVector{TEl}, method :: FritschButlandMonotonicInterpolation) where {TCoeffs, TEl} | ||
|
||
# based on Fritsch & Butland (1984), | ||
# "A Method for Constructing Local Monotone Piecewise Cubic Interpolants", | ||
|
@@ -324,7 +343,7 @@ function calcTangents(::Type{TCoeffs}, x::AbstractVector{<:Number}, | |
end | ||
|
||
function calcTangents(::Type{TCoeffs}, x::AbstractVector{<:Number}, | ||
y::AbstractVector{Tel}, method::SteffenMonotonicInterpolation) where {TCoeffs, Tel} | ||
y::AbstractVector{TEl}, method::SteffenMonotonicInterpolation) where {TCoeffs, TEl} | ||
|
||
# Steffen (1990), | ||
# "A Simple Method for Monotonic Interpolation in One Dimension", | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nice! This is almost what we want, but the type of
x
should probably beVarArg{<:Number,1}
- see e.g. how it's done forGridded
.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I've just checked it and it looks like
function gradient(itp::MonotonicInterpolation, x::Number)
works. I thinkgradient
isn't really a vararg function when it expects exactly two arguments :)I tried vararg with hessian first and with
function hessian(itp::MonotonicInterpolation, x::Vararg{<:Number,1})
I got stack overflow due to this function but withfunction hessian(itp::MonotonicInterpolation, x::Number)
it just works.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
gradient
is a varargs function, because the notationargs::Vararg{T,N}
means "supplyN
separate arguments of typeT
." But in your case since allMonotonicInterpolation
s are 1-dimensional, this is equivalent togradient(itp::MonotonicInterpolation, x::Number)
.x::Vararg{Number,1}
should be the same asx::Number
, butVararg{<:Number,1}
may not be.n-dimensional positions are encoded as varargs; like the rest of julia, indexing with vectors means something like this:
This is not what you mean here, so definitely get rid of the
AbstractArray
.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'll change
x::AbstractArray{<Number,1}
tox::Number
then. In this casex::Vararg{Number,1}
causes a stack overflow whilex::Number
does not:There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
My mistake, I checked again and using
x::Number
leads to the same stack overflow error. I don't know what I should do.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes,
Interpolations.gradient(itp, 0.3)
works, butInterpolations.gradient(itp, [0.3])
andInterpolations.hessian(itp, [0.3])
do not:These error messages aren't helpful.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I just pushed some fixes to your branch.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks! Should I do something about that problem with gradient/hessian with array as an argument?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If you want to support
Interpolations.gradient(itp, [0.3])
you need a special method that returns an array of gradients, e.g., aVector{SVector{1,Float64}}
. But none of the other methods here support such a syntax:so I don't think you have to, either.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
OK, so I'll just remove the failing tests for gradient/hessian with array as an argument.