Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support sum of 2D vectors using tensor #43

Merged
merged 1 commit into from
Jan 2, 2024
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
10 changes: 3 additions & 7 deletions NetFabric.Numerics.sln
Original file line number Diff line number Diff line change
Expand Up @@ -56,13 +56,11 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "NetFabric.Numerics.Geodesy"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "NetFabric.Numerics.Geodesy.UnitTests", "src\NetFabric.Numerics.Geodesy.UnitTests\NetFabric.Numerics.Geodesy.UnitTests.csproj", "{4E4414C1-E5CF-4679-9016-7C3F70F13110}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "NetFabric.Numerics.Tensors", "src\NetFabric.Numerics.Tensors\NetFabric.Numerics.Tensors.csproj", "{8D2EE0A5-32A2-4019-AD55-7E9B70B76D67}"
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "NetFabric.Numerics.Tensors", "src\NetFabric.Numerics.Tensors\NetFabric.Numerics.Tensors.csproj", "{8D2EE0A5-32A2-4019-AD55-7E9B70B76D67}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{EC0C1351-970E-48A9-BDA3-3E5E2B095402}"
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "NetFabric.Numerics.Tensors.UnitTests", "src\NetFabric.Numerics.Tensors.UnitTests\NetFabric.Numerics.Tensors.UnitTests.csproj", "{31E6CCB0-E04A-4947-BF74-E8E70D5594E5}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "NetFabric.Numerics.Tensors.UnitTests", "src\NetFabric.Numerics.Tensors.UnitTests\NetFabric.Numerics.Tensors.UnitTests.csproj", "{31E6CCB0-E04A-4947-BF74-E8E70D5594E5}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "NetFabric.Numerics.Tensors.Benchmarks", "src\NetFabric.Numerics.Tensors.Benchmarks\NetFabric.Numerics.Tensors.Benchmarks.csproj", "{A8467A84-F9DD-4B6C-91BB-CF39029331A4}"
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "NetFabric.Numerics.Tensors.Benchmarks", "src\NetFabric.Numerics.Tensors.Benchmarks\NetFabric.Numerics.Tensors.Benchmarks.csproj", "{A8467A84-F9DD-4B6C-91BB-CF39029331A4}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Expand Down Expand Up @@ -118,8 +116,6 @@ Global
{A04C4E36-5F33-43F8-A7EE-15D81C7F8A58} = {762517CA-3F22-4DC6-955C-4F4FC69EB670}
{BA0837F2-730C-4537-B08B-5FFD2D91AF30} = {762517CA-3F22-4DC6-955C-4F4FC69EB670}
{83BDB68A-4F4B-4D7D-A7C7-55CC9F03657B} = {762517CA-3F22-4DC6-955C-4F4FC69EB670}
{31E6CCB0-E04A-4947-BF74-E8E70D5594E5} = {EC0C1351-970E-48A9-BDA3-3E5E2B095402}
{A8467A84-F9DD-4B6C-91BB-CF39029331A4} = {EC0C1351-970E-48A9-BDA3-3E5E2B095402}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {AACC30AE-BE57-461F-817B-2AE0F001A33F}
Expand Down
5 changes: 4 additions & 1 deletion src/NetFabric.Numerics.Angle/AngleAverage.cs
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,9 @@ public static partial class Angle
where TUnits : IAngleUnits
where T : struct, IFloatingPoint<T>, IMinMaxValue<T>
{
if(source.TryGetSpan(out var span))
return span.Average();

var sum = T.Zero;
var count = T.Zero;
foreach (var angle in source)
Expand Down Expand Up @@ -74,7 +77,7 @@ public static partial class Angle
public static Angle<TUnits, T>? Average<TUnits, T>(this ReadOnlySpan<Angle<TUnits, T>> source)
where TUnits : IAngleUnits
where T : struct, IFloatingPoint<T>, IMinMaxValue<T>
=> source.Length == 0
=> source.Length is 0
? null
: Sum(source) / T.CreateChecked(source.Length);
}
66 changes: 66 additions & 0 deletions src/NetFabric.Numerics.Benchmarks/ArrayAdditionBenchmarks.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
using BenchmarkDotNet.Attributes;
using BenchmarkDotNet.Configs;

namespace NetFabric.Numerics.Benchmarks;

