diff --git a/Algorithms.Tests/Numeric/TanhTest.cs b/Algorithms.Tests/Numeric/TanhTest.cs new file mode 100644 index 00000000..866ab7d1 --- /dev/null +++ b/Algorithms.Tests/Numeric/TanhTest.cs @@ -0,0 +1,109 @@ +using Algorithms.Numeric; +using NUnit.Framework; +using System; + +namespace Algorithms.Tests.Numeric; + +[TestFixture] +public static class TanhTests +{ + // Tolerance for floating-point comparisons + private const double Tolerance = 1e-9; + + // --- SCALAR TESTS (Tanh.Compute(double)) --- + + /// + /// Tests Tanh function for specific values, including zero and symmetric positive/negative inputs. + /// + [TestCase(0.0, 0.0)] + [TestCase(1.0, 0.7615941559557649)] + [TestCase(-1.0, -0.7615941559557649)] + [TestCase(5.0, 0.999909204262595)] + [TestCase(-5.0, -0.999909204262595)] + public static void TanhFunction_Scalar_ReturnsCorrectValue(double input, double expected) + { + var result = Tanh.Compute(input); + Assert.That(result, Is.EqualTo(expected).Within(Tolerance)); + } + + /// + /// Ensures the Tanh output approaches 1.0 for positive infinity and -1.0 for negative infinity. + /// + [Test] + public static void TanhFunction_Scalar_ApproachesLimits() + { + Assert.That(Tanh.Compute(double.PositiveInfinity), Is.EqualTo(1.0).Within(Tolerance)); + Assert.That(Tanh.Compute(double.NegativeInfinity), Is.EqualTo(-1.0).Within(Tolerance)); + Assert.That(Tanh.Compute(double.NaN), Is.NaN); + } + + /// + /// Checks that the Tanh result is always bounded between -1.0 and 1.0. + /// + [TestCase(100.0)] + [TestCase(-100.0)] + [TestCase(0.0001)] + public static void TanhFunction_Scalar_ResultIsBounded(double input) + { + var result = Tanh.Compute(input); + Assert.That(result, Is.GreaterThanOrEqualTo(-1.0)); + Assert.That(result, Is.LessThanOrEqualTo(1.0)); + } + + // --- VECTOR TESTS (Tanh.Compute(double[])) --- + + /// + /// Tests the element-wise computation for a vector input. + /// + [Test] + public static void TanhFunction_Vector_ReturnsCorrectValues() + { + // Input: [0.0, 1.0, -2.0] + var input = new[] { 0.0, 1.0, -2.0 }; + // Expected: [Tanh(0.0), Tanh(1.0), Tanh(-2.0)] + var expected = new[] { 0.0, 0.7615941559557649, -0.9640275800758169 }; + + var result = Tanh.Compute(input); + + // Assert deep equality within tolerance + Assert.That(result, Is.EqualTo(expected).Within(Tolerance)); + } + + /// + /// Tests vector handling of edge cases like infinity and NaN. + /// + [Test] + public static void TanhFunction_Vector_HandlesLimitsAndNaN() + { + var input = new[] { double.PositiveInfinity, 0.0, double.NaN }; + var expected = new[] { 1.0, 0.0, double.NaN }; + + var result = Tanh.Compute(input); + + Assert.That(result.Length, Is.EqualTo(expected.Length)); + Assert.That(result[0], Is.EqualTo(expected[0]).Within(Tolerance)); // Pos Inf -> 1.0 + Assert.That(result[2], Is.NaN); // NaN + } + + // --- EXCEPTION TESTS --- + + /// + /// Checks if the vector computation throws ArgumentNullException for null input. + /// + [Test] + public static void TanhFunction_Vector_ThrowsOnNullInput() + { + double[]? input = null; + Assert.Throws(() => Tanh.Compute(input!)); + } + + /// + /// Checks if the vector computation throws ArgumentException for an empty input array. + /// + [Test] + public static void TanhFunction_Vector_ThrowsOnEmptyInput() + { + var input = Array.Empty(); + Assert.Throws(() => Tanh.Compute(input)); + } +} \ No newline at end of file diff --git a/Algorithms/Numeric/Tanh.cs b/Algorithms/Numeric/Tanh.cs new file mode 100644 index 00000000..536c1e6b --- /dev/null +++ b/Algorithms/Numeric/Tanh.cs @@ -0,0 +1,51 @@ +namespace Algorithms.Numeric; + +/// +/// Implementation of the Hyperbolic Tangent (Tanh) function. +/// Tanh is an activation function that takes a real number as input and squashes +/// the output to a range between -1 and 1. +/// It is defined as: tanh(x) = (exp(x) - exp(-x)) / (exp(x) + exp(-x)). +/// https://en.wikipedia.org/wiki/Hyperbolic_function#Hyperbolic_tangent. +/// +public static class Tanh +{ + /// + /// Compute the Hyperbolic Tangent (Tanh) function for a single value. + /// The Math.Tanh() method is used for efficient and accurate computation. + /// + /// The input real number. + /// The output real number in the range [-1, 1]. + public static double Compute(double input) + { + // For a single double, we can directly use the optimized Math.Tanh method. + return Math.Tanh(input); + } + + /// + /// Compute the Hyperbolic Tangent (Tanh) function element-wise for a vector. + /// + /// The input vector of real numbers. + /// The output vector of real numbers, where each element is in the range [-1, 1]. + public static double[] Compute(double[] input) + { + if (input is null) + { + throw new ArgumentNullException(nameof(input)); + } + + if (input.Length == 0) + { + throw new ArgumentException("Array is empty."); + } + + var outputVector = new double[input.Length]; + + for (var index = 0; index < input.Length; index++) + { + // Apply Tanh to each element using the optimized Math.Tanh method. + outputVector[index] = Math.Tanh(input[index]); + } + + return outputVector; + } +}