Browse Source

Interpolation: more reasonable LinearSpline implementation

pull/184/head
Christoph Ruegg 13 years ago
parent
commit
5d9b0143e3
  1. 6
      src/Numerics/Interpolate.cs
  2. 182
      src/Numerics/Interpolation/LinearSpline.cs
  3. 188
      src/Numerics/Interpolation/LinearSplineInterpolation.cs
  4. 2
      src/Numerics/Numerics.csproj
  5. 29
      src/UnitTests/InterpolationTests/LinearSplineTest.cs

6
src/Numerics/Interpolate.cs

@ -63,11 +63,9 @@ namespace MathNet.Numerics
/// which can then be used to compute interpolations and extrapolations
/// on arbitrary points.
/// </returns>
public static IInterpolation LinearBetweenPoints(IList<double> points, IList<double> values)
public static IInterpolation LinearBetweenPoints(IEnumerable<double> points, IEnumerable<double> values)
{
var method = new LinearSplineInterpolation();
method.Initialize(points, values);
return method;
return LinearSpline.Interpolate(points, values);
}
/// <summary>

182
src/Numerics/Interpolation/LinearSpline.cs

@ -0,0 +1,182 @@
// <copyright file="LinearSpline.cs" company="Math.NET">
// Math.NET Numerics, part of the Math.NET Project
// http://numerics.mathdotnet.com
// http://github.com/mathnet/mathnet-numerics
// http://mathnetnumerics.codeplex.com
//
// Copyright (c) 2009-2013 Math.NET
//
// Permission is hereby granted, free of charge, to any person
// obtaining a copy of this software and associated documentation
// files (the "Software"), to deal in the Software without
// restriction, including without limitation the rights to use,
// copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the
// Software is furnished to do so, subject to the following
// conditions:
//
// The above copyright notice and this permission notice shall be
// included in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
// OTHER DEALINGS IN THE SOFTWARE.
// </copyright>
using System;
using System.Collections.Generic;
using System.Linq;
using MathNet.Numerics.Properties;
namespace MathNet.Numerics.Interpolation
{
/// <summary>
/// Linear Spline Interpolation.
/// </summary>
/// <remarks>Supports both differentiation and integration.</remarks>
public class LinearSpline : IInterpolation
{
readonly double[] _x;
readonly double[] _c0;
readonly double[] _c1;
/// <param name="x">Sample points (N+1), sorted ascending</param>
/// <param name="c0">Sample values (N or N+1) at the corresponding points; intercept, zero order coefficients</param>
/// <param name="c1">Slopes (N) at the sample points (first order coefficients): N</param>
public LinearSpline(double[] x, double[] c0, double[] c1)
{
_x = x;
_c0 = c0;
_c1 = c1;
}
/// <summary>
/// Create a linear spline interpolation from a set of (x,y) value pairs.
/// </summary>
/// <remarks>
/// The value pairs do not have to be sorted, but if they are not sorted ascendingly
/// and the passed x and y arguments are arrays, they will be sorted inplace and thus modified.
/// </remarks>
public static LinearSpline Interpolate(IEnumerable<double> x, IEnumerable<double> y)
{
var xx = (x as double[]) ?? x.ToArray();
var yy = (y as double[]) ?? y.ToArray();
if (xx.Length != yy.Length)
{
throw new ArgumentException(Resources.ArgumentVectorsSameLength);
}
Sorting.Sort(xx, yy);
var c1 = new double[xx.Length - 1];
for (int i = 0; i < c1.Length; i++)
{
c1[i] = (yy[i + 1] - yy[i])/(xx[i + 1] - xx[i]);
}
return new LinearSpline(xx, yy, c1);
}
/// <summary>
/// Gets a value indicating whether the algorithm supports differentiation (interpolated derivative).
/// </summary>
public bool SupportsDifferentiation
{
get { return true; }
}
/// <summary>
/// Gets a value indicating whether the algorithm supports integration (interpolated quadrature).
/// </summary>
public bool SupportsIntegration
{
get { return true; }
}
/// <summary>
/// Interpolate at point t.
/// </summary>
/// <param name="t">Point t to interpolate at.</param>
/// <returns>Interpolated value x(t).</returns>
public double Interpolate(double t)
{
int k = LeftBracketIndex(t);
return _c0[k] + (t - _x[k])*_c1[k];
}
/// <summary>
/// Differentiate at point t.
/// </summary>
/// <param name="t">Point t to interpolate at.</param>
/// <returns>Interpolated first derivative at point t.</returns>
public double Differentiate(double t)
{
int k = LeftBracketIndex(t);
return _c1[k];
}
/// <summary>
/// Differentiate twice at point t.
/// </summary>
/// <param name="t">Point t to interpolate at.</param>
/// <returns>Interpolated second derivative at point t.</returns>
public double Differentiate2(double t)
{
return 0d;
}
/// <summary>
/// Integrate up to point t.
/// </summary>
/// <param name="t">Right bound of the integration interval [a,t].</param>
/// <returns>Interpolated definite integral over the interval [a,t].</returns>
public double Integrate(double t)
{
int k = LeftBracketIndex(t);
var x = (t - _x[k]);
return x*(_c0[k] + x*0.5*_c1[k]);
}
/// <summary>
/// Interpolate, differentiate and 2nd differentiate at point t.
/// </summary>
/// <param name="t">Point t to interpolate at.</param>
/// <returns>Interpolated first derivative at point t.</returns>
public Tuple<double, double, double> DifferentiateAll(double t)
{
int k = LeftBracketIndex(t);
var x = (t - _x[k]);
return new Tuple<double, double, double>(_c0[k] + x*_c1[k], _c1[k], 0d);
}
/// <summary>
/// Find the index of the greatest sample point smaller than t.
/// </summary>
int LeftBracketIndex(double t)
{
// Binary search in the [ t[0], ..., t[n-2] ] (t[n-1] is not included)
int low = 0;
int high = _x.Length - 1;
while (low != high - 1)
{
int middle = (low + high)/2;
if (_x[middle] > t)
{
high = middle;
}
else
{
low = middle;
}
}
return low;
}
}
}

