Skip to content
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

Add support for efficient high accuracy convolution interpolation (up to 7th order accurate C11 continuous) in any number of dimensions #609

Open
wants to merge 15 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 1 addition & 2 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
## Build folder of Documenter.jl
docs/build/
Manifest.toml
.vscode
Manifest.toml
8 changes: 8 additions & 0 deletions src/Interpolations.jl
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@

constant_interpolation,
linear_interpolation,
convolution_interpolation,
cubic_spline_interpolation,

knots,
Expand Down Expand Up @@ -69,6 +70,9 @@
return true
end

struct ConvolutionMethod <: InterpolationType end
abstract type AbstractConvolutionInterpolation{T,N,TCoefs,IT<:Union{Tuple{Vararg{ConvolutionMethod}},ConvolutionMethod},Axs,KA,DT,DG,EQ} <: AbstractInterpolation{T,N,IT} end

"""
BoundaryCondition

Expand Down Expand Up @@ -434,6 +438,9 @@
_checkbounds(::CheckWillPass, itp, x) = true
_checkbounds(::NeedsCheck, itp, x) = checklubounds(lbounds(itp), ubounds(itp), x)

checkbounds(::Type{Bool}, itp::AbstractInterpolation, i::AbstractVector{Bool}) =

Check warning on line 441 in src/Interpolations.jl

View check run for this annotation

Codecov / codecov/patch

src/Interpolations.jl#L441

Added line #L441 was not covered by tests
throw(ArgumentError("Boolean indexing is not supported for interpolations"))

checklubounds(ls, us, xs) = _checklubounds(true, ls, us, xs)
_checklubounds(tf::Bool, ls::Tuple, us::Tuple, xs::Tuple) =
_checklubounds(tf & allbetween(ls[1], xs[1], us[1]), Base.tail(ls), Base.tail(us), Base.tail(xs))
Expand Down Expand Up @@ -465,6 +472,7 @@
include("iterate.jl")
include("chainrules/chainrules.jl")
include("hermite/cubic.jl")
include("convolution/convolution.jl")
if VERSION >= v"1.6"
include("gpu_support.jl")
end
Expand Down
20 changes: 20 additions & 0 deletions src/convenience-constructors.jl
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,20 @@
bc = Line(OnGrid()), extrapolation_bc = Throw()) where {N,T} =
extrapolate(scale(interpolate(vs, BSpline(Cubic(bc))), ranges...), extrapolation_bc)

# convolution interpolation
function convolution_interpolation(knots::Union{AbstractVector,NTuple{N,AbstractVector}}, values::AbstractArray{T,N};

Check warning on line 33 in src/convenience-constructors.jl

View check run for this annotation

Codecov / codecov/patch

src/convenience-constructors.jl#L33

Added line #L33 was not covered by tests
degree::Int=3, fast::Bool=true, precompute::Int=1000, B=nothing, extrapolation_bc=Throw()) where {T,N}
if knots isa AbstractVector
knots = (knots,)

Check warning on line 36 in src/convenience-constructors.jl

View check run for this annotation

Codecov / codecov/patch

src/convenience-constructors.jl#L35-L36

Added lines #L35 - L36 were not covered by tests
end
if fast
itp = FastConvolutionInterpolation(knots, values; degree=degree, precompute=precompute, B=B)

Check warning on line 39 in src/convenience-constructors.jl

View check run for this annotation

Codecov / codecov/patch

src/convenience-constructors.jl#L38-L39

Added lines #L38 - L39 were not covered by tests
else
itp = ConvolutionInterpolation(knots, values; degree=degree, B=B)

Check warning on line 41 in src/convenience-constructors.jl

View check run for this annotation

Codecov / codecov/patch

src/convenience-constructors.jl#L41

Added line #L41 was not covered by tests
end
return extrapolate(itp, extrapolation_bc)

Check warning on line 43 in src/convenience-constructors.jl

View check run for this annotation

Codecov / codecov/patch

src/convenience-constructors.jl#L43

Added line #L43 was not covered by tests
end

"""
etp = linear_interpolation(knots, A; extrapolation_bc=Throw())

Expand Down Expand Up @@ -66,3 +80,9 @@
without scaling or extrapolation.
"""
cubic_spline_interpolation
"""
etp = convolution_interpolation(knots, values; extrapolation_bc=Throw())

