JuliaMath / Interpolations.jl

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

Weight computation for scaled interpolation #479

Open DylanMMarques opened 2 years ago

DylanMMarques commented 2 years ago

Hello,

I am working with Interpolations.jl and I am using the weight computation feature. However, the results are not what I was expecting. This code works fine if the interpolation is not scaled but it is not correct due to the scale step. Is it possible to calculate the weight for a scaled interpolation? Or does the weight calculation interface works differently than this with a scaled interpolation?

using Interpolations
x_X = range(0, 100., length = 10)
y = 1:length(x_X)
itp = interpolate(y, BSpline(Linear()))
itp_scaled = scale(itp, (x_X))

wis = Interpolations.weightedindexes((Interpolations.value_weights,), Interpolations.itpinfo(itp_scale)..., (2.5,))
y[wis[1]] # 2.5

itp_scaled(2.5) # 1.225

Thanks

mkitti commented 2 years ago

All scale interpolation does is scale the input: https://github.com/JuliaMath/Interpolations.jl/blob/af35e779a7c39e540555d2c35bc1b2ccde242bc2/src/scaling/scaling.jl#L102-L108

It does not change the weights.

DylanMMarques commented 2 years ago

The scale function stretch the x-axis of the interpolation. Therefore, the weights are dependent on the scale because the weights are dependent on the x-axis of the interpolated function. The output of the function:

wis = Interpolations.weightedindexes((Interpolations.value_weights,), Interpolations.itpinfo(itp_scale)..., (2.5,))
# wis = (Interpolations.WeightedAdjIndex{2, Float64}(2, (0.5, 0.5)),)

The itp_scale is: f(0) = y[1] and f(x_X[1]) = y[2], so the weights of x = 2.5 should be between y[1] and y[2], not y[2] and y[3] as the value of wis is.

mkitti commented 2 years ago

Are you asking how it currently works or are you suggesting an improvement? Interpolations.weightedindexes is not meant as a public interface.

DylanMMarques commented 2 years ago

I am tying to confirm that I understand how it works because, to me, it doesn't seem to work properly with an itp scaled by a StepRangeLength (the function is probably not meant for that). I analysed the code with more detail and this makes sense as Interpolation.itpinfo removes any reference about the scaling part.

If you agree with this, I would ask if there is any way of computing the weighted indexes of a scaled interpolation. If not, I am suggesting an improvement.

mkitti commented 2 years ago

Correct. The underlying Interpolation has no idea it is being scaled. What you would want to do is implement additional methods on ScaledInterpolation directly.

DylanMMarques commented 2 years ago

I am interested on the weights as I need to calculate an interpolation based on a (very sparse) matrix. This matrix is constructed based on the weights. For my application, it would be good to have a public interface of the weights that would work for any interpolation. Do you feel that such interface could be useful to more people? Or do you feel that the application of such interface is too small and might not be worth?

mkitti commented 2 years ago

It is possible. What would be the most consistent way to calculate the scaled weights from non-scaled weights? Would you be interested in submitting a pull request to implement Interpolations.weightedindexes on ScaledInterpolation?

DylanMMarques commented 2 years ago

Sure, I think that something like this should work nicely:

using Interpolations
x1 = range(0, 20, length = 11)
x2 = range(0, 20, length = 11)
y = x1 .+ x2'
itp = interpolate(y, BSpline(Linear()))
itp = scale(itp, x1, x2)

function weightedindexes(arg, scaleditp::ScaledInterpolation, x::NTuple{N}) where {N}
    ind = ntuple(i-> (x[i] - first(scaleditp.ranges[i])) / step(scaleditp.ranges[i]) + 1, N)
    return Interpolations.weightedindexes(arg, Interpolations.itpinfo(scaleditp)..., ind)
end

x = (5.4, 5.4)
wis = weightedindexes((Interpolations.value_weights,), itp, x)
y[wis...] == itp(x...) 

What do you think?

mkitti commented 2 years ago

This roughly looks fine to me. Could you turn this into a pull request?

DylanMMarques commented 2 years ago

I ran a few tests for the algorithm and it does not work for Interpolations.gradient_weights. For the gradients, the weight must be divided by step(scaleditp.ranges). I tried to do this but I am not very familiar with Interpolations.jl internal interface unfortunately, so it is quite challenging to me doing that. Do you think that you could update the code to return the correct values for the Interpolations.gradient_weights?

koehlerson commented 2 years ago

I would be interested in this as well. My use case is that I want to find the contributing points and their weights, so I don't need gradient_weights. Maybe I find time to make a PR