Skip to content

Commit

Permalink
Add unit tests and benchmarks
Browse files Browse the repository at this point in the history
  • Loading branch information
aalmada committed Jan 1, 2024
1 parent f951128 commit f7e36b6
Show file tree
Hide file tree
Showing 7 changed files with 174 additions and 9 deletions.
65 changes: 65 additions & 0 deletions src/NetFabric.Numerics.Tensors.Benchmarks/SumPairsBenchmarks.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
using BenchmarkDotNet.Attributes;
using System.Runtime.InteropServices;

namespace NetFabric.Numerics.Tensors.Benchmarks;

public class SumPairsBenchmarks
{
ValueTuple<short, short>[]? arrayShort;
ValueTuple<int, int>[]? arrayInt;
ValueTuple<long, long>[]? arrayLong;
ValueTuple<Half, Half>[]? arrayHalf;
ValueTuple<float, float>[]? arrayFloat;
ValueTuple<double, double>[]? arrayDouble;

[Params(10_000)]
public int Count { get; set; }

[GlobalSetup]
public void GlobalSetup()
{
var range = Enumerable.Range(0, Count);
arrayShort = range
.Select(value => ((short)value, (short)(value + 1)))
.ToArray();
arrayInt = range
.Select(value => ((int)value, (int)(value + 1)))
.ToArray();
arrayLong = range
.Select(value => ((long)value, (long)(value + 1)))
.ToArray();
arrayHalf = range
.Select(value => ((Half)value, (Half)(value + 1)))
.ToArray();
arrayFloat = range
.Select(value => ((float)value, (float)(value + 1)))
.ToArray();
arrayDouble = range
.Select(value => ((double)value, (double)(value + 1)))
.ToArray();
}

[Benchmark]
public ValueTuple<short, short> Sum_Short()
=> Tensor.SumPairs<short>(MemoryMarshal.Cast<ValueTuple<short, short>, short>(arrayShort!));

[Benchmark]
public ValueTuple<int, int> Sum_Int()
=> Tensor.SumPairs<int>(MemoryMarshal.Cast<ValueTuple<int, int>, int>(arrayInt!));

[Benchmark]
public ValueTuple<long, long> Sum_Long()
=> Tensor.SumPairs<long>(MemoryMarshal.Cast<ValueTuple<long, long>, long>(arrayLong!));

[Benchmark]
public ValueTuple<Half, Half> Sum_Half()
=> Tensor.SumPairs<Half>(MemoryMarshal.Cast<ValueTuple<Half, Half>, Half>(arrayHalf!));

[Benchmark]
public ValueTuple<float, float> Sum_Float()
=> Tensor.SumPairs<float>(MemoryMarshal.Cast<ValueTuple<float, float>, float>(arrayFloat!));

[Benchmark]
public ValueTuple<double, double> Sum_Double()
=> Tensor.SumPairs<double>(MemoryMarshal.Cast<ValueTuple<double, double>, double>(arrayDouble!));
}
90 changes: 90 additions & 0 deletions src/NetFabric.Numerics.Tensors.UnitTests/SumPairsTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
using System.Linq;
using System.Runtime.InteropServices;

namespace NetFabric.Numerics.Tensors.UnitTests;

