mirror of
https://github.com/mihakralj/QuanTAlib.git
synced 2026-08-16 09:38:05 +00:00
style patterns
This commit is contained in:
+281
-27
@@ -39,11 +39,15 @@ public static class ErrorHelpers
|
||||
Span<double> output)
|
||||
{
|
||||
if (actual.Length != predicted.Length || actual.Length != output.Length)
|
||||
{
|
||||
throw new ArgumentException(SpanLengthMismatchMessage, nameof(output));
|
||||
}
|
||||
|
||||
int len = actual.Length;
|
||||
if (len == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
double lastValidActual = FindFirstValidValue(actual);
|
||||
double lastValidPredicted = FindFirstValidValue(predicted);
|
||||
@@ -53,7 +57,9 @@ public static class ErrorHelpers
|
||||
{
|
||||
int processedCount = ComputeSignedErrorsSimdWithNaNDetection(actual, predicted, output, ref lastValidActual, ref lastValidPredicted);
|
||||
if (processedCount == len)
|
||||
{
|
||||
return; // All processed via SIMD
|
||||
}
|
||||
// Continue with scalar for remaining elements (NaN was detected)
|
||||
ComputeSignedErrorsScalar(actual.Slice(processedCount), predicted.Slice(processedCount), output.Slice(processedCount), lastValidActual, lastValidPredicted);
|
||||
return;
|
||||
@@ -74,11 +80,15 @@ public static class ErrorHelpers
|
||||
Span<double> output)
|
||||
{
|
||||
if (actual.Length != predicted.Length || actual.Length != output.Length)
|
||||
{
|
||||
throw new ArgumentException(SpanLengthMismatchMessage, nameof(output));
|
||||
}
|
||||
|
||||
int len = actual.Length;
|
||||
if (len == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
double lastValidActual = FindFirstValidValue(actual);
|
||||
double lastValidPredicted = FindFirstValidValue(predicted);
|
||||
@@ -88,7 +98,9 @@ public static class ErrorHelpers
|
||||
{
|
||||
int processedCount = ComputeAbsoluteErrorsSimdWithNaNDetection(actual, predicted, output, ref lastValidActual, ref lastValidPredicted);
|
||||
if (processedCount == len)
|
||||
{
|
||||
return; // All processed via SIMD
|
||||
}
|
||||
// Continue with scalar for remaining elements (NaN was detected)
|
||||
ComputeAbsoluteErrorsScalar(actual.Slice(processedCount), predicted.Slice(processedCount), output.Slice(processedCount), lastValidActual, lastValidPredicted);
|
||||
return;
|
||||
@@ -109,11 +121,15 @@ public static class ErrorHelpers
|
||||
Span<double> output)
|
||||
{
|
||||
if (actual.Length != predicted.Length || actual.Length != output.Length)
|
||||
{
|
||||
throw new ArgumentException(SpanLengthMismatchMessage, nameof(output));
|
||||
}
|
||||
|
||||
int len = actual.Length;
|
||||
if (len == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
double lastValidActual = FindFirstValidValue(actual);
|
||||
double lastValidPredicted = FindFirstValidValue(predicted);
|
||||
@@ -123,7 +139,9 @@ public static class ErrorHelpers
|
||||
{
|
||||
int processedCount = ComputeSquaredErrorsSimdWithNaNDetection(actual, predicted, output, ref lastValidActual, ref lastValidPredicted);
|
||||
if (processedCount == len)
|
||||
{
|
||||
return; // All processed via SIMD
|
||||
}
|
||||
// Continue with scalar for remaining elements (NaN was detected)
|
||||
ComputeSquaredErrorsScalar(actual.Slice(processedCount), predicted.Slice(processedCount), output.Slice(processedCount), lastValidActual, lastValidPredicted);
|
||||
return;
|
||||
@@ -145,11 +163,15 @@ public static class ErrorHelpers
|
||||
Span<double> output)
|
||||
{
|
||||
if (actual.Length != predicted.Length || actual.Length != weights.Length || actual.Length != output.Length)
|
||||
{
|
||||
throw new ArgumentException("All spans must have the same length", nameof(output));
|
||||
}
|
||||
|
||||
int len = actual.Length;
|
||||
if (len == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
double lastValidActual = FindFirstValidValue(actual);
|
||||
double lastValidPredicted = FindFirstValidValue(predicted);
|
||||
@@ -165,9 +187,32 @@ public static class ErrorHelpers
|
||||
double pred = predicted[i];
|
||||
double wgt = weights[i];
|
||||
|
||||
if (double.IsFinite(act)) currentValidActual = act; else act = currentValidActual;
|
||||
if (double.IsFinite(pred)) currentValidPredicted = pred; else pred = currentValidPredicted;
|
||||
if (double.IsFinite(wgt)) currentValidWeight = wgt; else wgt = currentValidWeight;
|
||||
if (double.IsFinite(act))
|
||||
{
|
||||
currentValidActual = act;
|
||||
}
|
||||
else
|
||||
{
|
||||
act = currentValidActual;
|
||||
}
|
||||
|
||||
if (double.IsFinite(pred))
|
||||
{
|
||||
currentValidPredicted = pred;
|
||||
}
|
||||
else
|
||||
{
|
||||
pred = currentValidPredicted;
|
||||
}
|
||||
|
||||
if (double.IsFinite(wgt))
|
||||
{
|
||||
currentValidWeight = wgt;
|
||||
}
|
||||
else
|
||||
{
|
||||
wgt = currentValidWeight;
|
||||
}
|
||||
|
||||
double diff = act - pred;
|
||||
output[i] = wgt * diff * diff;
|
||||
@@ -186,11 +231,15 @@ public static class ErrorHelpers
|
||||
double epsilon = 1e-10)
|
||||
{
|
||||
if (actual.Length != predicted.Length || actual.Length != output.Length)
|
||||
{
|
||||
throw new ArgumentException(SpanLengthMismatchMessage, nameof(output));
|
||||
}
|
||||
|
||||
int len = actual.Length;
|
||||
if (len == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
double lastValidActual = FindFirstValidValue(actual);
|
||||
double lastValidPredicted = FindFirstValidValue(predicted);
|
||||
@@ -227,11 +276,15 @@ public static class ErrorHelpers
|
||||
double epsilon = 1e-10)
|
||||
{
|
||||
if (actual.Length != predicted.Length || actual.Length != output.Length)
|
||||
{
|
||||
throw new ArgumentException(SpanLengthMismatchMessage, nameof(output));
|
||||
}
|
||||
|
||||
int len = actual.Length;
|
||||
if (len == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
double lastValidActual = FindFirstValidValue(actual);
|
||||
double lastValidPredicted = FindFirstValidValue(predicted);
|
||||
@@ -244,8 +297,23 @@ public static class ErrorHelpers
|
||||
double act = actual[i];
|
||||
double pred = predicted[i];
|
||||
|
||||
if (double.IsFinite(act)) currentValidActual = act; else act = currentValidActual;
|
||||
if (double.IsFinite(pred)) currentValidPredicted = pred; else pred = currentValidPredicted;
|
||||
if (double.IsFinite(act))
|
||||
{
|
||||
currentValidActual = act;
|
||||
}
|
||||
else
|
||||
{
|
||||
act = currentValidActual;
|
||||
}
|
||||
|
||||
if (double.IsFinite(pred))
|
||||
{
|
||||
currentValidPredicted = pred;
|
||||
}
|
||||
else
|
||||
{
|
||||
pred = currentValidPredicted;
|
||||
}
|
||||
|
||||
double denominator = (Math.Abs(act) + Math.Abs(pred)) / 2.0;
|
||||
output[i] = denominator < epsilon
|
||||
@@ -265,11 +333,15 @@ public static class ErrorHelpers
|
||||
Span<double> output)
|
||||
{
|
||||
if (actual.Length != predicted.Length || actual.Length != output.Length)
|
||||
{
|
||||
throw new ArgumentException(SpanLengthMismatchMessage, nameof(output));
|
||||
}
|
||||
|
||||
int len = actual.Length;
|
||||
if (len == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
double lastValidActual = FindFirstValidValue(actual);
|
||||
double lastValidPredicted = FindFirstValidValue(predicted);
|
||||
@@ -282,8 +354,23 @@ public static class ErrorHelpers
|
||||
double act = actual[i];
|
||||
double pred = predicted[i];
|
||||
|
||||
if (double.IsFinite(act)) currentValidActual = act; else act = currentValidActual;
|
||||
if (double.IsFinite(pred)) currentValidPredicted = pred; else pred = currentValidPredicted;
|
||||
if (double.IsFinite(act))
|
||||
{
|
||||
currentValidActual = act;
|
||||
}
|
||||
else
|
||||
{
|
||||
act = currentValidActual;
|
||||
}
|
||||
|
||||
if (double.IsFinite(pred))
|
||||
{
|
||||
currentValidPredicted = pred;
|
||||
}
|
||||
else
|
||||
{
|
||||
pred = currentValidPredicted;
|
||||
}
|
||||
|
||||
double diff = act - pred;
|
||||
// log(cosh(x)) ≈ |x| - log(2) for large |x|, numerically stable
|
||||
@@ -303,11 +390,15 @@ public static class ErrorHelpers
|
||||
double delta = 1.0)
|
||||
{
|
||||
if (actual.Length != predicted.Length || actual.Length != output.Length)
|
||||
{
|
||||
throw new ArgumentException(SpanLengthMismatchMessage, nameof(output));
|
||||
}
|
||||
|
||||
int len = actual.Length;
|
||||
if (len == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
double lastValidActual = FindFirstValidValue(actual);
|
||||
double lastValidPredicted = FindFirstValidValue(predicted);
|
||||
@@ -321,8 +412,23 @@ public static class ErrorHelpers
|
||||
double act = actual[i];
|
||||
double pred = predicted[i];
|
||||
|
||||
if (double.IsFinite(act)) currentValidActual = act; else act = currentValidActual;
|
||||
if (double.IsFinite(pred)) currentValidPredicted = pred; else pred = currentValidPredicted;
|
||||
if (double.IsFinite(act))
|
||||
{
|
||||
currentValidActual = act;
|
||||
}
|
||||
else
|
||||
{
|
||||
act = currentValidActual;
|
||||
}
|
||||
|
||||
if (double.IsFinite(pred))
|
||||
{
|
||||
currentValidPredicted = pred;
|
||||
}
|
||||
else
|
||||
{
|
||||
pred = currentValidPredicted;
|
||||
}
|
||||
|
||||
double diff = act - pred;
|
||||
double ratio = diff / delta;
|
||||
@@ -345,11 +451,15 @@ public static class ErrorHelpers
|
||||
double c = 4.685)
|
||||
{
|
||||
if (actual.Length != predicted.Length || actual.Length != output.Length)
|
||||
{
|
||||
throw new ArgumentException(SpanLengthMismatchMessage, nameof(output));
|
||||
}
|
||||
|
||||
int len = actual.Length;
|
||||
if (len == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
double lastValidActual = FindFirstValidValue(actual);
|
||||
double lastValidPredicted = FindFirstValidValue(predicted);
|
||||
@@ -363,8 +473,23 @@ public static class ErrorHelpers
|
||||
double act = actual[i];
|
||||
double pred = predicted[i];
|
||||
|
||||
if (double.IsFinite(act)) currentValidActual = act; else act = currentValidActual;
|
||||
if (double.IsFinite(pred)) currentValidPredicted = pred; else pred = currentValidPredicted;
|
||||
if (double.IsFinite(act))
|
||||
{
|
||||
currentValidActual = act;
|
||||
}
|
||||
else
|
||||
{
|
||||
act = currentValidActual;
|
||||
}
|
||||
|
||||
if (double.IsFinite(pred))
|
||||
{
|
||||
currentValidPredicted = pred;
|
||||
}
|
||||
else
|
||||
{
|
||||
pred = currentValidPredicted;
|
||||
}
|
||||
|
||||
double diff = act - pred;
|
||||
double absDiff = Math.Abs(diff);
|
||||
@@ -396,11 +521,15 @@ public static class ErrorHelpers
|
||||
double delta = 1.0)
|
||||
{
|
||||
if (actual.Length != predicted.Length || actual.Length != output.Length)
|
||||
{
|
||||
throw new ArgumentException(SpanLengthMismatchMessage, nameof(output));
|
||||
}
|
||||
|
||||
int len = actual.Length;
|
||||
if (len == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
double lastValidActual = FindFirstValidValue(actual);
|
||||
double lastValidPredicted = FindFirstValidValue(predicted);
|
||||
@@ -414,8 +543,23 @@ public static class ErrorHelpers
|
||||
double act = actual[i];
|
||||
double pred = predicted[i];
|
||||
|
||||
if (double.IsFinite(act)) currentValidActual = act; else act = currentValidActual;
|
||||
if (double.IsFinite(pred)) currentValidPredicted = pred; else pred = currentValidPredicted;
|
||||
if (double.IsFinite(act))
|
||||
{
|
||||
currentValidActual = act;
|
||||
}
|
||||
else
|
||||
{
|
||||
act = currentValidActual;
|
||||
}
|
||||
|
||||
if (double.IsFinite(pred))
|
||||
{
|
||||
currentValidPredicted = pred;
|
||||
}
|
||||
else
|
||||
{
|
||||
pred = currentValidPredicted;
|
||||
}
|
||||
|
||||
double diff = act - pred;
|
||||
double absDiff = Math.Abs(diff);
|
||||
@@ -438,13 +582,20 @@ public static class ErrorHelpers
|
||||
int resyncInterval = 1000)
|
||||
{
|
||||
if (errors.Length != output.Length)
|
||||
{
|
||||
throw new ArgumentException("Spans must have the same length", nameof(output));
|
||||
}
|
||||
|
||||
if (period <= 0)
|
||||
{
|
||||
throw new ArgumentException("Period must be greater than 0", nameof(period));
|
||||
}
|
||||
|
||||
int len = errors.Length;
|
||||
if (len == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
double[]? rented = null;
|
||||
|
||||
@@ -477,7 +628,10 @@ public static class ErrorHelpers
|
||||
buffer[bufferIndex] = error;
|
||||
|
||||
bufferIndex++;
|
||||
if (bufferIndex >= period) bufferIndex = 0;
|
||||
if (bufferIndex >= period)
|
||||
{
|
||||
bufferIndex = 0;
|
||||
}
|
||||
|
||||
output[i] = sum / period;
|
||||
|
||||
@@ -486,7 +640,11 @@ public static class ErrorHelpers
|
||||
{
|
||||
tickCount = 0;
|
||||
double recalcSum = 0;
|
||||
for (int k = 0; k < period; k++) recalcSum += buffer[k];
|
||||
for (int k = 0; k < period; k++)
|
||||
{
|
||||
recalcSum += buffer[k];
|
||||
}
|
||||
|
||||
sum = recalcSum;
|
||||
}
|
||||
}
|
||||
@@ -511,13 +669,20 @@ public static class ErrorHelpers
|
||||
int resyncInterval = 1000)
|
||||
{
|
||||
if (squaredErrors.Length != output.Length)
|
||||
{
|
||||
throw new ArgumentException("Spans must have the same length", nameof(output));
|
||||
}
|
||||
|
||||
if (period <= 0)
|
||||
{
|
||||
throw new ArgumentException("Period must be greater than 0", nameof(period));
|
||||
}
|
||||
|
||||
int len = squaredErrors.Length;
|
||||
if (len == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
double[]? rented = null;
|
||||
|
||||
@@ -550,7 +715,10 @@ public static class ErrorHelpers
|
||||
buffer[bufferIndex] = sqError;
|
||||
|
||||
bufferIndex++;
|
||||
if (bufferIndex >= period) bufferIndex = 0;
|
||||
if (bufferIndex >= period)
|
||||
{
|
||||
bufferIndex = 0;
|
||||
}
|
||||
|
||||
output[i] = Math.Sqrt(sum / period);
|
||||
|
||||
@@ -559,7 +727,11 @@ public static class ErrorHelpers
|
||||
{
|
||||
tickCount = 0;
|
||||
double recalcSum = 0;
|
||||
for (int k = 0; k < period; k++) recalcSum += buffer[k];
|
||||
for (int k = 0; k < period; k++)
|
||||
{
|
||||
recalcSum += buffer[k];
|
||||
}
|
||||
|
||||
sum = recalcSum;
|
||||
}
|
||||
}
|
||||
@@ -586,13 +758,20 @@ public static class ErrorHelpers
|
||||
int resyncInterval = 1000)
|
||||
{
|
||||
if (weightedSquaredErrors.Length != output.Length || weightedSquaredErrors.Length != weights.Length)
|
||||
{
|
||||
throw new ArgumentException("Spans must have the same length", nameof(output));
|
||||
}
|
||||
|
||||
if (period <= 0)
|
||||
{
|
||||
throw new ArgumentException("Period must be greater than 0", nameof(period));
|
||||
}
|
||||
|
||||
int len = weightedSquaredErrors.Length;
|
||||
if (len == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
double[]? rentedErrors = null;
|
||||
double[]? rentedWeights = null;
|
||||
@@ -637,7 +816,10 @@ public static class ErrorHelpers
|
||||
weightBuffer[bufferIndex] = wgt;
|
||||
|
||||
bufferIndex++;
|
||||
if (bufferIndex >= period) bufferIndex = 0;
|
||||
if (bufferIndex >= period)
|
||||
{
|
||||
bufferIndex = 0;
|
||||
}
|
||||
|
||||
output[i] = sumWeights > 1e-10 ? Math.Sqrt(sumErrors / sumWeights) : 0.0;
|
||||
|
||||
@@ -686,11 +868,15 @@ public static class ErrorHelpers
|
||||
Span<double> predictedOut)
|
||||
{
|
||||
if (actual.Length != predicted.Length || actual.Length != actualOut.Length || actual.Length != predictedOut.Length)
|
||||
{
|
||||
throw new ArgumentException(SpanLengthMismatchMessage, nameof(predictedOut));
|
||||
}
|
||||
|
||||
int len = actual.Length;
|
||||
if (len == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
double lastValidActual = FindFirstValidValue(actual);
|
||||
double lastValidPredicted = FindFirstValidValue(predicted);
|
||||
@@ -700,8 +886,23 @@ public static class ErrorHelpers
|
||||
double act = actual[i];
|
||||
double pred = predicted[i];
|
||||
|
||||
if (double.IsFinite(act)) lastValidActual = act; else act = lastValidActual;
|
||||
if (double.IsFinite(pred)) lastValidPredicted = pred; else pred = lastValidPredicted;
|
||||
if (double.IsFinite(act))
|
||||
{
|
||||
lastValidActual = act;
|
||||
}
|
||||
else
|
||||
{
|
||||
act = lastValidActual;
|
||||
}
|
||||
|
||||
if (double.IsFinite(pred))
|
||||
{
|
||||
lastValidPredicted = pred;
|
||||
}
|
||||
else
|
||||
{
|
||||
pred = lastValidPredicted;
|
||||
}
|
||||
|
||||
actualOut[i] = act;
|
||||
predictedOut[i] = pred;
|
||||
@@ -717,7 +918,9 @@ public static class ErrorHelpers
|
||||
for (int i = 0; i < span.Length; i++)
|
||||
{
|
||||
if (double.IsFinite(span[i]))
|
||||
{
|
||||
return span[i];
|
||||
}
|
||||
}
|
||||
return 0.0;
|
||||
}
|
||||
@@ -751,14 +954,18 @@ public static class ErrorHelpers
|
||||
// MoveMask returns a bitmask; all-ones means all finite (mask == 0b1111 for 4 doubles)
|
||||
int mask = Avx.MoveMask(combined);
|
||||
if (mask != 0b1111)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// Scalar tail
|
||||
for (int i = vectorEnd; i < len; i++)
|
||||
{
|
||||
if (!double.IsFinite(actual[i]) || !double.IsFinite(predicted[i]))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
@@ -767,7 +974,9 @@ public static class ErrorHelpers
|
||||
for (int i = 0; i < len; i++)
|
||||
{
|
||||
if (!double.IsFinite(actual[i]) || !double.IsFinite(predicted[i]))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
@@ -890,8 +1099,23 @@ public static class ErrorHelpers
|
||||
double act = actual[i];
|
||||
double pred = predicted[i];
|
||||
|
||||
if (double.IsFinite(act)) currentValidActual = act; else act = currentValidActual;
|
||||
if (double.IsFinite(pred)) currentValidPredicted = pred; else pred = currentValidPredicted;
|
||||
if (double.IsFinite(act))
|
||||
{
|
||||
currentValidActual = act;
|
||||
}
|
||||
else
|
||||
{
|
||||
act = currentValidActual;
|
||||
}
|
||||
|
||||
if (double.IsFinite(pred))
|
||||
{
|
||||
currentValidPredicted = pred;
|
||||
}
|
||||
else
|
||||
{
|
||||
pred = currentValidPredicted;
|
||||
}
|
||||
|
||||
output[i] = act - pred;
|
||||
}
|
||||
@@ -1099,8 +1323,23 @@ public static class ErrorHelpers
|
||||
double act = actual[i];
|
||||
double pred = predicted[i];
|
||||
|
||||
if (double.IsFinite(act)) currentValidActual = act; else act = currentValidActual;
|
||||
if (double.IsFinite(pred)) currentValidPredicted = pred; else pred = currentValidPredicted;
|
||||
if (double.IsFinite(act))
|
||||
{
|
||||
currentValidActual = act;
|
||||
}
|
||||
else
|
||||
{
|
||||
act = currentValidActual;
|
||||
}
|
||||
|
||||
if (double.IsFinite(pred))
|
||||
{
|
||||
currentValidPredicted = pred;
|
||||
}
|
||||
else
|
||||
{
|
||||
pred = currentValidPredicted;
|
||||
}
|
||||
|
||||
output[i] = Math.Abs(act - pred);
|
||||
}
|
||||
@@ -1156,8 +1395,23 @@ public static class ErrorHelpers
|
||||
double act = actual[i];
|
||||
double pred = predicted[i];
|
||||
|
||||
if (double.IsFinite(act)) currentValidActual = act; else act = currentValidActual;
|
||||
if (double.IsFinite(pred)) currentValidPredicted = pred; else pred = currentValidPredicted;
|
||||
if (double.IsFinite(act))
|
||||
{
|
||||
currentValidActual = act;
|
||||
}
|
||||
else
|
||||
{
|
||||
act = currentValidActual;
|
||||
}
|
||||
|
||||
if (double.IsFinite(pred))
|
||||
{
|
||||
currentValidPredicted = pred;
|
||||
}
|
||||
else
|
||||
{
|
||||
pred = currentValidPredicted;
|
||||
}
|
||||
|
||||
double diff = act - pred;
|
||||
output[i] = diff * diff;
|
||||
@@ -1181,4 +1435,4 @@ public static class ErrorHelpers
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
|
||||
@@ -112,7 +112,9 @@ public class SimdExtensionsTests
|
||||
{
|
||||
double[] data = new double[1000];
|
||||
for (int i = 0; i < data.Length; i++)
|
||||
{
|
||||
data[i] = i + 1.0;
|
||||
}
|
||||
|
||||
var span = new ReadOnlySpan<double>(data);
|
||||
const double expected = 1000.0 * 1001.0 / 2.0;
|
||||
@@ -992,4 +994,4 @@ public class SimdScalarFallbackTests
|
||||
|
||||
Assert.Throws<ArgumentException>(() => SimdExtensions.Subtract(left, right, result));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+159
-25
@@ -20,7 +20,9 @@ public static class SimdExtensions
|
||||
for (int i = 0; i < span.Length; i++)
|
||||
{
|
||||
if (!double.IsFinite(span[i]))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
@@ -30,7 +32,10 @@ public static class SimdExtensions
|
||||
{
|
||||
double scalar = 0.0;
|
||||
for (int i = 0; i < span.Length; i++)
|
||||
{
|
||||
scalar += span[i];
|
||||
}
|
||||
|
||||
return scalar;
|
||||
}
|
||||
|
||||
@@ -38,13 +43,17 @@ public static class SimdExtensions
|
||||
internal static double MinScalar(ReadOnlySpan<double> span)
|
||||
{
|
||||
if (span.Length == 0)
|
||||
{
|
||||
throw new ArgumentException("Span must not be empty", nameof(span));
|
||||
}
|
||||
|
||||
double min = span[0];
|
||||
for (int i = 1; i < span.Length; i++)
|
||||
{
|
||||
if (span[i] < min)
|
||||
{
|
||||
min = span[i];
|
||||
}
|
||||
}
|
||||
return min;
|
||||
}
|
||||
@@ -53,13 +62,17 @@ public static class SimdExtensions
|
||||
internal static double MaxScalar(ReadOnlySpan<double> span)
|
||||
{
|
||||
if (span.Length == 0)
|
||||
{
|
||||
throw new ArgumentException("Span must not be empty", nameof(span));
|
||||
}
|
||||
|
||||
double max = span[0];
|
||||
for (int i = 1; i < span.Length; i++)
|
||||
{
|
||||
if (span[i] > max)
|
||||
{
|
||||
max = span[i];
|
||||
}
|
||||
}
|
||||
return max;
|
||||
}
|
||||
@@ -69,7 +82,9 @@ public static class SimdExtensions
|
||||
{
|
||||
// Match VarianceSIMD behavior: return 0.0 for length <= 1 to avoid divide-by-zero
|
||||
if (span.Length <= 1)
|
||||
{
|
||||
return 0.0;
|
||||
}
|
||||
|
||||
double sumSquares = 0.0;
|
||||
for (int i = 0; i < span.Length; i++)
|
||||
@@ -84,14 +99,23 @@ public static class SimdExtensions
|
||||
internal static (double Min, double Max) MinMaxScalar(ReadOnlySpan<double> span)
|
||||
{
|
||||
if (span.Length == 0)
|
||||
{
|
||||
throw new ArgumentException("Span must not be empty", nameof(span));
|
||||
}
|
||||
|
||||
double scalarMin = span[0];
|
||||
double scalarMax = span[0];
|
||||
for (int i = 1; i < span.Length; i++)
|
||||
{
|
||||
if (span[i] < scalarMin) scalarMin = span[i];
|
||||
if (span[i] > scalarMax) scalarMax = span[i];
|
||||
if (span[i] < scalarMin)
|
||||
{
|
||||
scalarMin = span[i];
|
||||
}
|
||||
|
||||
if (span[i] > scalarMax)
|
||||
{
|
||||
scalarMax = span[i];
|
||||
}
|
||||
}
|
||||
return (scalarMin, scalarMax);
|
||||
}
|
||||
@@ -105,7 +129,10 @@ public static class SimdExtensions
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static bool ContainsNonFinite(this ReadOnlySpan<double> span)
|
||||
{
|
||||
if (span.IsEmpty) return false;
|
||||
if (span.IsEmpty)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (Vector.IsHardwareAccelerated && span.Length >= Vector<double>.Count)
|
||||
{
|
||||
@@ -120,19 +147,25 @@ public static class SimdExtensions
|
||||
// NaN check: NaN != NaN, so Vector.Equals(v, v) will be false for NaN lanes
|
||||
var nanCheck = Vector.Equals(vector, vector);
|
||||
if (!nanCheck.Equals(Vector<long>.AllBitsSet))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
// Infinity check: |v| > MaxValue (Infinity has magnitude > MaxValue)
|
||||
var absVec = Vector.Abs(vector);
|
||||
var infCheck = Vector.GreaterThan(absVec, maxValue);
|
||||
if (!infCheck.Equals(Vector<long>.Zero))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
for (; i < span.Length; i++)
|
||||
{
|
||||
if (!double.IsFinite(span[i]))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
@@ -151,7 +184,10 @@ public static class SimdExtensions
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static double SumSIMD(this ReadOnlySpan<double> span)
|
||||
{
|
||||
if (span.IsEmpty) return 0.0;
|
||||
if (span.IsEmpty)
|
||||
{
|
||||
return 0.0;
|
||||
}
|
||||
|
||||
if (Vector.IsHardwareAccelerated && span.Length >= Vector<double>.Count)
|
||||
{
|
||||
@@ -167,16 +203,22 @@ public static class SimdExtensions
|
||||
|
||||
double result = 0.0;
|
||||
for (int j = 0; j < vectorSize; j++)
|
||||
{
|
||||
result += sum[j];
|
||||
}
|
||||
|
||||
for (; i < span.Length; i++)
|
||||
{
|
||||
result += span[i];
|
||||
}
|
||||
|
||||
// Lazy check: if result is non-finite AND input contained non-finite values, return NaN
|
||||
// NaN + anything = NaN, Inf + anything finite = Inf
|
||||
// If result is infinite from overflow (no input NaN/Inf), return as-is
|
||||
if (!double.IsFinite(result) && span.ContainsNonFinite())
|
||||
{
|
||||
return double.NaN;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
@@ -194,11 +236,21 @@ public static class SimdExtensions
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static double MinSIMD(this ReadOnlySpan<double> span)
|
||||
{
|
||||
if (span.IsEmpty) return double.NaN;
|
||||
if (span.Length == 1) return span[0];
|
||||
if (span.IsEmpty)
|
||||
{
|
||||
return double.NaN;
|
||||
}
|
||||
|
||||
if (span.Length == 1)
|
||||
{
|
||||
return span[0];
|
||||
}
|
||||
|
||||
// Guard against non-finite inputs
|
||||
if (span.ContainsNonFinite()) return double.NaN;
|
||||
if (span.ContainsNonFinite())
|
||||
{
|
||||
return double.NaN;
|
||||
}
|
||||
|
||||
if (Vector.IsHardwareAccelerated && span.Length >= Vector<double>.Count)
|
||||
{
|
||||
@@ -216,13 +268,17 @@ public static class SimdExtensions
|
||||
for (int j = 1; j < vectorSize; j++)
|
||||
{
|
||||
if (minVec[j] < result)
|
||||
{
|
||||
result = minVec[j];
|
||||
}
|
||||
}
|
||||
|
||||
for (; i < span.Length; i++)
|
||||
{
|
||||
if (span[i] < result)
|
||||
{
|
||||
result = span[i];
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
@@ -239,11 +295,21 @@ public static class SimdExtensions
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static double MaxSIMD(this ReadOnlySpan<double> span)
|
||||
{
|
||||
if (span.IsEmpty) return double.NaN;
|
||||
if (span.Length == 1) return span[0];
|
||||
if (span.IsEmpty)
|
||||
{
|
||||
return double.NaN;
|
||||
}
|
||||
|
||||
if (span.Length == 1)
|
||||
{
|
||||
return span[0];
|
||||
}
|
||||
|
||||
// Guard against non-finite inputs
|
||||
if (span.ContainsNonFinite()) return double.NaN;
|
||||
if (span.ContainsNonFinite())
|
||||
{
|
||||
return double.NaN;
|
||||
}
|
||||
|
||||
if (Vector.IsHardwareAccelerated && span.Length >= Vector<double>.Count)
|
||||
{
|
||||
@@ -261,13 +327,17 @@ public static class SimdExtensions
|
||||
for (int j = 1; j < vectorSize; j++)
|
||||
{
|
||||
if (maxVec[j] > result)
|
||||
{
|
||||
result = maxVec[j];
|
||||
}
|
||||
}
|
||||
|
||||
for (; i < span.Length; i++)
|
||||
{
|
||||
if (span[i] > result)
|
||||
{
|
||||
result = span[i];
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
@@ -284,7 +354,10 @@ public static class SimdExtensions
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static double AverageSIMD(this ReadOnlySpan<double> span)
|
||||
{
|
||||
if (span.IsEmpty) return double.NaN;
|
||||
if (span.IsEmpty)
|
||||
{
|
||||
return double.NaN;
|
||||
}
|
||||
// SumSIMD already guards against non-finite, which will propagate NaN
|
||||
return span.SumSIMD() / span.Length;
|
||||
}
|
||||
@@ -300,13 +373,20 @@ public static class SimdExtensions
|
||||
public static double VarianceSIMD(this ReadOnlySpan<double> span, double? mean = null)
|
||||
{
|
||||
// Match VarianceScalar behavior: return 0.0 for length <= 1 to avoid inconsistency
|
||||
if (span.Length <= 1) return 0.0;
|
||||
if (span.Length <= 1)
|
||||
{
|
||||
return 0.0;
|
||||
}
|
||||
|
||||
double m;
|
||||
if (mean.HasValue)
|
||||
{
|
||||
// Mean provided externally - need explicit non-finite check
|
||||
if (span.ContainsNonFinite()) return double.NaN;
|
||||
if (span.ContainsNonFinite())
|
||||
{
|
||||
return double.NaN;
|
||||
}
|
||||
|
||||
m = mean.Value;
|
||||
}
|
||||
else
|
||||
@@ -317,7 +397,10 @@ public static class SimdExtensions
|
||||
}
|
||||
|
||||
// If mean is NaN (from input NaN or explicit NaN mean), return NaN
|
||||
if (!double.IsFinite(m)) return double.NaN;
|
||||
if (!double.IsFinite(m))
|
||||
{
|
||||
return double.NaN;
|
||||
}
|
||||
|
||||
if (Vector.IsHardwareAccelerated && span.Length >= Vector<double>.Count)
|
||||
{
|
||||
@@ -335,7 +418,9 @@ public static class SimdExtensions
|
||||
|
||||
double result = 0.0;
|
||||
for (int j = 0; j < vectorSize; j++)
|
||||
{
|
||||
result += sumSq[j];
|
||||
}
|
||||
|
||||
for (; i < span.Length; i++)
|
||||
{
|
||||
@@ -368,11 +453,21 @@ public static class SimdExtensions
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static (double Min, double Max) MinMaxSIMD(this ReadOnlySpan<double> span)
|
||||
{
|
||||
if (span.IsEmpty) return (double.NaN, double.NaN);
|
||||
if (span.Length == 1) return (span[0], span[0]);
|
||||
if (span.IsEmpty)
|
||||
{
|
||||
return (double.NaN, double.NaN);
|
||||
}
|
||||
|
||||
if (span.Length == 1)
|
||||
{
|
||||
return (span[0], span[0]);
|
||||
}
|
||||
|
||||
// Guard against non-finite inputs
|
||||
if (span.ContainsNonFinite()) return (double.NaN, double.NaN);
|
||||
if (span.ContainsNonFinite())
|
||||
{
|
||||
return (double.NaN, double.NaN);
|
||||
}
|
||||
|
||||
if (Vector.IsHardwareAccelerated && span.Length >= Vector<double>.Count)
|
||||
{
|
||||
@@ -392,14 +487,28 @@ public static class SimdExtensions
|
||||
double max = maxVec[0];
|
||||
for (int j = 1; j < vectorSize; j++)
|
||||
{
|
||||
if (minVec[j] < min) min = minVec[j];
|
||||
if (maxVec[j] > max) max = maxVec[j];
|
||||
if (minVec[j] < min)
|
||||
{
|
||||
min = minVec[j];
|
||||
}
|
||||
|
||||
if (maxVec[j] > max)
|
||||
{
|
||||
max = maxVec[j];
|
||||
}
|
||||
}
|
||||
|
||||
for (; i < span.Length; i++)
|
||||
{
|
||||
if (span[i] < min) min = span[i];
|
||||
if (span[i] > max) max = span[i];
|
||||
if (span[i] < min)
|
||||
{
|
||||
min = span[i];
|
||||
}
|
||||
|
||||
if (span[i] > max)
|
||||
{
|
||||
max = span[i];
|
||||
}
|
||||
}
|
||||
|
||||
return (min, max);
|
||||
@@ -416,7 +525,9 @@ public static class SimdExtensions
|
||||
public static void Add(ReadOnlySpan<double> left, ReadOnlySpan<double> right, Span<double> result)
|
||||
{
|
||||
if (left.Length != right.Length || left.Length != result.Length)
|
||||
{
|
||||
throw new ArgumentException("All spans must have the same length", nameof(result));
|
||||
}
|
||||
|
||||
int i = 0;
|
||||
if (Vector.IsHardwareAccelerated && left.Length >= Vector<double>.Count)
|
||||
@@ -444,7 +555,9 @@ public static class SimdExtensions
|
||||
public static void Scale(ReadOnlySpan<double> source, double scalar, Span<double> result)
|
||||
{
|
||||
if (source.Length != result.Length)
|
||||
{
|
||||
throw new ArgumentException("Source and result spans must have the same length", nameof(result));
|
||||
}
|
||||
|
||||
int i = 0;
|
||||
if (Vector.IsHardwareAccelerated && source.Length >= Vector<double>.Count)
|
||||
@@ -472,7 +585,9 @@ public static class SimdExtensions
|
||||
public static void Subtract(ReadOnlySpan<double> left, ReadOnlySpan<double> right, Span<double> result)
|
||||
{
|
||||
if (left.Length != right.Length || left.Length != result.Length)
|
||||
{
|
||||
throw new ArgumentException("All spans must have the same length", nameof(result));
|
||||
}
|
||||
|
||||
int i = 0;
|
||||
if (Vector.IsHardwareAccelerated && left.Length >= Vector<double>.Count)
|
||||
@@ -500,9 +615,14 @@ public static class SimdExtensions
|
||||
public static double DotProduct(this ReadOnlySpan<double> a, ReadOnlySpan<double> b)
|
||||
{
|
||||
if (a.Length != b.Length)
|
||||
{
|
||||
throw new ArgumentException("Spans must have equal length", nameof(b));
|
||||
}
|
||||
|
||||
if (a.IsEmpty) return 0.0;
|
||||
if (a.IsEmpty)
|
||||
{
|
||||
return 0.0;
|
||||
}
|
||||
|
||||
int len = a.Length;
|
||||
|
||||
@@ -513,19 +633,33 @@ public static class SimdExtensions
|
||||
ref double bRef = ref MemoryMarshal.GetReference(b);
|
||||
|
||||
double sum = aRef * bRef;
|
||||
if (len > 1) sum += Unsafe.Add(ref aRef, 1) * Unsafe.Add(ref bRef, 1);
|
||||
if (len > 2) sum += Unsafe.Add(ref aRef, 2) * Unsafe.Add(ref bRef, 2);
|
||||
if (len > 1)
|
||||
{
|
||||
sum += Unsafe.Add(ref aRef, 1) * Unsafe.Add(ref bRef, 1);
|
||||
}
|
||||
|
||||
if (len > 2)
|
||||
{
|
||||
sum += Unsafe.Add(ref aRef, 2) * Unsafe.Add(ref bRef, 2);
|
||||
}
|
||||
|
||||
return sum;
|
||||
}
|
||||
|
||||
if (Avx512F.IsSupported)
|
||||
{
|
||||
return DotProductAvx512(a, b);
|
||||
}
|
||||
|
||||
if (Avx2.IsSupported)
|
||||
{
|
||||
return DotProductAvx2(a, b);
|
||||
}
|
||||
|
||||
if (AdvSimd.Arm64.IsSupported)
|
||||
{
|
||||
return DotProductNeon(a, b);
|
||||
}
|
||||
|
||||
double s1 = 0, s2 = 0, s3 = 0, s4 = 0;
|
||||
ref double ar = ref MemoryMarshal.GetReference(a);
|
||||
@@ -779,4 +913,4 @@ public static class SimdExtensions
|
||||
|
||||
return sum;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user