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 5ddec7b23519ac..902e4c180f82d1 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 @@ -147,20 +147,8 @@ public static void AddMultiply(ReadOnlySpan x, float y, ReadOnlySpan /// - public static void Cosh(ReadOnlySpan x, Span destination) - { - if (x.Length > destination.Length) - { - ThrowHelper.ThrowArgument_DestinationTooShort(); - } - - ValidateInputOutputSpanNonOverlapping(x, destination); - - for (int i = 0; i < x.Length; i++) - { - destination[i] = MathF.Cosh(x[i]); - } - } + public static void Cosh(ReadOnlySpan x, Span destination) => + InvokeSpanIntoSpan(x, destination); /// Computes the cosine similarity between the two specified non-empty, equal-length tensors of single-precision floating-point numbers. /// The first tensor, represented as a span. @@ -1012,20 +1000,8 @@ public static void Sigmoid(ReadOnlySpan x, Span destination) /// operating systems or architectures. /// /// - public static void Sinh(ReadOnlySpan x, Span destination) - { - if (x.Length > destination.Length) - { - ThrowHelper.ThrowArgument_DestinationTooShort(); - } - - ValidateInputOutputSpanNonOverlapping(x, destination); - - for (int i = 0; i < x.Length; i++) - { - destination[i] = MathF.Sinh(x[i]); - } - } + public static void Sinh(ReadOnlySpan x, Span destination) => + InvokeSpanIntoSpan(x, destination); /// Computes the softmax function over the specified non-empty tensor of single-precision floating-point numbers. /// The tensor, represented as a span. @@ -1177,20 +1153,8 @@ public static float SumOfSquares(ReadOnlySpan x) => /// operating systems or architectures. /// /// - public static void Tanh(ReadOnlySpan x, Span destination) - { - if (x.Length > destination.Length) - { - ThrowHelper.ThrowArgument_DestinationTooShort(); - } - - ValidateInputOutputSpanNonOverlapping(x, destination); - - for (int i = 0; i < x.Length; i++) - { - destination[i] = MathF.Tanh(x[i]); - } - } + public static void Tanh(ReadOnlySpan x, Span destination) => + InvokeSpanIntoSpan(x, destination); /// Throws an exception if the and spans overlap and don't begin at the same memory location. [MethodImpl(MethodImplOptions.AggressiveInlining)] diff --git a/src/libraries/System.Numerics.Tensors/src/System/Numerics/Tensors/TensorPrimitives.netcore.cs b/src/libraries/System.Numerics.Tensors/src/System/Numerics/Tensors/TensorPrimitives.netcore.cs index 75515ed9187c80..5580d6f7ad2fd1 100644 --- a/src/libraries/System.Numerics.Tensors/src/System/Numerics/Tensors/TensorPrimitives.netcore.cs +++ b/src/libraries/System.Numerics.Tensors/src/System/Numerics/Tensors/TensorPrimitives.netcore.cs @@ -2992,6 +2992,183 @@ public static Vector512 Invoke(Vector512 x) #endif } + /// MathF.Sinh(x) + private readonly struct SinhOperator : IUnaryOperator + { + public static float Invoke(float x) => MathF.Sinh(x); + public static Vector128 Invoke(Vector128 x) => (ExpOperator.Invoke(x) - ExpOperator.Invoke(-x)) / Vector128.Create(2f); + public static Vector256 Invoke(Vector256 x) => (ExpOperator.Invoke(x) - ExpOperator.Invoke(-x)) / Vector256.Create(2f); +#if NET8_0_OR_GREATER + public static Vector512 Invoke(Vector512 x) => (ExpOperator.Invoke(x) - ExpOperator.Invoke(-x)) / Vector512.Create(2f); +#endif + } + + /// MathF.Cosh(x) + private readonly struct CoshOperator : IUnaryOperator + { + // This code is based on `vrs4_coshf` from amd/aocl-libm-ose + // Copyright (C) 2008-2022 Advanced Micro Devices, Inc. All rights reserved. + // + // Licensed under the BSD 3-Clause "New" or "Revised" License + // See THIRD-PARTY-NOTICES.TXT for the full license text + + // Spec: + // coshf(|x| > 89.415985107421875) = Infinity + // coshf(Infinity) = infinity + // coshf(-Infinity) = infinity + // + // cosh(x) = (exp(x) + exp(-x))/2 + // cosh(-x) = +cosh(x) + // + // checks for special cases + // if ( asint(x) > infinity) return x with overflow exception and + // return x. + // if x is NaN then raise invalid FP operation exception and return x. + // + // coshf = v/2 * exp(x - log(v)) where v = 0x1.0000e8p-1 + + private const uint SIGN_MASK = 0x7FFFFFFF; + private const uint ARG_MAX = 0x42B2D4FC; + private const uint LOGV = 0x3f317300; + private const uint HALFV = 0x3f800074; + private const uint INVV2 = 0x3e7ffe30; + + public static float Invoke(float x) => MathF.Cosh(x); + + public static Vector128 Invoke(Vector128 x) + { + Vector128 ux = x.AsUInt32() & Vector128.Create(SIGN_MASK); + if (Vector128.GreaterThanAny(ux, Vector128.Create(ARG_MAX))) + { + return Vector128.Create( + MathF.Cosh(x.GetElement(0)), + MathF.Cosh(x.GetElement(1)), + MathF.Cosh(x.GetElement(2)), + MathF.Cosh(x.GetElement(3))); + } + + Vector128 y = ux.AsSingle(); + Vector128 z = ExpOperator.Invoke(y - Vector128.Create(LOGV).AsSingle()); + return Vector128.Create(HALFV).AsSingle() * (z + Vector128.Create(INVV2).AsSingle() * 1f / z); + } + + public static Vector256 Invoke(Vector256 x) + { + Vector256 ux = x.AsUInt32() & Vector256.Create(SIGN_MASK); + if (Vector256.GreaterThanAny(ux, Vector256.Create(ARG_MAX))) + { + return Vector256.Create(Invoke(x.GetLower()), Invoke(x.GetUpper())); + } + + Vector256 y = ux.AsSingle(); + Vector256 z = ExpOperator.Invoke(y - Vector256.Create(LOGV).AsSingle()); + return Vector256.Create(HALFV).AsSingle() * (z + Vector256.Create(INVV2).AsSingle() * 1f / z); + } + +#if NET8_0_OR_GREATER + public static Vector512 Invoke(Vector512 x) + { + Vector512 ux = x.AsUInt32() & Vector512.Create(SIGN_MASK); + if (Vector512.GreaterThanAny(ux, Vector512.Create(ARG_MAX))) + { + return Vector512.Create(Invoke(x.GetLower()), Invoke(x.GetUpper())); + } + + Vector512 y = ux.AsSingle(); + Vector512 z = ExpOperator.Invoke(y - Vector512.Create(LOGV).AsSingle()); + return Vector512.Create(HALFV).AsSingle() * (z + Vector512.Create(INVV2).AsSingle() * 1f / z); + } +#endif + } + + /// MathF.Tanh(x) + private readonly struct TanhOperator : IUnaryOperator + { + // This code is based on `vrs4_tanhf` from amd/aocl-libm-ose + // Copyright (C) 2008-2022 Advanced Micro Devices, Inc. All rights reserved. + // + // Licensed under the BSD 3-Clause "New" or "Revised" License + // See THIRD-PARTY-NOTICES.TXT for the full license text + + // To compute vrs4_tanhf(v_f32x4_t x) + // Let y = |x| + // If 0 <= y < 0x1.154246p3 + // Let z = e^(-2.0 * y) - 1 -(1) + // + // Using (1), tanhf(y) can be calculated as, + // tanhf(y) = -z / (z + 2.0) + // + // For other cases, call scalar tanhf() + // + // If x < 0, then we use the identity + // tanhf(-x) = -tanhf(x) + + private const uint V4_TANHF_ARG_MAX = 0x410AA123; + private const uint V4_TANHF_SIGN_MASK = 0x7FFFFFFF; + + public static float Invoke(float x) => MathF.Tanh(x); + + public static Vector128 Invoke(Vector128 x) + { + Vector128 ux = x.AsUInt32(); + Vector128 sign = ux & Vector128.Create(~V4_TANHF_SIGN_MASK); + + ux &= Vector128.Create(V4_TANHF_SIGN_MASK); + if (Vector128.GreaterThanAny(ux, Vector128.Create(V4_TANHF_ARG_MAX))) + { + return Vector128.Create( + MathF.Tanh(x.GetElement(0)), + MathF.Tanh(x.GetElement(1)), + MathF.Tanh(x.GetElement(2)), + MathF.Tanh(x.GetElement(3))); + } + + Vector128 y = ux.AsSingle(); + Vector128 z = ExpOperator.Invoke(Vector128.Create(-2f) * y) - Vector128.Create(1f); + Vector128 result = sign ^ (-z / (z + Vector128.Create(2f))).AsUInt32(); + + return result.AsSingle(); + } + + public static Vector256 Invoke(Vector256 x) + { + Vector256 ux = x.AsUInt32(); + Vector256 sign = ux & Vector256.Create(~V4_TANHF_SIGN_MASK); + + ux &= Vector256.Create(V4_TANHF_SIGN_MASK); + if (Vector256.GreaterThanAny(ux, Vector256.Create(V4_TANHF_ARG_MAX))) + { + return Vector256.Create(Invoke(x.GetLower()), Invoke(x.GetUpper())); + } + + Vector256 y = ux.AsSingle(); + Vector256 z = ExpOperator.Invoke(Vector256.Create(-2f) * y) - Vector256.Create(1f); + Vector256 result = sign ^ (-z / (z + Vector256.Create(2f))).AsUInt32(); + + return result.AsSingle(); + } + +#if NET8_0_OR_GREATER + public static Vector512 Invoke(Vector512 x) + { + Vector512 ux = x.AsUInt32(); + Vector512 sign = ux & Vector512.Create(~V4_TANHF_SIGN_MASK); + + ux &= Vector512.Create(V4_TANHF_SIGN_MASK); + if (Vector512.GreaterThanAny(ux, Vector512.Create(V4_TANHF_ARG_MAX))) + { + return Vector512.Create(Invoke(x.GetLower()), Invoke(x.GetUpper())); + } + + Vector512 y = ux.AsSingle(); + Vector512 z = ExpOperator.Invoke(Vector512.Create(-2f) * y) - Vector512.Create(1f); + Vector512 result = sign ^ (-z / (z + Vector512.Create(2f))).AsUInt32(); + + return result.AsSingle(); + } +#endif + } + /// MathF.Log(x) private readonly struct LogOperator : IUnaryOperator { diff --git a/src/libraries/System.Numerics.Tensors/src/System/Numerics/Tensors/TensorPrimitives.netstandard.cs b/src/libraries/System.Numerics.Tensors/src/System/Numerics/Tensors/TensorPrimitives.netstandard.cs index 28c5e2ac5d8bd5..15d0a8aaf72928 100644 --- a/src/libraries/System.Numerics.Tensors/src/System/Numerics/Tensors/TensorPrimitives.netstandard.cs +++ b/src/libraries/System.Numerics.Tensors/src/System/Numerics/Tensors/TensorPrimitives.netstandard.cs @@ -1026,6 +1026,7 @@ public Vector Invoke(Vector x, Vector y) public Vector Invoke(Vector x) => Vector.Abs(x); } + /// MathF.Exp(x) private readonly struct ExpOperator : IUnaryOperator { public bool CanVectorize => false; @@ -1035,6 +1036,36 @@ public Vector Invoke(Vector x) => throw new NotImplementedException(); } + /// MathF.Sinh(x) + private readonly struct SinhOperator : IUnaryOperator + { + public bool CanVectorize => false; + public float Invoke(float x) => MathF.Sinh(x); + public Vector Invoke(Vector x) => + // requires ShiftLeft (.NET 7+) + throw new NotImplementedException(); + } + + /// MathF.Cosh(x) + private readonly struct CoshOperator : IUnaryOperator + { + public bool CanVectorize => false; + public float Invoke(float x) => MathF.Cosh(x); + public Vector Invoke(Vector x) => + // requires ShiftLeft (.NET 7+) + throw new NotImplementedException(); + } + + /// MathF.Tanh(x) + private readonly struct TanhOperator : IUnaryOperator + { + public bool CanVectorize => false; + public float Invoke(float x) => MathF.Tanh(x); + public Vector Invoke(Vector x) => + // requires ShiftLeft (.NET 7+) + throw new NotImplementedException(); + } + /// MathF.Log(x) private readonly struct LogOperator : IUnaryOperator { diff --git a/src/libraries/System.Numerics.Tensors/tests/TensorPrimitivesTests.cs b/src/libraries/System.Numerics.Tensors/tests/TensorPrimitivesTests.cs index 1bb23713357364..c1f809e06c9acb 100644 --- a/src/libraries/System.Numerics.Tensors/tests/TensorPrimitivesTests.cs +++ b/src/libraries/System.Numerics.Tensors/tests/TensorPrimitivesTests.cs @@ -6,6 +6,7 @@ using System.Linq; using System.Runtime.InteropServices; using Xunit; +using Xunit.Sdk; #pragma warning disable xUnit1025 // reporting duplicate test cases due to not distinguishing 0.0 from -0.0 @@ -14,8 +15,6 @@ namespace System.Numerics.Tensors.Tests public static partial class TensorPrimitivesTests { #region Test Utilities - private const double Tolerance = 0.0001; - public static IEnumerable TensorLengthsIncluding0 => TensorLengths.Concat(new object[][] { [0] }); @@ -23,6 +22,17 @@ public static partial class TensorPrimitivesTests from length in Enumerable.Range(1, 128) select new object[] { length }; + public static IEnumerable VectorLengthAndIteratedRange(float min, float max, float increment) + { + foreach (int length in new[] { 4, 8, 16 }) + { + for (float f = min; f <= max; f += increment) + { + yield return new object[] { length, f }; + } + } + } + private static readonly Random s_random = new Random(20230828); private static BoundedMemory CreateTensor(int size) => BoundedMemory.Allocate(size); @@ -46,6 +56,16 @@ private static float NextSingle() => // For testing purposes, get a mix of negative and positive values. (float)((s_random.NextDouble() * 2) - 1); + private static void AssertEqual(double expected, double actual, double tolerance = 0.00001f) + { + double diff = Math.Abs(expected - actual); + if (diff > tolerance && + diff > Math.Max(Math.Abs(expected), Math.Abs(actual)) * tolerance) + { + throw new EqualException(expected, actual); + } + } + private static unsafe float MathFMaxMagnitude(float x, float y) { float ax = MathF.Abs(x), ay = MathF.Abs(y); @@ -157,7 +177,7 @@ public static void Abs(int tensorLength) for (int i = 0; i < x.Length; i++) { - Assert.Equal(MathF.Abs(x[i]), destination[i], Tolerance); + AssertEqual(MathF.Abs(x[i]), destination[i]); } } @@ -172,7 +192,7 @@ public static void Abs_InPlace(int tensorLength) for (int i = 0; i < x.Length; i++) { - Assert.Equal(MathF.Abs(xOrig[i]), x[i], Tolerance); + AssertEqual(MathF.Abs(xOrig[i]), x[i]); } } @@ -207,7 +227,7 @@ public static void Add_TwoTensors(int tensorLength) TensorPrimitives.Add(x, y, destination); for (int i = 0; i < tensorLength; i++) { - Assert.Equal(x[i] + y[i], destination[i], Tolerance); + AssertEqual(x[i] + y[i], destination[i]); } float[] xOrig = x.Span.ToArray(); @@ -216,7 +236,7 @@ public static void Add_TwoTensors(int tensorLength) TensorPrimitives.Add(x, x, x); for (int i = 0; i < tensorLength; i++) { - Assert.Equal(xOrig[i] + xOrig[i], x[i], Tolerance); + AssertEqual(xOrig[i] + xOrig[i], x[i]); } } @@ -231,7 +251,7 @@ public static void Add_TwoTensors_InPlace(int tensorLength) for (int i = 0; i < tensorLength; i++) { - Assert.Equal(xOrig[i] + xOrig[i], x[i], Tolerance); + AssertEqual(xOrig[i] + xOrig[i], x[i]); } } @@ -280,7 +300,7 @@ public static void Add_TensorScalar(int tensorLength) for (int i = 0; i < tensorLength; i++) { - Assert.Equal(x[i] + y, destination[i], Tolerance); + AssertEqual(x[i] + y, destination[i]); } } @@ -296,7 +316,7 @@ public static void Add_TensorScalar_InPlace(int tensorLength) for (int i = 0; i < tensorLength; i++) { - Assert.Equal(xOrig[i] + y, x[i], Tolerance); + AssertEqual(xOrig[i] + y, x[i]); } } @@ -334,7 +354,7 @@ public static void AddMultiply_ThreeTensors(int tensorLength) for (int i = 0; i < tensorLength; i++) { - Assert.Equal((x[i] + y[i]) * multiplier[i], destination[i], Tolerance); + AssertEqual((x[i] + y[i]) * multiplier[i], destination[i]); } } @@ -349,7 +369,7 @@ public static void AddMultiply_ThreeTensors_InPlace(int tensorLength) for (int i = 0; i < tensorLength; i++) { - Assert.Equal((xOrig[i] + xOrig[i]) * xOrig[i], x[i], Tolerance); + AssertEqual((xOrig[i] + xOrig[i]) * xOrig[i], x[i]); } } @@ -404,7 +424,7 @@ public static void AddMultiply_TensorTensorScalar(int tensorLength) for (int i = 0; i < tensorLength; i++) { - Assert.Equal((x[i] + y[i]) * multiplier, destination[i], Tolerance); + AssertEqual((x[i] + y[i]) * multiplier, destination[i]); } } @@ -420,7 +440,7 @@ public static void AddMultiply_TensorTensorScalar_InPlace(int tensorLength) for (int i = 0; i < tensorLength; i++) { - Assert.Equal((xOrig[i] + xOrig[i]) * multiplier, x[i], Tolerance); + AssertEqual((xOrig[i] + xOrig[i]) * multiplier, x[i]); } } @@ -472,7 +492,7 @@ public static void AddMultiply_TensorScalarTensor(int tensorLength) for (int i = 0; i < tensorLength; i++) { - Assert.Equal((x[i] + y) * multiplier[i], destination[i], Tolerance); + AssertEqual((x[i] + y) * multiplier[i], destination[i]); } } @@ -488,7 +508,7 @@ public static void AddMultiply_TensorScalarTensor_InPlace(int tensorLength) for (int i = 0; i < tensorLength; i++) { - Assert.Equal((xOrig[i] + y) * xOrig[i], x[i], Tolerance); + AssertEqual((xOrig[i] + y) * xOrig[i], x[i]); } } @@ -540,7 +560,7 @@ public static void Cosh(int tensorLength) for (int i = 0; i < tensorLength; i++) { - Assert.Equal(MathF.Cosh(x[i]), destination[i], Tolerance); + AssertEqual(MathF.Cosh(x[i]), destination[i]); } } @@ -555,7 +575,42 @@ public static void Cosh_InPlace(int tensorLength) for (int i = 0; i < tensorLength; i++) { - Assert.Equal(MathF.Cosh(xOrig[i]), x[i], Tolerance); + AssertEqual(MathF.Cosh(xOrig[i]), x[i]); + } + } + + [Theory] + [MemberData(nameof(TensorLengths))] + [ActiveIssue("https://github.com/dotnet/runtime/issues/92885", TestRuntimes.Mono)] + public static void Cosh_SpecialValues(int tensorLength) + { + using BoundedMemory x = CreateAndFillTensor(tensorLength); + using BoundedMemory destination = CreateTensor(tensorLength); + + RunForEachSpecialValue(() => + { + TensorPrimitives.Cosh(x, destination); + for (int i = 0; i < tensorLength; i++) + { + AssertEqual(MathF.Cosh(x[i]), destination[i]); + } + }, x); + } + + [Theory] + [MemberData(nameof(VectorLengthAndIteratedRange), new object[] { -90f, 90f, 3f })] + public static void Cosh_ValueRange(int vectorLength, float element) + { + float[] x = new float[vectorLength]; + float[] dest = new float[vectorLength]; + + x.AsSpan().Fill(element); + TensorPrimitives.Cosh(x, dest); + + float expected = MathF.Cosh(element); + foreach (float actual in dest) + { + AssertEqual(expected, actual); } } @@ -603,7 +658,7 @@ public static void CosineSimilarity_ThrowsForEmpty() [InlineData(new float[] { 1, 1, 1, 1, 1, 0 }, new float[] { 1, 1, 1, 1, 0, 1 }, 0.80f)] public static void CosineSimilarity_KnownValues(float[] x, float[] y, float expectedResult) { - Assert.Equal(expectedResult, TensorPrimitives.CosineSimilarity(x, y), Tolerance); + AssertEqual(expectedResult, TensorPrimitives.CosineSimilarity(x, y)); } [Theory] @@ -621,7 +676,7 @@ public static void CosineSimilarity(int tensorLength) squareY += y[i] * y[i]; } - Assert.Equal(dot / (Math.Sqrt(squareX) * Math.Sqrt(squareY)), TensorPrimitives.CosineSimilarity(x, y), Tolerance); + AssertEqual(dot / (MathF.Sqrt(squareX) * MathF.Sqrt(squareY)), TensorPrimitives.CosineSimilarity(x, y)); } #endregion @@ -648,11 +703,11 @@ public static void Distance_ThrowsForMismatchedLengths(int tensorLength) [Theory] [InlineData(new float[] { 3, 2 }, new float[] { 4, 1 }, 1.4142f)] [InlineData(new float[] { 0, 4 }, new float[] { 6, 2 }, 6.3245f)] - [InlineData(new float[] { 1, 2, 3 }, new float[] { 4, 5, 6 }, 5.1961f)] + [InlineData(new float[] { 1, 2, 3 }, new float[] { 4, 5, 6 }, 5.19615f)] [InlineData(new float[] { 5, 1, 6, 10 }, new float[] { 7, 2, 8, 4 }, 6.7082f)] public static void Distance_KnownValues(float[] x, float[] y, float expectedResult) { - Assert.Equal(expectedResult, TensorPrimitives.Distance(x, y), Tolerance); + AssertEqual(expectedResult, TensorPrimitives.Distance(x, y)); } [Theory] @@ -668,7 +723,7 @@ public static void Distance(int tensorLength) distance += (x[i] - y[i]) * (x[i] - y[i]); } - Assert.Equal(Math.Sqrt(distance), TensorPrimitives.Distance(x, y), Tolerance); + AssertEqual(MathF.Sqrt(distance), TensorPrimitives.Distance(x, y)); } #endregion @@ -685,7 +740,7 @@ public static void Divide_TwoTensors(int tensorLength) for (int i = 0; i < tensorLength; i++) { - Assert.Equal(x[i] / y[i], destination[i], Tolerance); + AssertEqual(x[i] / y[i], destination[i]); } } @@ -700,7 +755,7 @@ public static void Divide_TwoTensors_InPlace(int tensorLength) for (int i = 0; i < tensorLength; i++) { - Assert.Equal(xOrig[i] / xOrig[i], x[i], Tolerance); + AssertEqual(xOrig[i] / xOrig[i], x[i]); } } @@ -749,7 +804,7 @@ public static void Divide_TensorScalar(int tensorLength) for (int i = 0; i < tensorLength; i++) { - Assert.Equal(x[i] / y, destination[i], Tolerance); + AssertEqual(x[i] / y, destination[i]); } } @@ -765,7 +820,7 @@ public static void Divide_TensorScalar_InPlace(int tensorLength) for (int i = 0; i < tensorLength; i++) { - Assert.Equal(xOrig[i] / y, x[i], Tolerance); + AssertEqual(xOrig[i] / y, x[i]); } } @@ -810,7 +865,7 @@ public static void Dot_ThrowsForMismatchedLengths_x_y(int tensorLength) [InlineData(new float[] { }, new float[] { }, 0)] public static void Dot_KnownValues(float[] x, float[] y, float expectedResult) { - Assert.Equal(expectedResult, TensorPrimitives.Dot(x, y), Tolerance); + AssertEqual(expectedResult, TensorPrimitives.Dot(x, y)); } [Theory] @@ -826,7 +881,7 @@ public static void Dot(int tensorLength) dot += x[i] * y[i]; } - Assert.Equal(dot, TensorPrimitives.Dot(x, y), Tolerance); + AssertEqual(dot, TensorPrimitives.Dot(x, y)); } #endregion @@ -842,7 +897,7 @@ public static void Exp(int tensorLength) for (int i = 0; i < tensorLength; i++) { - Assert.Equal(MathF.Exp(x[i]), destination[i], Tolerance); + AssertEqual(MathF.Exp(x[i]), destination[i]); } } @@ -857,7 +912,7 @@ public static void Exp_InPlace(int tensorLength) for (int i = 0; i < tensorLength; i++) { - Assert.Equal(MathF.Exp(xOrig[i]), x[i], Tolerance); + AssertEqual(MathF.Exp(xOrig[i]), x[i]); } } @@ -874,7 +929,7 @@ public static void Exp_SpecialValues(int tensorLength) TensorPrimitives.Exp(x, destination); for (int i = 0; i < tensorLength; i++) { - Assert.Equal(MathF.Exp(x[i]), destination[i], Tolerance); + AssertEqual(MathF.Exp(x[i]), destination[i]); } }, x); } @@ -1091,7 +1146,7 @@ public static void Log(int tensorLength) for (int i = 0; i < tensorLength; i++) { - Assert.Equal(MathF.Log(x[i]), destination[i], Tolerance); + AssertEqual(MathF.Log(x[i]), destination[i]); } } @@ -1106,7 +1161,7 @@ public static void Log_InPlace(int tensorLength) for (int i = 0; i < tensorLength; i++) { - Assert.Equal(MathF.Log(xOrig[i]), x[i], Tolerance); + AssertEqual(MathF.Log(xOrig[i]), x[i]); } } @@ -1122,7 +1177,7 @@ public static void Log_SpecialValues(int tensorLength) TensorPrimitives.Log(x, destination); for (int i = 0; i < tensorLength; i++) { - Assert.Equal(MathF.Log(x[i]), destination[i], Tolerance); + AssertEqual(MathF.Log(x[i]), destination[i]); } }, x); } @@ -1158,7 +1213,7 @@ public static void Log2(int tensorLength) for (int i = 0; i < tensorLength; i++) { - Assert.Equal(MathF.Log(x[i], 2), destination[i], Tolerance); + AssertEqual(MathF.Log(x[i], 2), destination[i]); } } @@ -1173,7 +1228,7 @@ public static void Log2_InPlace(int tensorLength) for (int i = 0; i < tensorLength; i++) { - Assert.Equal(MathF.Log(xOrig[i], 2), x[i], Tolerance); + AssertEqual(MathF.Log(xOrig[i], 2), x[i]); } } @@ -1189,7 +1244,7 @@ public static void Log2_SpecialValues(int tensorLength) TensorPrimitives.Log2(x, destination); for (int i = 0; i < tensorLength; i++) { - Assert.Equal(MathF.Log(x[i], 2), destination[i], Tolerance); + AssertEqual(MathF.Log(x[i], 2), destination[i]); } }, x); } @@ -1270,7 +1325,7 @@ public static void Max_TwoTensors(int tensorLength) for (int i = 0; i < tensorLength; i++) { - Assert.Equal(MathF.Max(x[i], y[i]), destination[i], Tolerance); + AssertEqual(MathF.Max(x[i], y[i]), destination[i]); } } @@ -1286,7 +1341,7 @@ public static void Max_TwoTensors_InPlace(int tensorLength) for (int i = 0; i < tensorLength; i++) { - Assert.Equal(MathF.Max(xOrig[i], y[i]), x[i], Tolerance); + AssertEqual(MathF.Max(xOrig[i], y[i]), x[i]); } xOrig.AsSpan().CopyTo(x.Span); @@ -1296,7 +1351,7 @@ public static void Max_TwoTensors_InPlace(int tensorLength) for (int i = 0; i < tensorLength; i++) { - Assert.Equal(MathF.Max(x[i], yOrig[i]), y[i], Tolerance); + AssertEqual(MathF.Max(x[i], yOrig[i]), y[i]); } } @@ -1313,13 +1368,13 @@ public static void Max_TwoTensors_SpecialValues(int tensorLength) TensorPrimitives.Max(x, y, destination); for (int i = 0; i < tensorLength; i++) { - Assert.Equal(MathF.Max(x[i], y[i]), destination[i], Tolerance); + AssertEqual(MathF.Max(x[i], y[i]), destination[i]); } TensorPrimitives.Max(y, x, destination); for (int i = 0; i < tensorLength; i++) { - Assert.Equal(MathF.Max(y[i], x[i]), destination[i], Tolerance); + AssertEqual(MathF.Max(y[i], x[i]), destination[i]); } } @@ -1376,7 +1431,7 @@ public static void MaxMagnitude_Tensor(int tensorLength) maxMagnitude = MathFMaxMagnitude(maxMagnitude, i); } - Assert.Equal(maxMagnitude, TensorPrimitives.MaxMagnitude(x), Tolerance); + AssertEqual(maxMagnitude, TensorPrimitives.MaxMagnitude(x)); } [Theory] @@ -1415,7 +1470,7 @@ public static void MaxMagnitude_TwoTensors(int tensorLength) for (int i = 0; i < tensorLength; i++) { - Assert.Equal(MathFMaxMagnitude(x[i], y[i]), destination[i], Tolerance); + AssertEqual(MathFMaxMagnitude(x[i], y[i]), destination[i]); } } @@ -1431,7 +1486,7 @@ public static void MaxMagnitude_TwoTensors_InPlace(int tensorLength) for (int i = 0; i < tensorLength; i++) { - Assert.Equal(MathFMaxMagnitude(xOrig[i], y[i]), x[i], Tolerance); + AssertEqual(MathFMaxMagnitude(xOrig[i], y[i]), x[i]); } xOrig.AsSpan().CopyTo(x.Span); @@ -1441,7 +1496,7 @@ public static void MaxMagnitude_TwoTensors_InPlace(int tensorLength) for (int i = 0; i < tensorLength; i++) { - Assert.Equal(MathFMaxMagnitude(x[i], yOrig[i]), y[i], Tolerance); + AssertEqual(MathFMaxMagnitude(x[i], yOrig[i]), y[i]); } } @@ -1458,13 +1513,13 @@ public static void MaxMagnitude_TwoTensors_SpecialValues(int tensorLength) TensorPrimitives.MaxMagnitude(x, y, destination); for (int i = 0; i < tensorLength; i++) { - Assert.Equal(MathFMaxMagnitude(x[i], y[i]), destination[i], Tolerance); + AssertEqual(MathFMaxMagnitude(x[i], y[i]), destination[i]); } TensorPrimitives.MaxMagnitude(y, x, destination); for (int i = 0; i < tensorLength; i++) { - Assert.Equal(MathFMaxMagnitude(y[i], x[i]), destination[i], Tolerance); + AssertEqual(MathFMaxMagnitude(y[i], x[i]), destination[i]); } } @@ -1559,7 +1614,7 @@ public static void Min_TwoTensors(int tensorLength) for (int i = 0; i < tensorLength; i++) { - Assert.Equal(MathF.Min(x[i], y[i]), destination[i], Tolerance); + AssertEqual(MathF.Min(x[i], y[i]), destination[i]); } } @@ -1575,7 +1630,7 @@ public static void Min_TwoTensors_InPlace(int tensorLength) for (int i = 0; i < tensorLength; i++) { - Assert.Equal(MathF.Min(xOrig[i], y[i]), x[i], Tolerance); + AssertEqual(MathF.Min(xOrig[i], y[i]), x[i]); } xOrig.AsSpan().CopyTo(x.Span); @@ -1585,7 +1640,7 @@ public static void Min_TwoTensors_InPlace(int tensorLength) for (int i = 0; i < tensorLength; i++) { - Assert.Equal(MathF.Min(x[i], yOrig[i]), y[i], Tolerance); + AssertEqual(MathF.Min(x[i], yOrig[i]), y[i]); } } @@ -1602,13 +1657,13 @@ public static void Min_TwoTensors_SpecialValues(int tensorLength) TensorPrimitives.Min(x, y, destination); for (int i = 0; i < tensorLength; i++) { - Assert.Equal(MathF.Min(x[i], y[i]), destination[i], Tolerance); + AssertEqual(MathF.Min(x[i], y[i]), destination[i]); } TensorPrimitives.Min(y, x, destination); for (int i = 0; i < tensorLength; i++) { - Assert.Equal(MathF.Min(y[i], x[i]), destination[i], Tolerance); + AssertEqual(MathF.Min(y[i], x[i]), destination[i]); } } @@ -1665,7 +1720,7 @@ public static void MinMagnitude_Tensor(int tensorLength) minMagnitude = MathFMinMagnitude(minMagnitude, i); } - Assert.Equal(minMagnitude, TensorPrimitives.MinMagnitude(x), Tolerance); + AssertEqual(minMagnitude, TensorPrimitives.MinMagnitude(x)); } [Theory] @@ -1702,7 +1757,7 @@ public static void MinMagnitude_TwoTensors(int tensorLength) for (int i = 0; i < tensorLength; i++) { - Assert.Equal(MathFMinMagnitude(x[i], y[i]), destination[i], Tolerance); + AssertEqual(MathFMinMagnitude(x[i], y[i]), destination[i]); } } @@ -1718,7 +1773,7 @@ public static void MinMagnitude_TwoTensors_InPlace(int tensorLength) for (int i = 0; i < tensorLength; i++) { - Assert.Equal(MathFMinMagnitude(xOrig[i], y[i]), x[i], Tolerance); + AssertEqual(MathFMinMagnitude(xOrig[i], y[i]), x[i]); } xOrig.AsSpan().CopyTo(x.Span); @@ -1728,7 +1783,7 @@ public static void MinMagnitude_TwoTensors_InPlace(int tensorLength) for (int i = 0; i < tensorLength; i++) { - Assert.Equal(MathFMinMagnitude(x[i], yOrig[i]), y[i], Tolerance); + AssertEqual(MathFMinMagnitude(x[i], yOrig[i]), y[i]); } } @@ -1745,13 +1800,13 @@ public static void MinMagnitude_TwoTensors_SpecialValues(int tensorLength) TensorPrimitives.MinMagnitude(x, y, destination); for (int i = 0; i < tensorLength; i++) { - Assert.Equal(MathFMinMagnitude(x[i], y[i]), destination[i], Tolerance); + AssertEqual(MathFMinMagnitude(x[i], y[i]), destination[i]); } TensorPrimitives.MinMagnitude(y, x, destination); for (int i = 0; i < tensorLength; i++) { - Assert.Equal(MathFMinMagnitude(y[i], x[i]), destination[i], Tolerance); + AssertEqual(MathFMinMagnitude(y[i], x[i]), destination[i]); } } @@ -1802,7 +1857,7 @@ public static void Multiply_TwoTensors(int tensorLength) for (int i = 0; i < tensorLength; i++) { - Assert.Equal(x[i] * y[i], destination[i], Tolerance); + AssertEqual(x[i] * y[i], destination[i]); } } @@ -1817,7 +1872,7 @@ public static void Multiply_TwoTensors_InPlace(int tensorLength) for (int i = 0; i < tensorLength; i++) { - Assert.Equal(xOrig[i] * xOrig[i], x[i], Tolerance); + AssertEqual(xOrig[i] * xOrig[i], x[i]); } } @@ -1866,7 +1921,7 @@ public static void Multiply_TensorScalar(int tensorLength) for (int i = 0; i < tensorLength; i++) { - Assert.Equal(x[i] * y, destination[i], Tolerance); + AssertEqual(x[i] * y, destination[i]); } } @@ -1882,7 +1937,7 @@ public static void Multiply_TensorScalar_InPlace(int tensorLength) for (int i = 0; i < tensorLength; i++) { - Assert.Equal(xOrig[i] * y, x[i], Tolerance); + AssertEqual(xOrig[i] * y, x[i]); } } @@ -1920,7 +1975,7 @@ public static void MultiplyAdd_ThreeTensors(int tensorLength) for (int i = 0; i < tensorLength; i++) { - Assert.Equal((x[i] * y[i]) + addend[i], destination[i], Tolerance); + AssertEqual((x[i] * y[i]) + addend[i], destination[i]); } } @@ -1935,7 +1990,7 @@ public static void MultiplyAdd_ThreeTensors_InPlace(int tensorLength) for (int i = 0; i < tensorLength; i++) { - Assert.Equal((xOrig[i] * xOrig[i]) + xOrig[i], x[i], Tolerance); + AssertEqual((xOrig[i] * xOrig[i]) + xOrig[i], x[i]); } } @@ -1990,7 +2045,7 @@ public static void MultiplyAdd_TensorTensorScalar(int tensorLength) for (int i = 0; i < tensorLength; i++) { - Assert.Equal((x[i] * y[i]) + addend, destination[i], Tolerance); + AssertEqual((x[i] * y[i]) + addend, destination[i]); } } @@ -2006,7 +2061,7 @@ public static void MultiplyAdd_TensorTensorScalar_InPlace(int tensorLength) for (int i = 0; i < tensorLength; i++) { - Assert.Equal((xOrig[i] * xOrig[i]) + addend, x[i], Tolerance); + AssertEqual((xOrig[i] * xOrig[i]) + addend, x[i]); } } @@ -2045,7 +2100,7 @@ public static void MultiplyAdd_TensorScalarTensor(int tensorLength) for (int i = 0; i < tensorLength; i++) { - Assert.Equal((x[i] * y) + addend[i], destination[i], Tolerance); + AssertEqual((x[i] * y) + addend[i], destination[i]); } } @@ -2061,7 +2116,7 @@ public static void MultiplyAdd_TensorScalarTensor_InPlace(int tensorLength) for (int i = 0; i < tensorLength; i++) { - Assert.Equal((xOrig[i] * y) + xOrig[i], x[i], Tolerance); + AssertEqual((xOrig[i] * y) + xOrig[i], x[i]); } } @@ -2100,7 +2155,7 @@ public static void Negate(int tensorLength) for (int i = 0; i < tensorLength; i++) { - Assert.Equal(-x[i], destination[i], Tolerance); + AssertEqual(-x[i], destination[i]); } } @@ -2115,7 +2170,7 @@ public static void Negate_InPlace(int tensorLength) for (int i = 0; i < tensorLength; i++) { - Assert.Equal(-xOrig[i], x[i], Tolerance); + AssertEqual(-xOrig[i], x[i]); } } @@ -2147,7 +2202,7 @@ public static void Negate_ThrowsForOverlapppingInputsWithOutputs() [InlineData(new float[] { }, 0f)] public static void Norm_KnownValues(float[] x, float expectedResult) { - Assert.Equal(expectedResult, TensorPrimitives.Norm(x), Tolerance); + AssertEqual(expectedResult, TensorPrimitives.Norm(x)); } [Theory] @@ -2162,7 +2217,7 @@ public static void Norm(int tensorLength) sumOfSquares += x[i] * x[i]; } - Assert.Equal(Math.Sqrt(sumOfSquares), TensorPrimitives.Norm(x), Tolerance); + AssertEqual(MathF.Sqrt(sumOfSquares), TensorPrimitives.Norm(x)); } #endregion @@ -2185,22 +2240,23 @@ public static void Product(int tensorLength) f *= x[i]; } - Assert.Equal(f, TensorPrimitives.Product(x), Tolerance); + AssertEqual(f, TensorPrimitives.Product(x)); } - [Fact] - public static void Product_KnownValues() - { - Assert.Equal(1, TensorPrimitives.Product([1])); - Assert.Equal(-2, TensorPrimitives.Product([1, -2])); - Assert.Equal(-6, TensorPrimitives.Product([1, -2, 3])); - Assert.Equal(24, TensorPrimitives.Product([1, -2, 3, -4])); - Assert.Equal(120, TensorPrimitives.Product([1, -2, 3, -4, 5])); - Assert.Equal(-720, TensorPrimitives.Product([1, -2, 3, -4, 5, -6])); - Assert.Equal(0, TensorPrimitives.Product([1, -2, 3, -4, 5, -6, 0])); - Assert.Equal(0, TensorPrimitives.Product([0, 1, -2, 3, -4, 5, -6])); - Assert.Equal(0, TensorPrimitives.Product([1, -2, 3, 0, -4, 5, -6])); - Assert.Equal(float.NaN, TensorPrimitives.Product([1, -2, 3, float.NaN, -4, 5, -6])); + [Theory] + [InlineData(1, new float[] { 1 })] + [InlineData(-2, new float[] { 1, -2 })] + [InlineData(-6, new float[] { 1, -2, 3 })] + [InlineData(24, new float[] { 1, -2, 3, -4 })] + [InlineData(120, new float[] { 1, -2, 3, -4, 5 })] + [InlineData(-720, new float[] { 1, -2, 3, -4, 5, -6 })] + [InlineData(0, new float[] { 1, -2, 3, -4, 5, -6, 0 })] + [InlineData(0, new float[] { 0, 1, -2, 3, -4, 5, -6 })] + [InlineData(0, new float[] { 1, -2, 3, 0, -4, 5, -6 })] + [InlineData(float.NaN, new float[] { 1, -2, 3, float.NaN, -4, 5, -6 })] + public static void Product_KnownValues(float expected, float[] input) + { + Assert.Equal(expected, TensorPrimitives.Product(input)); } #endregion @@ -2227,20 +2283,22 @@ public static void ProductOfDifferences(int tensorLength) { f *= x[i] - y[i]; } - Assert.Equal(f, TensorPrimitives.ProductOfDifferences(x, y), Tolerance); + AssertEqual(f, TensorPrimitives.ProductOfDifferences(x, y)); } - [Fact] - public static void ProductOfDifferences_KnownValues() + [Theory] + [InlineData(0, new float[] {0 }, new float[] {0})] + [InlineData(0, new float[] {1 }, new float[] {1})] + [InlineData(1, new float[] {1 }, new float[] {0})] + [InlineData(-1, new float[] {0 }, new float[] {1})] + [InlineData(-1, new float[] {1, 2, 3, 4, 5 }, new float[] {2, 3, 4, 5, 6})] + [InlineData(120, new float[] {1, 2, 3, 4, 5 }, new float[] {0, 0, 0, 0, 0})] + [InlineData(-120, new float[] {0, 0, 0, 0, 0 }, new float[] {1, 2, 3, 4, 5})] + [InlineData(float.NaN, new float[] {1, 2, float.NaN, 4, 5 }, new float[] {0, 0, 0, 0, 0})] + public static void ProductOfDifferences_KnownValues(float expected, float[] x, float[] y) { - Assert.Equal(0, TensorPrimitives.ProductOfDifferences([0], [0])); - Assert.Equal(0, TensorPrimitives.ProductOfDifferences([1], [1])); - Assert.Equal(1, TensorPrimitives.ProductOfDifferences([1], [0])); - Assert.Equal(-1, TensorPrimitives.ProductOfDifferences([0], [1])); - Assert.Equal(-1, TensorPrimitives.ProductOfDifferences([1, 2, 3, 4, 5], [2, 3, 4, 5, 6])); - Assert.Equal(120, TensorPrimitives.ProductOfDifferences([1, 2, 3, 4, 5], [0, 0, 0, 0, 0])); - Assert.Equal(-120, TensorPrimitives.ProductOfDifferences([0, 0, 0, 0, 0], [1, 2, 3, 4, 5])); - Assert.Equal(float.NaN, TensorPrimitives.ProductOfDifferences([1, 2, float.NaN, 4, 5], [0, 0, 0, 0, 0])); + Assert.Equal(expected, TensorPrimitives.ProductOfDifferences(x, y)); + } #endregion @@ -2267,20 +2325,21 @@ public static void ProductOfSums(int tensorLength) { f *= x[i] + y[i]; } - Assert.Equal(f, TensorPrimitives.ProductOfSums(x, y), Tolerance); + AssertEqual(f, TensorPrimitives.ProductOfSums(x, y)); } - [Fact] - public static void ProductOfSums_KnownValues() + [Theory] + [InlineData(0, new float[] {0 }, new float[] { 0 })] + [InlineData(1, new float[] {0 }, new float[] { 1 })] + [InlineData(1, new float[] {1 }, new float[] { 0 })] + [InlineData(2, new float[] {1 }, new float[] { 1 })] + [InlineData(10395, new float[] {1, 2, 3, 4, 5 }, new float[] { 2, 3, 4, 5, 6 })] + [InlineData(120, new float[] {1, 2, 3, 4, 5 }, new float[] { 0, 0, 0, 0, 0 })] + [InlineData(120, new float[] {0, 0, 0, 0, 0 }, new float[] { 1, 2, 3, 4, 5 })] + [InlineData(float.NaN, new float[] {1, 2, float.NaN, 4, 5 }, new float[] { 0, 0, 0, 0, 0 })] + public static void ProductOfSums_KnownValues(float expected, float[] x, float[] y) { - Assert.Equal(0, TensorPrimitives.ProductOfSums([0], [0])); - Assert.Equal(1, TensorPrimitives.ProductOfSums([0], [1])); - Assert.Equal(1, TensorPrimitives.ProductOfSums([1], [0])); - Assert.Equal(2, TensorPrimitives.ProductOfSums([1], [1])); - Assert.Equal(10395, TensorPrimitives.ProductOfSums([1, 2, 3, 4, 5], [2, 3, 4, 5, 6])); - Assert.Equal(120, TensorPrimitives.ProductOfSums([1, 2, 3, 4, 5], [0, 0, 0, 0, 0])); - Assert.Equal(120, TensorPrimitives.ProductOfSums([0, 0, 0, 0, 0], [1, 2, 3, 4, 5])); - Assert.Equal(float.NaN, TensorPrimitives.ProductOfSums([1, 2, float.NaN, 4, 5], [0, 0, 0, 0, 0])); + Assert.Equal(expected, TensorPrimitives.ProductOfSums(x, y)); } #endregion @@ -2296,7 +2355,7 @@ public static void Sigmoid(int tensorLength) for (int i = 0; i < tensorLength; i++) { - Assert.Equal(1f / (1f + MathF.Exp(-x[i])), destination[i], Tolerance); + AssertEqual(1f / (1f + MathF.Exp(-x[i])), destination[i]); } } @@ -2311,7 +2370,7 @@ public static void Sigmoid_InPlace(int tensorLength) for (int i = 0; i < tensorLength; i++) { - Assert.Equal(1f / (1f + MathF.Exp(-xOrig[i])), x[i], Tolerance); + AssertEqual(1f / (1f + MathF.Exp(-xOrig[i])), x[i]); } } @@ -2328,7 +2387,7 @@ public static void Sigmoid_SpecialValues(int tensorLength) TensorPrimitives.Sigmoid(x, destination); for (int i = 0; i < tensorLength; i++) { - Assert.Equal(1f / (1f + MathF.Exp(-x[i])), destination[i], Tolerance); + AssertEqual(1f / (1f + MathF.Exp(-x[i])), destination[i]); } }, x); } @@ -2344,15 +2403,14 @@ public static void Sigmoid_KnownValues(float[] x, float[] expectedResult) for (int i = 0; i < x.Length; i++) { - Assert.Equal(expectedResult[i], dest[i], Tolerance); + AssertEqual(expectedResult[i], dest[i], 0.0001f); } } - [Fact] - public static void Sigmoid_DestinationLongerThanSource() + [Theory] + [InlineData(new float[] { -5, -4.5f, -4 }, new float[] { 0.0066f, 0.0109f, 0.0179f })] + public static void Sigmoid_DestinationLongerThanSource(float[] x, float[] expectedResult) { - float[] x = [-5, -4.5f, -4]; - float[] expectedResult = [0.0066f, 0.0109f, 0.0179f]; using BoundedMemory dest = CreateTensor(x.Length + 1); TensorPrimitives.Sigmoid(x, dest); @@ -2360,7 +2418,7 @@ public static void Sigmoid_DestinationLongerThanSource() float originalLast = dest[dest.Length - 1]; for (int i = 0; i < x.Length; i++) { - Assert.Equal(expectedResult[i], dest[i], Tolerance); + AssertEqual(expectedResult[i], dest[i], 0.0001f); } Assert.Equal(originalLast, dest[dest.Length - 1]); } @@ -2402,7 +2460,7 @@ public static void Sinh(int tensorLength) for (int i = 0; i < tensorLength; i++) { - Assert.Equal(MathF.Sinh(x[i]), destination[i], Tolerance); + AssertEqual(MathF.Sinh(x[i]), destination[i]); } } @@ -2417,7 +2475,42 @@ public static void Sinh_InPlace(int tensorLength) for (int i = 0; i < tensorLength; i++) { - Assert.Equal(MathF.Sinh(xOrig[i]), x[i], Tolerance); + AssertEqual(MathF.Sinh(xOrig[i]), x[i]); + } + } + + [Theory] + [MemberData(nameof(TensorLengths))] + [ActiveIssue("https://github.com/dotnet/runtime/issues/92885", TestRuntimes.Mono)] + public static void Sinh_SpecialValues(int tensorLength) + { + using BoundedMemory x = CreateAndFillTensor(tensorLength); + using BoundedMemory destination = CreateTensor(tensorLength); + + RunForEachSpecialValue(() => + { + TensorPrimitives.Sinh(x, destination); + for (int i = 0; i < tensorLength; i++) + { + AssertEqual(MathF.Sinh(x[i]), destination[i]); + } + }, x); + } + + [Theory] + [MemberData(nameof(VectorLengthAndIteratedRange), new object[] { -90f, 90f, 3f })] + public static void Sinh_ValueRange(int vectorLengths, float element) + { + float[] x = new float[vectorLengths]; + float[] dest = new float[vectorLengths]; + + x.AsSpan().Fill(element); + TensorPrimitives.Sinh(x, dest); + + float expected = MathF.Sinh(element); + foreach (float actual in dest) + { + AssertEqual(expected, actual); } } @@ -2453,7 +2546,7 @@ public static void SoftMax(int tensorLength) float expSum = MemoryMarshal.ToEnumerable(x.Memory).Sum(MathF.Exp); for (int i = 0; i < tensorLength; i++) { - Assert.Equal(MathF.Exp(x[i]) / expSum, destination[i], Tolerance); + AssertEqual(MathF.Exp(x[i]) / expSum, destination[i]); } } @@ -2469,7 +2562,7 @@ public static void SoftMax_InPlace(int tensorLength) float expSum = xOrig.Sum(MathF.Exp); for (int i = 0; i < tensorLength; i++) { - Assert.Equal(MathF.Exp(xOrig[i]) / expSum, x[i], Tolerance); + AssertEqual(MathF.Exp(xOrig[i]) / expSum, x[i]); } } @@ -2485,7 +2578,7 @@ public static void SoftMax_KnownValues(float[] x, float[] expectedResult) for (int i = 0; i < x.Length; i++) { - Assert.Equal(expectedResult[i], dest[i], Tolerance); + AssertEqual(expectedResult[i], dest[i], 0.0001f); } } @@ -2499,7 +2592,7 @@ public static void SoftMax_DestinationLongerThanSource() for (int i = 0; i < x.Length; i++) { - Assert.Equal(expectedResult[i], dest[i], Tolerance); + AssertEqual(expectedResult[i], dest[i]); } } @@ -2541,7 +2634,7 @@ public static void Subtract_TwoTensors(int tensorLength) for (int i = 0; i < tensorLength; i++) { - Assert.Equal(x[i] - y[i], destination[i], Tolerance); + AssertEqual(x[i] - y[i], destination[i]); } } @@ -2556,7 +2649,7 @@ public static void Subtract_TwoTensors_InPlace(int tensorLength) for (int i = 0; i < tensorLength; i++) { - Assert.Equal(xOrig[i] - xOrig[i], x[i], Tolerance); + AssertEqual(xOrig[i] - xOrig[i], x[i]); } } @@ -2605,7 +2698,7 @@ public static void Subtract_TensorScalar(int tensorLength) for (int i = 0; i < tensorLength; i++) { - Assert.Equal(x[i] - y, destination[i], Tolerance); + AssertEqual(x[i] - y, destination[i]); } } @@ -2621,7 +2714,7 @@ public static void Subtract_TensorScalar_InPlace(int tensorLength) for (int i = 0; i < tensorLength; i++) { - Assert.Equal(xOrig[i] - y, x[i], Tolerance); + AssertEqual(xOrig[i] - y, x[i]); } } @@ -2652,24 +2745,25 @@ public static void Sum(int tensorLength) { using BoundedMemory x = CreateAndFillTensor(tensorLength); - Assert.Equal(MemoryMarshal.ToEnumerable(x.Memory).Sum(), TensorPrimitives.Sum(x), Tolerance); + AssertEqual(MemoryMarshal.ToEnumerable(x.Memory).Sum(), TensorPrimitives.Sum(x)); float sum = 0; foreach (float f in x.Span) { sum += f; } - Assert.Equal(sum, TensorPrimitives.Sum(x), Tolerance); + AssertEqual(sum, TensorPrimitives.Sum(x)); } - [Fact] - public static void Sum_KnownValues() + [Theory] + [InlineData(0, new float[] { 0 })] + [InlineData(1, new float[] { 0, 1 })] + [InlineData(6, new float[] { 1, 2, 3 })] + [InlineData(0, new float[] { -3, 0, 3 })] + [InlineData(float.NaN, new float[] { -3, float.NaN, 3 })] + public static void Sum_KnownValues(float expected, float[] x) { - Assert.Equal(0, TensorPrimitives.Sum([0])); - Assert.Equal(1, TensorPrimitives.Sum([0, 1])); - Assert.Equal(6, TensorPrimitives.Sum([1, 2, 3])); - Assert.Equal(0, TensorPrimitives.Sum([-3, 0, 3])); - Assert.Equal(float.NaN, TensorPrimitives.Sum([-3, float.NaN, 3])); + Assert.Equal(expected, TensorPrimitives.Sum(x)); } #endregion @@ -2680,24 +2774,25 @@ public static void SumOfMagnitudes(int tensorLength) { using BoundedMemory x = CreateAndFillTensor(tensorLength); - Assert.Equal(Enumerable.Sum(MemoryMarshal.ToEnumerable(x.Memory), MathF.Abs), TensorPrimitives.SumOfMagnitudes(x), Tolerance); + AssertEqual(Enumerable.Sum(MemoryMarshal.ToEnumerable(x.Memory), MathF.Abs), TensorPrimitives.SumOfMagnitudes(x)); float sum = 0; foreach (float f in x.Span) { sum += MathF.Abs(f); } - Assert.Equal(sum, TensorPrimitives.SumOfMagnitudes(x), Tolerance); + AssertEqual(sum, TensorPrimitives.SumOfMagnitudes(x)); } - [Fact] - public static void SumOfMagnitudes_KnownValues() + [Theory] + [InlineData(0, new float[] { 0 })] + [InlineData(1, new float[] { 0, 1 })] + [InlineData(6, new float[] { 1, 2, 3 })] + [InlineData(6, new float[] { -3, 0, 3 })] + [InlineData(float.NaN, new float[] { -3, float.NaN, 3 })] + public static void SumOfMagnitudes_KnownValues(float expected, float[] x) { - Assert.Equal(0, TensorPrimitives.SumOfMagnitudes([0])); - Assert.Equal(1, TensorPrimitives.SumOfMagnitudes([0, 1])); - Assert.Equal(6, TensorPrimitives.SumOfMagnitudes([1, 2, 3])); - Assert.Equal(6, TensorPrimitives.SumOfMagnitudes([-3, 0, 3])); - Assert.Equal(float.NaN, TensorPrimitives.SumOfMagnitudes([-3, float.NaN, 3])); + Assert.Equal(expected, TensorPrimitives.SumOfMagnitudes(x)); } #endregion @@ -2708,24 +2803,25 @@ public static void SumOfSquares(int tensorLength) { using BoundedMemory x = CreateAndFillTensor(tensorLength); - Assert.Equal(Enumerable.Sum(MemoryMarshal.ToEnumerable(x.Memory), v => v * v), TensorPrimitives.SumOfSquares(x), Tolerance); + AssertEqual(Enumerable.Sum(MemoryMarshal.ToEnumerable(x.Memory), v => v * v), TensorPrimitives.SumOfSquares(x)); float sum = 0; foreach (float f in x.Span) { sum += f * f; } - Assert.Equal(sum, TensorPrimitives.SumOfSquares(x), Tolerance); + AssertEqual(sum, TensorPrimitives.SumOfSquares(x)); } - [Fact] - public static void SumOfSquares_KnownValues() + [Theory] + [InlineData(0, new float[] { 0 })] + [InlineData(1, new float[] { 0, 1 })] + [InlineData(14, new float[] { 1, 2, 3 })] + [InlineData(18, new float[] { -3, 0, 3 })] + [InlineData(float.NaN, new float[] { -3, float.NaN, 3 })] + public static void SumOfSquares_KnownValues(float expected, float[] x) { - Assert.Equal(0, TensorPrimitives.SumOfSquares([0])); - Assert.Equal(1, TensorPrimitives.SumOfSquares([0, 1])); - Assert.Equal(14, TensorPrimitives.SumOfSquares([1, 2, 3])); - Assert.Equal(18, TensorPrimitives.SumOfSquares([-3, 0, 3])); - Assert.Equal(float.NaN, TensorPrimitives.SumOfSquares([-3, float.NaN, 3])); + Assert.Equal(expected, TensorPrimitives.SumOfSquares(x)); } #endregion @@ -2741,7 +2837,7 @@ public static void Tanh(int tensorLength) for (int i = 0; i < tensorLength; i++) { - Assert.Equal(MathF.Tanh(x[i]), destination[i], Tolerance); + AssertEqual(MathF.Tanh(x[i]), destination[i]); } } @@ -2756,7 +2852,42 @@ public static void Tanh_InPlace(int tensorLength) for (int i = 0; i < tensorLength; i++) { - Assert.Equal(MathF.Tanh(xOrig[i]), x[i], Tolerance); + AssertEqual(MathF.Tanh(xOrig[i]), x[i]); + } + } + + [Theory] + [MemberData(nameof(TensorLengths))] + [ActiveIssue("https://github.com/dotnet/runtime/issues/92885", TestRuntimes.Mono)] + public static void Tanh_SpecialValues(int tensorLength) + { + using BoundedMemory x = CreateAndFillTensor(tensorLength); + using BoundedMemory destination = CreateTensor(tensorLength); + + RunForEachSpecialValue(() => + { + TensorPrimitives.Tanh(x, destination); + for (int i = 0; i < tensorLength; i++) + { + AssertEqual(MathF.Tanh(x[i]), destination[i]); + } + }, x); + } + + [Theory] + [MemberData(nameof(VectorLengthAndIteratedRange), new object[] { -11f, 11f, 0.2f })] + public static void Tanh_ValueRange(int vectorLengths, float element) + { + float[] x = new float[vectorLengths]; + float[] dest = new float[vectorLengths]; + + x.AsSpan().Fill(element); + TensorPrimitives.Tanh(x, dest); + + float expected = MathF.Tanh(element); + foreach (float actual in dest) + { + AssertEqual(expected, actual); } }