diff --git a/src/Numerics.Tests/FitTests.cs b/src/Numerics.Tests/FitTests.cs index d85ce792c..ce66854a8 100644 --- a/src/Numerics.Tests/FitTests.cs +++ b/src/Numerics.Tests/FitTests.cs @@ -28,6 +28,7 @@ // using System; +using System.Diagnostics; using System.Linq; using MathNet.Numerics.LinearRegression; @@ -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 (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() { diff --git a/src/Numerics/LinearRegression/SimpleRegression.cs b/src/Numerics/LinearRegression/SimpleRegression.cs index 5a3897fe8..5a74e5639 100644 --- a/src/Numerics/LinearRegression/SimpleRegression.cs +++ b/src/Numerics/LinearRegression/SimpleRegression.cs @@ -133,6 +133,29 @@ public static double FitThroughOrigin(double[] x, double[] y) return mxy / mxx; } + public static double FitThroughOrigin(Span x, Span 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; + } + /// /// Least-Squares fitting the points (x,y) to a line y : x -> b*x, diff --git a/src/Numerics/Numerics.csproj b/src/Numerics/Numerics.csproj index 866dda541..3469e5741 100644 --- a/src/Numerics/Numerics.csproj +++ b/src/Numerics/Numerics.csproj @@ -29,5 +29,6 @@ many contributions, proper release notes with attributions will follow. thank yo all runtime; build; native; contentfiles; analyzers; buildtransitive +