Skip to content

Commit

Permalink
Added support for Span<double> to SimpleRegression.FitThroughOrigin()…
Browse files Browse the repository at this point in the history
… as POC for Span support suggested in issue mathnet#1083.
  • Loading branch information
strMikhailPotapenko committed Jul 29, 2024
1 parent f196418 commit 3f4dcdd
Show file tree
Hide file tree
Showing 3 changed files with 74 additions and 0 deletions.
50 changes: 50 additions & 0 deletions src/Numerics.Tests/FitTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
// </copyright>

using System;
using System.Diagnostics;
using System.Linq;

using MathNet.Numerics.LinearRegression;
Expand Down Expand Up @@ -98,6 +99,55 @@ public void FitsToBestLineThroughOrigin()
Assert.AreEqual(-0.467791, respSeq, 1e-4);
}

[Test]
public void FitsToBestLineThroughOriginSpanInterface()
{
// Mathematica: Fit[{{1,4.986},{2,2.347},{3,2.061},{4,-2.995},{5,-2.352},{6,-5.782}}, {x}, x]
// -> -0.467791 x

var x = Enumerable.Range(1, 6).Select(Convert.ToDouble).ToArray().AsSpan();
var y = new Span<double> (new[] { 4.986, 2.347, 2.061, -2.995, -2.352, -5.782 });

var resp = SimpleRegression.FitThroughOrigin(x, y);
Assert.AreEqual(-0.467791, resp, 1e-4);
}

[TestCase(10000, 100)]
public void FitsToBestLineThroughOriginBenchmarkSpanVsArray(int dataLength, int windowLength)
{
var xarray = new double[dataLength];
var yarray = new double[dataLength];

var window = windowLength;

var resultArray = new double[dataLength];
var resultSpan = new double[dataLength];

Stopwatch stopwatchArraySlicing = Stopwatch.StartNew();
// Use Array Slicing
for (int i = 0; i < xarray.Length - window; ++i)
{
var xx = xarray.Skip(i).Take(window).ToArray();
var yy = yarray.Skip(i).Take(window).ToArray();
resultArray[i] = SimpleRegression.FitThroughOrigin(xx, yy);
}
stopwatchArraySlicing.Stop();

Stopwatch stopwatchSpanSlicing = Stopwatch.StartNew();
// Use Span Slicing
for (int i = 0; i < xarray.Length - window; ++i)
{
var xx = xarray.AsSpan(i, window);
var yy = yarray.AsSpan(i, window);
resultSpan[i] = SimpleRegression.FitThroughOrigin(xx, yy);
}
stopwatchSpanSlicing.Stop();

Assert.AreEqual(resultArray, resultSpan);

Console.WriteLine($"Array Slicing Timing: {stopwatchArraySlicing.Elapsed.TotalMilliseconds} ms, Span Slicing Timing: {stopwatchSpanSlicing.Elapsed.TotalMilliseconds} ms");
}

[Test]
public void FitsToLineSameAsExcelTrendLine()
{
Expand Down
23 changes: 23 additions & 0 deletions src/Numerics/LinearRegression/SimpleRegression.cs
Original file line number Diff line number Diff line change
Expand Up @@ -133,6 +133,29 @@ public static double FitThroughOrigin(double[] x, double[] y)
return mxy / mxx;
}

public static double FitThroughOrigin(Span<double> x, Span<double> y)
{
if (x.Length != y.Length)
{
throw new ArgumentException($"All sample vectors must have the same length. However, vectors with disagreeing length {x.Length} and {y.Length} have been provided. A sample with index i is given by the value at index i of each provided vector.");
}

if (x.Length <= 1)
{
throw new ArgumentException($"A regression of the requested order requires at least {2} samples. Only {x.Length} samples have been provided.");
}

double mxy = 0.0;
double mxx = 0.0;
for (int i = 0; i < x.Length; i++)
{
mxx += x[i] * x[i];
mxy += x[i] * y[i];
}

return mxy / mxx;
}


/// <summary>
/// Least-Squares fitting the points (x,y) to a line y : x -> b*x,
Expand Down
1 change: 1 addition & 0 deletions src/Numerics/Numerics.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -29,5 +29,6 @@ many contributions, proper release notes with attributions will follow. thank yo
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="System.Memory" Version="4.5.5" />
</ItemGroup>
</Project>

0 comments on commit 3f4dcdd

Please sign in to comment.