188
src/Numerics/Interpolation/LinearSplineInterpolation.cs

@ -1,188 +0,0 @@
// <copyright file="LinearSplineInterpolation.cs" company="Math.NET">
// Math.NET Numerics, part of the Math.NET Project
// http://numerics.mathdotnet.com
// http://github.com/mathnet/mathnet-numerics
// http://mathnetnumerics.codeplex.com
//
// Copyright (c) 2009-2010 Math.NET
//
// Permission is hereby granted, free of charge, to any person
// obtaining a copy of this software and associated documentation
// files (the "Software"), to deal in the Software without
// restriction, including without limitation the rights to use,
// copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the
// Software is furnished to do so, subject to the following
// conditions:
//
// The above copyright notice and this permission notice shall be
// included in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
// OTHER DEALINGS IN THE SOFTWARE.
// </copyright>
using System;
using System.Collections.Generic;
using MathNet.Numerics.Properties;
namespace MathNet.Numerics.Interpolation
{
/// <summary>
/// Linear Spline Interpolation Algorithm.
/// </summary>
/// <remarks>
/// This algorithm supports both differentiation and integration.
/// </remarks>
public class LinearSplineInterpolation : IInterpolation
{
/// <summary>
/// Internal Spline Interpolation
/// </summary>
readonly SplineInterpolation _spline;
/// <summary>
/// Initializes a new instance of the LinearSplineInterpolation class.
/// </summary>
public LinearSplineInterpolation()
{
_spline = new SplineInterpolation();
}
/// <summary>
/// Initializes a new instance of the LinearSplineInterpolation class.
/// </summary>
/// <param name="samplePoints">Sample Points t, sorted ascending.</param>
/// <param name="sampleValues">Sample Values x(t)</param>
public LinearSplineInterpolation(IList<double> samplePoints, IList<double> sampleValues)
{
_spline = new SplineInterpolation();
Initialize(samplePoints, sampleValues);
}
/// <summary>
/// Gets a value indicating whether the algorithm supports differentiation (interpolated derivative).
/// </summary>
/// <seealso cref="Differentiate(double)"/>
/// <seealso cref="DifferentiateAll(double)"/>
bool IInterpolation.SupportsDifferentiation
{
get { return true; }
}
/// <summary>
/// Gets a value indicating whether the algorithm supports integration (interpolated quadrature).
/// </summary>
/// <seealso cref="Integrate"/>
bool IInterpolation.SupportsIntegration
{
get { return true; }
}
/// <summary>
/// Initialize the interpolation method with the given spline coefficients (sorted by the sample points t).
/// </summary>
/// <param name="samplePoints">Sample Points t, sorted ascending.</param>
/// <param name="sampleValues">Sample Values x(t)</param>
public void Initialize(IList<double> samplePoints, IList<double> sampleValues)
{
double[] coefficients = EvaluateSplineCoefficients(samplePoints, sampleValues);
_spline.Initialize(samplePoints, coefficients);
}
/// <summary>
/// Evaluate the spline coefficients as used
/// internally by this interpolation algorithm.
/// </summary>
/// <param name="samplePoints">Sample Points t, sorted ascending.</param>
/// <param name="sampleValues">Sample Values x(t)</param>
/// <returns>Spline Coefficient Vector</returns>
public static double[] EvaluateSplineCoefficients(IList<double> samplePoints, IList<double> sampleValues)
{
if (null == samplePoints)
{
throw new ArgumentNullException("samplePoints");
}
if (null == sampleValues)
{
throw new ArgumentNullException("sampleValues");
}
if (samplePoints.Count < 2)
{
throw new ArgumentOutOfRangeException("samplePoints");
}
if (samplePoints.Count != sampleValues.Count)
{
throw new ArgumentException(Resources.ArgumentVectorsSameLength);
}
for (var i = 1; i < samplePoints.Count; ++i)
if (samplePoints[i] <= samplePoints[i - 1])
throw new ArgumentException(Resources.Interpolation_Initialize_SamplePointsNotStrictlyAscendingOrder, "samplePoints");
var coefficients = new double[4*(samplePoints.Count - 1)];
for (int i = 0, j = 0; i < samplePoints.Count - 1; i++, j += 4)
{
coefficients[j] = sampleValues[i];
coefficients[j + 1] = (sampleValues[i + 1] - sampleValues[i])/(samplePoints[i + 1] - samplePoints[i]);
coefficients[j + 2] = 0;
coefficients[j + 3] = 0;
}
return coefficients;
}
/// <summary>
/// Interpolate at point t.
/// </summary>
/// <param name="t">Point t to interpolate at.</param>
/// <returns>Interpolated value x(t).</returns>
public double Interpolate(double t)
{
return _spline.Interpolate(t);
}
/// <summary>
/// Differentiate at point t.
/// </summary>
/// <param name="t">Point t to interpolate at.</param>
/// <returns>Interpolated first derivative at point t.</returns>
/// <seealso cref="IInterpolation.SupportsDifferentiation"/>
/// <seealso cref="DifferentiateAll(double)"/>
public double Differentiate(double t)
{
return _spline.Differentiate(t);
}
/// <summary>
/// Interpolate, differentiate and 2nd differentiate at point t.
/// </summary>
/// <param name="t">Point t to interpolate at.</param>
/// <returns>Interpolated first derivative at point t.</returns>
/// <seealso cref="IInterpolation.SupportsDifferentiation"/>
/// <seealso cref="Differentiate(double)"/>
public Tuple<double, double, double> DifferentiateAll(double t)
{
return _spline.DifferentiateAll(t);
}
/// <summary>
/// Integrate up to point t.
/// </summary>
/// <param name="t">Right bound of the integration interval [a,t].</param>
/// <returns>Interpolated definite integral over the interval [a,t].</returns>
/// <seealso cref="IInterpolation.SupportsIntegration"/>
public double Integrate(double t)
{
return _spline.Integrate(t);
}
}
}