[GroupBenchmarksBy(BenchmarkLogicalGroupRule.ByCategory)]
[CategoriesColumn]
public class ArrayAdditionBenchmarks
{
Rectangular2D.Vector<int>[]? rectangular2_int;
Rectangular2D.Vector<long>[]? rectangular2_long;
Rectangular2D.Vector<float>[]? rectangular2_float;
Rectangular2D.Vector<double>[]? rectangular2_double;

Polar.Vector<Degrees, float>[]? polar_float;
Polar.Vector<Degrees, double>[]? polar_double;

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

[GlobalSetup]
public void GlobalSetup()
{
rectangular2_int = GetEnumerable(Count).Select<(int x, int y), Rectangular2D.Vector<int>>(item => new(item.x, item.y)).ToArray();
rectangular2_long = GetEnumerable(Count).Select<(int x, int y), Rectangular2D.Vector<long>>(item => new(item.x, item.y)).ToArray();
rectangular2_float = GetEnumerable(Count).Select<(int x, int y), Rectangular2D.Vector<float>>(item => new(item.x, item.y)).ToArray();
rectangular2_double = GetEnumerable(Count).Select<(int x, int y), Rectangular2D.Vector<double>>(item => new(item.x, item.y)).ToArray();

polar_float = GetEnumerable(Count).Select<(int x, int y), Polar.Vector<Degrees, float>>(item => new(item.x, new(item.y))).ToArray();
polar_double = GetEnumerable(Count).Select<(int x, int y), Polar.Vector<Degrees, double>>(item => new(item.x, new(item.y))).ToArray();

static IEnumerable<(int x, int y)> GetEnumerable(int count)
{
var random = new Random(42);
for (var index = 0; index < count; index++)
{
yield return (random.Next(100), random.Next(100));
}
}
}

[Benchmark]
public void Rectangular2D_Int()
=> Rectangular2D.Vector.Add<int>(rectangular2_int!, rectangular2_int!, rectangular2_int!);

[Benchmark]
public void Rectangular2D_Long()
=> Rectangular2D.Vector.Add<long>(rectangular2_long!, rectangular2_long!, rectangular2_long!);

[Benchmark]
public void Rectangular2D_Float()
=> Rectangular2D.Vector.Add<float>(rectangular2_float!, rectangular2_float!, rectangular2_float!);

[Benchmark]
public void Polar_Float()
=> Polar.Vector.Add<Degrees, float>(polar_float!, polar_float!, polar_float!);

[Benchmark]
public void Rectangular2D_Double()
=> Rectangular2D.Vector.Add<double>(rectangular2_double!, rectangular2_double!, rectangular2_double!);

[Benchmark]
public void Polar_Double()
=> Polar.Vector.Add<Degrees, double>(polar_double!, polar_double!, polar_double!);

}
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
4 changes: 4 additions & 0 deletions src/NetFabric.Numerics.Tensors/Add.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,17 @@

namespace NetFabric.Numerics;

public static partial class Tensor

Check warning on line 5 in src/NetFabric.Numerics.Tensors/Add.cs

View workflow job for this annotation

GitHub Actions / build

Missing XML comment for publicly visible type or member 'Tensor'
{
public static void Add<T>(ReadOnlySpan<T> x, T y, Span<T> destination)

Check warning on line 7 in src/NetFabric.Numerics.Tensors/Add.cs

View workflow job for this annotation

GitHub Actions / build

Missing XML comment for publicly visible type or member 'Tensor.Add<T>(ReadOnlySpan<T>, T, Span<T>)'
where T : struct, IAdditionOperators<T, T, T>
=> Apply<T, AddOperator<T>>(x, y, destination);

public static void Add<T>(ReadOnlySpan<T> x, ValueTuple<T, T> y, Span<T> destination)

Check warning on line 11 in src/NetFabric.Numerics.Tensors/Add.cs

View workflow job for this annotation

GitHub Actions / build

Missing XML comment for publicly visible type or member 'Tensor.Add<T>(ReadOnlySpan<T>, (T, T), Span<T>)'
where T : struct, IAdditionOperators<T, T, T>
=> Apply<T, AddOperator<T>>(x, y, destination);

public static void Add<T>(ReadOnlySpan<T> x, ReadOnlySpan<T> y, Span<T> destination)

Check warning on line 15 in src/NetFabric.Numerics.Tensors/Add.cs

View workflow job for this annotation

GitHub Actions / build

Missing XML comment for publicly visible type or member 'Tensor.Add<T>(ReadOnlySpan<T>, ReadOnlySpan<T>, Span<T>)'
where T : struct, IAdditionOperators<T, T, T>
=> Apply<T, AddOperator<T>>(x, y, destination);
}
55 changes: 55 additions & 0 deletions src/NetFabric.Numerics.Tensors/AggregatePairs.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@

using System;
using System.Numerics;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;

namespace NetFabric.Numerics
{
public static partial class Tensor
{
/// <summary>
/// Aggregates the elements in the specified <see cref="ReadOnlySpan{T}"/> using the specified <see cref="IAggregationOperator{T}"/>.
/// </summary>
/// <typeparam name="T">The type of the elements in the span.</typeparam>
/// <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> AggregatePairs<T, TOperator>(ReadOnlySpan<T> source)
where T : struct
where TOperator : struct, IAggregationPairsOperator<T>
{
if (source.Length % 2 is not 0)
Throw.ArgumentException(nameof(source), "source span must have an even size.");

var result = TOperator.Seed;
var resultVector = Vector<T>.Zero;

nint index = 0;

if (Vector.IsHardwareAccelerated &&
Vector<T>.IsSupported &&
Vector<T>.Count % 2 is 0 &&
source.Length >= Vector<T>.Count)
{
var sourceVectors = MemoryMarshal.Cast<T, Vector<T>>(source);
resultVector = GetVector(TOperator.Seed);

ref var sourceVectorsRef = ref MemoryMarshal.GetReference(sourceVectors);
for (nint indexVector = 0; indexVector < sourceVectors.Length; indexVector++)
resultVector = TOperator.Invoke(resultVector, Unsafe.Add(ref sourceVectorsRef, indexVector));

index = source.Length - source.Length % Vector<T>.Count;
}

ref var sourceRef = ref MemoryMarshal.GetReference(source);
for (; index < source.Length; index += 2)
{
result.Item1 = TOperator.Invoke(result.Item1, Unsafe.Add(ref sourceRef, index));
result.Item2 = TOperator.Invoke(result.Item2, Unsafe.Add(ref sourceRef, index + 1));
}

return TOperator.ResultSelector(result, resultVector);
}
}
}
Loading
Loading