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 method with option to not allow extrapolation. #995

Open
wants to merge 1 commit 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
13 changes: 13 additions & 0 deletions src/Numerics.Tests/InterpolationTests/LinearSplineTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@

using MathNet.Numerics.Interpolation;
using NUnit.Framework;
using System;

namespace MathNet.Numerics.Tests.InterpolationTests
{
Expand Down Expand Up @@ -138,5 +139,17 @@ public void FewSamples()
Assert.That(() => LinearSpline.Interpolate(new double[1], new double[1]), Throws.ArgumentException);
Assert.That(LinearSpline.Interpolate(new[] { 1.0, 2.0 }, new[] { 2.0, 2.0 }).Interpolate(1.0), Is.EqualTo(2.0));
}

[Test]
public void DoNotExtrapolate()
{
LinearSpline ip = LinearSpline.Interpolate(_t, _y);

Assert.Throws<ArgumentOutOfRangeException>(() => ip.Interpolate(-2.1, false));
Assert.Throws<ArgumentOutOfRangeException>(() => ip.Interpolate(10.0, false));

Assert.Throws<ArgumentOutOfRangeException>(() => LinearSpline.Interpolate(new[] { 1.0, 2.0 }, new[] { 2.0, 2.0 }).Interpolate(0.9, false));
Assert.Throws<ArgumentOutOfRangeException>(() => LinearSpline.Interpolate(new[] { 1.0, 2.0 }, new[] { 2.0, 2.0 }).Interpolate(3.0, false));
}
}
}
18 changes: 18 additions & 0 deletions src/Numerics/Interpolation/LinearSpline.cs
Original file line number Diff line number Diff line change
Expand Up @@ -130,6 +130,24 @@ public static LinearSpline Interpolate(IEnumerable<double> x, IEnumerable<double
/// <returns>Interpolated value x(t).</returns>
public double Interpolate(double t)
{
return Interpolate(t, true);
}

/// <summary>
/// Interpolate at point t.
/// </summary>
/// <param name="t">Point t to interpolate at.</param>
/// <param name="allowExtrapolate">set to fals if extrapolate should not be allowed</param>
/// <returns>Interpolated value x(t).</returns>
/// <exception cref="ArgumentOutOfRangeException">Thrown when t is outside range and would result in Extrapolation!</exception>
public double Interpolate(double t, bool allowExtrapolate)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am not a big fan of bool variables in public APIs which act like toggle switches. I would rather create another public function, something like InterpolateWithPossibleExtrapolation, which probably needs to defined on the interface, so every instance can implement it.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I understand, it was intended to keep the interface clean and avoid polution of weird functions. Adding an additional function like InterpolateWithoutExtrapolation felt a bit like polluting the clean interface.
Also I didn't want to break any compatibility since the default behavior is currently with possible extrapolation.
To be honest (now I read it again) I can see it isn't making the interface less confusing since the flag name can give the impression that extrapolation is by default not allowed.

As stated in the pull request as well. If you do see potential in the addition of such functionality we can discuss what the most suitable implementation is and how it fits the library best.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hi again. I am in favor of having a clean API, and I feel that your proposal is in the right direction.

You are right, the name I suggested should have been InterpolateWithoutExtrapolation

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That all the interpolators extrapolate by default is possibly surprising behaviour, especially given that is not documented (as far as I can tell).
But I wonder if InterpolateWithoutExtrapolation or the proposed implementation (with allowExtrapolate) will resolve this in general.
Presumably users of the other splines would also want this method which would mean adding this method to all classes and to the interface.

Perhaps a cleaner implementation would be to create a decorator class called NonExtrapolatingSpline (i.e., implements IInterpolation and wraps IInterpolation), that handles this in general for all the interpolators.
Also, maybe doc comments could be added to each of the interpolators indicating that they extrapolate by default, but that this new class exists if anyone wants to opt out.

{
if (!allowExtrapolate &&
(t < _x[0] || t > _x[_x.Length - 1]))
{
throw new ArgumentOutOfRangeException("'t' is outside of the constructed array. This would result in Extrapolation!");
}

int k = LeftSegmentIndex(t);
return _c0[k] + (t - _x[k])*_c1[k];
}
Expand Down