style patterns

This commit is contained in:
Miha Kralj
2026-01-25 16:01:45 -08:00
parent 2836f253c4
commit e59665c8f0
399 changed files with 6892 additions and 1323 deletions
+281 -27
View File
@@ -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
}
}
+3 -1
View File
@@ -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
View File
@@ -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;
}
}
}