Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
101 changes: 101 additions & 0 deletions Sources/AngouriMath/Convenience/MathS.cs
Original file line number Diff line number Diff line change
Expand Up @@ -973,6 +973,107 @@ public static Integer GreatestCommonDivisor(Integer a, Integer b)
[MethodImpl(MethodImplOptions.AggressiveInlining), NativeExport]
public static Entity Abs(Entity a) => new Absf(a);

/// <summary>
/// The logistic function, <c>1 / (1 + e^(-a))</c>.
/// <a href="https://en.wikipedia.org/wiki/Sigmoid_function"/>
/// </summary>
/// <param name="a">The argument of the sigmoid.</param>
/// <returns>
/// The expression it is defined as, not a node of its own — so everything that already
/// works on an exponential works on this: it differentiates, it evaluates, it prints.
/// </returns>
/// <remarks>
/// One of the "non-kernel" functions of
/// <a href="https://github.com/asc-community/AngouriMath/issues/321">#321</a>: things
/// worth being able to <i>write</i>, which need no new node type because the kernel can
/// already say them. Adding a node instead would mean the eleven steps of
/// <c>Docs/Contributing/AddingNode.cs</c> and a new shape for every rule to ignore.
/// </remarks>
/// <example>
/// <code>
/// using System;
/// using AngouriMath;
/// using static AngouriMath.MathS;
///
/// Console.WriteLine(Sigmoid("x"));
/// Console.WriteLine(Sigmoid(0).EvalNumerical());
/// </code>
/// Prints
/// <code>
/// 1 / (1 + e ^ (-x))
/// 1/2
/// </code>
/// </example>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static Entity Sigmoid(Entity a) => 1 / (1 + Pow(e, -a));

/// <summary>
/// The arithmetic mean of a matrix's cells.
/// <a href="https://en.wikipedia.org/wiki/Arithmetic_mean"/>
/// </summary>
/// <param name="m">The matrix whose cells are averaged. A vector is a matrix here.</param>
/// <returns>Their sum over their count.</returns>
/// <remarks>
/// Symbolic, so it averages what it is given rather than what it can evaluate:
/// <c>Avg</c> of <c>[a, b]</c> is <c>(a + b) / 2</c>. See
/// <a href="https://github.com/asc-community/AngouriMath/issues/321">#321</a>.
/// </remarks>
/// <exception cref="System.ArgumentNullException">Thrown where the matrix is null.</exception>
/// <example>
/// <code>
/// using System;
/// using AngouriMath;
/// using static AngouriMath.MathS;
///
/// Console.WriteLine(Average(Vector(1, 2, 3)).Simplify());
/// Console.WriteLine(Average(Vector("a", "b")).Simplify());
/// </code>
/// Prints
/// <code>
/// 2
/// (a + b) / 2
/// </code>
/// </example>
public static Entity Average(Matrix m)
{
if (m is null)
throw new System.ArgumentNullException(nameof(m));
var cells = m.RowCount * m.ColumnCount;
Entity sum = 0;
for (var row = 0; row < m.RowCount; row++)
for (var column = 0; column < m.ColumnCount; column++)
sum += m[row, column];
return sum / cells;
}

/// <summary>
/// The Euclidean length of a vector, <c>sqrt(sum of the squares of its cells)</c>.
/// </summary>
/// <param name="v">The vector whose length is taken.</param>
/// <returns>Its length, as <see cref="Abs(Entity)"/> of the vector.</returns>
/// <remarks>
/// A name rather than a new capability: <c>abs([3, 4])</c> has always answered <c>5</c>,
/// and this is that spelled so it can be found. Named on
/// <a href="https://github.com/asc-community/AngouriMath/issues/321">#321</a>, where the
/// observation that "VectorLength is <c>Abs</c>" is the whole implementation.
/// </remarks>
/// <exception cref="System.ArgumentNullException">Thrown where the vector is null.</exception>
/// <example>
/// <code>
/// using System;
/// using AngouriMath;
/// using static AngouriMath.MathS;
///
/// Console.WriteLine(VectorLength(Vector(3, 4)).Simplify());
/// </code>
/// Prints
/// <code>
/// 5
/// </code>
/// </example>
public static Entity VectorLength(Matrix v)
=> v is null ? throw new System.ArgumentNullException(nameof(v)) : Abs(v);

