From 5c09c0e0959886130c6a67e5e658373848520420 Mon Sep 17 00:00:00 2001 From: Ricardo Ribeiro Rodrigues <72521349+RicardoRibeiroRodrigues@users.noreply.github.com> Date: Tue, 27 Aug 2024 14:56:00 -0300 Subject: [PATCH] Add SoftMax Function (#469) --- Algorithms.Tests/Numeric/SoftMaxTests.cs | 48 ++++++++++++++++++++++++ Algorithms/Numeric/SoftMax.cs | 45 ++++++++++++++++++++++ README.md | 1 + 3 files changed, 94 insertions(+) create mode 100644 Algorithms.Tests/Numeric/SoftMaxTests.cs create mode 100644 Algorithms/Numeric/SoftMax.cs diff --git a/Algorithms.Tests/Numeric/SoftMaxTests.cs b/Algorithms.Tests/Numeric/SoftMaxTests.cs new file mode 100644 index 00000000..d44925e0 --- /dev/null +++ b/Algorithms.Tests/Numeric/SoftMaxTests.cs @@ -0,0 +1,48 @@ +using System; +using Algorithms.Numeric; +using NUnit.Framework; + +namespace Algorithms.Tests.Numeric; + +public static class SoftMaxTests +{ + [TestCase(new[] {5.0, 5.0}, new[] {0.5, 0.5})] + [TestCase(new[] {1.0, 2.0, 3.0}, new[] {0.09003057317038046, 0.24472847105479767, 0.6652409557748219})] + [TestCase(new[] {0.0}, new[] {1.0})] + public static void SoftMaxFunction(double[] input, double[] expected) + { + // Act + var result = SoftMax.Compute(input); + + // Assert + Assert.That(result, Is.EqualTo(expected).Within(1e-9)); + } + + [Test] + public static void SoftMaxFunctionThrowsArgumentException() + { + // Arrange + var input = Array.Empty(); + + // Assert + Assert.Throws(() => SoftMax.Compute(input)); + } + + [TestCase(new[] {1.0, 2.0, 3.0, 4.0, 5.0})] + [TestCase(new[] {0.0, 0.0, 0.0, 0.0, 0.0})] + [TestCase(new[] {5.0})] + public static void SoftMaxFunctionSumsToOne(double[] input) + { + // Act + var result = SoftMax.Compute(input); + + var sum = 0.0; + foreach (var value in result) + { + sum += value; + } + + // Assert + Assert.That(sum, Is.EqualTo(1.0).Within(1e-9)); + } +} \ No newline at end of file diff --git a/Algorithms/Numeric/SoftMax.cs b/Algorithms/Numeric/SoftMax.cs new file mode 100644 index 00000000..c20a54b5 --- /dev/null +++ b/Algorithms/Numeric/SoftMax.cs @@ -0,0 +1,45 @@ +using System; + +namespace Algorithms.Numeric; + +/// +/// Implementation of the SoftMax function. +/// Its a function that takes as input a vector of K real numbers, and normalizes +/// it into a probability distribution consisting of K probabilities proportional +/// to the exponentials of the input numbers. After softmax, the elements of the vector always sum up to 1. +/// https://en.wikipedia.org/wiki/Softmax_function. +/// +public static class SoftMax +{ + /// + /// Compute the SoftMax function. + /// The SoftMax function is defined as: + /// softmax(x_i) = exp(x_i) / sum(exp(x_j)) for j = 1 to n + /// where x_i is the i-th element of the input vector. + /// The elements of the output vector are the probabilities of the input vector, the output sums up to 1. + /// + /// The input vector of real numbers. + /// The output vector of real numbers. + public static double[] Compute(double[] input) + { + if (input.Length == 0) + { + throw new ArgumentException("Array is empty."); + } + + var exponentVector = new double[input.Length]; + var sum = 0.0; + for (var index = 0; index < input.Length; index++) + { + exponentVector[index] = Math.Exp(input[index]); + sum += exponentVector[index]; + } + + for (var index = 0; index < input.Length; index++) + { + exponentVector[index] /= sum; + } + + return exponentVector; + } +} diff --git a/README.md b/README.md index 6ad84ea3..fd4f1942 100644 --- a/README.md +++ b/README.md @@ -95,6 +95,7 @@ find more than one implementation for the same objective but using different alg * [Automorphic Number](./Algorithms/Numeric/AutomorphicNumber.cs) * [Josephus Problem](./Algorithms/Numeric/JosephusProblem.cs) * [Newton's Square Root Calculation](./Algorithms/NewtonSquareRoot.cs) + * [SoftMax Function](./Algorithms/Numeric/SoftMax.cs) * [Searches](./Algorithms/Search) * [A-Star](./Algorithms/Search/AStar/) * [Binary Search](./Algorithms/Search/BinarySearcher.cs)