diff --git a/src/libraries/System.Numerics.Tensors/src/System.Numerics.Tensors.csproj b/src/libraries/System.Numerics.Tensors/src/System.Numerics.Tensors.csproj index 2b656cc1d3ee5..8eef4ed0de9c8 100644 --- a/src/libraries/System.Numerics.Tensors/src/System.Numerics.Tensors.csproj +++ b/src/libraries/System.Numerics.Tensors/src/System.Numerics.Tensors.csproj @@ -97,9 +97,11 @@ + + diff --git a/src/libraries/System.Numerics.Tensors/src/System/Numerics/Tensors/TensorPrimitives.Single.cs b/src/libraries/System.Numerics.Tensors/src/System/Numerics/Tensors/TensorPrimitives.Single.cs index ae6df0ed43f5c..a8d4c6815ca40 100644 --- a/src/libraries/System.Numerics.Tensors/src/System/Numerics/Tensors/TensorPrimitives.Single.cs +++ b/src/libraries/System.Numerics.Tensors/src/System/Numerics/Tensors/TensorPrimitives.Single.cs @@ -440,7 +440,7 @@ public static float Max(ReadOnlySpan x) => /// /// public static void Max(ReadOnlySpan x, ReadOnlySpan y, Span destination) => - InvokeSpanSpanIntoSpan(x, y, destination); + InvokeSpanSpanIntoSpan(x, y, destination); /// Searches for the single-precision floating-point number with the largest magnitude in the specified tensor. /// The tensor, represented as a span. @@ -476,7 +476,7 @@ public static float MaxMagnitude(ReadOnlySpan x) => /// /// public static void MaxMagnitude(ReadOnlySpan x, ReadOnlySpan y, Span destination) => - InvokeSpanSpanIntoSpan(x, y, destination); + InvokeSpanSpanIntoSpan(x, y, destination); /// Searches for the smallest single-precision floating-point number in the specified tensor. /// The tensor, represented as a span. @@ -517,7 +517,7 @@ public static float Min(ReadOnlySpan x) => /// /// public static void Min(ReadOnlySpan x, ReadOnlySpan y, Span destination) => - InvokeSpanSpanIntoSpan(x, y, destination); + InvokeSpanSpanIntoSpan(x, y, destination); /// Searches for the single-precision floating-point number with the smallest magnitude in the specified tensor. /// The tensor, represented as a span. @@ -558,7 +558,7 @@ public static float MinMagnitude(ReadOnlySpan x) => /// /// public static void MinMagnitude(ReadOnlySpan x, ReadOnlySpan y, Span destination) => - InvokeSpanSpanIntoSpan(x, y, destination); + InvokeSpanSpanIntoSpan(x, y, destination); /// Computes the element-wise product of single-precision floating-point numbers in the specified tensors. /// The first tensor, represented as a span. diff --git a/src/libraries/System.Numerics.Tensors/src/System/Numerics/Tensors/netcore/TensorPrimitives.CopySign.cs b/src/libraries/System.Numerics.Tensors/src/System/Numerics/Tensors/netcore/TensorPrimitives.CopySign.cs index 0277c729a17aa..e914e271bd015 100644 --- a/src/libraries/System.Numerics.Tensors/src/System/Numerics/Tensors/netcore/TensorPrimitives.CopySign.cs +++ b/src/libraries/System.Numerics.Tensors/src/System/Numerics/Tensors/netcore/TensorPrimitives.CopySign.cs @@ -47,6 +47,9 @@ public static void CopySign(ReadOnlySpan x, T sign, Span destination) public static Vector128 Invoke(Vector128 x, Vector128 y) { +#if NET9_0_OR_GREATER + return Vector128.CopySign(x, y); +#else if (typeof(T) == typeof(float)) { return Vector128.ConditionalSelect(Vector128.Create(-0.0f).As(), y, x); @@ -71,10 +74,14 @@ public static Vector128 Invoke(Vector128 x, Vector128 y) } return x; +#endif } public static Vector256 Invoke(Vector256 x, Vector256 y) { +#if NET9_0_OR_GREATER + return Vector256.CopySign(x, y); +#else if (typeof(T) == typeof(float)) { return Vector256.ConditionalSelect(Vector256.Create(-0.0f).As(), y, x); @@ -99,10 +106,14 @@ public static Vector256 Invoke(Vector256 x, Vector256 y) } return x; +#endif } public static Vector512 Invoke(Vector512 x, Vector512 y) { +#if NET9_0_OR_GREATER + return Vector512.CopySign(x, y); +#else if (typeof(T) == typeof(float)) { return Vector512.ConditionalSelect(Vector512.Create(-0.0f).As(), y, x); @@ -127,6 +138,7 @@ public static Vector512 Invoke(Vector512 x, Vector512 y) } return x; +#endif } } } diff --git a/src/libraries/System.Numerics.Tensors/src/System/Numerics/Tensors/netcore/TensorPrimitives.DegreesToRadians.cs b/src/libraries/System.Numerics.Tensors/src/System/Numerics/Tensors/netcore/TensorPrimitives.DegreesToRadians.cs index 27d11cf14ce72..bf21253c4436a 100644 --- a/src/libraries/System.Numerics.Tensors/src/System/Numerics/Tensors/netcore/TensorPrimitives.DegreesToRadians.cs +++ b/src/libraries/System.Numerics.Tensors/src/System/Numerics/Tensors/netcore/TensorPrimitives.DegreesToRadians.cs @@ -1,6 +1,7 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. +using System.Diagnostics; using System.Runtime.Intrinsics; namespace System.Numerics.Tensors @@ -25,10 +26,59 @@ public static void DegreesToRadians(ReadOnlySpan x, Span destination) private readonly struct DegreesToRadiansOperator : IUnaryOperator where T : ITrigonometricFunctions { public static bool Vectorizable => true; + public static T Invoke(T x) => T.DegreesToRadians(x); - public static Vector128 Invoke(Vector128 x) => (x * T.Pi) / T.CreateChecked(180); - public static Vector256 Invoke(Vector256 x) => (x * T.Pi) / T.CreateChecked(180); - public static Vector512 Invoke(Vector512 x) => (x * T.Pi) / T.CreateChecked(180); + + public static Vector128 Invoke(Vector128 x) + { +#if NET9_0_OR_GREATER + if (typeof(T) == typeof(double)) + { + return Vector128.DegreesToRadians(x.AsDouble()).As(); + } + else + { + Debug.Assert(typeof(T) == typeof(float)); + return Vector128.DegreesToRadians(x.AsSingle()).As(); + } +#else + return (x * T.Pi) / T.CreateChecked(180); +#endif + } + + public static Vector256 Invoke(Vector256 x) + { +#if NET9_0_OR_GREATER + if (typeof(T) == typeof(double)) + { + return Vector256.DegreesToRadians(x.AsDouble()).As(); + } + else + { + Debug.Assert(typeof(T) == typeof(float)); + return Vector256.DegreesToRadians(x.AsSingle()).As(); + } +#else + return (x * T.Pi) / T.CreateChecked(180); +#endif + } + + public static Vector512 Invoke(Vector512 x) + { +#if NET9_0_OR_GREATER + if (typeof(T) == typeof(double)) + { + return Vector512.DegreesToRadians(x.AsDouble()).As(); + } + else + { + Debug.Assert(typeof(T) == typeof(float)); + return Vector512.DegreesToRadians(x.AsSingle()).As(); + } +#else + return (x * T.Pi) / T.CreateChecked(180); +#endif + } } } } diff --git a/src/libraries/System.Numerics.Tensors/src/System/Numerics/Tensors/netcore/TensorPrimitives.Hypot.cs b/src/libraries/System.Numerics.Tensors/src/System/Numerics/Tensors/netcore/TensorPrimitives.Hypot.cs index f0c46347ac244..c806856672f90 100644 --- a/src/libraries/System.Numerics.Tensors/src/System/Numerics/Tensors/netcore/TensorPrimitives.Hypot.cs +++ b/src/libraries/System.Numerics.Tensors/src/System/Numerics/Tensors/netcore/TensorPrimitives.Hypot.cs @@ -1,6 +1,7 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. +using System.Diagnostics; using System.Runtime.Intrinsics; namespace System.Numerics.Tensors @@ -29,10 +30,59 @@ public static void Hypot(ReadOnlySpan x, ReadOnlySpan y, Span destin where T : IRootFunctions { public static bool Vectorizable => true; + public static T Invoke(T x, T y) => T.Hypot(x, y); - public static Vector128 Invoke(Vector128 x, Vector128 y) => Vector128.Sqrt((x * x) + (y * y)); - public static Vector256 Invoke(Vector256 x, Vector256 y) => Vector256.Sqrt((x * x) + (y * y)); - public static Vector512 Invoke(Vector512 x, Vector512 y) => Vector512.Sqrt((x * x) + (y * y)); + + public static Vector128 Invoke(Vector128 x, Vector128 y) + { +#if NET9_0_OR_GREATER + if (typeof(T) == typeof(double)) + { + return Vector128.Hypot(x.AsDouble(), y.AsDouble()).As(); + } + else + { + Debug.Assert(typeof(T) == typeof(float)); + return Vector128.Hypot(x.AsSingle(), y.AsSingle()).As(); + } +#else + return Vector128.Sqrt((x * x) + (y * y)); +#endif + } + + public static Vector256 Invoke(Vector256 x, Vector256 y) + { +#if NET9_0_OR_GREATER + if (typeof(T) == typeof(double)) + { + return Vector256.Hypot(x.AsDouble(), y.AsDouble()).As(); + } + else + { + Debug.Assert(typeof(T) == typeof(float)); + return Vector256.Hypot(x.AsSingle(), y.AsSingle()).As(); + } +#else + return Vector256.Sqrt((x * x) + (y * y)); +#endif + } + + public static Vector512 Invoke(Vector512 x, Vector512 y) + { +#if NET9_0_OR_GREATER + if (typeof(T) == typeof(double)) + { + return Vector512.Hypot(x.AsDouble(), y.AsDouble()).As(); + } + else + { + Debug.Assert(typeof(T) == typeof(float)); + return Vector512.Hypot(x.AsSingle(), y.AsSingle()).As(); + } +#else + return Vector512.Sqrt((x * x) + (y * y)); +#endif + } } } } diff --git a/src/libraries/System.Numerics.Tensors/src/System/Numerics/Tensors/netcore/TensorPrimitives.Lerp.cs b/src/libraries/System.Numerics.Tensors/src/System/Numerics/Tensors/netcore/TensorPrimitives.Lerp.cs index a605b62430d3f..f31b3e50f77d2 100644 --- a/src/libraries/System.Numerics.Tensors/src/System/Numerics/Tensors/netcore/TensorPrimitives.Lerp.cs +++ b/src/libraries/System.Numerics.Tensors/src/System/Numerics/Tensors/netcore/TensorPrimitives.Lerp.cs @@ -1,6 +1,7 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. +using System.Diagnostics; using System.Runtime.Intrinsics; namespace System.Numerics.Tensors @@ -75,9 +76,57 @@ public static void Lerp(ReadOnlySpan x, T y, ReadOnlySpan amount, Span< private readonly struct LerpOperator : ITernaryOperator where T : IFloatingPointIeee754 { public static T Invoke(T x, T y, T amount) => T.Lerp(x, y, amount); - public static Vector128 Invoke(Vector128 x, Vector128 y, Vector128 amount) => (x * (Vector128.One - amount)) + (y * amount); - public static Vector256 Invoke(Vector256 x, Vector256 y, Vector256 amount) => (x * (Vector256.One - amount)) + (y * amount); - public static Vector512 Invoke(Vector512 x, Vector512 y, Vector512 amount) => (x * (Vector512.One - amount)) + (y * amount); + + public static Vector128 Invoke(Vector128 x, Vector128 y, Vector128 amount) + { +#if NET9_0_OR_GREATER + if (typeof(T) == typeof(double)) + { + return Vector128.Lerp(x.AsDouble(), y.AsDouble(), amount.AsDouble()).As(); + } + else + { + Debug.Assert(typeof(T) == typeof(float)); + return Vector128.Lerp(x.AsSingle(), y.AsSingle(), amount.AsSingle()).As(); + } +#else + return (x * (Vector128.One - amount)) + (y * amount); +#endif + } + + public static Vector256 Invoke(Vector256 x, Vector256 y, Vector256 amount) + { +#if NET9_0_OR_GREATER + if (typeof(T) == typeof(double)) + { + return Vector256.Lerp(x.AsDouble(), y.AsDouble(), amount.AsDouble()).As(); + } + else + { + Debug.Assert(typeof(T) == typeof(float)); + return Vector256.Lerp(x.AsSingle(), y.AsSingle(), amount.AsSingle()).As(); + } +#else + return (x * (Vector256.One - amount)) + (y * amount); +#endif + } + + public static Vector512 Invoke(Vector512 x, Vector512 y, Vector512 amount) + { +#if NET9_0_OR_GREATER + if (typeof(T) == typeof(double)) + { + return Vector512.Lerp(x.AsDouble(), y.AsDouble(), amount.AsDouble()).As(); + } + else + { + Debug.Assert(typeof(T) == typeof(float)); + return Vector512.Lerp(x.AsSingle(), y.AsSingle(), amount.AsSingle()).As(); + } +#else + return (x * (Vector512.One - amount)) + (y * amount); +#endif + } } } } diff --git a/src/libraries/System.Numerics.Tensors/src/System/Numerics/Tensors/netcore/TensorPrimitives.Max.cs b/src/libraries/System.Numerics.Tensors/src/System/Numerics/Tensors/netcore/TensorPrimitives.Max.cs index 2e760e30fb7c6..3a9c370e0078d 100644 --- a/src/libraries/System.Numerics.Tensors/src/System/Numerics/Tensors/netcore/TensorPrimitives.Max.cs +++ b/src/libraries/System.Numerics.Tensors/src/System/Numerics/Tensors/netcore/TensorPrimitives.Max.cs @@ -1,6 +1,7 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. +using System.Diagnostics; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using System.Runtime.Intrinsics; @@ -51,7 +52,7 @@ public static T Max(ReadOnlySpan x) /// public static void Max(ReadOnlySpan x, ReadOnlySpan y, Span destination) where T : INumber => - InvokeSpanSpanIntoSpan>(x, y, destination); + InvokeSpanSpanIntoSpan>(x, y, destination); /// Computes the element-wise maximum of the numbers in the specified tensors. /// The first tensor, represented as a span. @@ -74,45 +75,30 @@ public static void Max(ReadOnlySpan x, ReadOnlySpan y, Span destinat /// public static void Max(ReadOnlySpan x, T y, Span destination) where T : INumber => - InvokeSpanScalarIntoSpan>(x, y, destination); + InvokeSpanScalarIntoSpan>(x, y, destination); - /// T.Max(x, y) (but NaNs may not be propagated) - internal readonly struct MaxOperator : IAggregationOperator where T : INumber + /// Max(x, y) + internal readonly struct MaxOperator : IAggregationOperator + where T : INumber { public static bool Vectorizable => true; - public static T Invoke(T x, T y) - { - if (typeof(T) == typeof(Half) || typeof(T) == typeof(float) || typeof(T) == typeof(double)) - { - return x == y ? - (IsNegative(x) ? y : x) : - (y > x ? y : x); - } - - return T.Max(x, y); - } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static T Invoke(T x, T y) => T.Max(x, y); [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector128 Invoke(Vector128 x, Vector128 y) { - if (typeof(T) == typeof(float) || typeof(T) == typeof(double)) +#if !NET9_0_OR_GREATER + if ((typeof(T) == typeof(float)) || (typeof(T) == typeof(double))) { - if (AdvSimd.IsSupported && typeof(T) == typeof(float)) - { - return AdvSimd.Max(x.AsSingle(), y.AsSingle()).As(); - } - - if (AdvSimd.Arm64.IsSupported && typeof(T) == typeof(double)) - { - return AdvSimd.Arm64.Max(x.AsDouble(), y.AsDouble()).As(); - } - - return - Vector128.ConditionalSelect(Vector128.Equals(x, y), - Vector128.ConditionalSelect(IsNegative(x), y, x), - Vector128.Max(x, y)); + return Vector128.ConditionalSelect( + (Vector128.Equals(x, y) & IsNegative(y)) | IsNaN(x) | Vector128.LessThan(y, x), + x, + y + ); } +#endif return Vector128.Max(x, y); } @@ -120,13 +106,16 @@ public static Vector128 Invoke(Vector128 x, Vector128 y) [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector256 Invoke(Vector256 x, Vector256 y) { - if (typeof(T) == typeof(float) || typeof(T) == typeof(double)) +#if !NET9_0_OR_GREATER + if ((typeof(T) == typeof(float)) || (typeof(T) == typeof(double))) { - return - Vector256.ConditionalSelect(Vector256.Equals(x, y), - Vector256.ConditionalSelect(IsNegative(x), y, x), - Vector256.Max(x, y)); + return Vector256.ConditionalSelect( + (Vector256.Equals(x, y) & IsNegative(y)) | ~Vector256.Equals(x, x) | Vector256.LessThan(y, x), + x, + y + ); } +#endif return Vector256.Max(x, y); } @@ -134,13 +123,16 @@ public static Vector256 Invoke(Vector256 x, Vector256 y) [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector512 Invoke(Vector512 x, Vector512 y) { - if (typeof(T) == typeof(float) || typeof(T) == typeof(double)) +#if !NET9_0_OR_GREATER + if ((typeof(T) == typeof(float)) || (typeof(T) == typeof(double))) { - return - Vector512.ConditionalSelect(Vector512.Equals(x, y), - Vector512.ConditionalSelect(IsNegative(x), y, x), - Vector512.Max(x, y)); + return Vector512.ConditionalSelect( + (Vector512.Equals(x, y) & IsNegative(y)) | ~Vector512.Equals(x, x) | Vector512.LessThan(y, x), + x, + y + ); } +#endif return Vector512.Max(x, y); } @@ -150,84 +142,65 @@ public static Vector512 Invoke(Vector512 x, Vector512 y) public static T Invoke(Vector512 x) => HorizontalAggregate>(x); } - /// Max(x, y) - internal readonly struct MaxPropagateNaNOperator : IBinaryOperator - where T : INumber + /// Gets whether each specified is NaN. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static Vector128 IsNaN(Vector128 vector) { - public static bool Vectorizable => true; - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static T Invoke(T x, T y) => T.Max(x, y); - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Vector128 Invoke(Vector128 x, Vector128 y) +#if NET9_0_OR_GREATER + return Vector128.IsNaN(vector); +#else + if ((typeof(T) == typeof(float)) || (typeof(T) == typeof(double))) { - if (typeof(T) == typeof(float) || typeof(T) == typeof(double)) - { - if (AdvSimd.IsSupported && typeof(T) == typeof(float)) - { - return AdvSimd.Max(x.AsSingle(), y.AsSingle()).As(); - } - - if (AdvSimd.Arm64.IsSupported && typeof(T) == typeof(double)) - { - return AdvSimd.Arm64.Max(x.AsDouble(), y.AsDouble()).As(); - } - - return - Vector128.ConditionalSelect(Vector128.Equals(x, x), - Vector128.ConditionalSelect(Vector128.Equals(y, y), - Vector128.ConditionalSelect(Vector128.Equals(x, y), - Vector128.ConditionalSelect(IsNegative(x), y, x), - Vector128.Max(x, y)), - y), - x); - } - - return Vector128.Max(x, y); + return ~Vector128.Equals(vector, vector); } + return Vector128.Zero; +#endif + } - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Vector256 Invoke(Vector256 x, Vector256 y) + /// Gets whether each specified is NaN. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static Vector256 IsNaN(Vector256 vector) + { +#if NET9_0_OR_GREATER + return Vector256.IsNaN(vector); +#else + if ((typeof(T) == typeof(float)) || (typeof(T) == typeof(double))) { - if (typeof(T) == typeof(float) || typeof(T) == typeof(double)) - { - return - Vector256.ConditionalSelect(Vector256.Equals(x, x), - Vector256.ConditionalSelect(Vector256.Equals(y, y), - Vector256.ConditionalSelect(Vector256.Equals(x, y), - Vector256.ConditionalSelect(IsNegative(x), y, x), - Vector256.Max(x, y)), - y), - x); - } - - return Vector256.Max(x, y); + return ~Vector256.Equals(vector, vector); } + return Vector256.Zero; +#endif + } - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Vector512 Invoke(Vector512 x, Vector512 y) + /// Gets whether each specified is NaN. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static Vector512 IsNaN(Vector512 vector) + { +#if NET9_0_OR_GREATER + return Vector512.IsNaN(vector); +#else + if ((typeof(T) == typeof(float)) || (typeof(T) == typeof(double))) { - if (typeof(T) == typeof(float) || typeof(T) == typeof(double)) - { - return - Vector512.ConditionalSelect(Vector512.Equals(x, x), - Vector512.ConditionalSelect(Vector512.Equals(y, y), - Vector512.ConditionalSelect(Vector512.Equals(x, y), - Vector512.ConditionalSelect(IsNegative(x), y, x), - Vector512.Max(x, y)), - y), - x); - } - - return Vector512.Max(x, y); + return ~Vector512.Equals(vector, vector); } + return Vector512.Zero; +#endif } +#if !NET9_0_OR_GREATER /// Gets whether each specified is negative. [MethodImpl(MethodImplOptions.AggressiveInlining)] private static Vector128 IsNegative(Vector128 vector) { + if ((typeof(T) == typeof(byte)) + || (typeof(T) == typeof(ushort)) + || (typeof(T) == typeof(uint)) + || (typeof(T) == typeof(ulong)) + || (typeof(T) == typeof(nuint))) + { + return Vector128.Zero; + } + if (typeof(T) == typeof(float)) { return Vector128.LessThan(vector.AsInt32(), Vector128.Zero).As(); @@ -245,6 +218,15 @@ private static Vector128 IsNegative(Vector128 vector) [MethodImpl(MethodImplOptions.AggressiveInlining)] private static Vector256 IsNegative(Vector256 vector) { + if ((typeof(T) == typeof(byte)) + || (typeof(T) == typeof(ushort)) + || (typeof(T) == typeof(uint)) + || (typeof(T) == typeof(ulong)) + || (typeof(T) == typeof(nuint))) + { + return Vector256.Zero; + } + if (typeof(T) == typeof(float)) { return Vector256.LessThan(vector.AsInt32(), Vector256.Zero).As(); @@ -262,6 +244,15 @@ private static Vector256 IsNegative(Vector256 vector) [MethodImpl(MethodImplOptions.AggressiveInlining)] private static Vector512 IsNegative(Vector512 vector) { + if ((typeof(T) == typeof(byte)) + || (typeof(T) == typeof(ushort)) + || (typeof(T) == typeof(uint)) + || (typeof(T) == typeof(ulong)) + || (typeof(T) == typeof(nuint))) + { + return Vector512.Zero; + } + if (typeof(T) == typeof(float)) { return Vector512.LessThan(vector.AsInt32(), Vector512.Zero).As(); @@ -275,6 +266,85 @@ private static Vector512 IsNegative(Vector512 vector) return Vector512.LessThan(vector, Vector512.Zero); } + /// Gets whether each specified is positive. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static Vector128 IsPositive(Vector128 vector) + { + if ((typeof(T) == typeof(byte)) + || (typeof(T) == typeof(ushort)) + || (typeof(T) == typeof(uint)) + || (typeof(T) == typeof(ulong)) + || (typeof(T) == typeof(nuint))) + { + return Vector128.AllBitsSet; + } + + if (typeof(T) == typeof(float)) + { + return Vector128.GreaterThanOrEqual(vector.AsInt32(), Vector128.Zero).As(); + } + + if (typeof(T) == typeof(double)) + { + return Vector128.GreaterThanOrEqual(vector.AsInt64(), Vector128.Zero).As(); + } + + return Vector128.GreaterThanOrEqual(vector, Vector128.Zero); + } + + /// Gets whether each specified is positive. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static Vector256 IsPositive(Vector256 vector) + { + if ((typeof(T) == typeof(byte)) + || (typeof(T) == typeof(ushort)) + || (typeof(T) == typeof(uint)) + || (typeof(T) == typeof(ulong)) + || (typeof(T) == typeof(nuint))) + { + return Vector256.AllBitsSet; + } + + if (typeof(T) == typeof(float)) + { + return Vector256.GreaterThanOrEqual(vector.AsInt32(), Vector256.Zero).As(); + } + + if (typeof(T) == typeof(double)) + { + return Vector256.GreaterThanOrEqual(vector.AsInt64(), Vector256.Zero).As(); + } + + return Vector256.GreaterThanOrEqual(vector, Vector256.Zero); + } + + /// Gets whether each specified is positive. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static Vector512 IsPositive(Vector512 vector) + { + if ((typeof(T) == typeof(byte)) + || (typeof(T) == typeof(ushort)) + || (typeof(T) == typeof(uint)) + || (typeof(T) == typeof(ulong)) + || (typeof(T) == typeof(nuint))) + { + return Vector512.AllBitsSet; + } + + if (typeof(T) == typeof(float)) + { + return Vector512.GreaterThanOrEqual(vector.AsInt32(), Vector512.Zero).As(); + } + + if (typeof(T) == typeof(double)) + { + return Vector512.GreaterThanOrEqual(vector.AsInt64(), Vector512.Zero).As(); + } + + return Vector512.GreaterThanOrEqual(vector, Vector512.Zero); + } +#endif + /// /// This is the same as /// with an identity transform, except it early exits on NaN. @@ -306,7 +376,7 @@ private static T MinMaxCore(ReadOnlySpan x) if (typeof(T) == typeof(float) || typeof(T) == typeof(double)) { // Check for NaNs - nanMask = ~Vector512.Equals(result, result); + nanMask = IsNaN(result); if (nanMask != Vector512.Zero) { return result.GetElement(IndexOfFirstMatch(nanMask)); @@ -437,7 +507,7 @@ private static T MinMaxCore(ReadOnlySpan x) if (typeof(T) == typeof(float) || typeof(T) == typeof(double)) { // Check for NaNs - nanMask = ~Vector128.Equals(result, result); + nanMask = IsNaN(result); if (nanMask != Vector128.Zero) { return result.GetElement(IndexOfFirstMatch(nanMask)); @@ -456,7 +526,7 @@ private static T MinMaxCore(ReadOnlySpan x) if (typeof(T) == typeof(float) || typeof(T) == typeof(double)) { // Check for NaNs - nanMask = ~Vector128.Equals(current, current); + nanMask = IsNaN(current); if (nanMask != Vector128.Zero) { return current.GetElement(IndexOfFirstMatch(nanMask)); @@ -475,7 +545,7 @@ private static T MinMaxCore(ReadOnlySpan x) if (typeof(T) == typeof(float) || typeof(T) == typeof(double)) { // Check for NaNs - nanMask = ~Vector128.Equals(current, current); + nanMask = IsNaN(current); if (nanMask != Vector128.Zero) { return current.GetElement(IndexOfFirstMatch(nanMask)); diff --git a/src/libraries/System.Numerics.Tensors/src/System/Numerics/Tensors/netcore/TensorPrimitives.MaxMagnitude.cs b/src/libraries/System.Numerics.Tensors/src/System/Numerics/Tensors/netcore/TensorPrimitives.MaxMagnitude.cs index eb28249ed1ea4..29a50e32f2ca9 100644 --- a/src/libraries/System.Numerics.Tensors/src/System/Numerics/Tensors/netcore/TensorPrimitives.MaxMagnitude.cs +++ b/src/libraries/System.Numerics.Tensors/src/System/Numerics/Tensors/netcore/TensorPrimitives.MaxMagnitude.cs @@ -44,7 +44,7 @@ public static T MaxMagnitude(ReadOnlySpan x) /// public static void MaxMagnitude(ReadOnlySpan x, ReadOnlySpan y, Span destination) where T : INumberBase => - InvokeSpanSpanIntoSpan>(x, y, destination); + InvokeSpanSpanIntoSpan>(x, y, destination); /// Computes the element-wise number with the largest magnitude in the specified tensors. /// The first tensor, represented as a span. @@ -61,27 +61,9 @@ public static void MaxMagnitude(ReadOnlySpan x, ReadOnlySpan y, Span /// public static void MaxMagnitude(ReadOnlySpan x, T y, Span destination) where T : INumberBase => - InvokeSpanScalarIntoSpan>(x, y, destination); + InvokeSpanScalarIntoSpan>(x, y, destination); - /// Searches for the smallest number in the specified tensor. - /// The tensor, represented as a span. - /// The minimum element in . - /// Length of must be greater than zero. - /// - /// - /// The determination of the minimum element matches the IEEE 754:2019 `minimum` function. If any value is equal to - /// is present, the first is returned. Negative 0 is considered smaller than positive 0. - /// - /// - /// This method may call into the underlying C runtime or employ instructions specific to the current architecture. Exact results may differ between different - /// operating systems or architectures. - /// - /// - public static T Min(ReadOnlySpan x) - where T : INumber => - MinMaxCore>(x); - - /// Operator to get x or y based on which has the larger MathF.Abs (but NaNs may not be propagated) + /// Operator to get x or y based on which has the larger MathF.Abs internal readonly struct MaxMagnitudeOperator : IAggregationOperator where T : INumberBase { @@ -93,151 +75,69 @@ public static T Min(ReadOnlySpan x) [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector128 Invoke(Vector128 x, Vector128 y) { - Vector128 xMag = Vector128.Abs(x), yMag = Vector128.Abs(y); - - Vector128 result = - Vector128.ConditionalSelect(Vector128.Equals(xMag, yMag), - Vector128.ConditionalSelect(IsNegative(x), y, x), - Vector128.ConditionalSelect(Vector128.GreaterThan(xMag, yMag), x, y)); - - // Handle minimum signed value that should have the largest magnitude - if (typeof(T) == typeof(sbyte) || typeof(T) == typeof(short) || typeof(T) == typeof(int) || typeof(T) == typeof(long) || typeof(T) == typeof(nint)) +#if NET9_0_OR_GREATER + return Vector128.MaxMagnitude(x, y); +#else + if ((typeof(T) == typeof(float)) || (typeof(T) == typeof(double))) { - Vector128 negativeMagnitudeX = Vector128.LessThan(xMag, Vector128.Zero); - Vector128 negativeMagnitudeY = Vector128.LessThan(yMag, Vector128.Zero); - result = Vector128.ConditionalSelect(negativeMagnitudeX, + Vector128 xMag = Vector128.Abs(x); + Vector128 yMag = Vector128.Abs(y); + return Vector128.ConditionalSelect( + Vector128.GreaterThan(xMag, yMag) | IsNaN(xMag) | (Vector128.Equals(xMag, yMag) & IsPositive(x)), x, - Vector128.ConditionalSelect(negativeMagnitudeY, - y, - result)); + y + ); } - return result; + return MaxMagnitudeNumberOperator.Invoke(x, y); +#endif } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector256 Invoke(Vector256 x, Vector256 y) { - Vector256 xMag = Vector256.Abs(x), yMag = Vector256.Abs(y); - - Vector256 result = - Vector256.ConditionalSelect(Vector256.Equals(xMag, yMag), - Vector256.ConditionalSelect(IsNegative(x), y, x), - Vector256.ConditionalSelect(Vector256.GreaterThan(xMag, yMag), x, y)); - - // Handle minimum signed value that should have the largest magnitude - if (typeof(T) == typeof(sbyte) || typeof(T) == typeof(short) || typeof(T) == typeof(int) || typeof(T) == typeof(long) || typeof(T) == typeof(nint)) +#if NET9_0_OR_GREATER + return Vector256.MaxMagnitude(x, y); +#else + if ((typeof(T) == typeof(float)) || (typeof(T) == typeof(double))) { - Vector256 negativeMagnitudeX = Vector256.LessThan(xMag, Vector256.Zero); - Vector256 negativeMagnitudeY = Vector256.LessThan(yMag, Vector256.Zero); - result = Vector256.ConditionalSelect(negativeMagnitudeX, + Vector256 xMag = Vector256.Abs(x); + Vector256 yMag = Vector256.Abs(y); + return Vector256.ConditionalSelect( + Vector256.GreaterThan(xMag, yMag) | IsNaN(xMag) | (Vector256.Equals(xMag, yMag) & IsPositive(x)), x, - Vector256.ConditionalSelect(negativeMagnitudeY, - y, - result)); + y + ); } - return result; + return MaxMagnitudeNumberOperator.Invoke(x, y); +#endif } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector512 Invoke(Vector512 x, Vector512 y) { - Vector512 xMag = Vector512.Abs(x), yMag = Vector512.Abs(y); - - Vector512 result = - Vector512.ConditionalSelect(Vector512.Equals(xMag, yMag), - Vector512.ConditionalSelect(IsNegative(x), y, x), - Vector512.ConditionalSelect(Vector512.GreaterThan(xMag, yMag), x, y)); - - // Handle minimum signed value that should have the largest magnitude - if (typeof(T) == typeof(sbyte) || typeof(T) == typeof(short) || typeof(T) == typeof(int) || typeof(T) == typeof(long) || typeof(T) == typeof(nint)) +#if NET9_0_OR_GREATER + return Vector512.MaxMagnitude(x, y); +#else + if ((typeof(T) == typeof(float)) || (typeof(T) == typeof(double))) { - Vector512 negativeMagnitudeX = Vector512.LessThan(xMag, Vector512.Zero); - Vector512 negativeMagnitudeY = Vector512.LessThan(yMag, Vector512.Zero); - result = Vector512.ConditionalSelect(negativeMagnitudeX, + Vector512 xMag = Vector512.Abs(x); + Vector512 yMag = Vector512.Abs(y); + return Vector512.ConditionalSelect( + Vector512.GreaterThan(xMag, yMag) | IsNaN(xMag) | (Vector512.Equals(xMag, yMag) & IsPositive(x)), x, - Vector512.ConditionalSelect(negativeMagnitudeY, - y, - result)); + y + ); } - return result; + return MaxMagnitudeNumberOperator.Invoke(x, y); +#endif } public static T Invoke(Vector128 x) => HorizontalAggregate>(x); public static T Invoke(Vector256 x) => HorizontalAggregate>(x); public static T Invoke(Vector512 x) => HorizontalAggregate>(x); } - - /// Operator to get x or y based on which has the larger MathF.Abs - internal readonly struct MaxMagnitudePropagateNaNOperator : IBinaryOperator - where T : INumberBase - { - public static bool Vectorizable => true; - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static T Invoke(T x, T y) => T.MaxMagnitude(x, y); - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Vector128 Invoke(Vector128 x, Vector128 y) - { - // Handle NaNs - if (typeof(T) == typeof(float) || typeof(T) == typeof(double)) - { - Vector128 xMag = Vector128.Abs(x), yMag = Vector128.Abs(y); - return - Vector128.ConditionalSelect(Vector128.Equals(x, x), - Vector128.ConditionalSelect(Vector128.Equals(y, y), - Vector128.ConditionalSelect(Vector128.Equals(yMag, xMag), - Vector128.ConditionalSelect(IsNegative(x), y, x), - Vector128.ConditionalSelect(Vector128.GreaterThan(yMag, xMag), y, x)), - y), - x); - } - - return MaxMagnitudeOperator.Invoke(x, y); - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Vector256 Invoke(Vector256 x, Vector256 y) - { - // Handle NaNs - if (typeof(T) == typeof(float) || typeof(T) == typeof(double)) - { - Vector256 xMag = Vector256.Abs(x), yMag = Vector256.Abs(y); - return - Vector256.ConditionalSelect(Vector256.Equals(x, x), - Vector256.ConditionalSelect(Vector256.Equals(y, y), - Vector256.ConditionalSelect(Vector256.Equals(xMag, yMag), - Vector256.ConditionalSelect(IsNegative(x), y, x), - Vector256.ConditionalSelect(Vector256.GreaterThan(xMag, yMag), x, y)), - y), - x); - } - - return MaxMagnitudeOperator.Invoke(x, y); - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Vector512 Invoke(Vector512 x, Vector512 y) - { - // Handle NaNs - if (typeof(T) == typeof(float) || typeof(T) == typeof(double)) - { - Vector512 xMag = Vector512.Abs(x), yMag = Vector512.Abs(y); - return - Vector512.ConditionalSelect(Vector512.Equals(x, x), - Vector512.ConditionalSelect(Vector512.Equals(y, y), - Vector512.ConditionalSelect(Vector512.Equals(xMag, yMag), - Vector512.ConditionalSelect(IsNegative(x), y, x), - Vector512.ConditionalSelect(Vector512.GreaterThan(xMag, yMag), x, y)), - y), - x); - } - - return MaxMagnitudeOperator.Invoke(x, y); - } - } } } diff --git a/src/libraries/System.Numerics.Tensors/src/System/Numerics/Tensors/netcore/TensorPrimitives.MaxMagnitudeNumber.cs b/src/libraries/System.Numerics.Tensors/src/System/Numerics/Tensors/netcore/TensorPrimitives.MaxMagnitudeNumber.cs new file mode 100644 index 0000000000000..0e9c91613ad7e --- /dev/null +++ b/src/libraries/System.Numerics.Tensors/src/System/Numerics/Tensors/netcore/TensorPrimitives.MaxMagnitudeNumber.cs @@ -0,0 +1,152 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Diagnostics; +using System.Runtime.CompilerServices; +using System.Runtime.Intrinsics; + +namespace System.Numerics.Tensors +{ + public static partial class TensorPrimitives + { + /// Operator to get x or y based on which has the larger MathF.Abs + internal readonly struct MaxMagnitudeNumberOperator : IAggregationOperator + where T : INumberBase + { + public static bool Vectorizable => true; + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static T Invoke(T x, T y) => T.MaxMagnitudeNumber(x, y); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector128 Invoke(Vector128 x, Vector128 y) + { +#if NET9_0_OR_GREATER + return Vector128.MaxMagnitudeNumber(x, y); +#else + if ((typeof(T) == typeof(byte)) + || (typeof(T) == typeof(ushort)) + || (typeof(T) == typeof(uint)) + || (typeof(T) == typeof(ulong)) + || (typeof(T) == typeof(nuint))) + { + return Vector128.Max(x, y); + } + + Vector128 xMag = Vector128.Abs(x); + Vector128 yMag = Vector128.Abs(y); + + if ((typeof(T) == typeof(float)) || (typeof(T) == typeof(double)) + ) + { + return Vector128.ConditionalSelect( + Vector128.GreaterThan(xMag, yMag) | IsNaN(yMag) | (Vector128.Equals(xMag, yMag) & IsPositive(x)), + x, + y + ); + } + + Debug.Assert((typeof(T) == typeof(sbyte)) + || (typeof(T) == typeof(short)) + || (typeof(T) == typeof(int)) + || (typeof(T) == typeof(long)) + || (typeof(T) == typeof(nint))); + + return Vector128.ConditionalSelect( + (Vector128.GreaterThan(xMag, yMag) & IsPositive(yMag)) | IsNegative(xMag), + x, + y + ); +#endif + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 Invoke(Vector256 x, Vector256 y) + { +#if NET9_0_OR_GREATER + return Vector256.MaxMagnitudeNumber(x, y); +#else + if ((typeof(T) == typeof(byte)) + || (typeof(T) == typeof(ushort)) + || (typeof(T) == typeof(uint)) + || (typeof(T) == typeof(ulong)) + || (typeof(T) == typeof(nuint))) + { + return Vector256.Max(x, y); + } + + Vector256 xMag = Vector256.Abs(x); + Vector256 yMag = Vector256.Abs(y); + + if ((typeof(T) == typeof(float)) || (typeof(T) == typeof(double)) + ) + { + return Vector256.ConditionalSelect( + Vector256.GreaterThan(xMag, yMag) | IsNaN(yMag) | (Vector256.Equals(xMag, yMag) & IsPositive(x)), + x, + y + ); + } + + Debug.Assert((typeof(T) == typeof(sbyte)) + || (typeof(T) == typeof(short)) + || (typeof(T) == typeof(int)) + || (typeof(T) == typeof(long)) + || (typeof(T) == typeof(nint))); + + return Vector256.ConditionalSelect( + (Vector256.GreaterThan(xMag, yMag) & IsPositive(yMag)) | IsNegative(xMag), + x, + y + ); +#endif + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 Invoke(Vector512 x, Vector512 y) + { +#if NET9_0_OR_GREATER + return Vector512.MaxMagnitudeNumber(x, y); +#else + if ((typeof(T) == typeof(byte)) + || (typeof(T) == typeof(ushort)) + || (typeof(T) == typeof(uint)) + || (typeof(T) == typeof(ulong)) + || (typeof(T) == typeof(nuint))) + { + return Vector512.Max(x, y); + } + + Vector512 xMag = Vector512.Abs(x); + Vector512 yMag = Vector512.Abs(y); + + if ((typeof(T) == typeof(float)) || (typeof(T) == typeof(double)) + ) + { + return Vector512.ConditionalSelect( + Vector512.GreaterThan(xMag, yMag) | IsNaN(yMag) | (Vector512.Equals(xMag, yMag) & IsPositive(x)), + x, + y + ); + } + + Debug.Assert((typeof(T) == typeof(sbyte)) + || (typeof(T) == typeof(short)) + || (typeof(T) == typeof(int)) + || (typeof(T) == typeof(long)) + || (typeof(T) == typeof(nint))); + + return Vector512.ConditionalSelect( + (Vector512.GreaterThan(xMag, yMag) & IsPositive(yMag)) | IsNegative(xMag), + x, + y + ); +#endif + } + + public static T Invoke(Vector128 x) => HorizontalAggregate>(x); + public static T Invoke(Vector256 x) => HorizontalAggregate>(x); + public static T Invoke(Vector512 x) => HorizontalAggregate>(x); + } + } +} diff --git a/src/libraries/System.Numerics.Tensors/src/System/Numerics/Tensors/netcore/TensorPrimitives.MaxNumber.cs b/src/libraries/System.Numerics.Tensors/src/System/Numerics/Tensors/netcore/TensorPrimitives.MaxNumber.cs index dc0019ce66016..52df9ea1f1b5c 100644 --- a/src/libraries/System.Numerics.Tensors/src/System/Numerics/Tensors/netcore/TensorPrimitives.MaxNumber.cs +++ b/src/libraries/System.Numerics.Tensors/src/System/Numerics/Tensors/netcore/TensorPrimitives.MaxNumber.cs @@ -86,93 +86,58 @@ public static void MaxNumber(ReadOnlySpan x, T y, Span destination) [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector128 Invoke(Vector128 x, Vector128 y) { - if (typeof(T) == typeof(float) || typeof(T) == typeof(double)) +#if NET9_0_OR_GREATER + return Vector128.MaxNumber(x, y); +#else + if ((typeof(T) == typeof(float)) || (typeof(T) == typeof(double))) { - // We can't use AdvSimd.MaxNumber here because it doesn't correctly - // handle sNaN (it converts it to qNaN as per the now deprecated - // maxNum function defined by IEEE 754:2008, but which is not inline - // with the maximumNumber function that replaces it in IEEE 754:2019) - - Vector128 max; - - if (Sse.IsSupported && typeof(T) == typeof(float)) - { - max = Sse.Max(x.AsSingle(), y.AsSingle()).As(); - } - else if (Sse2.IsSupported && typeof(T) == typeof(double)) - { - max = Sse2.Max(x.AsDouble(), y.AsDouble()).As(); - } - else - { - max = Vector128.ConditionalSelect(Vector128.LessThan(y, x), x, y); - } - - return - Vector128.ConditionalSelect(Vector128.Equals(x, y), - Vector128.ConditionalSelect(IsNegative(y), x, y), - Vector128.ConditionalSelect(Vector128.Equals(y, y), max, x)); + return Vector128.ConditionalSelect( + (Vector128.Equals(x, y) & IsNegative(y)) | IsNaN(y) | Vector128.LessThan(y, x), + x, + y + ); } return Vector128.Max(x, y); +#endif } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector256 Invoke(Vector256 x, Vector256 y) { - if (typeof(T) == typeof(float) || typeof(T) == typeof(double)) +#if NET9_0_OR_GREATER + return Vector256.MaxNumber(x, y); +#else + if ((typeof(T) == typeof(float)) || (typeof(T) == typeof(double))) { - Vector256 max; - - if (Avx.IsSupported && typeof(T) == typeof(float)) - { - max = Avx.Max(x.AsSingle(), y.AsSingle()).As(); - } - else if (Avx.IsSupported && typeof(T) == typeof(double)) - { - max = Avx.Max(x.AsDouble(), y.AsDouble()).As(); - } - else - { - max = Vector256.ConditionalSelect(Vector256.LessThan(y, x), x, y); - } - - return - Vector256.ConditionalSelect(Vector256.Equals(x, y), - Vector256.ConditionalSelect(IsNegative(y), x, y), - Vector256.ConditionalSelect(Vector256.Equals(y, y), max, x)); + return Vector256.ConditionalSelect( + (Vector256.Equals(x, y) & IsNegative(y)) | IsNaN(y) | Vector256.LessThan(y, x), + x, + y + ); } return Vector256.Max(x, y); +#endif } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector512 Invoke(Vector512 x, Vector512 y) { - if (typeof(T) == typeof(float) || typeof(T) == typeof(double)) +#if NET9_0_OR_GREATER + return Vector512.MaxNumber(x, y); +#else + if ((typeof(T) == typeof(float)) || (typeof(T) == typeof(double))) { - Vector512 max; - - if (Avx512F.IsSupported && typeof(T) == typeof(float)) - { - max = Avx512F.Max(x.AsSingle(), y.AsSingle()).As(); - } - else if (Avx512F.IsSupported && typeof(T) == typeof(double)) - { - max = Avx512F.Max(x.AsDouble(), y.AsDouble()).As(); - } - else - { - max = Vector512.ConditionalSelect(Vector512.LessThan(y, x), x, y); - } - - return - Vector512.ConditionalSelect(Vector512.Equals(x, y), - Vector512.ConditionalSelect(IsNegative(y), x, y), - Vector512.ConditionalSelect(Vector512.Equals(y, y), max, x)); + return Vector512.ConditionalSelect( + (Vector512.Equals(x, y) & IsNegative(y)) | IsNaN(y) | Vector512.LessThan(y, x), + x, + y + ); } return Vector512.Max(x, y); +#endif } public static T Invoke(Vector128 x) => HorizontalAggregate>(x); diff --git a/src/libraries/System.Numerics.Tensors/src/System/Numerics/Tensors/netcore/TensorPrimitives.Min.cs b/src/libraries/System.Numerics.Tensors/src/System/Numerics/Tensors/netcore/TensorPrimitives.Min.cs index fd3931fa3d194..2159d81b3f49f 100644 --- a/src/libraries/System.Numerics.Tensors/src/System/Numerics/Tensors/netcore/TensorPrimitives.Min.cs +++ b/src/libraries/System.Numerics.Tensors/src/System/Numerics/Tensors/netcore/TensorPrimitives.Min.cs @@ -9,6 +9,24 @@ namespace System.Numerics.Tensors { public static partial class TensorPrimitives { + /// Searches for the smallest number in the specified tensor. + /// The tensor, represented as a span. + /// The minimum element in . + /// Length of must be greater than zero. + /// + /// + /// The determination of the minimum element matches the IEEE 754:2019 `minimum` function. If any value is equal to + /// is present, the first is returned. Negative 0 is considered smaller than positive 0. + /// + /// + /// This method may call into the underlying C runtime or employ instructions specific to the current architecture. Exact results may differ between different + /// operating systems or architectures. + /// + /// + public static T Min(ReadOnlySpan x) + where T : INumber => + MinMaxCore>(x); + /// Computes the element-wise minimum of the numbers in the specified tensors. /// The first tensor, represented as a span. /// The second tensor, represented as a span. @@ -32,7 +50,7 @@ public static partial class TensorPrimitives /// public static void Min(ReadOnlySpan x, ReadOnlySpan y, Span destination) where T : INumber => - InvokeSpanSpanIntoSpan>(x, y, destination); + InvokeSpanSpanIntoSpan>(x, y, destination); /// Computes the element-wise minimum of the numbers in the specified tensors. /// The first tensor, represented as a span. @@ -55,47 +73,30 @@ public static void Min(ReadOnlySpan x, ReadOnlySpan y, Span destinat /// public static void Min(ReadOnlySpan x, T y, Span destination) where T : INumber => - InvokeSpanScalarIntoSpan>(x, y, destination); + InvokeSpanScalarIntoSpan>(x, y, destination); - /// T.Min(x, y) (but NaNs may not be propagated) + /// T.Min(x, y) internal readonly struct MinOperator : IAggregationOperator where T : INumber { public static bool Vectorizable => true; [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static T Invoke(T x, T y) - { - if (typeof(T) == typeof(Half) || typeof(T) == typeof(float) || typeof(T) == typeof(double)) - { - return x == y ? - (IsNegative(y) ? y : x) : - (y < x ? y : x); - } - - return T.Min(x, y); - } + public static T Invoke(T x, T y) => T.Min(x, y); [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector128 Invoke(Vector128 x, Vector128 y) { - if (typeof(T) == typeof(float) || typeof(T) == typeof(double)) +#if !NET9_0_OR_GREATER + if ((typeof(T) == typeof(float)) || (typeof(T) == typeof(double))) { - if (AdvSimd.IsSupported && typeof(T) == typeof(float)) - { - return AdvSimd.Min(x.AsSingle(), y.AsSingle()).As(); - } - - if (AdvSimd.Arm64.IsSupported && typeof(T) == typeof(double)) - { - return AdvSimd.Arm64.Min(x.AsDouble(), y.AsDouble()).As(); - } - - return - Vector128.ConditionalSelect(Vector128.Equals(x, y), - Vector128.ConditionalSelect(IsNegative(y), y, x), - Vector128.Min(x, y)); + return Vector128.ConditionalSelect( + (Vector128.Equals(x, y) & Vector128.IsNegative(x)) | Vector128.IsNaN(x) | Vector128.LessThan(x, y), + x, + y + ); } +#endif return Vector128.Min(x, y); } @@ -103,12 +104,16 @@ public static Vector128 Invoke(Vector128 x, Vector128 y) [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector256 Invoke(Vector256 x, Vector256 y) { - if (typeof(T) == typeof(float) || typeof(T) == typeof(double)) +#if !NET9_0_OR_GREATER + if ((typeof(T) == typeof(float)) || (typeof(T) == typeof(double))) { - return Vector256.ConditionalSelect(Vector256.Equals(x, y), - Vector256.ConditionalSelect(IsNegative(y), y, x), - Vector256.Min(x, y)); + return Vector256.ConditionalSelect( + (Vector256.Equals(x, y) & Vector256.IsNegative(x)) | Vector256.IsNaN(x) | Vector256.LessThan(x, y), + x, + y + ); } +#endif return Vector256.Min(x, y); } @@ -116,12 +121,16 @@ public static Vector256 Invoke(Vector256 x, Vector256 y) [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector512 Invoke(Vector512 x, Vector512 y) { - if (typeof(T) == typeof(float) || typeof(T) == typeof(double)) +#if !NET9_0_OR_GREATER + if ((typeof(T) == typeof(float)) || (typeof(T) == typeof(double))) { - return Vector512.ConditionalSelect(Vector512.Equals(x, y), - Vector512.ConditionalSelect(IsNegative(y), y, x), - Vector512.Min(x, y)); + return Vector512.ConditionalSelect( + (Vector512.Equals(x, y) & Vector512.IsNegative(x)) | Vector512.IsNaN(x) | Vector512.LessThan(x, y), + x, + y + ); } +#endif return Vector512.Min(x, y); } @@ -130,79 +139,5 @@ public static Vector512 Invoke(Vector512 x, Vector512 y) public static T Invoke(Vector256 x) => HorizontalAggregate>(x); public static T Invoke(Vector512 x) => HorizontalAggregate>(x); } - - /// T.Min(x, y) - internal readonly struct MinPropagateNaNOperator : IBinaryOperator - where T : INumber - { - public static bool Vectorizable => true; - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static T Invoke(T x, T y) => T.Min(x, y); - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Vector128 Invoke(Vector128 x, Vector128 y) - { - if (typeof(T) == typeof(float) || typeof(T) == typeof(double)) - { - if (AdvSimd.IsSupported && typeof(T) == typeof(float)) - { - return AdvSimd.Min(x.AsSingle(), y.AsSingle()).As(); - } - - if (AdvSimd.Arm64.IsSupported && typeof(T) == typeof(double)) - { - return AdvSimd.Arm64.Min(x.AsDouble(), y.AsDouble()).As(); - } - - return - Vector128.ConditionalSelect(Vector128.Equals(x, x), - Vector128.ConditionalSelect(Vector128.Equals(y, y), - Vector128.ConditionalSelect(Vector128.Equals(x, y), - Vector128.ConditionalSelect(IsNegative(x), x, y), - Vector128.Min(x, y)), - y), - x); - } - - return Vector128.Min(x, y); - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Vector256 Invoke(Vector256 x, Vector256 y) - { - if (typeof(T) == typeof(float) || typeof(T) == typeof(double)) - { - return - Vector256.ConditionalSelect(Vector256.Equals(x, x), - Vector256.ConditionalSelect(Vector256.Equals(y, y), - Vector256.ConditionalSelect(Vector256.Equals(x, y), - Vector256.ConditionalSelect(IsNegative(x), x, y), - Vector256.Min(x, y)), - y), - x); - } - - return Vector256.Min(x, y); - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Vector512 Invoke(Vector512 x, Vector512 y) - { - if (typeof(T) == typeof(float) || typeof(T) == typeof(double)) - { - return - Vector512.ConditionalSelect(Vector512.Equals(x, x), - Vector512.ConditionalSelect(Vector512.Equals(y, y), - Vector512.ConditionalSelect(Vector512.Equals(x, y), - Vector512.ConditionalSelect(IsNegative(x), x, y), - Vector512.Min(x, y)), - y), - x); - } - - return Vector512.Min(x, y); - } - } } } diff --git a/src/libraries/System.Numerics.Tensors/src/System/Numerics/Tensors/netcore/TensorPrimitives.MinMagnitude.cs b/src/libraries/System.Numerics.Tensors/src/System/Numerics/Tensors/netcore/TensorPrimitives.MinMagnitude.cs index 47b492eaffb8a..8b4cae6ee22c2 100644 --- a/src/libraries/System.Numerics.Tensors/src/System/Numerics/Tensors/netcore/TensorPrimitives.MinMagnitude.cs +++ b/src/libraries/System.Numerics.Tensors/src/System/Numerics/Tensors/netcore/TensorPrimitives.MinMagnitude.cs @@ -49,7 +49,7 @@ public static T MinMagnitude(ReadOnlySpan x) /// public static void MinMagnitude(ReadOnlySpan x, ReadOnlySpan y, Span destination) where T : INumberBase => - InvokeSpanSpanIntoSpan>(x, y, destination); + InvokeSpanSpanIntoSpan>(x, y, destination); /// Computes the element-wise number with the smallest magnitude in the specified tensors. /// The first tensor, represented as a span. @@ -71,9 +71,9 @@ public static void MinMagnitude(ReadOnlySpan x, ReadOnlySpan y, Span /// public static void MinMagnitude(ReadOnlySpan x, T y, Span destination) where T : INumberBase => - InvokeSpanScalarIntoSpan>(x, y, destination); + InvokeSpanScalarIntoSpan>(x, y, destination); - /// Operator to get x or y based on which has the smaller MathF.Abs (but NaNs may not be propagated) + /// Operator to get x or y based on which has the smaller MathF.Abs internal readonly struct MinMagnitudeOperator : IAggregationOperator where T : INumberBase { @@ -85,148 +85,72 @@ public static void MinMagnitude(ReadOnlySpan x, T y, Span destination) [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector128 Invoke(Vector128 x, Vector128 y) { - Vector128 xMag = Vector128.Abs(x), yMag = Vector128.Abs(y); - - Vector128 result = - Vector128.ConditionalSelect(Vector128.Equals(yMag, xMag), - Vector128.ConditionalSelect(IsNegative(y), y, x), - Vector128.ConditionalSelect(Vector128.LessThan(yMag, xMag), y, x)); +#if NET9_0_OR_GREATER + return Vector128.MinMagnitude(x, y); +#else - if (typeof(T) == typeof(sbyte) || typeof(T) == typeof(short) || typeof(T) == typeof(int) || typeof(T) == typeof(long) || typeof(T) == typeof(nint)) + if ((typeof(T) == typeof(float)) || (typeof(T) == typeof(double))) { - Vector128 negativeMagnitudeX = Vector128.LessThan(xMag, Vector128.Zero); - Vector128 negativeMagnitudeY = Vector128.LessThan(yMag, Vector128.Zero); - result = Vector128.ConditionalSelect(negativeMagnitudeX, - y, - Vector128.ConditionalSelect(negativeMagnitudeY, - x, - result)); + Vector128 xMag = Vector128.Abs(x); + Vector128 yMag = Vector128.Abs(y); + + return Vector128.ConditionalSelect( + Vector128.LessThan(xMag, yMag) | IsNaN(xMag) | (Vector128.Equals(xMag, yMag) & IsNegative(x)), + x, + y + ); } - - return result; + return MinMagnitudeOperator.Invoke(x, y); +#endif } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector256 Invoke(Vector256 x, Vector256 y) { - Vector256 xMag = Vector256.Abs(x), yMag = Vector256.Abs(y); - - Vector256 result = - Vector256.ConditionalSelect(Vector256.Equals(yMag, xMag), - Vector256.ConditionalSelect(IsNegative(y), y, x), - Vector256.ConditionalSelect(Vector256.LessThan(yMag, xMag), y, x)); +#if NET9_0_OR_GREATER + return Vector256.MinMagnitude(x, y); +#else - if (typeof(T) == typeof(sbyte) || typeof(T) == typeof(short) || typeof(T) == typeof(int) || typeof(T) == typeof(long) || typeof(T) == typeof(nint)) + if ((typeof(T) == typeof(float)) || (typeof(T) == typeof(double))) { - Vector256 negativeMagnitudeX = Vector256.LessThan(xMag, Vector256.Zero); - Vector256 negativeMagnitudeY = Vector256.LessThan(yMag, Vector256.Zero); - result = Vector256.ConditionalSelect(negativeMagnitudeX, - y, - Vector256.ConditionalSelect(negativeMagnitudeY, - x, - result)); + Vector256 xMag = Vector256.Abs(x); + Vector256 yMag = Vector256.Abs(y); + + return Vector256.ConditionalSelect( + Vector256.LessThan(xMag, yMag) | IsNaN(xMag) | (Vector256.Equals(xMag, yMag) & IsNegative(x)), + x, + y + ); } - - return result; + return MinMagnitudeOperator.Invoke(x, y); +#endif } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector512 Invoke(Vector512 x, Vector512 y) { - Vector512 xMag = Vector512.Abs(x), yMag = Vector512.Abs(y); - - Vector512 result = - Vector512.ConditionalSelect(Vector512.Equals(yMag, xMag), - Vector512.ConditionalSelect(IsNegative(y), y, x), - Vector512.ConditionalSelect(Vector512.LessThan(yMag, xMag), y, x)); +#if NET9_0_OR_GREATER + return Vector512.MinMagnitude(x, y); +#else - if (typeof(T) == typeof(sbyte) || typeof(T) == typeof(short) || typeof(T) == typeof(int) || typeof(T) == typeof(long) || typeof(T) == typeof(nint)) + if ((typeof(T) == typeof(float)) || (typeof(T) == typeof(double))) { - Vector512 negativeMagnitudeX = Vector512.LessThan(xMag, Vector512.Zero); - Vector512 negativeMagnitudeY = Vector512.LessThan(yMag, Vector512.Zero); - result = Vector512.ConditionalSelect(negativeMagnitudeX, - y, - Vector512.ConditionalSelect(negativeMagnitudeY, - x, - result)); + Vector512 xMag = Vector512.Abs(x); + Vector512 yMag = Vector512.Abs(y); + + return Vector512.ConditionalSelect( + Vector512.LessThan(xMag, yMag) | IsNaN(xMag) | (Vector512.Equals(xMag, yMag) & IsNegative(x)), + x, + y + ); } - - return result; + return MinMagnitudeOperator.Invoke(x, y); +#endif } public static T Invoke(Vector128 x) => HorizontalAggregate>(x); public static T Invoke(Vector256 x) => HorizontalAggregate>(x); public static T Invoke(Vector512 x) => HorizontalAggregate>(x); } - - /// Operator to get x or y based on which has the smaller MathF.Abs - internal readonly struct MinMagnitudePropagateNaNOperator : IBinaryOperator - where T : INumberBase - { - public static bool Vectorizable => true; - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static T Invoke(T x, T y) => T.MinMagnitude(x, y); - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Vector128 Invoke(Vector128 x, Vector128 y) - { - // Handle NaNs - if (typeof(T) == typeof(float) || typeof(T) == typeof(double)) - { - Vector128 xMag = Vector128.Abs(x), yMag = Vector128.Abs(y); - return - Vector128.ConditionalSelect(Vector128.Equals(x, x), - Vector128.ConditionalSelect(Vector128.Equals(y, y), - Vector128.ConditionalSelect(Vector128.Equals(yMag, xMag), - Vector128.ConditionalSelect(IsNegative(x), x, y), - Vector128.ConditionalSelect(Vector128.LessThan(xMag, yMag), x, y)), - y), - x); - } - - return MinMagnitudeOperator.Invoke(x, y); - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Vector256 Invoke(Vector256 x, Vector256 y) - { - // Handle NaNs - if (typeof(T) == typeof(float) || typeof(T) == typeof(double)) - { - Vector256 xMag = Vector256.Abs(x), yMag = Vector256.Abs(y); - return - Vector256.ConditionalSelect(Vector256.Equals(x, x), - Vector256.ConditionalSelect(Vector256.Equals(y, y), - Vector256.ConditionalSelect(Vector256.Equals(yMag, xMag), - Vector256.ConditionalSelect(IsNegative(x), x, y), - Vector256.ConditionalSelect(Vector256.LessThan(xMag, yMag), x, y)), - y), - x); - } - - return MinMagnitudeOperator.Invoke(x, y); - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Vector512 Invoke(Vector512 x, Vector512 y) - { - // Handle NaNs - if (typeof(T) == typeof(float) || typeof(T) == typeof(double)) - { - Vector512 xMag = Vector512.Abs(x), yMag = Vector512.Abs(y); - return - Vector512.ConditionalSelect(Vector512.Equals(x, x), - Vector512.ConditionalSelect(Vector512.Equals(y, y), - Vector512.ConditionalSelect(Vector512.Equals(yMag, xMag), - Vector512.ConditionalSelect(IsNegative(x), x, y), - Vector512.ConditionalSelect(Vector512.LessThan(xMag, yMag), x, y)), - y), - x); - } - - return MinMagnitudeOperator.Invoke(x, y); - } - } } } diff --git a/src/libraries/System.Numerics.Tensors/src/System/Numerics/Tensors/netcore/TensorPrimitives.MinMagnitudeNumber.cs b/src/libraries/System.Numerics.Tensors/src/System/Numerics/Tensors/netcore/TensorPrimitives.MinMagnitudeNumber.cs new file mode 100644 index 0000000000000..b9ab89b52743c --- /dev/null +++ b/src/libraries/System.Numerics.Tensors/src/System/Numerics/Tensors/netcore/TensorPrimitives.MinMagnitudeNumber.cs @@ -0,0 +1,151 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Diagnostics; +using System.Runtime.CompilerServices; +using System.Runtime.Intrinsics; + +namespace System.Numerics.Tensors +{ + public static partial class TensorPrimitives + { + /// Operator to get x or y based on which has the smaller MathF.Abs + internal readonly struct MinMagnitudeNumberOperator : IAggregationOperator + where T : INumberBase + { + public static bool Vectorizable => true; + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static T Invoke(T x, T y) => T.MinMagnitudeNumber(x, y); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector128 Invoke(Vector128 x, Vector128 y) + { +#if NET9_0_OR_GREATER + return Vector128.MinMagnitudeNumber(x, y); +#else + if ((typeof(T) == typeof(byte)) + || (typeof(T) == typeof(ushort)) + || (typeof(T) == typeof(uint)) + || (typeof(T) == typeof(ulong)) + || (typeof(T) == typeof(nuint))) + { + return Vector128.Min(x, y); + } + + Vector128 xMag = Vector128.Abs(x); + Vector128 yMag = Vector128.Abs(y); + + if ((typeof(T) == typeof(float)) || (typeof(T) == typeof(double))) + { + return Vector128.ConditionalSelect( + Vector128.LessThan(xMag, yMag) | IsNaN(yMag) | (Vector128.Equals(xMag, yMag) & IsNegative(x)), + x, + y + ); + } + + Debug.Assert((typeof(T) == typeof(sbyte)) + || (typeof(T) == typeof(short)) + || (typeof(T) == typeof(int)) + || (typeof(T) == typeof(long)) + || (typeof(T) == typeof(nint))); + + return Vector128.ConditionalSelect( + (Vector128.LessThan(xMag, yMag) & IsPositive(xMag)) | IsNegative(yMag), + x, + y + ); +#endif + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector256 Invoke(Vector256 x, Vector256 y) + { +#if NET9_0_OR_GREATER + return Vector256.MinMagnitudeNumber(x, y); +#else + if ((typeof(T) == typeof(byte)) + || (typeof(T) == typeof(ushort)) + || (typeof(T) == typeof(uint)) + || (typeof(T) == typeof(ulong)) + || (typeof(T) == typeof(nuint))) + { + return Vector256.ConditionalSelect(Vector256.LessThan(y, x), x, y); + } + + Vector256 xMag = Vector256.Abs(x); + Vector256 yMag = Vector256.Abs(y); + + if ((typeof(T) == typeof(float)) || (typeof(T) == typeof(double)) + ) + { + return Vector256.ConditionalSelect( + Vector256.GreaterThan(xMag, yMag) | IsNaN(yMag) | (Vector256.Equals(xMag, yMag) & IsPositive(x)), + x, + y + ); + } + + Debug.Assert((typeof(T) == typeof(sbyte)) + || (typeof(T) == typeof(short)) + || (typeof(T) == typeof(int)) + || (typeof(T) == typeof(long)) + || (typeof(T) == typeof(nint))); + + return Vector256.ConditionalSelect( + (Vector256.GreaterThan(xMag, yMag) & IsPositive(yMag)) | IsNegative(xMag), + x, + y + ); +#endif + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Vector512 Invoke(Vector512 x, Vector512 y) + { +#if NET9_0_OR_GREATER + return Vector512.MinMagnitudeNumber(x, y); +#else + if ((typeof(T) == typeof(byte)) + || (typeof(T) == typeof(ushort)) + || (typeof(T) == typeof(uint)) + || (typeof(T) == typeof(ulong)) + || (typeof(T) == typeof(nuint))) + { + return Vector512.ConditionalSelect(Vector512.LessThan(y, x), x, y); + } + + Vector512 xMag = Vector512.Abs(x); + Vector512 yMag = Vector512.Abs(y); + + if ((typeof(T) == typeof(float)) || (typeof(T) == typeof(double)) + ) + { + return Vector512.ConditionalSelect( + Vector512.GreaterThan(xMag, yMag) | IsNaN(yMag) | (Vector512.Equals(xMag, yMag) & IsPositive(x)), + x, + y + ); + } + + Debug.Assert((typeof(T) == typeof(sbyte)) + || (typeof(T) == typeof(short)) + || (typeof(T) == typeof(int)) + || (typeof(T) == typeof(long)) + || (typeof(T) == typeof(nint))); + + return Vector512.ConditionalSelect( + (Vector512.GreaterThan(xMag, yMag) & IsPositive(yMag)) | IsNegative(xMag), + x, + y + ); +#endif + } + + public static T Invoke(Vector128 x) => HorizontalAggregate>(x); + public static T Invoke(Vector256 x) => HorizontalAggregate>(x); + public static T Invoke(Vector512 x) => HorizontalAggregate>(x); + } + } +} diff --git a/src/libraries/System.Numerics.Tensors/src/System/Numerics/Tensors/netcore/TensorPrimitives.MinNumber.cs b/src/libraries/System.Numerics.Tensors/src/System/Numerics/Tensors/netcore/TensorPrimitives.MinNumber.cs index 79e04bf6d747d..653e8c7383eae 100644 --- a/src/libraries/System.Numerics.Tensors/src/System/Numerics/Tensors/netcore/TensorPrimitives.MinNumber.cs +++ b/src/libraries/System.Numerics.Tensors/src/System/Numerics/Tensors/netcore/TensorPrimitives.MinNumber.cs @@ -86,93 +86,58 @@ public static void MinNumber(ReadOnlySpan x, T y, Span destination) [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector128 Invoke(Vector128 x, Vector128 y) { - if (typeof(T) == typeof(float) || typeof(T) == typeof(double)) +#if NET9_0_OR_GREATER + return Vector128.MinNumber(x, y); +#else + if ((typeof(T) == typeof(float)) || (typeof(T) == typeof(double))) { - // We can't use AdvSimd.MinNumber here because it doesn't correctly - // handle sNaN (it converts it to qNaN as per the now deprecated - // minNum function defined by IEEE 754:2008, but which is not inline - // with the minimumNumber function that replaces it in IEEE 754:2019) - - Vector128 min; - - if (Sse.IsSupported && typeof(T) == typeof(float)) - { - min = Sse.Min(x.AsSingle(), y.AsSingle()).As(); - } - else if (Sse2.IsSupported && typeof(T) == typeof(double)) - { - min = Sse2.Min(x.AsDouble(), y.AsDouble()).As(); - } - else - { - min = Vector128.ConditionalSelect(Vector128.LessThan(x, y), x, y); - } - - return - Vector128.ConditionalSelect(Vector128.Equals(x, y), - Vector128.ConditionalSelect(IsNegative(x), x, y), - Vector128.ConditionalSelect(Vector128.Equals(y, y), min, x)); + return Vector128.ConditionalSelect( + (Vector128.Equals(x, y) & IsNegative(x)) | IsNaN(y) | Vector128.LessThan(x, y), + x, + y + ); } return Vector128.Min(x, y); +#endif } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector256 Invoke(Vector256 x, Vector256 y) { - if (typeof(T) == typeof(float) || typeof(T) == typeof(double)) +#if NET9_0_OR_GREATER + return Vector256.MinNumber(x, y); +#else + if ((typeof(T) == typeof(float)) || (typeof(T) == typeof(double))) { - Vector256 min; - - if (Avx.IsSupported && typeof(T) == typeof(float)) - { - min = Avx.Min(x.AsSingle(), y.AsSingle()).As(); - } - else if (Avx.IsSupported && typeof(T) == typeof(double)) - { - min = Avx.Min(x.AsDouble(), y.AsDouble()).As(); - } - else - { - min = Vector256.ConditionalSelect(Vector256.LessThan(x, y), x, y); - } - - return - Vector256.ConditionalSelect(Vector256.Equals(x, y), - Vector256.ConditionalSelect(IsNegative(x), x, y), - Vector256.ConditionalSelect(Vector256.Equals(y, y), min, x)); + return Vector256.ConditionalSelect( + (Vector256.Equals(x, y) & IsNegative(x)) | IsNaN(y) | Vector256.LessThan(x, y), + x, + y + ); } return Vector256.Min(x, y); +#endif } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Vector512 Invoke(Vector512 x, Vector512 y) { - if (typeof(T) == typeof(float) || typeof(T) == typeof(double)) +#if NET9_0_OR_GREATER + return Vector512.MinNumber(x, y); +#else + if ((typeof(T) == typeof(float)) || (typeof(T) == typeof(double))) { - Vector512 min; - - if (Avx512F.IsSupported && typeof(T) == typeof(float)) - { - min = Avx512F.Min(x.AsSingle(), y.AsSingle()).As(); - } - else if (Avx512F.IsSupported && typeof(T) == typeof(double)) - { - min = Avx512F.Min(x.AsDouble(), y.AsDouble()).As(); - } - else - { - min = Vector512.ConditionalSelect(Vector512.LessThan(x, y), x, y); - } - - return - Vector512.ConditionalSelect(Vector512.Equals(x, y), - Vector512.ConditionalSelect(IsNegative(x), x, y), - Vector512.ConditionalSelect(Vector512.Equals(y, y), min, x)); + return Vector512.ConditionalSelect( + (Vector512.Equals(x, y) & IsNegative(x)) | IsNaN(y) | Vector512.LessThan(x, y), + x, + y + ); } return Vector512.Min(x, y); +#endif } public static T Invoke(Vector128 x) => HorizontalAggregate>(x); diff --git a/src/libraries/System.Numerics.Tensors/src/System/Numerics/Tensors/netcore/TensorPrimitives.RadiansToDegrees.cs b/src/libraries/System.Numerics.Tensors/src/System/Numerics/Tensors/netcore/TensorPrimitives.RadiansToDegrees.cs index 53298f5cf3c0e..077934dd1099b 100644 --- a/src/libraries/System.Numerics.Tensors/src/System/Numerics/Tensors/netcore/TensorPrimitives.RadiansToDegrees.cs +++ b/src/libraries/System.Numerics.Tensors/src/System/Numerics/Tensors/netcore/TensorPrimitives.RadiansToDegrees.cs @@ -1,6 +1,7 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. +using System.Diagnostics; using System.Runtime.Intrinsics; namespace System.Numerics.Tensors @@ -25,10 +26,59 @@ public static void RadiansToDegrees(ReadOnlySpan x, Span destination) private readonly struct RadiansToDegreesOperator : IUnaryOperator where T : ITrigonometricFunctions { public static bool Vectorizable => true; + public static T Invoke(T x) => T.RadiansToDegrees(x); - public static Vector128 Invoke(Vector128 x) => (x * T.CreateChecked(180)) / T.Pi; - public static Vector256 Invoke(Vector256 x) => (x * T.CreateChecked(180)) / T.Pi; - public static Vector512 Invoke(Vector512 x) => (x * T.CreateChecked(180)) / T.Pi; + + public static Vector128 Invoke(Vector128 x) + { +#if NET9_0_OR_GREATER + if (typeof(T) == typeof(double)) + { + return Vector128.RadiansToDegrees(x.AsDouble()).As(); + } + else + { + Debug.Assert(typeof(T) == typeof(float)); + return Vector128.RadiansToDegrees(x.AsSingle()).As(); + } +#else + return (x * T.CreateChecked(180)) / T.Pi; +#endif + } + + public static Vector256 Invoke(Vector256 x) + { +#if NET9_0_OR_GREATER + if (typeof(T) == typeof(double)) + { + return Vector256.RadiansToDegrees(x.AsDouble()).As(); + } + else + { + Debug.Assert(typeof(T) == typeof(float)); + return Vector256.RadiansToDegrees(x.AsSingle()).As(); + } +#else + return (x * T.CreateChecked(180)) / T.Pi; +#endif + } + + public static Vector512 Invoke(Vector512 x) + { +#if NET9_0_OR_GREATER + if (typeof(T) == typeof(double)) + { + return Vector512.RadiansToDegrees(x.AsDouble()).As(); + } + else + { + Debug.Assert(typeof(T) == typeof(float)); + return Vector512.RadiansToDegrees(x.AsSingle()).As(); + } +#else + return (x * T.CreateChecked(180)) / T.Pi; +#endif + } } } } diff --git a/src/libraries/System.Numerics.Tensors/src/System/Numerics/Tensors/netcore/TensorPrimitives.Round.cs b/src/libraries/System.Numerics.Tensors/src/System/Numerics/Tensors/netcore/TensorPrimitives.Round.cs index 83cd73d184435..439b5dbfdcfb1 100644 --- a/src/libraries/System.Numerics.Tensors/src/System/Numerics/Tensors/netcore/TensorPrimitives.Round.cs +++ b/src/libraries/System.Numerics.Tensors/src/System/Numerics/Tensors/netcore/TensorPrimitives.Round.cs @@ -176,23 +176,59 @@ public static void Round(ReadOnlySpan x, int digits, MidpointRounding mode public static Vector128 Invoke(Vector128 x) { +#if NET9_0_OR_GREATER + if (typeof(T) == typeof(double)) + { + return Vector128.Round(x.AsDouble()).As(); + } + else + { + Debug.Assert(typeof(T) == typeof(float)); + return Vector128.Round(x.AsSingle()).As(); + } +#else Vector128 boundary = Vector128.Create(typeof(T) == typeof(float) ? T.CreateTruncating(SingleBoundary) : T.CreateTruncating(DoubleBoundary)); Vector128 temp = CopySignOperator.Invoke(boundary, x); return Vector128.ConditionalSelect(Vector128.GreaterThan(Vector128.Abs(x), boundary), x, CopySignOperator.Invoke((x + temp) - temp, x)); +#endif } public static Vector256 Invoke(Vector256 x) { +#if NET9_0_OR_GREATER + if (typeof(T) == typeof(double)) + { + return Vector256.Round(x.AsDouble()).As(); + } + else + { + Debug.Assert(typeof(T) == typeof(float)); + return Vector256.Round(x.AsSingle()).As(); + } +#else Vector256 boundary = Vector256.Create(typeof(T) == typeof(float) ? T.CreateTruncating(SingleBoundary) : T.CreateTruncating(DoubleBoundary)); Vector256 temp = CopySignOperator.Invoke(boundary, x); return Vector256.ConditionalSelect(Vector256.GreaterThan(Vector256.Abs(x), boundary), x, CopySignOperator.Invoke((x + temp) - temp, x)); +#endif } public static Vector512 Invoke(Vector512 x) { +#if NET9_0_OR_GREATER + if (typeof(T) == typeof(double)) + { + return Vector512.Round(x.AsDouble()).As(); + } + else + { + Debug.Assert(typeof(T) == typeof(float)); + return Vector512.Round(x.AsSingle()).As(); + } +#else Vector512 boundary = Vector512.Create(typeof(T) == typeof(float) ? T.CreateTruncating(SingleBoundary) : T.CreateTruncating(DoubleBoundary)); Vector512 temp = CopySignOperator.Invoke(boundary, x); return Vector512.ConditionalSelect(Vector512.GreaterThan(Vector512.Abs(x), boundary), x, CopySignOperator.Invoke((x + temp) - temp, x)); +#endif } } diff --git a/src/libraries/System.Numerics.Tensors/src/System/Numerics/Tensors/netcore/TensorPrimitives.Single.netcore.cs b/src/libraries/System.Numerics.Tensors/src/System/Numerics/Tensors/netcore/TensorPrimitives.Single.netcore.cs index b1f6309e6dbca..67d09e0ce4a18 100644 --- a/src/libraries/System.Numerics.Tensors/src/System/Numerics/Tensors/netcore/TensorPrimitives.Single.netcore.cs +++ b/src/libraries/System.Numerics.Tensors/src/System/Numerics/Tensors/netcore/TensorPrimitives.Single.netcore.cs @@ -21,13 +21,13 @@ global using LogOperator_Single = System.Numerics.Tensors.TensorPrimitives.LogOperator; global using Log2Operator_Single = System.Numerics.Tensors.TensorPrimitives.Log2Operator; global using MaxOperator_Single = System.Numerics.Tensors.TensorPrimitives.MaxOperator; -global using MaxPropagateNaNOperator_Single = System.Numerics.Tensors.TensorPrimitives.MaxPropagateNaNOperator; +global using MaxPropagateNaNOperator_Single = System.Numerics.Tensors.TensorPrimitives.MaxOperator; global using MaxMagnitudeOperator_Single = System.Numerics.Tensors.TensorPrimitives.MaxMagnitudeOperator; -global using MaxMagnitudePropagateNaNOperator_Single = System.Numerics.Tensors.TensorPrimitives.MaxMagnitudePropagateNaNOperator; +global using MaxMagnitudePropagateNaNOperator_Single = System.Numerics.Tensors.TensorPrimitives.MaxMagnitudeOperator; global using MinOperator_Single = System.Numerics.Tensors.TensorPrimitives.MinOperator; -global using MinPropagateNaNOperator_Single = System.Numerics.Tensors.TensorPrimitives.MinPropagateNaNOperator; +global using MinPropagateNaNOperator_Single = System.Numerics.Tensors.TensorPrimitives.MinOperator; global using MinMagnitudeOperator_Single = System.Numerics.Tensors.TensorPrimitives.MinMagnitudeOperator; -global using MinMagnitudePropagateNaNOperator_Single = System.Numerics.Tensors.TensorPrimitives.MinMagnitudePropagateNaNOperator; +global using MinMagnitudePropagateNaNOperator_Single = System.Numerics.Tensors.TensorPrimitives.MinMagnitudeOperator; global using MultiplyAddOperator_Single = System.Numerics.Tensors.TensorPrimitives.MultiplyAddOperator; global using NegateOperator_Single = System.Numerics.Tensors.TensorPrimitives.NegateOperator; global using IdentityOperator_Single = System.Numerics.Tensors.TensorPrimitives.IdentityOperator; diff --git a/src/libraries/System.Numerics.Tensors/src/System/Numerics/Tensors/netcore/TensorPrimitives.Truncate.cs b/src/libraries/System.Numerics.Tensors/src/System/Numerics/Tensors/netcore/TensorPrimitives.Truncate.cs index 3a2e3cea290af..f50c9b6e20d85 100644 --- a/src/libraries/System.Numerics.Tensors/src/System/Numerics/Tensors/netcore/TensorPrimitives.Truncate.cs +++ b/src/libraries/System.Numerics.Tensors/src/System/Numerics/Tensors/netcore/TensorPrimitives.Truncate.cs @@ -32,6 +32,17 @@ public static void Truncate(ReadOnlySpan x, Span destination) public static Vector128 Invoke(Vector128 x) { +#if NET9_0_OR_GREATER + if (typeof(T) == typeof(double)) + { + return Vector128.Truncate(x.AsDouble()).As(); + } + else + { + Debug.Assert(typeof(T) == typeof(float)); + return Vector128.Truncate(x.AsSingle()).As(); + } +#else if (typeof(T) == typeof(float)) { if (Sse41.IsSupported) return Sse41.RoundToZero(x.AsSingle()).As(); @@ -52,10 +63,22 @@ public static Vector128 Invoke(Vector128 x) Vector128.Floor(x.AsDouble()).As(), Vector128.Ceiling(x.AsDouble()).As()); } +#endif } public static Vector256 Invoke(Vector256 x) { +#if NET9_0_OR_GREATER + if (typeof(T) == typeof(double)) + { + return Vector256.Truncate(x.AsDouble()).As(); + } + else + { + Debug.Assert(typeof(T) == typeof(float)); + return Vector256.Truncate(x.AsSingle()).As(); + } +#else if (typeof(T) == typeof(float)) { if (Avx.IsSupported) return Avx.RoundToZero(x.AsSingle()).As(); @@ -74,10 +97,22 @@ public static Vector256 Invoke(Vector256 x) Vector256.Floor(x.AsDouble()).As(), Vector256.Ceiling(x.AsDouble()).As()); } +#endif } public static Vector512 Invoke(Vector512 x) { +#if NET9_0_OR_GREATER + if (typeof(T) == typeof(double)) + { + return Vector512.Truncate(x.AsDouble()).As(); + } + else + { + Debug.Assert(typeof(T) == typeof(float)); + return Vector512.Truncate(x.AsSingle()).As(); + } +#else if (typeof(T) == typeof(float)) { if (Avx512F.IsSupported) return Avx512F.RoundScale(x.AsSingle(), 0b11).As(); @@ -96,6 +131,7 @@ public static Vector512 Invoke(Vector512 x) Vector512.Floor(x.AsDouble()).As(), Vector512.Ceiling(x.AsDouble()).As()); } +#endif } } } diff --git a/src/libraries/System.Private.CoreLib/src/System/Runtime/Intrinsics/VectorMath.cs b/src/libraries/System.Private.CoreLib/src/System/Runtime/Intrinsics/VectorMath.cs index 21994c9610f5e..4b45e834627bc 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Runtime/Intrinsics/VectorMath.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Runtime/Intrinsics/VectorMath.cs @@ -1156,7 +1156,7 @@ public static TVector MaxMagnitudeNumber(TVector x, TVector y) || (typeof(T) == typeof(ulong)) || (typeof(T) == typeof(nuint))) { - return TVector.ConditionalSelect(TVector.LessThan(y, x), x, y); + return TVector.Max(x, y); } TVector xMag = TVector.Abs(x); @@ -1198,7 +1198,7 @@ public static TVector MaxNumber(TVector x, TVector y) ); } - return TVector.ConditionalSelect(TVector.LessThan(y, x), x, y); + return TVector.Max(x, y); } [MethodImpl(MethodImplOptions.AggressiveInlining)] @@ -1244,7 +1244,7 @@ public static TVector MinMagnitudeNumber(TVector x, TVector y) || (typeof(T) == typeof(ulong)) || (typeof(T) == typeof(nuint))) { - return TVector.ConditionalSelect(TVector.LessThan(x, y), x, y); + return TVector.Min(x, y); } TVector xMag = TVector.Abs(x); @@ -1284,7 +1284,7 @@ public static TVector MinNumber(TVector x, TVector y) y ); } - return TVector.ConditionalSelect(TVector.LessThan(x, y), x, y); + return TVector.Min(x, y); } [MethodImpl(MethodImplOptions.AggressiveInlining)]