public class SumPairsTests
{
public static TheoryData<int> SumData
=> new() { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37 };

[Theory]
[MemberData(nameof(SumData))]
public void SumPairs_Short_Should_Succeed(int count)
{
// arrange
var source = Enumerable.Range(0, count).Select(value => ((short)value, (short)(value + 1))).ToArray();
var expected = source.Aggregate((0, 0), (sum, value) => (sum.Item1 + value.Item1, sum.Item2 + value.Item2));

// act
var result = Tensor.SumPairs<short>(MemoryMarshal.Cast<ValueTuple<short, short>, short>(source));

// assert
result.Item1.Should().Be((short)expected.Item1);
result.Item2.Should().Be((short)expected.Item2);
}

[Theory]
[MemberData(nameof(SumData))]
public void SumPairs_Int_Should_Succeed(int count)
{
// arrange
var source = Enumerable.Range(0, count).Select(value => (value, value + 1)).ToArray();
var expected = source.Aggregate((0, 0), (sum, value) => (sum.Item1 + value.Item1, sum.Item2 + value.Item2));

// act
var result = Tensor.SumPairs<int>(MemoryMarshal.Cast<ValueTuple<int, int>, int>(source));

// assert
result.Item1.Should().Be(expected.Item1);
result.Item2.Should().Be(expected.Item2);
}

[Theory]
[MemberData(nameof(SumData))]
public void SumPairs_Long_Should_Succeed(int count)
{
// arrange
var source = Enumerable.Range(0, count).Select(value => ((long)value, (long)(value + 1))).ToArray();
var expected = source.Aggregate((0l, 0l), (sum, value) => (sum.Item1 + value.Item1, sum.Item2 + value.Item2));

// act
var result = Tensor.SumPairs<long>(MemoryMarshal.Cast<ValueTuple<long, long>, long>(source));

// assert
result.Item1.Should().Be(expected.Item1);
result.Item2.Should().Be(expected.Item2);
}

[Theory]
[MemberData(nameof(SumData))]
public void SumPairs_Float_Should_Succeed(int count)
{
// arrange
var source = Enumerable.Range(0, count).Select(value => ((float)value, (float)(value + 1))).ToArray();
var expected = source.Aggregate((0.0f, 0.0f), (sum, value) => (sum.Item1 + value.Item1, sum.Item2 + value.Item2));

// act
var result = Tensor.SumPairs<float>(MemoryMarshal.Cast<ValueTuple<float, float>, float>(source));

// assert
result.Item1.Should().Be(expected.Item1);
result.Item2.Should().Be(expected.Item2);
}

[Theory]
[MemberData(nameof(SumData))]
public void SumPairs_Double_Should_Succeed(int count)
{
// arrange
var source = Enumerable.Range(0, count).Select(value => ((double)value, (double)(value + 1))).ToArray();
var expected = source.Aggregate((0.0, 0.0), (sum, value) => (sum.Item1 + value.Item1, sum.Item2 + value.Item2));

// act
var result = Tensor.SumPairs<double>(MemoryMarshal.Cast<ValueTuple<double, double>, double>(source));

// assert
result.Item1.Should().Be(expected.Item1);
result.Item2.Should().Be(expected.Item2);
}
}
2 changes: 1 addition & 1 deletion src/NetFabric.Numerics.Tensors.UnitTests/SumTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ public void Sum_Short_Should_Succeed(int count)
{
// arrange
var source = Enumerable.Range(0, count).Select(value => (short)value).ToArray();
var expected = Enumerable.Range(0, count).Sum();
var expected = source.Aggregate(0, (sum, value) => sum + value);

// act
var result = Tensor.Sum<short>(source);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,9 @@ public static partial class Tensor
/// <typeparam name="TOperator">The type of the aggregation operator.</typeparam>
/// <param name="source">The source span.</param>
/// <returns>The aggregated value.</returns>
public static ValueTuple<T, T> AggregatePair<T, TOperator>(ReadOnlySpan<T> source)
public static ValueTuple<T, T> AggregatePairs<T, TOperator>(ReadOnlySpan<T> source)
where T : struct
where TOperator : struct, IPairAggregationOperator<T>
where TOperator : struct, IAggregationPairsOperator<T>
{
var result = TOperator.Seed;
var resultVector = Vector<T>.Zero;
Expand Down
2 changes: 1 addition & 1 deletion src/NetFabric.Numerics.Tensors/ITensorOperation.cs
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ static virtual T Seed
static abstract T ResultSelector(T value, Vector<T> vector);
}

public interface IPairAggregationOperator<T>
public interface IAggregationPairsOperator<T>
: IBinaryOperator<T>
where T : struct
{
Expand Down
4 changes: 2 additions & 2 deletions src/NetFabric.Numerics.Tensors/Operators/SumOperator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,8 @@ public static Vector<T> Invoke(Vector<T> x, Vector<T> y)
=> x + y;
}

public readonly struct SumPairOperator<T>
: IPairAggregationOperator<T>
public readonly struct SumPairsOperator<T>
: IAggregationPairsOperator<T>
where T : struct, IAdditiveIdentity<T, T>, IAdditionOperators<T, T, T>
{
public static ValueTuple<T, T> Seed
Expand Down
16 changes: 13 additions & 3 deletions src/NetFabric.Numerics.Tensors/Sum.cs
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,18 @@ public static T Sum<T>(ReadOnlySpan<T> source)
where T : struct, IAdditionOperators<T, T, T>, IAdditiveIdentity<T, T>
=> Aggregate<T, SumOperator<T>>(source);

public static ValueTuple<T, T> SumPair<T>(ReadOnlySpan<T> source)
/// <summary>
/// Computes the sum of pairs of values in a span.
/// </summary>
/// <typeparam name="T">The type of the elements in the span.</typeparam>
/// <param name="source">The input span.</param>
/// <returns>A tuple containing the sum of pairs of elements.</returns>
/// <remarks>
/// This method can be used to calculate the sum of 2D vectors.
/// </remarks>
/// This method requires the type <typeparamref name="T"/> to implement the <see cref="IAdditionOperators{T, T, T}"/> and <see cref="IAdditiveIdentity{T, T}"/> interfaces.
/// </remarks>

Check warning on line 30 in src/NetFabric.Numerics.Tensors/Sum.cs

View workflow job for this annotation

GitHub Actions / build

XML comment has badly formed XML -- 'End tag was not expected at this location.'
public static ValueTuple<T, T> SumPairs<T>(ReadOnlySpan<T> source)
where T : struct, IAdditionOperators<T, T, T>, IAdditiveIdentity<T, T>
=> AggregatePair<T, SumPairOperator<T>>(source);

=> AggregatePairs<T, SumPairsOperator<T>>(source);
}

0 comments on commit f7e36b6

Please sign in to comment.