/// <summary>
/// The greatest integer not above <paramref name="a"/>.
/// </summary>
Expand Down
3 changes: 3 additions & 0 deletions Sources/Tests/UnitTests/Common/PublicApi.txt
Original file line number Diff line number Diff line change
Expand Up @@ -2397,6 +2397,7 @@ AngouriMath.MathS.Arccotan(AngouriMath.Entity) : AngouriMath.Entity
AngouriMath.MathS.Arcsec(AngouriMath.Entity) : AngouriMath.Entity
AngouriMath.MathS.Arcsin(AngouriMath.Entity) : AngouriMath.Entity
AngouriMath.MathS.Arctan(AngouriMath.Entity) : AngouriMath.Entity
AngouriMath.MathS.Average(AngouriMath.Entity+Matrix) : AngouriMath.Entity
AngouriMath.MathS.Cbrt(AngouriMath.Entity) : AngouriMath.Entity
AngouriMath.MathS.Ceil(AngouriMath.Entity) : AngouriMath.Entity
AngouriMath.MathS.Conjunction(AngouriMath.Entity, AngouriMath.Entity) : AngouriMath.Entity
Expand Down Expand Up @@ -2462,6 +2463,7 @@ AngouriMath.MathS.Round(AngouriMath.Entity) : AngouriMath.Entity
AngouriMath.MathS.Scalar(AngouriMath.Entity) : AngouriMath.Entity+Matrix
AngouriMath.MathS.Sec(AngouriMath.Entity) : AngouriMath.Entity
AngouriMath.MathS.SetSubtraction(AngouriMath.Entity, AngouriMath.Entity) : AngouriMath.Entity+Set
AngouriMath.MathS.Sigmoid(AngouriMath.Entity) : AngouriMath.Entity
AngouriMath.MathS.Signum(AngouriMath.Entity) : AngouriMath.Entity
AngouriMath.MathS.Sin(AngouriMath.Entity) : AngouriMath.Entity
AngouriMath.MathS.SolveBooleanTable(AngouriMath.Entity, AngouriMath.Entity+Variable[]) : AngouriMath.Entity+Matrix
Expand All @@ -2479,6 +2481,7 @@ AngouriMath.MathS.Var(System.String, System.String, System.String) : System.Valu
AngouriMath.MathS.Var(System.String, System.String, System.String, System.String) : System.ValueTuple<AngouriMath.Entity+Variable,AngouriMath.Entity+Variable,AngouriMath.Entity+Variable,AngouriMath.Entity+Variable>
AngouriMath.MathS.Var(System.String, System.String, System.String, System.String, System.String) : System.ValueTuple<AngouriMath.Entity+Variable,AngouriMath.Entity+Variable,AngouriMath.Entity+Variable,AngouriMath.Entity+Variable,AngouriMath.Entity+Variable>
AngouriMath.MathS.Vector(AngouriMath.Entity[]) : AngouriMath.Entity+Matrix
AngouriMath.MathS.VectorLength(AngouriMath.Entity+Matrix) : AngouriMath.Entity
AngouriMath.MathS.ZeroMatrix(System.Int32) : AngouriMath.Entity+Matrix
AngouriMath.MathS.ZeroMatrix(System.Int32, System.Int32) : AngouriMath.Entity+Matrix
AngouriMath.MathS.ZeroVector(System.Int32) : AngouriMath.Entity+Matrix
Expand Down
94 changes: 94 additions & 0 deletions Sources/Tests/UnitTests/Convenience/NonKernelFunctionsTest.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
//
// Copyright (c) 2019-2026 Angouri.
// AngouriMath is licensed under MIT.
// Details: https://github.com/asc-community/AngouriMath/blob/master/LICENSE.md.
// Website: https://am.angouri.org.
//

