Files
QuanTAlib/lib/statistics/covariance/Covariance.Simd.Tests.cs
T
Miha Kralj ac8b2dbb3f feat(tests): enhance tests with GBM for noise generation and improve tolerance for MAMA validation
feat(trends): implement IDisposable in Bessel and Conv classes to manage event subscriptions
fix(trends): add validation for period and parameters in Kama and MGDI calculations
fix(trends): clamp logarithmic calculations in JMA to avoid -Infinity
2025-12-25 20:18:14 -08:00

135 lines
4.6 KiB
C#

using System;
using System.Linq;
using Xunit;
namespace QuanTAlib.Tests;
public class CovarianceSimdTests
{
[Fact]
public void Covariance_Simd_Matches_Scalar_LargeDataset()
{
// Arrange
int count = 1000; // > 256 to trigger SIMD
int period = 20;
var gbmX = new GBM(startPrice: 100, mu: 0.05, sigma: 0.2, seed: 42);
var gbmY = new GBM(startPrice: 100, mu: 0.05, sigma: 0.2, seed: 123);
var dataX = new double[count];
var dataY = new double[count];
for (int i = 0; i < count; i++)
{
dataX[i] = gbmX.Next().Close;
dataY[i] = gbmY.Next().Close;
}
var sourceX = new TSeries();
sourceX.Add(dataX);
var sourceY = new TSeries();
sourceY.Add(dataY);
// Act
// This will use SIMD if available and length >= 256
var simdResult = Covariance.Calculate(sourceX, sourceY, period);
// Calculate expected using scalar loop (simulating by using small chunks or manual calc,
// but easier to just use the streaming update which is scalar)
var scalarCov = new Covariance(period);
var expectedValues = new double[count];
for (int i = 0; i < count; i++)
{
var res = scalarCov.Update(dataX[i], dataY[i]);
expectedValues[i] = res.Value;
}
// Assert
for (int i = 0; i < count; i++)
{
Assert.Equal(expectedValues[i], simdResult.Values[i], precision: 7);
}
}
[Fact]
public void Covariance_Simd_Handles_NaN_Correctly()
{
// Arrange
int count = 500;
int period = 50;
var dataX = Enumerable.Range(0, count).Select(x => (double)x).ToArray();
var dataY = Enumerable.Range(0, count).Select(x => (double)x * 2).ToArray();
// Inject NaN
dataX[300] = double.NaN;
dataY[350] = double.NaN;
var sourceX = new TSeries();
sourceX.Add(dataX);
var sourceY = new TSeries();
sourceY.Add(dataY);
// Act
// The implementation checks for ContainsNonFinite() before using SIMD.
// If NaN is present, it should fall back to Scalar.
// We want to verify that the result is correct regardless of the path taken.
var result = Covariance.Calculate(sourceX, sourceY, period);
// Assert
// Verify around the NaN values
// Index 300 has NaN in X. Covariance should handle it (likely treat as 0 or propagate last valid if logic dictates,
// but current implementation replaces non-finite with 0 in scalar core).
// Let's verify against streaming which we know uses scalar logic
// BUT: Batch implementation replaces NaN with 0, while Streaming propagates NaN.
// To compare, we must feed 0 instead of NaN to streaming.
var scalarCov = new Covariance(period);
for (int i = 0; i < count; i++)
{
double x = dataX[i];
double y = dataY[i];
if (!double.IsFinite(x)) x = 0;
if (!double.IsFinite(y)) y = 0;
var res = scalarCov.Update(x, y);
Assert.Equal(res.Value, result.Values[i], precision: 9);
}
}
[Fact]
public void Covariance_Simd_Resync_Check()
{
// Arrange
// Create a dataset large enough to trigger resync in SIMD loop (ResyncInterval = 1000)
// We need > 1000 elements processed in the SIMD loop.
// The SIMD loop starts at 'period' and goes up to 'simdEnd'.
// So we need length > period + 1000.
int period = 10;
int count = 2000;
// Use simple linear data to make verification easy
// y = 2x
var dataX = Enumerable.Range(0, count).Select(x => (double)x).ToArray();
var dataY = Enumerable.Range(0, count).Select(x => (double)x * 2).ToArray();
var sourceX = new TSeries();
sourceX.Add(dataX);
var sourceY = new TSeries();
sourceY.Add(dataY);
// Act
var result = Covariance.Calculate(sourceX, sourceY, period);
// Assert
// For y=2x, Cov(X,Y) = 2*Var(X)
// Var(X) of sequence 0,1,2... is constant for fixed period?
// For period 10: 0..9. Variance is constant.
// Var(0..9) = 9.16666... (Population) or 10.185... (Sample)?
// Let's just compare with scalar truth.
var scalarCov = new Covariance(period);
for (int i = 0; i < count; i++)
{
var res = scalarCov.Update(dataX[i], dataY[i]);
Assert.Equal(res.Value, result.Values[i], precision: 9);
}
}
}