A shorthand for `extrapolate(ConvolutionInterpolation(knots, values), extrapolation_bc)`.
"""
convolution_interpolation
139 changes: 139 additions & 0 deletions src/convolution/convolution.jl
Original file line number Diff line number Diff line change
@@ -0,0 +1,139 @@
export ConvolutionInterpolation, FastConvolutionInterpolation, ConvolutionMethod, convolution_interpolation

# for type stability of specialized interpolation functions
struct HigherDimension{N} end
HigherDimension(::Val{N}) where N = HigherDimension{N}()

Check warning on line 5 in src/convolution/convolution.jl

View check run for this annotation

Codecov / codecov/patch

src/convolution/convolution.jl#L5

Added line #L5 was not covered by tests
struct ConvolutionKernel{DG} end
ConvolutionKernel(::Val{DG}) where {DG} = ConvolutionKernel{DG}()

Check warning on line 7 in src/convolution/convolution.jl

View check run for this annotation

Codecov / codecov/patch

src/convolution/convolution.jl#L7

Added line #L7 was not covered by tests
struct GaussianConvolutionKernel{B} end
GaussianConvolutionKernel(::Val{B}) where B = GaussianConvolutionKernel{B}()

Check warning on line 9 in src/convolution/convolution.jl

View check run for this annotation

Codecov / codecov/patch

src/convolution/convolution.jl#L9

Added line #L9 was not covered by tests

struct ConvolutionInterpolation{T,N,TCoefs<:AbstractArray,IT<:NTuple{N,ConvolutionMethod},
Axs<:Tuple,KA,DT,DG,EQ} <: AbstractConvolutionInterpolation{T,N,TCoefs,IT,Axs,KA,DT,DG,EQ}
coefs::TCoefs
knots::Axs
it::IT
h::NTuple{N,Float64}
kernel::KA
dimension::DT
deg::DG
eqs::EQ
end

struct FastConvolutionInterpolation{T,N,TCoefs<:AbstractArray,IT<:NTuple{N,ConvolutionMethod},
Axs<:Tuple,KA,DT,DG,EQ,PR,KP} <: AbstractConvolutionInterpolation{T,N,TCoefs,IT,Axs,KA,DT,DG,EQ}
coefs::TCoefs
knots::Axs
it::IT
h::NTuple{N,Float64}
kernel::KA
dimension::DT
deg::DG
eqs::EQ
pre_range::PR
kernel_pre::KP
end

include("convolution_extrapolation.jl")
include("convolution_coefs.jl")
include("convolution_fast_interpolation.jl")
include("convolution_kernel_interpolation.jl")
include("convolution_kernels.jl")

function ConvolutionInterpolation(knots::NTuple{N,AbstractVector}, vs::AbstractArray{T,N};

Check warning on line 43 in src/convolution/convolution.jl

View check run for this annotation

Codecov / codecov/patch

src/convolution/convolution.jl#L43

Added line #L43 was not covered by tests
degree::Int=3, B=nothing) where {T,N}

eqs = B === nothing ? get_equations_for_degree(degree) : 50
h = map(k -> k[2] - k[1], knots)
it = ntuple(_ -> ConvolutionMethod(), N)

Check warning on line 48 in src/convolution/convolution.jl

View check run for this annotation

Codecov / codecov/patch

src/convolution/convolution.jl#L46-L48

Added lines #L46 - L48 were not covered by tests

knots_new = expand_knots(knots, eqs-1) # expand boundaries
coefs = create_convolutional_coefs(vs, h, eqs) # create boundaries
kernel = B === nothing ? ConvolutionKernel(Val(degree)) : GaussianConvolutionKernel(Val(B))
dimension = N <= 3 ? Val(N) : HigherDimension(Val(N))
degree = Val(degree)

Check warning on line 54 in src/convolution/convolution.jl

View check run for this annotation

Codecov / codecov/patch

src/convolution/convolution.jl#L50-L54

Added lines #L50 - L54 were not covered by tests

ConvolutionInterpolation{T,N,typeof(coefs),typeof(it),typeof(knots_new),typeof(kernel),typeof(dimension),typeof(degree),typeof(eqs)}(

Check warning on line 56 in src/convolution/convolution.jl

View check run for this annotation

Codecov / codecov/patch

src/convolution/convolution.jl#L56

Added line #L56 was not covered by tests
coefs, knots_new, it, h, kernel, dimension, degree, eqs
)
end

function FastConvolutionInterpolation(knots::NTuple{N,AbstractVector}, vs::AbstractArray{T,N};

Check warning on line 61 in src/convolution/convolution.jl

View check run for this annotation

Codecov / codecov/patch

src/convolution/convolution.jl#L61

Added line #L61 was not covered by tests
degree::Int=3, precompute::Int=1000, B=nothing) where {T,N}

eqs = B === nothing ? get_equations_for_degree(degree) : 50
h = map(k -> k[2] - k[1], knots)
it = ntuple(_ -> ConvolutionMethod(), N)

Check warning on line 66 in src/convolution/convolution.jl

View check run for this annotation

Codecov / codecov/patch

src/convolution/convolution.jl#L64-L66

Added lines #L64 - L66 were not covered by tests

knots_new = expand_knots(knots, eqs-1) # expand boundaries
coefs = create_convolutional_coefs(vs, h, eqs) # create boundaries
kernel = B === nothing ? ConvolutionKernel(Val(degree)) : GaussianConvolutionKernel(Val(B))
dimension = N <= 3 ? Val(N) : HigherDimension(Val(N))
pre_range = range(0.0, 1.0, length=precompute)
kernel_pre = zeros(T, precompute, 2*eqs)
for i = 1:2*eqs
kernel_pre[:,i] .= kernel.(pre_range .- eqs .+ i .- 1)
end
degree = Val(degree)

Check warning on line 77 in src/convolution/convolution.jl

View check run for this annotation

Codecov / codecov/patch

src/convolution/convolution.jl#L68-L77

Added lines #L68 - L77 were not covered by tests

FastConvolutionInterpolation{T,N,typeof(coefs),typeof(it),typeof(knots_new),typeof(kernel),typeof(dimension),typeof(degree),typeof(eqs),typeof(pre_range),typeof(kernel_pre)}(

Check warning on line 79 in src/convolution/convolution.jl

View check run for this annotation

Codecov / codecov/patch

src/convolution/convolution.jl#L79

Added line #L79 was not covered by tests
coefs, knots_new, it, h, kernel, dimension, degree, eqs, pre_range, kernel_pre
)
end

function extend_vector(x::AbstractVector, n_extra::Integer)
step_start = x[2] - x[1]
step_end = x[end] - x[end-1]

Check warning on line 86 in src/convolution/convolution.jl

View check run for this annotation

Codecov / codecov/patch

src/convolution/convolution.jl#L84-L86

Added lines #L84 - L86 were not covered by tests

start_extension = range(x[1] - n_extra * step_start, step=step_start, length=n_extra)
end_extension = range(x[end] + step_end, step=step_end, length=n_extra)

Check warning on line 89 in src/convolution/convolution.jl

View check run for this annotation

Codecov / codecov/patch

src/convolution/convolution.jl#L88-L89

Added lines #L88 - L89 were not covered by tests

return vcat(start_extension, x, end_extension)

Check warning on line 91 in src/convolution/convolution.jl

View check run for this annotation

Codecov / codecov/patch

src/convolution/convolution.jl#L91

Added line #L91 was not covered by tests
end

function expand_knots(knots::NTuple{N,AbstractVector}, n_extra::Integer) where N
knots_new = ntuple(i -> extend_vector(knots[i], n_extra), N)
return knots_new

Check warning on line 96 in src/convolution/convolution.jl

View check run for this annotation

Codecov / codecov/patch

src/convolution/convolution.jl#L94-L96

Added lines #L94 - L96 were not covered by tests
end

getknots(itp::ConvolutionInterpolation) = itp.knots
Base.axes(itp::ConvolutionInterpolation) = axes(itp.coefs)
Base.size(itp::ConvolutionInterpolation) = size(itp.coefs)
lbounds(itp::ConvolutionInterpolation) = first.(itp.knots)
ubounds(itp::ConvolutionInterpolation) = last.(itp.knots)
itpflag(::Type{<:ConvolutionInterpolation{T,N,TCoefs,IT}}) where {T,N,TCoefs,IT} = IT()
coefficients(itp::ConvolutionInterpolation) = itp.coefs
Base.length(itp::ConvolutionInterpolation) = length(itp.coefs)
Base.iterate(itp::ConvolutionInterpolation, state=1) = state > length(itp) ? nothing : (itp[state], state+1)
itpflag(itp::ConvolutionInterpolation) = itp.it
getknots(itp::FastConvolutionInterpolation) = itp.knots
Base.axes(itp::FastConvolutionInterpolation) = axes(itp.coefs)
Base.size(itp::FastConvolutionInterpolation) = size(itp.coefs)
lbounds(itp::FastConvolutionInterpolation) = first.(itp.knots)
ubounds(itp::FastConvolutionInterpolation) = last.(itp.knots)
itpflag(::Type{<:FastConvolutionInterpolation{T,N,TCoefs,IT}}) where {T,N,TCoefs,IT} = IT()
coefficients(itp::FastConvolutionInterpolation) = itp.coefs
Base.length(itp::FastConvolutionInterpolation) = length(itp.coefs)
Base.iterate(itp::FastConvolutionInterpolation, state=1) = state > length(itp) ? nothing : (itp[state], state+1)
itpflag(itp::FastConvolutionInterpolation) = itp.it
lbound(ax::AbstractRange, itp::ConvolutionMethod) = lbound(ax, itpflag(itp))

Check warning on line 119 in src/convolution/convolution.jl

View check run for this annotation

Codecov / codecov/patch

src/convolution/convolution.jl#L99-L119

Added lines #L99 - L119 were not covered by tests

# accessing the coefficients
function Base.getindex(itp::ConvolutionInterpolation{T,N,TCoefs,IT,Axs,ConvolutionKernel{DG},DT}, I::Vararg{Integer,N}) where {T,N,TCoefs,IT,Axs,DT,DG}
return itp.coefs[I...]

Check warning on line 123 in src/convolution/convolution.jl

View check run for this annotation

Codecov / codecov/patch

src/convolution/convolution.jl#L122-L123

Added lines #L122 - L123 were not covered by tests
end
function (itp::ConvolutionInterpolation{T,1,TCoefs,IT,Axs,ConvolutionKernel{DG},Val{1}})(i::Integer) where {T,TCoefs,IT,Axs,DG}
return itp.coefs[i]

Check warning on line 126 in src/convolution/convolution.jl

View check run for this annotation

Codecov / codecov/patch

src/convolution/convolution.jl#L125-L126

Added lines #L125 - L126 were not covered by tests
end
function (itp::ConvolutionInterpolation{T,N,TCoefs,IT,Axs,ConvolutionKernel{DG},DT})(I::Vararg{Integer,N}) where {T,N,TCoefs,IT,Axs,DT,DG}
return itp.coefs[I...]

Check warning on line 129 in src/convolution/convolution.jl

View check run for this annotation

Codecov / codecov/patch

src/convolution/convolution.jl#L128-L129

Added lines #L128 - L129 were not covered by tests
end
function Base.getindex(itp::FastConvolutionInterpolation{T,N,TCoefs,IT,Axs,ConvolutionKernel{DG},DT}, I::Vararg{Integer,N}) where {T,N,TCoefs,IT,Axs,DT,DG}
return itp.coefs[I...]

Check warning on line 132 in src/convolution/convolution.jl

View check run for this annotation

Codecov / codecov/patch

src/convolution/convolution.jl#L131-L132

Added lines #L131 - L132 were not covered by tests
end
function (itp::FastConvolutionInterpolation{T,1,TCoefs,IT,Axs,ConvolutionKernel{DG},Val{1}})(i::Integer) where {T,TCoefs,IT,Axs,DG}
return itp.coefs[i]

Check warning on line 135 in src/convolution/convolution.jl

View check run for this annotation

Codecov / codecov/patch

src/convolution/convolution.jl#L134-L135

Added lines #L134 - L135 were not covered by tests
end
function (itp::FastConvolutionInterpolation{T,N,TCoefs,IT,Axs,ConvolutionKernel{DG},DT})(I::Vararg{Integer,N}) where {T,N,TCoefs,IT,Axs,DT,DG}
return itp.coefs[I...]

Check warning on line 138 in src/convolution/convolution.jl

View check run for this annotation

Codecov / codecov/patch

src/convolution/convolution.jl#L137-L138

Added lines #L137 - L138 were not covered by tests
end
Loading
Loading