2
src/Numerics/Numerics.csproj

@ -383,7 +383,7 @@
<Compile Include="Interpolation\CubicSplineInterpolation.cs" />
<Compile Include="Interpolation\CubicHermiteSplineInterpolation.cs" />
<Compile Include="Interpolation\FloaterHormannRationalInterpolation.cs" />
<Compile Include="Interpolation\LinearSplineInterpolation.cs" />
<Compile Include="Interpolation\LinearSpline.cs" />
<Compile Include="Interpolation\NevillePolynomialInterpolation.cs" />
<Compile Include="Interpolation\SplineInterpolation.cs" />
<Compile Include="Interpolation\IInterpolation.cs" />

29
src/UnitTests/InterpolationTests/LinearSplineTest.cs

@ -28,12 +28,11 @@
// OTHER DEALINGS IN THE SOFTWARE.
// </copyright>
using MathNet.Numerics.Interpolation;
using NUnit.Framework;
namespace MathNet.Numerics.UnitTests.InterpolationTests
{
using System;
using Interpolation;
using NUnit.Framework;
/// <summary>
/// LinearSpline test case
/// </summary>
@ -43,12 +42,12 @@ namespace MathNet.Numerics.UnitTests.InterpolationTests
/// <summary>
/// Sample points.
/// </summary>
private readonly double[] _t = new[] { -2.0, -1.0, 0.0, 1.0, 2.0 };
readonly double[] _t = { -2.0, -1.0, 0.0, 1.0, 2.0 };
/// <summary>
/// Sample values.
/// </summary>
private readonly double[] _x = new[] { 1.0, 2.0, -1.0, 0.0, 1.0 };
readonly double[] _x = { 1.0, 2.0, -1.0, 0.0, 1.0 };
/// <summary>
/// Verifies that the interpolation matches the given value at all the provided sample points.
@ -56,7 +55,7 @@ namespace MathNet.Numerics.UnitTests.InterpolationTests
[Test]
public void FitsAtSamplePoints()
{
IInterpolation interpolation = new LinearSplineInterpolation(_t, _x);
IInterpolation interpolation = LinearSpline.Interpolate(_t, _x);
for (int i = 0; i < _x.Length; i++)
{
@ -89,7 +88,7 @@ namespace MathNet.Numerics.UnitTests.InterpolationTests
[TestCase(-10.0, -7.0, 1e-15)]
public void FitsAtArbitraryPointsWithMaple(double t, double x, double maxAbsoluteError)
{
IInterpolation interpolation = new LinearSplineInterpolation(_t, _x);
IInterpolation interpolation = LinearSpline.Interpolate(_t, _x);
Assert.AreEqual(x, interpolation.Interpolate(t), maxAbsoluteError, "Interpolation at {0}", t);
@ -108,23 +107,11 @@ namespace MathNet.Numerics.UnitTests.InterpolationTests
{
double[] x, y, xtest, ytest;
LinearInterpolationCase.Build(out x, out y, out xtest, out ytest, samples);
IInterpolation interpolation = new LinearSplineInterpolation(x, y);
IInterpolation interpolation = LinearSpline.Interpolate(x, y);
for (int i = 0; i < xtest.Length; i++)
{
Assert.AreEqual(ytest[i], interpolation.Interpolate(xtest[i]), 1e-15, "Linear with {0} samples, sample {1}", samples, i);
}
}
/// <summary>
/// Verifies that sample points are required to be sorted in strictly monotonically ascending order.
/// </summary>
[Test]
[ExpectedException(typeof(ArgumentException))]
public void Constructor_SamplePointsNotStrictlyAscending_Throws()
{
var x = new[] { -1.0, 0.0, 1.5, 1.5, 2.5, 4.0 };
var y = new[] { 1.0, 0.3, -0.7, -0.6, -0.1, 0.4 };
var interpolation = new LinearSplineInterpolation(x, y);
}
}
}

Loading…
Cancel
Save