Skip to content

Commit

Permalink
Add operations SumNumber and SumOfSquaresNumber
Browse files Browse the repository at this point in the history
  • Loading branch information
aalmada committed Mar 22, 2024
1 parent 0051978 commit 1d42a1b
Show file tree
Hide file tree
Showing 8 changed files with 284 additions and 15 deletions.
62 changes: 62 additions & 0 deletions src/NetFabric.Numerics.Tensors.UnitTests/SumNumberTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
using System.Numerics.Tensors;

namespace NetFabric.Numerics.Tensors.UnitTests;

public class SumNumberTests
{
public static TheoryData<int> SumNumberData
=> new() {
{ 0 }, { 1 }, { 2 }, { 3 }, { 4 }, { 5 }, { 6 }, { 7 }, { 8 }, { 9 }, { 10 }, { 100 },
};

static void SumNumber_Should_Succeed<T>(int count)
where T : struct, INumber<T>
{
// arrange
var source = new T[count];
var expected = T.Zero;
var random = new Random(42);
for (var index = 0; index < source.Length; index++)
{
var value = T.CreateChecked(random.Next(10));
source[index] = value;
expected += value;
}

// act
var result = TensorOperations.SumNumber<T>(source);

// assert
Assert.Equal(expected, result);
}

[Theory]
[MemberData(nameof(SumNumberData))]
public void SumNumber_Short_Should_Succeed(int count)
=> SumNumber_Should_Succeed<short>(count);

[Theory]
[MemberData(nameof(SumNumberData))]
public void SumNumber_Int_Should_Succeed(int count)
=> SumNumber_Should_Succeed<int>(count);

[Theory]
[MemberData(nameof(SumNumberData))]
public void SumNumber_Long_Should_Succeed(int count)
=> SumNumber_Should_Succeed<long>(count);

[Theory]
[MemberData(nameof(SumNumberData))]
public void SumNumber_Half_Should_Succeed(int count)
=> SumNumber_Should_Succeed<Half>(count);

[Theory]
[MemberData(nameof(SumNumberData))]
public void SumNumber_Float_Should_Succeed(int count)
=> SumNumber_Should_Succeed<float>(count);

[Theory]
[MemberData(nameof(SumNumberData))]
public void SumNumber_Double_Should_Succeed(int count)
=> SumNumber_Should_Succeed<double>(count);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
using System.Numerics.Tensors;

namespace NetFabric.Numerics.Tensors.UnitTests;

public class SumOfSquaresNumberTests
{
public static TheoryData<int> SumOfSquaresNumberData
=> new() {
{ 0 }, { 1 }, { 2 }, { 3 }, { 4 }, { 5 }, { 6 }, { 7 }, { 8 }, { 9 }, { 10 }, { 100 },
};

static void SumOfSquaresNumber_Should_Succeed<T>(int count)
where T : struct, INumber<T>
{
// arrange
var source = new T[count];
var expected = T.Zero;
var random = new Random(42);
for (var index = 0; index < source.Length; index++)
{
var value = T.CreateChecked(random.Next(10));
source[index] = value;
expected += value * value;
}

// act
var result = TensorOperations.SumOfSquaresNumber<T>(source);

// assert
Assert.Equal(expected, result);
}

[Theory]
[MemberData(nameof(SumOfSquaresNumberData))]
public void SumOfSquaresNumber_Short_Should_Succeed(int count)
=> SumOfSquaresNumber_Should_Succeed<short>(count);

[Theory]
[MemberData(nameof(SumOfSquaresNumberData))]
public void SumOfSquaresNumber_Int_Should_Succeed(int count)
=> SumOfSquaresNumber_Should_Succeed<int>(count);

[Theory]
[MemberData(nameof(SumOfSquaresNumberData))]
public void SumOfSquaresNumber_Long_Should_Succeed(int count)
=> SumOfSquaresNumber_Should_Succeed<long>(count);

//[Theory]
//[MemberData(nameof(SumOfSquaresNumberData))]
//public void SumOfSquaresNumber_Half_Should_Succeed(int count)
// => SumOfSquaresNumber_Should_Succeed<Half>(count);

[Theory]
[MemberData(nameof(SumOfSquaresNumberData))]
public void SumOfSquaresNumber_Float_Should_Succeed(int count)
=> SumOfSquaresNumber_Should_Succeed<float>(count);

[Theory]
[MemberData(nameof(SumOfSquaresNumberData))]
public void SumOfSquaresNumber_Double_Should_Succeed(int count)
=> SumOfSquaresNumber_Should_Succeed<double>(count);
}
89 changes: 89 additions & 0 deletions src/NetFabric.Numerics.Tensors.UnitTests/SumOfSquaresTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
using System.Numerics.Tensors;

namespace NetFabric.Numerics.Tensors.UnitTests;

public class SumOfSquaresTests
{
public static TheoryData<float[]> SumOfSquaresNaNData
=> new() {
new[] { float.NaN, float.NaN, float.NaN, float.NaN, float.NaN, float.NaN, float.NaN, float.NaN, float.NaN, float.NaN, float.NaN },
new[] { float.NaN, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f },
new[] { 1.0f, float.NaN, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f },
new[] { 1.0f, 1.0f, float.NaN, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f },
new[] { 1.0f, 1.0f, 1.0f, float.NaN, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f },
new[] { 1.0f, 1.0f, 1.0f, 1.0f, float.NaN, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f },
new[] { 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, float.NaN, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f },
new[] { 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, float.NaN, 1.0f, 1.0f, 1.0f, 1.0f },
new[] { 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, float.NaN, 1.0f, 1.0f, 1.0f },
};

[Theory]
[MemberData(nameof(SumOfSquaresNaNData))]
public static void SumOfSquares_With_NaN_Should_Return_NaN(float[] source)
{
// arrange
var expected = TensorPrimitives.SumOfSquares(source);

// act
var result = TensorOperations.SumOfSquares<float>(source);

// assert
Assert.Equal(expected, result);
}

public static TheoryData<int> SumOfSquaresData
=> new() {
{ 0 }, { 1 }, { 2 }, { 3 }, { 4 }, { 5 }, { 6 }, { 7 }, { 8 }, { 9 }, { 10 }, { 100 },
};

static void SumOfSquares_Should_Succeed<T>(int count)
where T : struct, INumber<T>
{
// arrange
var source = new T[count];
var expected = T.Zero;
var random = new Random(42);
for (var index = 0; index < source.Length; index++)
{
var value = T.CreateChecked(random.Next(10));
source[index] = value;
expected += value * value;
}

// act
var result = TensorOperations.SumOfSquares<T>(source);

// assert
Assert.Equal(expected, result);
}

[Theory]
[MemberData(nameof(SumOfSquaresData))]
public void SumOfSquares_Short_Should_Succeed(int count)
=> SumOfSquares_Should_Succeed<short>(count);

[Theory]
[MemberData(nameof(SumOfSquaresData))]
public void SumOfSquares_Int_Should_Succeed(int count)
=> SumOfSquares_Should_Succeed<int>(count);

[Theory]
[MemberData(nameof(SumOfSquaresData))]
public void SumOfSquares_Long_Should_Succeed(int count)
=> SumOfSquares_Should_Succeed<long>(count);

[Theory]
[MemberData(nameof(SumOfSquaresData))]
public void SumOfSquares_Half_Should_Succeed(int count)
=> SumOfSquares_Should_Succeed<Half>(count);

[Theory]
[MemberData(nameof(SumOfSquaresData))]
public void SumOfSquares_Float_Should_Succeed(int count)
=> SumOfSquares_Should_Succeed<float>(count);

[Theory]
[MemberData(nameof(SumOfSquaresData))]
public void SumOfSquares_Double_Should_Succeed(int count)
=> SumOfSquares_Should_Succeed<double>(count);
}
31 changes: 30 additions & 1 deletion src/NetFabric.Numerics.Tensors.UnitTests/SumTests.cs
Original file line number Diff line number Diff line change
@@ -1,7 +1,36 @@
namespace NetFabric.Numerics.Tensors.UnitTests;
using System.Numerics.Tensors;

namespace NetFabric.Numerics.Tensors.UnitTests;

public class SumTests
{
public static TheoryData<float[]> SumNaNData
=> new() {
new[] { float.NaN, float.NaN, float.NaN, float.NaN, float.NaN, float.NaN, float.NaN, float.NaN, float.NaN, float.NaN, float.NaN },
new[] { float.NaN, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f },
new[] { 1.0f, float.NaN, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f },
new[] { 1.0f, 1.0f, float.NaN, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f },
new[] { 1.0f, 1.0f, 1.0f, float.NaN, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f },
new[] { 1.0f, 1.0f, 1.0f, 1.0f, float.NaN, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f },
new[] { 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, float.NaN, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f },
new[] { 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, float.NaN, 1.0f, 1.0f, 1.0f, 1.0f },
new[] { 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, float.NaN, 1.0f, 1.0f, 1.0f },
};

[Theory]
[MemberData(nameof(SumNaNData))]
public static void Sum_With_NaN_Should_Return_NaN(float[] source)
{
// arrange
var expected = TensorPrimitives.Sum(source);

// act
var result = TensorOperations.Sum<float>(source);

// assert
Assert.Equal(expected, result);
}

public static TheoryData<int> SumData
=> new() {
{ 0 }, { 1 }, { 2 }, { 3 }, { 4 }, { 5 }, { 6 }, { 7 }, { 8 }, { 9 }, { 10 }, { 100 },
Expand Down
27 changes: 21 additions & 6 deletions src/NetFabric.Numerics.Tensors/Aggregate.cs
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,26 @@ public static partial class Tensor
/// <typeparam name="TAggregateOperator">The type of the aggregation operator that must implement the <see cref="IAggregationOperator{T, T}"/> interface.</typeparam>
/// <param name="source">The span of elements to aggregate.</param>
/// <returns>The result of the aggregation.</returns>
/// <remarks>This methods follows the IEEE 754 standard for floating-point arithmetic, it returns NaN if any of the elements is NaN.</remarks>
/// <remarks>This methods follows the IEEE 754 standard for floating-point arithmetic, it returns NaN if the transformation and aggregation of any of the elements result in NaN.</remarks>
public static T Aggregate<T, TAggregateOperator>(ReadOnlySpan<T> source)
where T : struct, INumberBase<T>
where TAggregateOperator : struct, IAggregationOperator<T, T>
=> Aggregate<T, T, T, IdentityOperator<T>, TAggregateOperator>(source);
=> Aggregate<T, IdentityOperator<T>, TAggregateOperator>(source);

/// <summary>
/// Aggregates the elements of a <see cref="ReadOnlySpan{T}"/> using the specified aggregation operator.
/// </summary>
/// <typeparam name="T">The type of the elements in the source span.</typeparam>
/// <typeparam name="TTransformOperator">The type of the transform operator that must implement the <see cref="IUnaryOperator{TSource, TTransformed}"/> interface.</typeparam>
/// <typeparam name="TAggregateOperator">The type of the aggregation operator that must implement the <see cref="IAggregationOperator{T, T}"/> interface.</typeparam>
/// <param name="source">The span of elements to aggregate.</param>
/// <returns>The result of the aggregation.</returns>
/// <remarks>This methods follows the IEEE 754 standard for floating-point arithmetic, it returns NaN if the transformation and aggregation of any of the elements result in NaN.</remarks>
public static T Aggregate<T, TTransformOperator, TAggregateOperator>(ReadOnlySpan<T> source)
where T : struct, INumberBase<T>
where TTransformOperator : struct, IUnaryOperator<T, T>
where TAggregateOperator : struct, IAggregationOperator<T, T>
=> Aggregate<T, T, T, TTransformOperator, TAggregateOperator>(source);

/// <summary>
/// Aggregates the elements of a <see cref="ReadOnlySpan{T1}"/> using the specified transform and aggregation operators.
Expand All @@ -27,13 +42,13 @@ public static T Aggregate<T, TAggregateOperator>(ReadOnlySpan<T> source)
/// <returns>The result of the aggregation.</returns>
/// <remarks>
/// <para>The transform operator is applied to the source elements before the aggregation operator.</para>
/// <para>This methods follows the IEEE 754 standard for floating-point arithmetic, it returns NaN if any of the elements is NaN.</para>
/// <para>This methods follows the IEEE 754 standard for floating-point arithmetic, it returns NaN if the transformation and aggregation of any of the elements result in NaN.</para>
/// </remarks>
public static TResult Aggregate<TSource, TTransformed, TResult, TTransformOperator, TAggregateOperator>(ReadOnlySpan<TSource> source)
where TSource : struct
where TTransformed : struct
where TResult : struct, INumberBase<TResult>
where TTransformOperator : struct, IUnaryOperator<TSource, TTransformed>
where TResult : struct, INumberBase<TResult>
where TTransformOperator : struct, IUnaryOperator<TSource, TTransformed>
where TAggregateOperator : struct, IAggregationOperator<TTransformed, TResult>
{
// initialize aggregate
Expand Down Expand Up @@ -132,7 +147,7 @@ public static T Aggregate<T, TTransformOperator, TAggregateOperator>(ReadOnlySpa
/// <returns>The result of the aggregation.</returns>
/// <remarks>
/// <para>The transform operator is applied to the source elements before the aggregation operator.</para>
/// <para>This methods follows the IEEE 754 standard for floating-point arithmetic, it returns NaN if any of the elements is NaN.</para>
/// <para>This methods follows the IEEE 754 standard for floating-point arithmetic, it returns NaN if the transformation and aggregation of any of the elements result in NaN.</para>
/// </remarks>
public static TResult Aggregate<T1, T2, TTransformed, TResult, TTransformOperator, TAggregateOperator>(ReadOnlySpan<T1> x, ReadOnlySpan<T2> y)
where T1 : struct
Expand Down
8 changes: 4 additions & 4 deletions src/NetFabric.Numerics.Tensors/IndexOfAggregate.cs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ public static partial class Tensor
/// <typeparam name="TAggregateOperator">The type of the aggregation operator that must implement the <see cref="IAggregationOperator{T, T}"/> interface.</typeparam>
/// <param name="source">The span of elements to aggregate.</param>
/// <returns>The result of the aggregation.</returns>
/// <remarks>This methods follows the IEEE 754 standard for floating-point arithmetic, it returns the index of the first NaN element if found.</remarks>
/// <remarks>This methods follows the IEEE 754 standard for floating-point arithmetic, it returns the index of the first element to which the transformation and aggregation results in NaN.</remarks>
public static int IndexOfAggregate<T, TAggregateOperator>(ReadOnlySpan<T> source)
where T : struct, INumberBase<T>
where TAggregateOperator : struct, IAggregationOperator<T, T>
Expand All @@ -27,7 +27,7 @@ public static int IndexOfAggregate<T, TAggregateOperator>(ReadOnlySpan<T> source
/// <returns>The result of the aggregation.</returns>
/// <remarks>
/// <para>The transform operator is applied to the source elements before the aggregation operator.</para>
/// <para>This methods follows the IEEE 754 standard for floating-point arithmetic, it returns the index of the first NaN element if found.</para>
/// <para>This methods follows the IEEE 754 standard for floating-point arithmetic, it returns the index of the first element to which the transformation and aggregation results in NaN.</para>
/// </remarks>
public static int IndexOfAggregate<TSource, TTransformed, TResult, TTransformOperator, TAggregateOperator>(ReadOnlySpan<TSource> source)
where TSource : struct
Expand Down Expand Up @@ -141,7 +141,7 @@ public static int IndexOfAggregate<TSource, TTransformed, TResult, TTransformOpe
/// <returns>The result of the aggregation.</returns>
/// <remarks>
/// <para>The transform operator is applied to the source elements before the aggregation operator.</para>
/// <para>This methods follows the IEEE 754 standard for floating-point arithmetic, it returns the index of the first NaN element if found.</para>
/// <para>This methods follows the IEEE 754 standard for floating-point arithmetic, it returns the index of the first element to which the transformation and aggregation results in NaN.</para>
/// </remarks>
public static int IndexOfAggregate<T, TTransformOperator, TAggregateOperator>(ReadOnlySpan<T> x, ReadOnlySpan<T> y)
where T : struct, INumberBase<T>
Expand All @@ -163,7 +163,7 @@ public static int IndexOfAggregate<T, TTransformOperator, TAggregateOperator>(Re
/// <returns>The result of the aggregation.</returns>
/// <remarks>
/// <para>The transform operator is applied to the source elements before the aggregation operator.</para>
/// <para>This methods follows the IEEE 754 standard for floating-point arithmetic, it returns the index of the first NaN element if found.</para>
/// <para>This methods follows the IEEE 754 standard for floating-point arithmetic, it returns the index of the first element to which the transformation and aggregation results in NaN.</para>
/// </remarks>
public static int IndexOfAggregate<T1, T2, TTransformed, TResult, TTransformOperator, TAggregateOperator>(ReadOnlySpan<T1> x, ReadOnlySpan<T2> y)
where T1 : struct
Expand Down
8 changes: 4 additions & 4 deletions src/NetFabric.Numerics.Tensors/Operations/Sum.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,10 @@ namespace NetFabric.Numerics.Tensors;
public static partial class TensorOperations
{
public static T Sum<T>(ReadOnlySpan<T> source)
where T : struct, INumberBase<T>
=> Tensor.Aggregate<T, SumOperator<T>>(source);

public static T SumNumber<T>(ReadOnlySpan<T> source)
where T : struct, IAdditionOperators<T, T, T>, IAdditiveIdentity<T, T>
=> Tensor.AggregateNumber<T, SumOperator<T>>(source);

Expand All @@ -17,8 +21,4 @@ public static (T, T, T) Sum3D<T>(ReadOnlySpan<T> source)
public static (T, T, T, T) Sum4D<T>(ReadOnlySpan<T> source)
where T : struct, IAdditionOperators<T, T, T>, IAdditiveIdentity<T, T>
=> Tensor.AggregateNumber4D<T, SumOperator<T>>(source);

public static T SumOfSquares<T>(ReadOnlySpan<T> source)
where T : struct, IMultiplyOperators<T, T, T>, IAdditionOperators<T, T, T>, IAdditiveIdentity<T, T>
=> Tensor.AggregateNumber<T, T, T, SquareOperator<T>, SumOperator<T>>(source);
}
12 changes: 12 additions & 0 deletions src/NetFabric.Numerics.Tensors/Operations/SumOfSquares.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
namespace NetFabric.Numerics.Tensors;

public static partial class TensorOperations
{
public static T SumOfSquares<T>(ReadOnlySpan<T> source)
where T : struct, INumberBase<T>
=> Tensor.Aggregate<T, SquareOperator<T>, SumOperator<T>>(source);

public static T SumOfSquaresNumber<T>(ReadOnlySpan<T> source)
where T : struct, IMultiplyOperators<T, T, T>, IAdditionOperators<T, T, T>, IAdditiveIdentity<T, T>
=> Tensor.AggregateNumber<T, SquareOperator<T>, SumOperator<T>>(source);
}

0 comments on commit 1d42a1b

Please sign in to comment.