-
Notifications
You must be signed in to change notification settings - Fork 12
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
Convenience constructors for Interpolations.jl #47
Comments
A much saner interface for construction (if not calling) is https://github.com/kbarbary/Dierckx.jl |
Note that I found a PR in progress for #48 which we might be able to help with. Sounds like gridded cubic splines are not supported yet. But what about cubic splines on a uniform grid? This is where the scaling comes in that I just don't understand. |
Saw this: https://discourse.julialang.org/t/julia-version-of-matlab-function-interp1/8540 Not sure it is the best idea to do it that way, but gives you a sense of the usage patters. |
I made the following tutorial file that lists all possible scenarios and their solutions: https://github.com/chiyahn/notes/blob/master/interpolations/interpolation-scenarios.md including some suggestions for convenient constructors as well! For cubic splines (or other nonlinear splines) on irregular grids, I can think of the following two solutions as I explained in the markdown file:
|
Great, thanks! But I didn't see the convenience constructors? Also, see the final comments on #26 to get it ready to merge. |
Sorry, now I see the constructors. I don't love the One thing that could help is that we can dispatch based on the julia> typeof(1.0:0.1:2.0)
StepRangeLen{Float64,Base.TwicePrecision{Float64},Base.TwicePrecision{Float64}}
julia> typeof(1:10)
UnitRange{Int64} Is that a way to have a single interface support both irregular and regular grids? Maybe the convenience constructor should have the name of the interpolation method rather than the dimension? That is, f(x) = sin(x) / 2
xs = linspace(1.0, 2.0 ,10) #Note, this does not return a vector!
xs_irregular = collect(xs); #Now this is an an AbstractVector and no longer a StepRangeLen
#This knows it is a regular grid since xs is StepRangeLen or a StepRange
LinearInterp(xs, f.(xs))
#This could use the fact that typeof(1:10) is a UnitRange to realize that rescaling is not needed?
LinearInterp(1:10, f.(1:10))
#Cubic Spline knowing that xs is a StepRangeLen
CubicSplineInterp(xs, f.(xs)) #This knows it is a regular grid since xs is StepRangeLen
#Now, this uses the irregular grids, since it dispatches on the AbstractVector rather than the UnitRange or StepRangeLen?
LinearInterp(xs_irregular , f.(xs_irregular ))
#This isn't functional yet..., but could be added later.
CubicSplineInterp(xs_irregular , f.(xs_irregular )) If things moved to multiple dimensional interpolation, then I think that this pattern for convenience constructors would work well, we would just need to dispatch on what Note that with this, we could write a 1D |
I see your point! I was trying to imitate what MATLAB users are going to be more familiar with, but I agree with you that writing a constructor based on degrees (rather than dimension) is a more reasonable choice. The best way to handle it would be to create overloading constructors that are dependent on types of parameters so that (at least for linear case) it returns a scaled interpolation instance if the grids are regular and gridded one if they are not. function LinearInterp(xs::AbstractArray,ys)
itp = interpolate((xs,), ys, Gridded(Linear()))
return(itp)
end
function LinearInterp(xs::Range,ys)
itp = interpolate(ys, BSpline(Linear()), OnGrid())
return(scale(itp, xs))
end The following unit tests passed: # test regular grid cases
xs = linspace(xmin,xmax,N)
ys = f(xs)
itp = LinearInterp(xs, ys)
# unit tests
@test itp[xs[1]] == f(xs[1])
@test itp[xs[2]] == f(xs[2])
@test itp[(xs[1] + xs[2]) / 2] == (f(xs[1]) + f(xs[2])) / 2 # test irregular grid cases
xs = [x^2 for x = 1:xmax]
ys = f(xs)
itp = LinearInterp(xs, ys)
# unit tests
@test itp[xs[1]] == f(xs[1])
@test itp[xs[2]] == f(xs[2])
@test itp[(xs[1] + xs[2]) / 2] == (f(xs[1]) + f(xs[2])) / 2 I will look into multi-dimensional cases tomorrow as soon as the survey conference (😭) is finally done. |
Great! Though before multidimensional cases, I would try to finish JuliaMath/Interpolations.jl#206 (comment) so that we can tell people to merge your PR. A few small things on this, which seems extremely close:
but we might want to profile it to see if this slows things down considerably. |
I did a basic profiling with |
LinearInterp(vs, ranges::AbstractArray...) = interpolate(ranges, vs, Gridded(Linear()))
LinearInterp(vs, ranges::Range...) = scale(interpolate(vs, BSpline(Linear()), OnGrid()), ranges) but running the following lines returns an error: xs = 1:.1:10
ys = 1:.5:10
f(x, y) = log(x+y)
A = [f(x,y) for x in xs, y in ys]
LinearInterp(A, xs, ys) The reason is that when LinearInterp(vs, ranges::Range...) = ScaledInterpolation(interpolate(vs, BSpline(Linear()), OnGrid()), ranges)
|
Interesting. So that
I think it is reasonable to bypass the checks for simplicity. I like clear code, and the whole thing is kind of unsafe. |
Another, fancier, way to do it is to look into ways to represent cartesian products with operator overloading. We can see if http://juliaapproximation.github.io/ApproxFun.jl/latest/usage/domains.html does anything interesting... For example, instead of a tuple it might help to be explicit.
|
Check out QuantEcon/Expectations.jl#9 for some discussion of the generic, abstract types for ranges.... this will matter when it comes to the interface to call with uniform grid methods. |
Oh, I see your point; in fact the code you provided works by changing LinearInterp(ranges, vs) = scale(interpolate(vs, BSpline(Linear()), OnGrid()), ranges...) but the challenge here is that Tuple is not a LinearInterp(ranges::Tuple{T}, vs) where T <: Range = print(typeof(ranges)) in fact, one has to define it alternatively as We can, for instance, alternatively take |
On 0.6, julia> LinearInterp(ranges::NTuple{N,T}, vs) where {N,T <: Range} = print(typeof(ranges))
LinearInterp (generic function with 1 method)
julia> LinearInterp((1:10,2:5), 1)
Tuple{UnitRange{Int64},UnitRange{Int64}}
julia> LinearInterp((1:0.1:10,2:0.1:5), 1)
Tuple{StepRangeLen{Float64,Base.TwicePrecision{Float64},Base.TwicePrecision{Float64}},StepRangeLen{Float64,Base.TwicePrecision{Float64},Base.TwicePrecision{Float64}}} Still not 100% sure of the right approach. Regardless of which argument comes first, my feeling is that the ranges really represents some coherent "thing" and that variable argument parameters to the function do not capture that. Having it as a single object allows us to change behavior later to be more in line with the spirit of http://juliaapproximation.github.io/ApproxFun.jl/latest/usage/domains.html If what I suggested works, maybe we can get a tuple based approach and then ask for people's feedback. |
Whoa, didn't think about LinearInterp(ranges::NTuple{N,T}, vs) where {N,T <: Range} = scale(interpolate(vs, BSpline(Linear()), OnGrid()), ranges...)
LinearInterp(ranges::NTuple{N,T}, vs) where {N,T <: AbstractArray} = interpolate(ranges, vs, Gridded(Linear())) I tried some unit tests and they have all passed: https://github.com/chiyahn/notes/blob/master/interpolations/multivariate-constructor.md; we can use them for unit tests to be included in PR. Do you want me to make a PR for linear cases first? |
Maybe add in the throwing extrapolation as well? Lets stick with the Ntuple for now, Why don't we get the full version up and running in a PR first (maybe without docs) to get feedback. I think the key is
And that is pretty much it? After we get this done, we are 90% of the way there to making the library usable. Then we can think about whether it is the right library to stick with in the middle-term. The only other thing I would think about is whether we want to change the names? I asked Spencer and he doesn't see a reason to have this stuff using QuantEcon naming. My suggestion is |
No problem, I'll add in throwing extrapolations as well and work on the version for cubic spline interpolation! Meanwhile, note that the current version supports both univariate and multivariate cases; unit tests I created support both cases. And I agree -- I prefer longer names too. |
I think this is the right strategy, and lets not overthink the interface. These were intended to patch up |
Okay, here I have made a pull request: JuliaMath/Interpolations.jl#214. Even though implementation itself didn't take that much time it took me a while to remind myself that I had to add new functions to |
FYI, here are unit tests I have run (included in |
(edit: hit enter too early!) Great! The last thing I see is that I think we need a 1D version of the interfaces... the whole tuple of one element thing is ugly. Maybe add in something like
That way you can just go
|
I think this can be closed now -- the PR (JuliaMath/Interpolations.jl#214) has been merged! 🎉 There are a few changes in |
Great! |
I find the construction methods for Interpolations.jl to be almost impenetrable. It would be really nice to have some convenience constructors to make things easier to use. Coupled with the
()
access, I think this makes Interpolations.jl feasible for teaching.I can't figure out whether we would usually want https://github.com/JuliaMath/Interpolations.jl#gridded-interpolation or https://github.com/JuliaMath/Interpolations.jl#scaled-bsplines ?
The big scenarios I can think of to make easy in 1 dimension are:
I think it would make sense to figure out:
The text was updated successfully, but these errors were encountered: