From b20c713eed7da3ad8165e2b14747b93cc4d26976 Mon Sep 17 00:00:00 2001 From: khanhkhanhlele Date: Mon, 10 Nov 2025 16:38:04 +0700 Subject: [PATCH 1/5] Add algorithm TanhFunction --- Algorithms.Tests/Numeric/TanhTest.cs | 79 ++++++++++++++++++++++++++++ Algorithms/Numeric/Tanh.cs | 46 ++++++++++++++++ 2 files changed, 125 insertions(+) create mode 100644 Algorithms.Tests/Numeric/TanhTest.cs create mode 100644 Algorithms/Numeric/Tanh.cs diff --git a/Algorithms.Tests/Numeric/TanhTest.cs b/Algorithms.Tests/Numeric/TanhTest.cs new file mode 100644 index 00000000..e3174d4c --- /dev/null +++ b/Algorithms.Tests/Numeric/TanhTest.cs @@ -0,0 +1,79 @@ +using Algorithms.Numeric; +using NUnit.Framework; + +namespace Algorithms.Tests.Numeric; + +/// +/// Contains unit tests for the Tanh (Hyperbolic Tangent) function implementation. +/// +[TestFixture] // Marks the class as containing test methods +public static class TanhTests +{ + private const double Tolerance = 1e-9; // Precision tolerance for floating-point comparisons + + /// + /// Tests the Tanh function with various positive, negative, and zero inputs. + /// tanh(x) is expected to return a value between -1.0 and 1.0. + /// + /// The input number. + /// The expected hyperbolic tangent value. + [TestCase(0.0, 0.0)] // Case 1: Tanh(0) = 0 + [TestCase(1.0, 0.7615941559557649)] // Case 2: Positive input + [TestCase(-1.0, -0.7615941559557649)] // Case 3: Negative input (Tanh is an odd function: tanh(-x) = -tanh(x)) + [TestCase(0.5, 0.46211715726000974)] // Case 4: Another positive value + [TestCase(-0.5, -0.46211715726000974)] // Case 5: Another negative value + [TestCase(5.0, 0.999909204262595)] // Case 6: Larger positive value (approaching 1.0) + [TestCase(-5.0, -0.999909204262595)] // Case 7: Larger negative value (approaching -1.0) + public static void TanhFunction_ReturnsCorrectValue(double input, double expected) + { + // Act + var result = Tanh.Compute(input); + + // Assert + Assert.That(result, Is.EqualTo(expected).Within(Tolerance)); + } + + // --- + + /// + /// Tests the Tanh function's behavior when input approaches extreme limits (infinity and NaN). + /// + [Test] + public static void TanhFunction_ApproachesLimits() + { + // Case 8: Positive Infinity + // Tanh(x) approaches 1.0 as x -> +infinity + var resultPosInfinity = Tanh.Compute(double.MaxValue); + Assert.That(resultPosInfinity, Is.EqualTo(1.0).Within(Tolerance)); + + // Case 9: Negative Infinity + // Tanh(x) approaches -1.0 as x -> -infinity + var resultNegInfinity = Tanh.Compute(double.MinValue); + Assert.That(resultNegInfinity, Is.EqualTo(-1.0).Within(Tolerance)); + + // Case 10: NaN input (Expected behavior is usually to return NaN) + var resultNaN = Tanh.Compute(double.NaN); + Assert.That(resultNaN, Is.NaN); + } + + // --- + + /// + /// Tests the result range to ensure the output is always between -1.0 and 1.0, which is a key property of tanh. + /// + /// A variety of input numbers. + [TestCase(100.0)] + [TestCase(-100.0)] + [TestCase(0.0001)] + [TestCase(-0.0001)] + public static void TanhFunction_ResultIsBounded(double input) + { + // Act + var result = Tanh.Compute(input); + + // Assert + // Result must be greater than or equal to -1.0 and less than or equal to 1.0 + Assert.That(result, Is.GreaterThanOrEqualTo(-1.0)); + Assert.That(result, Is.LessThanOrEqualTo(1.0)); + } +} \ No newline at end of file diff --git a/Algorithms/Numeric/Tanh.cs b/Algorithms/Numeric/Tanh.cs new file mode 100644 index 00000000..eeae7659 --- /dev/null +++ b/Algorithms/Numeric/Tanh.cs @@ -0,0 +1,46 @@ +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.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; + } +} From 1b7f7eb9ce23f1c97fa7101f3a1ba4641b061818 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=C3=AA=20Nam=20Kh=C3=A1nh?= <55955273+khanhkhanhlele@users.noreply.github.com> Date: Mon, 10 Nov 2025 16:41:31 +0700 Subject: [PATCH 2/5] Update Algorithms/Numeric/Tanh.cs Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- Algorithms/Numeric/Tanh.cs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/Algorithms/Numeric/Tanh.cs b/Algorithms/Numeric/Tanh.cs index eeae7659..ffd0efbe 100644 --- a/Algorithms/Numeric/Tanh.cs +++ b/Algorithms/Numeric/Tanh.cs @@ -28,6 +28,10 @@ public static double Compute(double input) /// 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."); From 5fb404320f8fbaf794626e415c6927db4ed5b110 Mon Sep 17 00:00:00 2001 From: khanhkhanhlele Date: Mon, 10 Nov 2025 16:45:47 +0700 Subject: [PATCH 3/5] update --- Algorithms/Numeric/Tanh.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/Algorithms/Numeric/Tanh.cs b/Algorithms/Numeric/Tanh.cs index ffd0efbe..536c1e6b 100644 --- a/Algorithms/Numeric/Tanh.cs +++ b/Algorithms/Numeric/Tanh.cs @@ -32,6 +32,7 @@ public static double[] Compute(double[] input) { throw new ArgumentNullException(nameof(input)); } + if (input.Length == 0) { throw new ArgumentException("Array is empty."); From ff811e09ce8a361431f4ccfcaebfc61efc75a846 Mon Sep 17 00:00:00 2001 From: khanhkhanhlele Date: Mon, 10 Nov 2025 16:56:44 +0700 Subject: [PATCH 4/5] add array test --- Algorithms.Tests/Numeric/TanhTest.cs | 126 +++++++++++++++++---------- 1 file changed, 78 insertions(+), 48 deletions(-) diff --git a/Algorithms.Tests/Numeric/TanhTest.cs b/Algorithms.Tests/Numeric/TanhTest.cs index e3174d4c..24236fcf 100644 --- a/Algorithms.Tests/Numeric/TanhTest.cs +++ b/Algorithms.Tests/Numeric/TanhTest.cs @@ -1,79 +1,109 @@ -using Algorithms.Numeric; +using Algorithms.Numeric; using NUnit.Framework; +using System; namespace Algorithms.Tests.Numeric; -/// -/// Contains unit tests for the Tanh (Hyperbolic Tangent) function implementation. -/// -[TestFixture] // Marks the class as containing test methods +[TestFixture] public static class TanhTests { - private const double Tolerance = 1e-9; // Precision tolerance for floating-point comparisons + // Tolerance for floating-point comparisons + private const double Tolerance = 1e-9; + + // --- SCALAR TESTS (Tanh.Compute(double)) --- /// - /// Tests the Tanh function with various positive, negative, and zero inputs. - /// tanh(x) is expected to return a value between -1.0 and 1.0. + /// Tests Tanh function for specific values, including zero and symmetric positive/negative inputs. /// - /// The input number. - /// The expected hyperbolic tangent value. - [TestCase(0.0, 0.0)] // Case 1: Tanh(0) = 0 - [TestCase(1.0, 0.7615941559557649)] // Case 2: Positive input - [TestCase(-1.0, -0.7615941559557649)] // Case 3: Negative input (Tanh is an odd function: tanh(-x) = -tanh(x)) - [TestCase(0.5, 0.46211715726000974)] // Case 4: Another positive value - [TestCase(-0.5, -0.46211715726000974)] // Case 5: Another negative value - [TestCase(5.0, 0.999909204262595)] // Case 6: Larger positive value (approaching 1.0) - [TestCase(-5.0, -0.999909204262595)] // Case 7: Larger negative value (approaching -1.0) - public static void TanhFunction_ReturnsCorrectValue(double input, double expected) + [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) { - // Act var result = Tanh.Compute(input); - - // Assert Assert.That(result, Is.EqualTo(expected).Within(Tolerance)); } - // --- - /// - /// Tests the Tanh function's behavior when input approaches extreme limits (infinity and NaN). + /// Ensures the Tanh output approaches 1.0 for positive infinity and -1.0 for negative infinity. /// [Test] - public static void TanhFunction_ApproachesLimits() + public static void TanhFunction_Scalar_ApproachesLimits() { - // Case 8: Positive Infinity - // Tanh(x) approaches 1.0 as x -> +infinity - var resultPosInfinity = Tanh.Compute(double.MaxValue); - Assert.That(resultPosInfinity, Is.EqualTo(1.0).Within(Tolerance)); - - // Case 9: Negative Infinity - // Tanh(x) approaches -1.0 as x -> -infinity - var resultNegInfinity = Tanh.Compute(double.MinValue); - Assert.That(resultNegInfinity, Is.EqualTo(-1.0).Within(Tolerance)); - - // Case 10: NaN input (Expected behavior is usually to return NaN) - var resultNaN = Tanh.Compute(double.NaN); - Assert.That(resultNaN, Is.NaN); + 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); } - // --- - /// - /// Tests the result range to ensure the output is always between -1.0 and 1.0, which is a key property of tanh. + /// Checks that the Tanh result is always bounded between -1.0 and 1.0. /// - /// A variety of input numbers. [TestCase(100.0)] [TestCase(-100.0)] [TestCase(0.0001)] - [TestCase(-0.0001)] - public static void TanhFunction_ResultIsBounded(double input) + public static void TanhFunction_Scalar_ResultIsBounded(double input) { - // Act var result = Tanh.Compute(input); - - // Assert - // Result must be greater than or equal to -1.0 and less than or equal to 1.0 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 double[] { 0.0, 1.0, -2.0 }; + // Expected: [Tanh(0.0), Tanh(1.0), Tanh(-2.0)] + var expected = new double[] { 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[] { double.PositiveInfinity, 0.0, double.NaN }; + var expected = new double[] { 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 From 22f2c4c00f3871f9aa41360a7aa4fe272dafaec7 Mon Sep 17 00:00:00 2001 From: khanhkhanhlele Date: Mon, 10 Nov 2025 17:08:21 +0700 Subject: [PATCH 5/5] fix code stype --- Algorithms.Tests/Numeric/TanhTest.cs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Algorithms.Tests/Numeric/TanhTest.cs b/Algorithms.Tests/Numeric/TanhTest.cs index 24236fcf..866ab7d1 100644 --- a/Algorithms.Tests/Numeric/TanhTest.cs +++ b/Algorithms.Tests/Numeric/TanhTest.cs @@ -59,9 +59,9 @@ public static void TanhFunction_Scalar_ResultIsBounded(double input) public static void TanhFunction_Vector_ReturnsCorrectValues() { // Input: [0.0, 1.0, -2.0] - var input = new double[] { 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 double[] { 0.0, 0.7615941559557649, -0.9640275800758169 }; + var expected = new[] { 0.0, 0.7615941559557649, -0.9640275800758169 }; var result = Tanh.Compute(input); @@ -75,8 +75,8 @@ public static void TanhFunction_Vector_ReturnsCorrectValues() [Test] public static void TanhFunction_Vector_HandlesLimitsAndNaN() { - var input = new double[] { double.PositiveInfinity, 0.0, double.NaN }; - var expected = new double[] { 1.0, 0.0, double.NaN }; + var input = new[] { double.PositiveInfinity, 0.0, double.NaN }; + var expected = new[] { 1.0, 0.0, double.NaN }; var result = Tanh.Compute(input);