using AngouriMath;
using AngouriMath.Extensions;
using Xunit;
using static AngouriMath.Entity.Number;
using static AngouriMath.MathS;

namespace AngouriMath.Tests.Convenience
{
/// <summary>
/// The functions of <a href="https://github.com/asc-community/AngouriMath/issues/321">#321</a>
/// that need no node of their own, because the kernel can already say what they mean.
/// </summary>
/// <remarks>
/// The point of these tests is less that the formulas are right — they are one line each —
/// than that being an ordinary expression is what buys everything else. Nothing below teaches
/// the library to differentiate a sigmoid or take its limit; those work because a sigmoid is
/// a quotient of things it already knew.
/// </remarks>
[Trait("Area", "Convenience")]
public sealed class NonKernelFunctionsTest
{
[Fact]
public void ASigmoidIsTheLogisticExpression() =>
Assert.Equal(MathS.Boolean.True,
Sigmoid("x").EqualTo(1 / (1 + Pow(e, -Var("x")))).Simplify());

[Fact]
public void ASigmoidIsAHalfAtZero() =>
Assert.Equal((Entity)Rational.Create(1, 2), Sigmoid(0).EvalNumerical());

/// <summary>
/// The whole argument for defining it this way: nothing here was implemented for the
/// sigmoid, and all of it works.
/// </summary>
[Theory]
[InlineData("+oo", "1")]
[InlineData("-oo", "0")]
public void ASigmoidSaturatesBecauseItIsAnOrdinaryExpression(string destination, string expected) =>
Assert.Equal(expected.ToEntity().Evaled,
Sigmoid(Var("x")).Limit("x", destination.ToEntity()).Simplify().Evaled);

[Fact]
public void ASigmoidDifferentiatesWithoutBeingTaughtTo()
{
var derivative = Sigmoid(Var("x")).Differentiate("x").Simplify();
Assert.DoesNotContain(derivative.Nodes, node => node is Entity.Derivativef);
Assert.NotEqual(MathS.NaN, derivative);
}

[Fact]
public void AnAverageOfNumbersIsTheirMean() =>
Assert.Equal((Entity)2, Average(Vector(1, 2, 3)).Simplify());

/// <summary>Symbolic, so it averages what it is given rather than what it can evaluate.</summary>
[Fact]
public void AnAverageOfSymbolsStaysSymbolic() =>
Assert.Equal(MathS.Boolean.True,
Average(Vector("a", "b")).EqualTo((Var("a") + Var("b")) / 2).Simplify());

[Fact]
public void AnAverageReadsEveryCellOfAMatrix() =>
Assert.Equal((Entity)Rational.Create(5, 2),
Average(Matrix(new Entity[,] { { 1, 2 }, { 3, 4 } })).Simplify());

[Fact]
public void AVectorsLengthIsEuclidean() =>
Assert.Equal((Entity)5, VectorLength(Vector(3, 4)).Simplify());

/// <summary>
/// It is a name for <see cref="MathS.Abs(Entity)"/> rather than a second implementation,
/// so the two must not be able to disagree.
/// </summary>
[Fact]
public void AVectorsLengthIsExactlyItsAbs()
{
var v = Vector(1, 2, 3);
Assert.Equal(Abs(v).Simplify(), VectorLength(v).Simplify());
}

[Fact]
public void NullIsRefusedRatherThanDereferenced()
{
Assert.Throws<System.ArgumentNullException>(() => Average(null!));
Assert.Throws<System.ArgumentNullException>(() => VectorLength(null!));
}
}
}
Loading