diff --git a/src/libraries/System.Numerics.Tensors/src/System/Numerics/Tensors/TensorPrimitives.cs b/src/libraries/System.Numerics.Tensors/src/System/Numerics/Tensors/TensorPrimitives.cs
index d28d4bacafdb8e..a4429a1eba080d 100644
--- a/src/libraries/System.Numerics.Tensors/src/System/Numerics/Tensors/TensorPrimitives.cs
+++ b/src/libraries/System.Numerics.Tensors/src/System/Numerics/Tensors/TensorPrimitives.cs
@@ -6,6 +6,14 @@ namespace System.Numerics.Tensors
/// Performs primitive tensor operations over spans of memory.
public static partial class TensorPrimitives
{
+ /// Computes the element-wise result of: MathF.Abs().
+ /// The tensor, represented as a span.
+ /// The destination tensor, represented as a span.
+ /// Destination is too short.
+ /// This method effectively does [i] = MathF.Abs([i]).
+ public static void Abs(ReadOnlySpan x, Span destination) =>
+ InvokeSpanIntoSpan(x, destination);
+
/// Computes the element-wise result of: + .
/// The first tensor, represented as a span.
/// The second tensor, represented as a span.
@@ -25,82 +33,6 @@ public static unsafe void Add(ReadOnlySpan x, ReadOnlySpan y, Span
public static void Add(ReadOnlySpan x, float y, Span destination) =>
InvokeSpanScalarIntoSpan(x, y, destination);
- /// Computes the element-wise result of: - .
- /// The first tensor, represented as a span.
- /// The second tensor, represented as a scalar.
- /// The destination tensor, represented as a span.
- /// Length of '' must be same as length of ''.
- /// Destination is too short.
- /// This method effectively does [i] = [i] - [i].
- public static void Subtract(ReadOnlySpan x, ReadOnlySpan y, Span destination) =>
- InvokeSpanSpanIntoSpan(x, y, destination);
-
- /// Computes the element-wise result of: - .
- /// The first tensor, represented as a span.
- /// The second tensor, represented as a scalar.
- /// The destination tensor, represented as a span.
- /// Destination is too short.
- /// This method effectively does [i] = [i] - .
- public static void Subtract(ReadOnlySpan x, float y, Span destination) =>
- InvokeSpanScalarIntoSpan(x, y, destination);
-
- /// Computes the element-wise result of: * .
- /// The first tensor, represented as a span.
- /// The second tensor, represented as a span.
- /// The destination tensor, represented as a span.
- /// Length of '' must be same as length of ''.
- /// Destination is too short.
- /// This method effectively does [i] = [i] * .
- public static void Multiply(ReadOnlySpan x, ReadOnlySpan y, Span destination) =>
- InvokeSpanSpanIntoSpan(x, y, destination);
-
- /// Computes the element-wise result of: * .
- /// The first tensor, represented as a span.
- /// The second tensor, represented as a scalar.
- /// The destination tensor, represented as a span.
- /// Destination is too short.
- ///
- /// This method effectively does [i] = [i] * .
- /// This method corresponds to the scal method defined by BLAS1.
- ///
- public static void Multiply(ReadOnlySpan x, float y, Span destination) =>
- InvokeSpanScalarIntoSpan(x, y, destination);
-
- /// Computes the element-wise result of: / .
- /// The first tensor, represented as a span.
- /// The second tensor, represented as a span.
- /// The destination tensor, represented as a span.
- /// Length of '' must be same as length of ''.
- /// Destination is too short.
- /// This method effectively does [i] = [i] / .
- public static void Divide(ReadOnlySpan x, ReadOnlySpan y, Span destination) =>
- InvokeSpanSpanIntoSpan(x, y, destination);
-
- /// Computes the element-wise result of: / .
- /// The first tensor, represented as a span.
- /// The second tensor, represented as a scalar.
- /// The destination tensor, represented as a span.
- /// Destination is too short.
- /// This method effectively does [i] = [i] / .
- public static void Divide(ReadOnlySpan x, float y, Span destination) =>
- InvokeSpanScalarIntoSpan(x, y, destination);
-
- /// Computes the element-wise result of: -.
- /// The tensor, represented as a span.
- /// The destination tensor, represented as a span.
- /// Destination is too short.
- /// This method effectively does [i] = -[i].
- public static void Negate(ReadOnlySpan x, Span destination) =>
- InvokeSpanIntoSpan(x, destination);
-
- /// Computes the element-wise result of: MathF.Abs().
- /// The tensor, represented as a span.
- /// The destination tensor, represented as a span.
- /// Destination is too short.
- /// This method effectively does [i] = MathF.Abs([i]).
- public static void Abs(ReadOnlySpan x, Span destination) =>
- InvokeSpanIntoSpan(x, destination);
-
/// Computes the element-wise result of: ( + ) * .
/// The first tensor, represented as a span.
/// The second tensor, represented as a span.
@@ -135,97 +67,6 @@ public static void AddMultiply(ReadOnlySpan x, ReadOnlySpan y, flo
public static void AddMultiply(ReadOnlySpan x, float y, ReadOnlySpan multiplier, Span destination) =>
InvokeSpanScalarSpanIntoSpan(x, y, multiplier, destination);
- /// Computes the element-wise result of: ( * ) + .
- /// The first tensor, represented as a span.
- /// The second tensor, represented as a span.
- /// The third tensor, represented as a span.
- /// The destination tensor, represented as a span.
- /// Length of '' must be same as length of ''.
- /// Length of '' must be same as length of ''.
- /// Destination is too short.
- /// This method effectively does [i] = ([i] * [i]) + [i].
- public static void MultiplyAdd(ReadOnlySpan x, ReadOnlySpan y, ReadOnlySpan addend, Span destination) =>
- InvokeSpanSpanSpanIntoSpan(x, y, addend, destination);
-
- /// Computes the element-wise result of: ( * ) + .
- /// The first tensor, represented as a span.
- /// The second tensor, represented as a span.
- /// The third tensor, represented as a span.
- /// The destination tensor, represented as a span.
- /// Length of '' must be same as length of ''.
- /// Destination is too short.
- ///
- /// This method effectively does [i] = ([i] * [i]) + .
- /// This method corresponds to the axpy method defined by BLAS1.
- ///
- public static void MultiplyAdd(ReadOnlySpan x, ReadOnlySpan y, float addend, Span destination) =>
- InvokeSpanSpanScalarIntoSpan(x, y, addend, destination);
-
- /// Computes the element-wise result of: ( * ) + .
- /// The first tensor, represented as a span.
- /// The second tensor, represented as a span.
- /// The third tensor, represented as a span.
- /// The destination tensor, represented as a span.
- /// Length of '' must be same as length of ''.
- /// Destination is too short.
- /// This method effectively does [i] = ([i] * ) + [i].
- public static void MultiplyAdd(ReadOnlySpan x, float y, ReadOnlySpan addend, Span destination) =>
- InvokeSpanScalarSpanIntoSpan(x, y, addend, destination);
-
- /// Computes the element-wise result of: pow(e, ).
- /// The tensor, represented as a span.
- /// The destination tensor, represented as a span.
- /// Destination is too short.
- /// This method effectively does [i] = .Exp([i]).
- public static void Exp(ReadOnlySpan x, Span destination)
- {
- if (x.Length > destination.Length)
- {
- ThrowHelper.ThrowArgument_DestinationTooShort();
- }
-
- for (int i = 0; i < x.Length; i++)
- {
- destination[i] = MathF.Exp(x[i]);
- }
- }
-
- /// Computes the element-wise result of: ln().
- /// The tensor, represented as a span.
- /// The destination tensor, represented as a span.
- /// Destination is too short.
- /// This method effectively does [i] = .Log([i]).
- public static void Log(ReadOnlySpan x, Span destination)
- {
- if (x.Length > destination.Length)
- {
- ThrowHelper.ThrowArgument_DestinationTooShort();
- }
-
- for (int i = 0; i < x.Length; i++)
- {
- destination[i] = MathF.Log(x[i]);
- }
- }
-
- /// Computes the element-wise result of: log2().
- /// The tensor, represented as a span.
- /// The destination tensor, represented as a span.
- /// Destination is too short.
- /// This method effectively does [i] = .Log2([i]).
- public static void Log2(ReadOnlySpan x, Span destination)
- {
- if (x.Length > destination.Length)
- {
- ThrowHelper.ThrowArgument_DestinationTooShort();
- }
-
- for (int i = 0; i < x.Length; i++)
- {
- destination[i] = Log2(x[i]);
- }
- }
-
/// Computes the element-wise result of: cosh().
/// The tensor, represented as a span.
/// The destination tensor, represented as a span.
@@ -244,42 +85,6 @@ public static void Cosh(ReadOnlySpan x, Span destination)
}
}
- /// Computes the element-wise result of: sinh().
- /// The tensor, represented as a span.
- /// The destination tensor, represented as a span.
- /// Destination is too short.
- /// This method effectively does [i] = .Sinh([i]).
- public static void Sinh(ReadOnlySpan x, Span destination)
- {
- if (x.Length > destination.Length)
- {
- ThrowHelper.ThrowArgument_DestinationTooShort();
- }
-
- for (int i = 0; i < x.Length; i++)
- {
- destination[i] = MathF.Sinh(x[i]);
- }
- }
-
- /// Computes the element-wise result of: tanh().
- /// The tensor, represented as a span.
- /// The destination tensor, represented as a span.
- /// Destination is too short.
- /// This method effectively does [i] = .Tanh([i]).
- public static void Tanh(ReadOnlySpan x, Span destination)
- {
- if (x.Length > destination.Length)
- {
- ThrowHelper.ThrowArgument_DestinationTooShort();
- }
-
- for (int i = 0; i < x.Length; i++)
- {
- destination[i] = MathF.Tanh(x[i]);
- }
- }
-
/// Computes the cosine similarity between two non-zero vectors.
/// The first tensor, represented as a span.
/// The second tensor, represented as a span.
@@ -292,6 +97,7 @@ public static float CosineSimilarity(ReadOnlySpan x, ReadOnlySpan
{
ThrowHelper.ThrowArgument_SpansMustBeNonEmpty();
}
+
if (x.Length != y.Length)
{
ThrowHelper.ThrowArgument_SpansMustHaveSameLength();
@@ -314,6 +120,7 @@ public static float Distance(ReadOnlySpan x, ReadOnlySpan y)
{
ThrowHelper.ThrowArgument_SpansMustBeNonEmpty();
}
+
if (x.Length != y.Length)
{
ThrowHelper.ThrowArgument_SpansMustHaveSameLength();
@@ -322,6 +129,25 @@ public static float Distance(ReadOnlySpan x, ReadOnlySpan y)
return MathF.Sqrt(Aggregate(0f, x, y));
}
+ /// Computes the element-wise result of: / .
+ /// The first tensor, represented as a span.
+ /// The second tensor, represented as a span.
+ /// The destination tensor, represented as a span.
+ /// Length of '' must be same as length of ''.
+ /// Destination is too short.
+ /// This method effectively does [i] = [i] / .
+ public static void Divide(ReadOnlySpan x, ReadOnlySpan y, Span destination) =>
+ InvokeSpanSpanIntoSpan(x, y, destination);
+
+ /// Computes the element-wise result of: / .
+ /// The first tensor, represented as a span.
+ /// The second tensor, represented as a scalar.
+ /// The destination tensor, represented as a span.
+ /// Destination is too short.
+ /// This method effectively does [i] = [i] / .
+ public static void Divide(ReadOnlySpan x, float y, Span destination) =>
+ InvokeSpanScalarIntoSpan(x, y, destination);
+
///
/// A mathematical operation that takes two vectors and returns a scalar.
///
@@ -339,60 +165,234 @@ public static float Dot(ReadOnlySpan x, ReadOnlySpan y) // BLAS1:
return Aggregate(0f, x, y);
}
- ///
- /// A mathematical operation that takes a vector and returns the L2 norm.
- ///
- /// The first tensor, represented as a span.
- /// The L2 norm.
- public static float Norm(ReadOnlySpan x) // BLAS1: nrm2
- {
- return MathF.Sqrt(Aggregate(0f, x));
- }
-
- ///
- /// A function that takes a collection of real numbers and returns a probability distribution.
- ///
- /// The first tensor, represented as a span.
- /// The destination tensor.
+ /// Computes the element-wise result of: pow(e, ).
+ /// The tensor, represented as a span.
+ /// The destination tensor, represented as a span.
/// Destination is too short.
- /// '' must not be empty.
- public static void SoftMax(ReadOnlySpan x, Span destination)
+ /// This method effectively does [i] = .Exp([i]).
+ public static void Exp(ReadOnlySpan x, Span destination)
{
- if (x.IsEmpty)
- {
- ThrowHelper.ThrowArgument_SpansMustBeNonEmpty();
- }
if (x.Length > destination.Length)
{
ThrowHelper.ThrowArgument_DestinationTooShort();
}
- float expSum = 0f;
-
for (int i = 0; i < x.Length; i++)
{
- expSum += MathF.Exp(x[i]);
+ destination[i] = MathF.Exp(x[i]);
}
+ }
- for (int i = 0; i < x.Length; i++)
+ /// Computes the index of the maximum element in .
+ /// The tensor, represented as a span.
+ /// The index of the maximum element in , or -1 if is empty.
+ public static unsafe int IndexOfMax(ReadOnlySpan x)
+ {
+ int result = -1;
+
+ if (!x.IsEmpty)
{
- destination[i] = MathF.Exp(x[i]) / expSum;
+ float max = float.NegativeInfinity;
+
+ for (int i = 0; i < x.Length; i++)
+ {
+ // This matches the IEEE 754:2019 `maximum` function.
+ // It propagates NaN inputs back to the caller and
+ // otherwise returns the greater of the inputs.
+ // It treats +0 as greater than -0 as per the specification.
+
+ float current = x[i];
+
+ if (current != max)
+ {
+ if (float.IsNaN(current))
+ {
+ return i;
+ }
+
+ if (max < current)
+ {
+ result = i;
+ max = current;
+ }
+ }
+ else if (IsNegative(max) && !IsNegative(current))
+ {
+ result = i;
+ max = current;
+ }
+ }
}
+
+ return result;
}
- ///
- /// A function that takes a real number and returns a value between 0 and 1.
- ///
- /// The first tensor, represented as a span.
- /// The destination tensor.
+ /// Computes the index of the element in with the maximum magnitude.
+ /// The tensor, represented as a span.
+ /// The index of the element with the maximum magnitude, or -1 if is empty.
+ /// This method corresponds to the iamax method defined by BLAS1.
+ public static unsafe int IndexOfMaxMagnitude(ReadOnlySpan x)
+ {
+ int result = -1;
+
+ if (!x.IsEmpty)
+ {
+ float max = float.NegativeInfinity;
+ float maxMag = float.NegativeInfinity;
+
+ for (int i = 0; i < x.Length; i++)
+ {
+ // This matches the IEEE 754:2019 `maximumMagnitude` function.
+ // It propagates NaN inputs back to the caller and
+ // otherwise returns the input with a greater magnitude.
+ // It treats +0 as greater than -0 as per the specification.
+
+ float current = x[i];
+ float currentMag = Math.Abs(current);
+
+ if (currentMag != maxMag)
+ {
+ if (float.IsNaN(currentMag))
+ {
+ return i;
+ }
+
+ if (maxMag < currentMag)
+ {
+ result = i;
+ max = current;
+ maxMag = currentMag;
+ }
+ }
+ else if (IsNegative(max) && !IsNegative(current))
+ {
+ result = i;
+ max = current;
+ maxMag = currentMag;
+ }
+ }
+ }
+
+ return result;
+ }
+
+ /// Computes the index of the minimum element in .
+ /// The tensor, represented as a span.
+ /// The index of the minimum element in , or -1 if is empty.
+ public static unsafe int IndexOfMin(ReadOnlySpan x)
+ {
+ int result = -1;
+
+ if (!x.IsEmpty)
+ {
+ float min = float.PositiveInfinity;
+
+ for (int i = 0; i < x.Length; i++)
+ {
+ // This matches the IEEE 754:2019 `minimum` function.
+ // It propagates NaN inputs back to the caller and
+ // otherwise returns the lesser of the inputs.
+ // It treats +0 as greater than -0 as per the specification.
+
+ float current = x[i];
+
+ if (current != min)
+ {
+ if (float.IsNaN(current))
+ {
+ return i;
+ }
+
+ if (current < min)
+ {
+ result = i;
+ min = current;
+ }
+ }
+ else if (IsNegative(current) && !IsNegative(min))
+ {
+ result = i;
+ min = current;
+ }
+ }
+ }
+
+ return result;
+ }
+
+ /// Computes the index of the element in with the minimum magnitude.
+ /// The tensor, represented as a span.
+ /// The index of the element with the minimum magnitude, or -1 if is empty.
+ public static unsafe int IndexOfMinMagnitude(ReadOnlySpan x)
+ {
+ int result = -1;
+
+ if (!x.IsEmpty)
+ {
+ float min = float.PositiveInfinity;
+ float minMag = float.PositiveInfinity;
+
+ for (int i = 0; i < x.Length; i++)
+ {
+ // This matches the IEEE 754:2019 `minimumMagnitude` function
+ // It propagates NaN inputs back to the caller and
+ // otherwise returns the input with a lesser magnitude.
+ // It treats +0 as greater than -0 as per the specification.
+
+ float current = x[i];
+ float currentMag = Math.Abs(current);
+
+ if (currentMag != minMag)
+ {
+ if (float.IsNaN(currentMag))
+ {
+ return i;
+ }
+
+ if (currentMag < minMag)
+ {
+ result = i;
+ min = current;
+ minMag = currentMag;
+ }
+ }
+ else if (IsNegative(current) && !IsNegative(min))
+ {
+ result = i;
+ min = current;
+ minMag = currentMag;
+ }
+ }
+ }
+
+ return result;
+ }
+
+ /// Computes the element-wise result of: ln().
+ /// The tensor, represented as a span.
+ /// The destination tensor, represented as a span.
/// Destination is too short.
- /// '' must not be empty.
- public static void Sigmoid(ReadOnlySpan x, Span destination)
+ /// This method effectively does [i] = .Log([i]).
+ public static void Log(ReadOnlySpan x, Span destination)
{
- if (x.IsEmpty)
+ if (x.Length > destination.Length)
{
- ThrowHelper.ThrowArgument_SpansMustBeNonEmpty();
+ ThrowHelper.ThrowArgument_DestinationTooShort();
+ }
+
+ for (int i = 0; i < x.Length; i++)
+ {
+ destination[i] = MathF.Log(x[i]);
}
+ }
+
+ /// Computes the element-wise result of: log2().
+ /// The tensor, represented as a span.
+ /// The destination tensor, represented as a span.
+ /// Destination is too short.
+ /// This method effectively does [i] = .Log2([i]).
+ public static void Log2(ReadOnlySpan x, Span destination)
+ {
if (x.Length > destination.Length)
{
ThrowHelper.ThrowArgument_DestinationTooShort();
@@ -400,7 +400,7 @@ public static void Sigmoid(ReadOnlySpan x, Span destination)
for (int i = 0; i < x.Length; i++)
{
- destination[i] = 1f / (1 + MathF.Exp(-x[i]));
+ destination[i] = Log2(x[i]);
}
}
@@ -472,57 +472,61 @@ public static void Max(ReadOnlySpan x, ReadOnlySpan y, Span
}
}
- /// Computes the minimum element in .
+ /// Computes the maximum magnitude of any element in .
/// The tensor, represented as a span.
- /// The minimum element in .
+ /// The maximum magnitude of any element in .
/// Length of '' must be greater than zero.
- public static float Min(ReadOnlySpan x)
+ public static float MaxMagnitude(ReadOnlySpan x)
{
if (x.IsEmpty)
{
ThrowHelper.ThrowArgument_SpansMustBeNonEmpty();
}
- float result = float.PositiveInfinity;
+ float result = float.NegativeInfinity;
+ float resultMag = float.NegativeInfinity;
for (int i = 0; i < x.Length; i++)
{
- // This matches the IEEE 754:2019 `minimum` function
+ // This matches the IEEE 754:2019 `maximumMagnitude` function.
// It propagates NaN inputs back to the caller and
- // otherwise returns the lesser of the inputs.
+ // otherwise returns the input with a greater magnitude.
// It treats +0 as greater than -0 as per the specification.
float current = x[i];
+ float currentMag = Math.Abs(current);
- if (current != result)
+ if (currentMag != resultMag)
{
- if (float.IsNaN(current))
+ if (float.IsNaN(currentMag))
{
- return current;
+ return currentMag;
}
- if (current < result)
+ if (resultMag < currentMag)
{
result = current;
+ resultMag = currentMag;
}
}
- else if (IsNegative(current))
+ else if (IsNegative(result))
{
result = current;
+ resultMag = currentMag;
}
}
return result;
}
- /// Computes the element-wise result of: MathF.Min(, ).
+ /// Computes the element-wise result of: MathF.MaxMagnitude(, ).
/// The first tensor, represented as a span.
/// The second tensor, represented as a span.
/// The destination tensor, represented as a span.
/// Length of '' must be same as length of ''.
/// Destination is too short.
- /// This method effectively does [i] = MathF.Min([i], [i]).
- public static void Min(ReadOnlySpan x, ReadOnlySpan y, Span destination)
+ /// This method effectively does [i] = MathF.MaxMagnitude([i], [i]).
+ public static void MaxMagnitude(ReadOnlySpan x, ReadOnlySpan y, Span destination)
{
if (x.Length != y.Length)
{
@@ -536,65 +540,61 @@ public static void Min(ReadOnlySpan x, ReadOnlySpan y, Span
for (int i = 0; i < x.Length; i++)
{
- destination[i] = MathF.Min(x[i], y[i]);
+ destination[i] = MaxMagnitude(x[i], y[i]);
}
}
- /// Computes the maximum magnitude of any element in .
+ /// Computes the minimum element in .
/// The tensor, represented as a span.
- /// The maximum magnitude of any element in .
+ /// The minimum element in .
/// Length of '' must be greater than zero.
- public static float MaxMagnitude(ReadOnlySpan x)
+ public static float Min(ReadOnlySpan x)
{
if (x.IsEmpty)
{
ThrowHelper.ThrowArgument_SpansMustBeNonEmpty();
}
- float result = float.NegativeInfinity;
- float resultMag = float.NegativeInfinity;
+ float result = float.PositiveInfinity;
for (int i = 0; i < x.Length; i++)
{
- // This matches the IEEE 754:2019 `maximumMagnitude` function.
+ // This matches the IEEE 754:2019 `minimum` function
// It propagates NaN inputs back to the caller and
- // otherwise returns the input with a greater magnitude.
+ // otherwise returns the lesser of the inputs.
// It treats +0 as greater than -0 as per the specification.
float current = x[i];
- float currentMag = Math.Abs(current);
- if (currentMag != resultMag)
+ if (current != result)
{
- if (float.IsNaN(currentMag))
+ if (float.IsNaN(current))
{
- return currentMag;
+ return current;
}
- if (resultMag < currentMag)
+ if (current < result)
{
result = current;
- resultMag = currentMag;
}
}
- else if (IsNegative(result))
+ else if (IsNegative(current))
{
result = current;
- resultMag = currentMag;
}
}
return result;
}
- /// Computes the element-wise result of: MathF.MaxMagnitude(, ).
+ /// Computes the element-wise result of: MathF.Min(, ).
/// The first tensor, represented as a span.
/// The second tensor, represented as a span.
/// The destination tensor, represented as a span.
/// Length of '' must be same as length of ''.
/// Destination is too short.
- /// This method effectively does [i] = MathF.MaxMagnitude([i], [i]).
- public static void MaxMagnitude(ReadOnlySpan x, ReadOnlySpan y, Span destination)
+ /// This method effectively does [i] = MathF.Min([i], [i]).
+ public static void Min(ReadOnlySpan x, ReadOnlySpan y, Span destination)
{
if (x.Length != y.Length)
{
@@ -608,7 +608,7 @@ public static void MaxMagnitude(ReadOnlySpan x, ReadOnlySpan y, Sp
for (int i = 0; i < x.Length; i++)
{
- destination[i] = MaxMagnitude(x[i], y[i]);
+ destination[i] = MathF.Min(x[i], y[i]);
}
}
@@ -684,204 +684,239 @@ public static void MinMagnitude(ReadOnlySpan x, ReadOnlySpan y, Sp
}
}
- /// Computes the index of the maximum element in .
- /// The tensor, represented as a span.
- /// The index of the maximum element in , or -1 if is empty.
- public static unsafe int IndexOfMax(ReadOnlySpan x)
- {
- int result = -1;
+ /// Computes the element-wise result of: * .
+ /// The first tensor, represented as a span.
+ /// The second tensor, represented as a span.
+ /// The destination tensor, represented as a span.
+ /// Length of '' must be same as length of ''.
+ /// Destination is too short.
+ /// This method effectively does [i] = [i] * .
+ public static void Multiply(ReadOnlySpan x, ReadOnlySpan y, Span destination) =>
+ InvokeSpanSpanIntoSpan(x, y, destination);
- if (!x.IsEmpty)
- {
- float max = float.NegativeInfinity;
+ /// Computes the element-wise result of: * .
+ /// The first tensor, represented as a span.
+ /// The second tensor, represented as a scalar.
+ /// The destination tensor, represented as a span.
+ /// Destination is too short.
+ ///
+ /// This method effectively does [i] = [i] * .
+ /// This method corresponds to the scal method defined by BLAS1.
+ ///
+ public static void Multiply(ReadOnlySpan x, float y, Span destination) =>
+ InvokeSpanScalarIntoSpan(x, y, destination);
- for (int i = 0; i < x.Length; i++)
- {
- // This matches the IEEE 754:2019 `maximum` function.
- // It propagates NaN inputs back to the caller and
- // otherwise returns the greater of the inputs.
- // It treats +0 as greater than -0 as per the specification.
+ /// Computes the element-wise result of: ( * ) + .
+ /// The first tensor, represented as a span.
+ /// The second tensor, represented as a span.
+ /// The third tensor, represented as a span.
+ /// The destination tensor, represented as a span.
+ /// Length of '' must be same as length of ''.
+ /// Length of '' must be same as length of ''.
+ /// Destination is too short.
+ /// This method effectively does [i] = ([i] * [i]) + [i].
+ public static void MultiplyAdd(ReadOnlySpan x, ReadOnlySpan y, ReadOnlySpan addend, Span destination) =>
+ InvokeSpanSpanSpanIntoSpan(x, y, addend, destination);
- float current = x[i];
+ /// Computes the element-wise result of: ( * ) + .
+ /// The first tensor, represented as a span.
+ /// The second tensor, represented as a span.
+ /// The third tensor, represented as a span.
+ /// The destination tensor, represented as a span.
+ /// Length of '' must be same as length of ''.
+ /// Destination is too short.
+ ///
+ /// This method effectively does [i] = ([i] * [i]) + .
+ /// This method corresponds to the axpy method defined by BLAS1.
+ ///
+ public static void MultiplyAdd(ReadOnlySpan x, ReadOnlySpan y, float addend, Span destination) =>
+ InvokeSpanSpanScalarIntoSpan(x, y, addend, destination);
- if (current != max)
- {
- if (float.IsNaN(current))
- {
- return i;
- }
+ /// Computes the element-wise result of: ( * ) + .
+ /// The first tensor, represented as a span.
+ /// The second tensor, represented as a span.
+ /// The third tensor, represented as a span.
+ /// The destination tensor, represented as a span.
+ /// Length of '' must be same as length of ''.
+ /// Destination is too short.
+ /// This method effectively does [i] = ([i] * ) + [i].
+ public static void MultiplyAdd(ReadOnlySpan x, float y, ReadOnlySpan addend, Span destination) =>
+ InvokeSpanScalarSpanIntoSpan(x, y, addend, destination);
- if (max < current)
- {
- result = i;
- max = current;
- }
- }
- else if (IsNegative(max) && !IsNegative(current))
- {
- result = i;
- max = current;
- }
- }
- }
+ /// Computes the element-wise result of: -.
+ /// The tensor, represented as a span.
+ /// The destination tensor, represented as a span.
+ /// Destination is too short.
+ /// This method effectively does [i] = -[i].
+ public static void Negate(ReadOnlySpan x, Span destination) =>
+ InvokeSpanIntoSpan(x, destination);
- return result;
- }
+ ///
+ /// A mathematical operation that takes a vector and returns the L2 norm.
+ ///
+ /// The first tensor, represented as a span.
+ /// The L2 norm.
+ public static float Norm(ReadOnlySpan x) => // BLAS1: nrm2
+ MathF.Sqrt(Aggregate(0f, x));
- /// Computes the index of the minimum element in .
+ /// Computes the product of all elements in .
/// The tensor, represented as a span.
- /// The index of the minimum element in , or -1 if is empty.
- public static unsafe int IndexOfMin(ReadOnlySpan x)
+ /// The result of multiplying all elements in .
+ /// Length of '' must be greater than zero.
+ public static float Product(ReadOnlySpan x)
{
- int result = -1;
-
- if (!x.IsEmpty)
+ if (x.IsEmpty)
{
- float min = float.PositiveInfinity;
-
- for (int i = 0; i < x.Length; i++)
- {
- // This matches the IEEE 754:2019 `minimum` function.
- // It propagates NaN inputs back to the caller and
- // otherwise returns the lesser of the inputs.
- // It treats +0 as greater than -0 as per the specification.
+ ThrowHelper.ThrowArgument_SpansMustBeNonEmpty();
+ }
- float current = x[i];
+ return Aggregate(1.0f, x);
+ }
- if (current != min)
- {
- if (float.IsNaN(current))
- {
- return i;
- }
+ /// Computes the product of the element-wise result of: - .
+ /// The first tensor, represented as a span.
+ /// The second tensor, represented as a span.
+ /// The result of multiplying the element-wise subtraction of the elements in the second tensor from the first tensor.
+ /// Length of both input spans must be greater than zero.
+ /// and must have the same length.
+ /// This method effectively does .Product(.Subtract(, )).
+ public static float ProductOfDifferences(ReadOnlySpan x, ReadOnlySpan y)
+ {
+ if (x.IsEmpty)
+ {
+ ThrowHelper.ThrowArgument_SpansMustBeNonEmpty();
+ }
- if (current < min)
- {
- result = i;
- min = current;
- }
- }
- else if (IsNegative(current) && !IsNegative(min))
- {
- result = i;
- min = current;
- }
- }
+ if (x.Length != y.Length)
+ {
+ ThrowHelper.ThrowArgument_SpansMustHaveSameLength();
}
- return result;
+ return Aggregate(1.0f, x, y);
}
- /// Computes the index of the element in with the maximum magnitude.
- /// The tensor, represented as a span.
- /// The index of the element with the maximum magnitude, or -1 if is empty.
- /// This method corresponds to the iamax method defined by BLAS1.
- public static unsafe int IndexOfMaxMagnitude(ReadOnlySpan x)
+ /// Computes the product of the element-wise result of: + .
+ /// The first tensor, represented as a span.
+ /// The second tensor, represented as a span.
+ /// The result of multiplying the element-wise additions of the elements in each tensor.
+ /// Length of both input spans must be greater than zero.
+ /// and must have the same length.
+ /// This method effectively does .Product(.Add(, )).
+ public static float ProductOfSums(ReadOnlySpan x, ReadOnlySpan y)
{
- int result = -1;
-
- if (!x.IsEmpty)
+ if (x.IsEmpty)
{
- float max = float.NegativeInfinity;
- float maxMag = float.NegativeInfinity;
+ ThrowHelper.ThrowArgument_SpansMustBeNonEmpty();
+ }
- for (int i = 0; i < x.Length; i++)
- {
- // This matches the IEEE 754:2019 `maximumMagnitude` function.
- // It propagates NaN inputs back to the caller and
- // otherwise returns the input with a greater magnitude.
- // It treats +0 as greater than -0 as per the specification.
+ if (x.Length != y.Length)
+ {
+ ThrowHelper.ThrowArgument_SpansMustHaveSameLength();
+ }
- float current = x[i];
- float currentMag = Math.Abs(current);
+ return Aggregate(1.0f, x, y);
+ }
- if (currentMag != maxMag)
- {
- if (float.IsNaN(currentMag))
- {
- return i;
- }
+ ///
+ /// A function that takes a real number and returns a value between 0 and 1.
+ ///
+ /// The first tensor, represented as a span.
+ /// The destination tensor.
+ /// Destination is too short.
+ /// '' must not be empty.
+ public static void Sigmoid(ReadOnlySpan x, Span destination)
+ {
+ if (x.IsEmpty)
+ {
+ ThrowHelper.ThrowArgument_SpansMustBeNonEmpty();
+ }
- if (maxMag < currentMag)
- {
- result = i;
- max = current;
- maxMag = currentMag;
- }
- }
- else if (IsNegative(max) && !IsNegative(current))
- {
- result = i;
- max = current;
- maxMag = currentMag;
- }
- }
+ if (x.Length > destination.Length)
+ {
+ ThrowHelper.ThrowArgument_DestinationTooShort();
}
- return result;
+ for (int i = 0; i < x.Length; i++)
+ {
+ destination[i] = 1f / (1 + MathF.Exp(-x[i]));
+ }
}
- /// Computes the index of the element in with the minimum magnitude.
+ /// Computes the element-wise result of: sinh().
/// The tensor, represented as a span.
- /// The index of the element with the minimum magnitude, or -1 if is empty.
- public static unsafe int IndexOfMinMagnitude(ReadOnlySpan x)
+ /// The destination tensor, represented as a span.
+ /// Destination is too short.
+ /// This method effectively does [i] = .Sinh([i]).
+ public static void Sinh(ReadOnlySpan x, Span destination)
{
- int result = -1;
+ if (x.Length > destination.Length)
+ {
+ ThrowHelper.ThrowArgument_DestinationTooShort();
+ }
- if (!x.IsEmpty)
+ for (int i = 0; i < x.Length; i++)
{
- float min = float.PositiveInfinity;
- float minMag = float.PositiveInfinity;
+ destination[i] = MathF.Sinh(x[i]);
+ }
+ }
- for (int i = 0; i < x.Length; i++)
- {
- // This matches the IEEE 754:2019 `minimumMagnitude` function
- // It propagates NaN inputs back to the caller and
- // otherwise returns the input with a lesser magnitude.
- // It treats +0 as greater than -0 as per the specification.
+ ///
+ /// A function that takes a collection of real numbers and returns a probability distribution.
+ ///
+ /// The first tensor, represented as a span.
+ /// The destination tensor.
+ /// Destination is too short.
+ /// '' must not be empty.
+ public static void SoftMax(ReadOnlySpan x, Span destination)
+ {
+ if (x.IsEmpty)
+ {
+ ThrowHelper.ThrowArgument_SpansMustBeNonEmpty();
+ }
- float current = x[i];
- float currentMag = Math.Abs(current);
+ if (x.Length > destination.Length)
+ {
+ ThrowHelper.ThrowArgument_DestinationTooShort();
+ }
- if (currentMag != minMag)
- {
- if (float.IsNaN(currentMag))
- {
- return i;
- }
+ float expSum = 0f;
- if (currentMag < minMag)
- {
- result = i;
- min = current;
- minMag = currentMag;
- }
- }
- else if (IsNegative(current) && !IsNegative(min))
- {
- result = i;
- min = current;
- minMag = currentMag;
- }
- }
+ for (int i = 0; i < x.Length; i++)
+ {
+ expSum += MathF.Exp(x[i]);
}
- return result;
+ for (int i = 0; i < x.Length; i++)
+ {
+ destination[i] = MathF.Exp(x[i]) / expSum;
+ }
}
+ /// Computes the element-wise result of: - .
+ /// The first tensor, represented as a span.
+ /// The second tensor, represented as a scalar.
+ /// The destination tensor, represented as a span.
+ /// Length of '' must be same as length of ''.
+ /// Destination is too short.
+ /// This method effectively does [i] = [i] - [i].
+ public static void Subtract(ReadOnlySpan x, ReadOnlySpan y, Span destination) =>
+ InvokeSpanSpanIntoSpan(x, y, destination);
+
+ /// Computes the element-wise result of: - .
+ /// The first tensor, represented as a span.
+ /// The second tensor, represented as a scalar.
+ /// The destination tensor, represented as a span.
+ /// Destination is too short.
+ /// This method effectively does [i] = [i] - .
+ public static void Subtract(ReadOnlySpan x, float y, Span destination) =>
+ InvokeSpanScalarIntoSpan(x, y, destination);
+
/// Computes the sum of all elements in .
/// The tensor, represented as a span.
/// The result of adding all elements in , or zero if is empty.
public static float Sum(ReadOnlySpan x) =>
Aggregate(0f, x);
- /// Computes the sum of the squares of every element in .
- /// The tensor, represented as a span.
- /// The result of adding every element in multiplied by itself, or zero if is empty.
- /// This method effectively does .Sum(.Multiply(, )).
- public static float SumOfSquares(ReadOnlySpan x) =>
- Aggregate(0f, x);
-
/// Computes the sum of the absolute values of every element in .
/// The tensor, represented as a span.
/// The result of adding the absolute value of every element in , or zero if is empty.
@@ -892,62 +927,29 @@ public static float SumOfSquares(ReadOnlySpan x) =>
public static float SumOfMagnitudes(ReadOnlySpan x) =>
Aggregate(0f, x);
- /// Computes the product of all elements in .
+ /// Computes the sum of the squares of every element in .
/// The tensor, represented as a span.
- /// The result of multiplying all elements in .
- /// Length of '' must be greater than zero.
- public static float Product(ReadOnlySpan x)
- {
- if (x.IsEmpty)
- {
- ThrowHelper.ThrowArgument_SpansMustBeNonEmpty();
- }
-
- return Aggregate(1.0f, x);
- }
-
- /// Computes the product of the element-wise result of: + .
- /// The first tensor, represented as a span.
- /// The second tensor, represented as a span.
- /// The result of multiplying the element-wise additions of the elements in each tensor.
- /// Length of both input spans must be greater than zero.
- /// and must have the same length.
- /// This method effectively does .Product(.Add(, )).
- public static float ProductOfSums(ReadOnlySpan x, ReadOnlySpan y)
- {
- if (x.IsEmpty)
- {
- ThrowHelper.ThrowArgument_SpansMustBeNonEmpty();
- }
-
- if (x.Length != y.Length)
- {
- ThrowHelper.ThrowArgument_SpansMustHaveSameLength();
- }
-
- return Aggregate(1.0f, x, y);
- }
+ /// The result of adding every element in multiplied by itself, or zero if is empty.
+ /// This method effectively does .Sum(.Multiply(, )).
+ public static float SumOfSquares(ReadOnlySpan x) =>
+ Aggregate(0f, x);
- /// Computes the product of the element-wise result of: - .
- /// The first tensor, represented as a span.
- /// The second tensor, represented as a span.
- /// The result of multiplying the element-wise subtraction of the elements in the second tensor from the first tensor.
- /// Length of both input spans must be greater than zero.
- /// and must have the same length.
- /// This method effectively does .Product(.Subtract(, )).
- public static float ProductOfDifferences(ReadOnlySpan x, ReadOnlySpan y)
+ /// Computes the element-wise result of: tanh().
+ /// The tensor, represented as a span.
+ /// The destination tensor, represented as a span.
+ /// Destination is too short.
+ /// This method effectively does [i] = .Tanh([i]).
+ public static void Tanh(ReadOnlySpan x, Span destination)
{
- if (x.IsEmpty)
+ if (x.Length > destination.Length)
{
- ThrowHelper.ThrowArgument_SpansMustBeNonEmpty();
+ ThrowHelper.ThrowArgument_DestinationTooShort();
}
- if (x.Length != y.Length)
+ for (int i = 0; i < x.Length; i++)
{
- ThrowHelper.ThrowArgument_SpansMustHaveSameLength();
+ destination[i] = MathF.Tanh(x[i]);
}
-
- return Aggregate(1.0f, x, y);
}
}
}
diff --git a/src/libraries/System.Numerics.Tensors/tests/TensorPrimitivesTests.cs b/src/libraries/System.Numerics.Tensors/tests/TensorPrimitivesTests.cs
index 288cd3edb8d5e6..1f2fc37ad1f086 100644
--- a/src/libraries/System.Numerics.Tensors/tests/TensorPrimitivesTests.cs
+++ b/src/libraries/System.Numerics.Tensors/tests/TensorPrimitivesTests.cs
@@ -13,6 +13,7 @@ namespace System.Numerics.Tensors.Tests
{
public static partial class TensorPrimitivesTests
{
+ #region Test Utilities
private const double Tolerance = 0.0001;
public static IEnumerable