JuliaMath / Interpolations.jl

Fast, continuous interpolation of discrete datasets in Julia
http://juliamath.github.io/Interpolations.jl/
Other
533 stars 110 forks source link

Add non-parenthetical BSpline constructor? #401

Open stillyslalom opened 3 years ago

stillyslalom commented 3 years ago

This is mostly a matter of taste, but the current BSpline constructor has too damn many parentheses.

interpolate(A, BSpline(Cubic(Line(OnGrid())))) - heaven help you if you don't have bracket-matching turned on!

∘(args...) allows a more-legible syntax:

julia> Interpolations.BSpline(args...) = BSpline(∘(args...)())

julia> BSpline(Cubic, Line, OnGrid)
BSpline(Cubic(Line(OnGrid())))

If it's a good idea, I can put together a PR.

mkitti commented 3 years ago

I agree. The parentheses can be confusing. Do note for this specific case, there is CubicSplineInterpolation:

julia> using Interpolations

help?> CubicSplineInterpolation
search: CubicSplineInterpolation

  etp = CubicSplineInterpolation(knots, A; bc=Line(OnGrid()), extrapolation_bc=Throw())

  A shorthand for extrapolate(interpolate(knots, A, BSpline(Cubic(bc))), extrapolation_bc).

One could also apply the alternative pipe syntax:

julia> OnGrid() |> Line |> Cubic |> BSpline
BSpline(Cubic(Line(OnGrid())))

I'm also trying to find a good reason that OnGrid needs to be constructed rather than being a singleton constant. The above line shouldn't need any parentheses:

julia> OnGridSingleton = OnGrid()
OnGrid()

julia> OnGridSingleton |> Line |> Cubic |> BSpline
BSpline(Cubic(Line(OnGrid())))

Perhaps this is better since it does not introduce new names with the current API:

julia> using Interpolations

julia> Interpolations.Line(::Type{T}) where T <: Interpolations.GridType = Line(T())

julia> OnGrid |> Line |> Cubic |> BSpline
BSpline(Cubic(Line(OnGrid())))

Rather than Interpolations.BSpline(args...) = BSpline(∘(args...)()) I would like a method with a more specific type signature. For your example,

julia> import Interpolations: BSpline, DegreeBC, BoundaryCondition, GridType

julia> BSpline(D::Type{ <:DegreeBC }, BC::Type{ <: BoundaryCondition }, GT::Type{ <: GridType }) = BSpline( D( BC( GT() ) ) )
BSpline

julia> BSpline(Cubic, Line, OnGrid)
BSpline(Cubic(Line(OnGrid())))

Is there a reason not to continue this pattern all the way up to interpolate?

julia> using Interpolations

julia> import Interpolations: interpolate, InterpolationType, DegreeBC, BoundaryCondition, GridType

julia> interpolate(A, I::Type{<: InterpolationType}, D::Type{ <:DegreeBC }, BC::Type{ <: BoundaryCondition }, GT::Type{ <: GridType }) = interpolate(A, ∘(I, D, BC, GT)() )
interpolate (generic function with 11 methods)

julia> interpolate(1:5, BSpline, Cubic, Line, OnGrid)
5-element interpolate(OffsetArray(::Array{Float64,1}, 0:6), BSpline(Cubic(Line(OnGrid())))) with element type Float64:
 0.9999999999999999
 1.9999999999999998
 3.0
 4.0
 5.0

I do think there is a reasonable pull request here. The implementation would need to be a bit more precise though. Another important aspect will be the documentation.

stillyslalom commented 3 years ago

re: a reason to not continue the pattern, it doesn't mesh with specification of different interpolation methods along each dimension of a multidimensional array.

An additional advantage of the \circ constructor: you can broadcast over the constructor with a tuple of boundary conditions!

julia> BSpline.(Cubic, (Periodic, Periodic, Line), OnGrid)
(BSpline(Cubic(Periodic(OnGrid()))), BSpline(Cubic(Periodic(OnGrid()))), BSpline(Cubic(Line(OnGrid()))))

What's the advantage of restricting the type signature? You'd need a separate method for BSpline(Linear()), which doesn't use any boundary conditions, and which works out of the box with \circ.

mkitti commented 3 years ago

Defining a method on args... is problematic for forward compatibility. We could easily need to add a more specific method later which might change behavior created by BSpline(args...). I do not have an issue with \circ being used for the implementation of the method.

julia> using Interpolations

julia> import Interpolations: BSpline, Degree, BoundaryCondition, GridType, Periodic

julia> BSpline(D::Type{ <:Degree }, BC::Type{ <: BoundaryCondition }, GT::Type{ <: GridType }) = BSpline( ∘(D,BC,GT)() )
BSpline

julia> BSpline(D::Type{ <:Degree }, BC::Type{ <: BoundaryCondition }) = BSpline( ∘(D,BC)() )
BSpline

julia> BSpline(D::Type{ <:Degree }) = BSpline( D() )
BSpline

julia> BSpline.(Cubic, (Periodic, Periodic, Line), OnGrid)
(BSpline(Cubic(Periodic(OnGrid()))), BSpline(Cubic(Periodic(OnGrid()))), BSpline(Cubic(Line(OnGrid()))))

julia> BSpline.(Cubic, (Periodic, Periodic, Line))
(BSpline(Cubic(Periodic())), BSpline(Cubic(Periodic())), BSpline(Cubic(Line())))

julia> BSpline(Linear)
BSpline(Linear())