From 571e8ef248c9ac4f014762aa6b173e63c8421972 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ant=C3=A3o=20Almada?= Date: Thu, 4 Jan 2024 20:57:09 +0000 Subject: [PATCH] Support triplets --- LICENSE | 2 +- src/Directory.Build.props | 2 +- .../AddMultiplyPairsTests.cs | 119 ++----- .../AddMultiplyTests.cs | 98 ++--- .../AddMultiplyTripletsTests.cs | 70 ++++ .../AddMultiplyValuePairsTests.cs | 337 +++++------------- .../AddMultiplyValueTests.cs | 261 ++++---------- .../AddMultiplyValueTripletsTests.cs | 182 ++++++++++ .../AddPairsTests.cs | 105 ++---- .../AddTests.cs | 84 ++--- .../AddTripletsTests.cs | 65 ++++ .../AddValuePairsTests.cs | 84 ++--- .../AddValueTests.cs | 85 ++--- .../AddValueTripletsTests.cs | 61 ++++ .../MyVector.cs | 35 ++ ...etFabric.Numerics.Tensors.UnitTests.csproj | 3 +- .../SquareTests.cs | 79 ++-- .../SumPairsTests.cs | 80 ++--- .../SumTests.cs | 75 ++-- .../SumTripletsTests.cs | 56 +++ src/NetFabric.Numerics.Tensors/Add.cs | 48 ++- src/NetFabric.Numerics.Tensors/AddMultiply.cs | 132 ++++++- src/NetFabric.Numerics.Tensors/Aggregate.cs | 51 +-- .../AggregatePairs.cs | 69 ++-- .../AggregateTriplets.cs | 33 ++ src/NetFabric.Numerics.Tensors/ApplyBinary.cs | 44 ++- .../ApplyTernary.cs | 136 +++++-- src/NetFabric.Numerics.Tensors/ApplyUnary.cs | 7 +- src/NetFabric.Numerics.Tensors/Average.cs | 2 - src/NetFabric.Numerics.Tensors/Divide.cs | 50 ++- .../ITensorOperation.cs | 117 +++++- src/NetFabric.Numerics.Tensors/Multiply.cs | 50 ++- src/NetFabric.Numerics.Tensors/Negate.cs | 10 +- .../NetFabric.Numerics.Tensors.csproj | 3 +- .../Operators/AddMultiplyOperator.cs | 38 +- .../Operators/AddOperator.cs | 18 +- .../Operators/DivideOperator.cs | 18 +- .../Operators/MultiplyAddOperator.cs | 20 +- .../Operators/MultiplyOperator.cs | 18 +- .../Operators/NegateOperator.cs | 16 +- .../Operators/SquareOperator.cs | 16 +- .../Operators/SubtractOperator.cs | 18 +- .../Operators/SumOperator.cs | 129 +++++-- src/NetFabric.Numerics.Tensors/README.md | 4 +- src/NetFabric.Numerics.Tensors/Square.cs | 12 +- src/NetFabric.Numerics.Tensors/Subtract.cs | 50 ++- src/NetFabric.Numerics.Tensors/Sum.cs | 17 +- src/NetFabric.Numerics.Tensors/Tensor.cs | 5 - .../VectorExtensions.cs | 15 - .../Rectangular3D/Vector.cs | 2 +- .../Rectangular3D/VectorSpanOperations.cs | 31 ++ 51 files changed, 1807 insertions(+), 1255 deletions(-) create mode 100644 src/NetFabric.Numerics.Tensors.UnitTests/AddMultiplyTripletsTests.cs create mode 100644 src/NetFabric.Numerics.Tensors.UnitTests/AddMultiplyValueTripletsTests.cs create mode 100644 src/NetFabric.Numerics.Tensors.UnitTests/AddTripletsTests.cs create mode 100644 src/NetFabric.Numerics.Tensors.UnitTests/AddValueTripletsTests.cs create mode 100644 src/NetFabric.Numerics.Tensors.UnitTests/MyVector.cs create mode 100644 src/NetFabric.Numerics.Tensors.UnitTests/SumTripletsTests.cs create mode 100644 src/NetFabric.Numerics.Tensors/AggregateTriplets.cs delete mode 100644 src/NetFabric.Numerics.Tensors/VectorExtensions.cs create mode 100644 src/NetFabric.Numerics/Rectangular3D/VectorSpanOperations.cs diff --git a/LICENSE b/LICENSE index 3680af3..c26a50a 100644 --- a/LICENSE +++ b/LICENSE @@ -1,6 +1,6 @@ MIT License -Copyright (c) 2023 Antão Almada +Copyright (c) 2023-2024 Antão Almada Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/src/Directory.Build.props b/src/Directory.Build.props index 56dfb69..1eca1f2 100644 --- a/src/Directory.Build.props +++ b/src/Directory.Build.props @@ -3,7 +3,7 @@ NetFabric Antao Almada - Copyright 2023 Antao Almada + Copyright 2023-2024 Antao Almada 12 strict enable diff --git a/src/NetFabric.Numerics.Tensors.UnitTests/AddMultiplyPairsTests.cs b/src/NetFabric.Numerics.Tensors.UnitTests/AddMultiplyPairsTests.cs index 7a6bea0..b326227 100644 --- a/src/NetFabric.Numerics.Tensors.UnitTests/AddMultiplyPairsTests.cs +++ b/src/NetFabric.Numerics.Tensors.UnitTests/AddMultiplyPairsTests.cs @@ -1,5 +1,4 @@ -using System.Linq; -using System.Runtime.InteropServices; +using System.Runtime.InteropServices; namespace NetFabric.Numerics.Tensors.UnitTests; @@ -8,23 +7,31 @@ public class AddMultiplyPairsTests public static TheoryData AddData => 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(AddData))] - public void AddMultiply_Short_Should_Succeed(int count) + static void AddMultiply_Should_Succeed(int count) + where T : struct, INumber { // arrange - var x = Enumerable.Range(0, count).Select(value => ((short)value, (short)(value + 1))).ToArray(); - var y = Enumerable.Range(0, count).Select(value => ((short)(value + 2), (short)(value + 3))).ToArray(); - var z = Enumerable.Range(0, count).Select(value => ((short)(value + 4), (short)(value + 5))).ToArray(); - var result = new ValueTuple[count]; - var expected = Enumerable.Range(0, count).Select(value => ((short)((value + value + 2) * (value + 4)), (short)((value + value + 4) * (value + 5)))).ToArray(); + var source = Enumerable.Range(0, count); + var x = source + .Select(value => new MyVector2(T.CreateChecked(value), T.CreateChecked(value + 1))) + .ToArray(); + var y = source + .Select(value => new MyVector2(T.CreateChecked(value + 2), T.CreateChecked(value + 3))) + .ToArray(); + var z = source + .Select(value => new MyVector2(T.CreateChecked(value + 4), T.CreateChecked(value + 5))) + .ToArray(); + var result = new MyVector2[count]; + var expected = source + .Select(value => new MyVector2(T.CreateChecked((value + value + 2) * (value + 4)), T.CreateChecked((value + value + 4) * (value + 5)))) + .ToArray(); // act Tensor.AddMultiply( - MemoryMarshal.Cast, short>(x), - MemoryMarshal.Cast, short>(y), - MemoryMarshal.Cast, short>(z), - MemoryMarshal.Cast, short>(result)); + MemoryMarshal.Cast, T>(x), + MemoryMarshal.Cast, T>(y), + MemoryMarshal.Cast, T>(z), + MemoryMarshal.Cast, T>(result)); // assert result.Should().Equal(expected); @@ -32,90 +39,32 @@ public void AddMultiply_Short_Should_Succeed(int count) [Theory] [MemberData(nameof(AddData))] - public void AddMultiply_Int_Should_Succeed(int count) - { - // arrange - var x = Enumerable.Range(0, count).Select(value => (value, value + 1)).ToArray(); - var y = Enumerable.Range(0, count).Select(value => (value + 2, value + 3)).ToArray(); - var z = Enumerable.Range(0, count).Select(value => (value + 4, value + 5)).ToArray(); - var result = new ValueTuple[count]; - var expected = Enumerable.Range(0, count).Select(value => ((value + value + 2) * (value + 4), (value + value + 4) * (value + 5))).ToArray(); - - // act - Tensor.AddMultiply( - MemoryMarshal.Cast, int>(x), - MemoryMarshal.Cast, int>(y), - MemoryMarshal.Cast, int>(z), - MemoryMarshal.Cast, int>(result)); + public void AddMultiply_Short_Should_Succeed(int count) + => AddMultiply_Should_Succeed(count); - // assert - result.Should().Equal(expected); - } + [Theory] + [MemberData(nameof(AddData))] + public void AddMultiply_Int_Should_Succeed(int count) + => AddMultiply_Should_Succeed(count); [Theory] [MemberData(nameof(AddData))] public void AddMultiply_Long_Should_Succeed(int count) - { - // arrange - var x = Enumerable.Range(0, count).Select(value => ((long)value, (long)(value + 1))).ToArray(); - var y = Enumerable.Range(0, count).Select(value => ((long)(value + 2), (long)(value + 3))).ToArray(); - var z = Enumerable.Range(0, count).Select(value => ((long)(value + 4), (long)(value + 5))).ToArray(); - var result = new ValueTuple[count]; - var expected = Enumerable.Range(0, count).Select(value => ((long)((value + value + 2) * (value + 4)), (long)((value + value + 4) * (value + 5)))).ToArray(); + => AddMultiply_Should_Succeed(count); - // act - Tensor.AddMultiply( - MemoryMarshal.Cast, long>(x), - MemoryMarshal.Cast, long>(y), - MemoryMarshal.Cast, long>(z), - MemoryMarshal.Cast, long>(result)); - - // assert - result.Should().Equal(expected); - } + [Theory] + [MemberData(nameof(AddData))] + public void AddMultiply_Half_Should_Succeed(int count) + => AddMultiply_Should_Succeed(count); [Theory] [MemberData(nameof(AddData))] public void AddMultiply_Float_Should_Succeed(int count) - { - // arrange - var x = Enumerable.Range(0, count).Select(value => ((float)value, (float)(value + 1))).ToArray(); - var y = Enumerable.Range(0, count).Select(value => ((float)(value + 2), (float)(value + 3))).ToArray(); - var z = Enumerable.Range(0, count).Select(value => ((float)(value + 4), (float)(value + 5))).ToArray(); - var result = new ValueTuple[count]; - var expected = Enumerable.Range(0, count).Select(value => ((float)((value + value + 2) * (value + 4)), (float)((value + value + 4) * (value + 5)))).ToArray(); - - // act - Tensor.AddMultiply( - MemoryMarshal.Cast, float>(x), - MemoryMarshal.Cast, float>(y), - MemoryMarshal.Cast, float>(z), - MemoryMarshal.Cast, float>(result)); - - // assert - result.Should().Equal(expected); - } + => AddMultiply_Should_Succeed(count); [Theory] [MemberData(nameof(AddData))] public void AddMultiply_Double_Should_Succeed(int count) - { - // arrange - var x = Enumerable.Range(0, count).Select(value => ((double)value, (double)(value + 1))).ToArray(); - var y = Enumerable.Range(0, count).Select(value => ((double)(value + 2), (double)(value + 3))).ToArray(); - var z = Enumerable.Range(0, count).Select(value => ((double)(value + 4), (double)(value + 5))).ToArray(); - var result = new ValueTuple[count]; - var expected = Enumerable.Range(0, count).Select(value => ((double)((value + value + 2) * (value + 4)), (double)((value + value + 4) * (value + 5)))).ToArray(); - - // act - Tensor.AddMultiply( - MemoryMarshal.Cast, double>(x), - MemoryMarshal.Cast, double>(y), - MemoryMarshal.Cast, double>(z), - MemoryMarshal.Cast, double>(result)); - - // assert - result.Should().Equal(expected); - } + => AddMultiply_Should_Succeed(count); } diff --git a/src/NetFabric.Numerics.Tensors.UnitTests/AddMultiplyTests.cs b/src/NetFabric.Numerics.Tensors.UnitTests/AddMultiplyTests.cs index 9c584d9..742aac2 100644 --- a/src/NetFabric.Numerics.Tensors.UnitTests/AddMultiplyTests.cs +++ b/src/NetFabric.Numerics.Tensors.UnitTests/AddMultiplyTests.cs @@ -1,25 +1,31 @@ -using System.Linq; - -namespace NetFabric.Numerics.Tensors.UnitTests; +namespace NetFabric.Numerics.Tensors.UnitTests; public class AddMultiplyTests { public static TheoryData AddData => 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(AddData))] - public void AddMultiply_Short_Should_Succeed(int count) + static void AddMultiply_Should_Succeed(int count) + where T : struct, INumber { // arrange - var x = Enumerable.Range(0, count).Select(value => (short)value).ToArray(); - var y = Enumerable.Range(0, count).Select(value => (short)(value + 1)).ToArray(); - var z = Enumerable.Range(0, count).Select(value => (short)(value + 2)).ToArray(); - var result = new short[count]; - var expected = Enumerable.Range(0, count).Select(value => (short)((value + value + 1) * (value + 2))).ToArray(); + var source = Enumerable.Range(0, count); + var x = source + .Select(value => T.CreateChecked(value)) + .ToArray(); + var y = source + .Select(value => T.CreateChecked(value + 1)) + .ToArray(); + var z = source + .Select(value => T.CreateChecked(value + 2)) + .ToArray(); + var result = new T[count]; + var expected = source + .Select(value => T.CreateChecked((value + value + 1) * (value + 2))) + .ToArray(); // act - Tensor.AddMultiply(x, y, z, result); + Tensor.AddMultiply(x, y, z, result); // assert result.Should().Equal(expected); @@ -27,74 +33,32 @@ public void AddMultiply_Short_Should_Succeed(int count) [Theory] [MemberData(nameof(AddData))] - public void AddMultiply_Int_Should_Succeed(int count) - { - // arrange - var x = Enumerable.Range(0, count).ToArray(); - var y = Enumerable.Range(0, count).Select(value => value + 1).ToArray(); - var z = Enumerable.Range(0, count).Select(value => value + 2).ToArray(); - var result = new int[count]; - var expected = Enumerable.Range(0, count).Select(value => (value + value + 1) * (value + 2)).ToArray(); - - // act - Tensor.AddMultiply(x, y, z, result); + public void AddMultiply_Short_Should_Succeed(int count) + => AddMultiply_Should_Succeed(count); - // assert - result.Should().Equal(expected); - } + [Theory] + [MemberData(nameof(AddData))] + public void AddMultiply_Int_Should_Succeed(int count) + => AddMultiply_Should_Succeed(count); [Theory] [MemberData(nameof(AddData))] public void AddMultiply_Long_Should_Succeed(int count) - { - // arrange - var x = Enumerable.Range(0, count).Select(value => (long)value).ToArray(); - var y = Enumerable.Range(0, count).Select(value => (long)(value + 1)).ToArray(); - var z = Enumerable.Range(0, count).Select(value => (long)(value + 2)).ToArray(); - var result = new long[count]; - var expected = Enumerable.Range(0, count).Select(value => (long)((value + value + 1) * (value + 2))).ToArray(); + => AddMultiply_Should_Succeed(count); - // act - Tensor.AddMultiply(x, y, z, result); - - // assert - result.Should().Equal(expected); - } + [Theory] + [MemberData(nameof(AddData))] + public void AddMultiply_Half_Should_Succeed(int count) + => AddMultiply_Should_Succeed(count); [Theory] [MemberData(nameof(AddData))] public void AddMultiply_Float_Should_Succeed(int count) - { - // arrange - var x = Enumerable.Range(0, count).Select(value => (float)value).ToArray(); - var y = Enumerable.Range(0, count).Select(value => (float)(value + 1)).ToArray(); - var z = Enumerable.Range(0, count).Select(value => (float)(value + 2)).ToArray(); - var result = new float[count]; - var expected = Enumerable.Range(0, count).Select(value => (float)((value + value + 1) * (value + 2))).ToArray(); - - // act - Tensor.AddMultiply(x, y, z, result); - - // assert - result.Should().Equal(expected); - } + => AddMultiply_Should_Succeed(count); [Theory] [MemberData(nameof(AddData))] public void AddMultiply_Double_Should_Succeed(int count) - { - // arrange - var x = Enumerable.Range(0, count).Select(value => (double)value).ToArray(); - var y = Enumerable.Range(0, count).Select(value => (double)(value + 1)).ToArray(); - var z = Enumerable.Range(0, count).Select(value => (double)(value + 2)).ToArray(); - var result = new double[count]; - var expected = Enumerable.Range(0, count).Select(value => (double)((value + value + 1) * (value + 2))).ToArray(); - - // act - Tensor.AddMultiply(x, y, z, result); - - // assert - result.Should().Equal(expected); - } + => AddMultiply_Should_Succeed(count); } diff --git a/src/NetFabric.Numerics.Tensors.UnitTests/AddMultiplyTripletsTests.cs b/src/NetFabric.Numerics.Tensors.UnitTests/AddMultiplyTripletsTests.cs new file mode 100644 index 0000000..fea6828 --- /dev/null +++ b/src/NetFabric.Numerics.Tensors.UnitTests/AddMultiplyTripletsTests.cs @@ -0,0 +1,70 @@ +using System.Runtime.InteropServices; + +namespace NetFabric.Numerics.Tensors.UnitTests; + +public class AddMultiplyTripletsTests +{ + public static TheoryData AddData + => 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 }; + + static void AddMultiply_Should_Succeed(int count) + where T : struct, INumber + { + // arrange + var source = Enumerable.Range(0, count); + var x = source + .Select(value => new MyVector3(T.CreateChecked(value), T.CreateChecked(value + 1), T.CreateChecked(value + 2))) + .ToArray(); + var y = source + .Select(value => new MyVector3(T.CreateChecked(value + 2), T.CreateChecked(value + 3), T.CreateChecked(value + 4))) + .ToArray(); + var z = source + .Select(value => new MyVector3(T.CreateChecked(value + 4), T.CreateChecked(value + 5), T.CreateChecked(value + 6))) + .ToArray(); + var result = new MyVector3[count]; + var expected = source + .Select(value => new MyVector3(T.CreateChecked((value + value + 2) * (value + 4)), T.CreateChecked((value + value + 4) * (value + 5)), T.CreateChecked((value + value + 6) * (value + 6)))) + .ToArray(); + + // act + Tensor.AddMultiply( + MemoryMarshal.Cast, T>(x), + MemoryMarshal.Cast, T>(y), + MemoryMarshal.Cast, T>(z), + MemoryMarshal.Cast, T>(result)); + + // assert + result.Should().Equal(expected); + } + + [Theory] + [MemberData(nameof(AddData))] + public void AddMultiply_Short_Should_Succeed(int count) + => AddMultiply_Should_Succeed(count); + + [Theory] + [MemberData(nameof(AddData))] + public void AddMultiply_Int_Should_Succeed(int count) + => AddMultiply_Should_Succeed(count); + + [Theory] + [MemberData(nameof(AddData))] + public void AddMultiply_Long_Should_Succeed(int count) + => AddMultiply_Should_Succeed(count); + + [Theory] + [MemberData(nameof(AddData))] + public void AddMultiply_Half_Should_Succeed(int count) + => AddMultiply_Should_Succeed(count); + + [Theory] + [MemberData(nameof(AddData))] + public void AddMultiply_Float_Should_Succeed(int count) + => AddMultiply_Should_Succeed(count); + + [Theory] + [MemberData(nameof(AddData))] + public void AddMultiply_Double_Should_Succeed(int count) + => AddMultiply_Should_Succeed(count); + +} diff --git a/src/NetFabric.Numerics.Tensors.UnitTests/AddMultiplyValuePairsTests.cs b/src/NetFabric.Numerics.Tensors.UnitTests/AddMultiplyValuePairsTests.cs index 3f9793d..0ed4136 100644 --- a/src/NetFabric.Numerics.Tensors.UnitTests/AddMultiplyValuePairsTests.cs +++ b/src/NetFabric.Numerics.Tensors.UnitTests/AddMultiplyValuePairsTests.cs @@ -1,5 +1,4 @@ -using System.Linq; -using System.Runtime.InteropServices; +using System.Runtime.InteropServices; namespace NetFabric.Numerics.Tensors.UnitTests; @@ -8,334 +7,178 @@ public class AddMultiplyValuePairsTests public static TheoryData AddData => 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(AddData))] - public void AddMultiply_First_Short_Should_Succeed(int count) + static void AddMultiply_First_Should_Succeed(int count) + where T : struct, INumber { // arrange - var x = Enumerable.Range(0, count).Select(value => ((short)value, (short)(value + 1))).ToArray(); - var y = ((short)24, (short)25); - var z = Enumerable.Range(0, count).Select(value => ((short)(value + 2), (short)(value + 3))).ToArray(); - var result = new ValueTuple[count]; - var expected = Enumerable.Range(0, count).Select(value => ((short)((value + 24) * (value + 2)), (short)((value + 26) * (value + 3)))).ToArray(); + var source = Enumerable.Range(0, count); + var x = source + .Select(value => new MyVector2(T.CreateChecked(value), T.CreateChecked(value + 1))) + .ToArray(); + var y = (T.CreateChecked(24), T.CreateChecked(25)); + var z = source + .Select(value => new MyVector2(T.CreateChecked(value + 2), T.CreateChecked(value + 3))) + .ToArray(); + var result = new MyVector2[count]; + var expected = source + .Select(value => new MyVector2(T.CreateChecked((value + 24) * (value + 2)), T.CreateChecked((value + 26) * (value + 3)))) + .ToArray(); // act Tensor.AddMultiply( - MemoryMarshal.Cast, short>(x), + MemoryMarshal.Cast, T>(x), y, - MemoryMarshal.Cast, short>(z), - MemoryMarshal.Cast, short>(result)); + MemoryMarshal.Cast, T>(z), + MemoryMarshal.Cast, T>(result)); // assert result.Should().Equal(expected); } - [Theory] - [MemberData(nameof(AddData))] - public void AddMultiply_Second_Short_Should_Succeed(int count) + + static void AddMultiply_Second_Should_Succeed(int count) + where T : struct, INumber { // arrange - var x = Enumerable.Range(0, count).Select(value => ((short)value, (short)(value + 1))).ToArray(); - var y = Enumerable.Range(0, count).Select(value => ((short)(value + 2), (short)(value + 3))).ToArray(); - var z = ((short)42, (short)43); - var result = new ValueTuple[count]; - var expected = Enumerable.Range(0, count).Select(value => ((short)((value + value + 2) * 42), (short)((value + value + 4) * 43))).ToArray(); + var source = Enumerable.Range(0, count); + var x = source + .Select(value => new MyVector2(T.CreateChecked(value), T.CreateChecked(value + 1))) + .ToArray(); + var y = source + .Select(value => new MyVector2(T.CreateChecked(value + 2), T.CreateChecked(value + 3))) + .ToArray(); + var z = (T.CreateChecked(42), T.CreateChecked(43)); + var result = new MyVector2[count]; + var expected = source + .Select(value => new MyVector2(T.CreateChecked((value + value + 2) * 42), T.CreateChecked((value + value + 4) * 43))) + .ToArray(); // act Tensor.AddMultiply( - MemoryMarshal.Cast, short>(x), - MemoryMarshal.Cast, short>(y), + MemoryMarshal.Cast, T>(x), + MemoryMarshal.Cast, T>(y), z, - MemoryMarshal.Cast, short>(result)); + MemoryMarshal.Cast, T>(result)); // assert result.Should().Equal(expected); } - [Theory] - [MemberData(nameof(AddData))] - public void AddMultiply_Both_Short_Should_Succeed(int count) + static void AddMultiply_Both_Should_Succeed(int count) + where T : struct, INumber { // arrange - var x = Enumerable.Range(0, count).Select(value => ((short)value, (short)(value + 1))).ToArray(); - var y = ((short)24, (short)25); - var z = ((short)42, (short)43); - var result = new ValueTuple[count]; - var expected = Enumerable.Range(0, count).Select(value => ((short)((value + 24) * 42), (short)((value + 26) * 43))).ToArray(); + var source = Enumerable.Range(0, count); + var x = source + .Select(value => new MyVector2(T.CreateChecked(value), T.CreateChecked(value + 1))) + .ToArray(); + var y = (T.CreateChecked(24), T.CreateChecked(25)); + var z = (T.CreateChecked(42), T.CreateChecked(43)); + var result = new MyVector2[count]; + var expected = source + .Select(value => new MyVector2(T.CreateChecked((value + 24) * 42), T.CreateChecked((value + 26) * 43))) + .ToArray(); // act Tensor.AddMultiply( - MemoryMarshal.Cast, short>(x), + MemoryMarshal.Cast, T>(x), y, z, - MemoryMarshal.Cast, short>(result)); + MemoryMarshal.Cast, T>(result)); // assert result.Should().Equal(expected); } + [Theory] [MemberData(nameof(AddData))] - public void AddMultiply_First_Int_Should_Succeed(int count) - { - // arrange - var x = Enumerable.Range(0, count).Select(value => (value, value + 1)).ToArray(); - var y = (24, 25); - var z = Enumerable.Range(0, count).Select(value => (value + 2, value + 3)).ToArray(); - var result = new ValueTuple[count]; - var expected = Enumerable.Range(0, count).Select(value => ((value + 24) * (value + 2), (value + 26) * (value + 3))).ToArray(); - - // act - Tensor.AddMultiply( - MemoryMarshal.Cast, int>(x), - y, - MemoryMarshal.Cast, int>(z), - MemoryMarshal.Cast, int>(result)); + public void AddMultiply_First_Short_Should_Succeed(int count) + => AddMultiply_First_Should_Succeed(count); - // assert - result.Should().Equal(expected); - } + [Theory] + [MemberData(nameof(AddData))] + public void AddMultiply_Second_Short_Should_Succeed(int count) + => AddMultiply_Second_Should_Succeed(count); [Theory] [MemberData(nameof(AddData))] - public void AddMultiply_Second_Int_Should_Succeed(int count) - { - // arrange - var x = Enumerable.Range(0, count).Select(value => (value, value + 1)).ToArray(); - var y = Enumerable.Range(0, count).Select(value => (value + 2, value + 3)).ToArray(); - var z = (42, 43); - var result = new ValueTuple[count]; - var expected = Enumerable.Range(0, count).Select(value => ((value + value + 2) * 42, (value + value + 4) * 43)).ToArray(); + public void AddMultiply_Both_Short_Should_Succeed(int count) + => AddMultiply_Both_Should_Succeed(count); - // act - Tensor.AddMultiply( - MemoryMarshal.Cast, int>(x), - MemoryMarshal.Cast, int>(y), - z, - MemoryMarshal.Cast, int>(result)); + [Theory] + [MemberData(nameof(AddData))] + public void AddMultiply_First_Int_Should_Succeed(int count) + => AddMultiply_First_Should_Succeed(count); - // assert - result.Should().Equal(expected); - } + [Theory] + [MemberData(nameof(AddData))] + public void AddMultiply_Second_Int_Should_Succeed(int count) + => AddMultiply_Second_Should_Succeed(count); [Theory] [MemberData(nameof(AddData))] public void AddMultiply_Both_Int_Should_Succeed(int count) - { - // arrange - var x = Enumerable.Range(0, count).Select(value => (value, value + 1)).ToArray(); - var y = (24, 25); - var z = (42, 43); - var result = new ValueTuple[count]; - var expected = Enumerable.Range(0, count).Select(value => ((value + 24) * 42, (value + 26) * 43)).ToArray(); - - // act - Tensor.AddMultiply( - MemoryMarshal.Cast, int>(x), - y, - z, - MemoryMarshal.Cast, int>(result)); - - // assert - result.Should().Equal(expected); - } + => AddMultiply_Both_Should_Succeed(count); [Theory] [MemberData(nameof(AddData))] public void AddMultiply_First_Long_Should_Succeed(int count) - { - // arrange - var x = Enumerable.Range(0, count).Select(value => ((long)value, (long)(value + 1))).ToArray(); - var y = (24l, 25l); - var z = Enumerable.Range(0, count).Select(value => ((long)(value + 2), (long)(value + 3))).ToArray(); - var result = new ValueTuple[count]; - var expected = Enumerable.Range(0, count).Select(value => ((long)((value + 24) * (value + 2)), (long)((value + 26) * (value + 3)))).ToArray(); - - // act - Tensor.AddMultiply( - MemoryMarshal.Cast, long>(x), - y, - MemoryMarshal.Cast, long>(z), - MemoryMarshal.Cast, long>(result)); - - // assert - result.Should().Equal(expected); - } + => AddMultiply_First_Should_Succeed(count); [Theory] [MemberData(nameof(AddData))] public void AddMultiply_Second_Long_Should_Succeed(int count) - { - // arrange - var x = Enumerable.Range(0, count).Select(value => ((long)value, (long)(value + 1))).ToArray(); - var y = Enumerable.Range(0, count).Select(value => ((long)(value + 2), (long)(value + 3))).ToArray(); - var z = (42l, 43l); - var result = new ValueTuple[count]; - var expected = Enumerable.Range(0, count).Select(value => ((long)((value + value + 2) * 42), (long)((value + value + 4) * 43))).ToArray(); - - // act - Tensor.AddMultiply( - MemoryMarshal.Cast, long>(x), - MemoryMarshal.Cast, long>(y), - z, - MemoryMarshal.Cast, long>(result)); - - // assert - result.Should().Equal(expected); - } + => AddMultiply_Second_Should_Succeed(count); [Theory] [MemberData(nameof(AddData))] public void AddMultiply_Both_Long_Should_Succeed(int count) - { - // arrange - var x = Enumerable.Range(0, count).Select(value => ((long)value, (long)(value + 1))).ToArray(); - var y = (24l, 25l); - var z = (42l, 43l); - var result = new ValueTuple[count]; - var expected = Enumerable.Range(0, count).Select(value => ((long)((value + 24) * 42), (long)((value + 26) * 43))).ToArray(); - - // act - Tensor.AddMultiply( - MemoryMarshal.Cast, long>(x), - y, - z, - MemoryMarshal.Cast, long>(result)); + => AddMultiply_Both_Should_Succeed(count); - // assert - result.Should().Equal(expected); - } + [Theory] + [MemberData(nameof(AddData))] + public void AddMultiply_First_Half_Should_Succeed(int count) + => AddMultiply_First_Should_Succeed(count); [Theory] [MemberData(nameof(AddData))] - public void AddMultiply_First_Float_Should_Succeed(int count) - { - // arrange - var x = Enumerable.Range(0, count).Select(value => ((float)value, (float)(value + 1))).ToArray(); - var y = (24f, 25f); - var z = Enumerable.Range(0, count).Select(value => ((float)(value + 2), (float)(value + 3))).ToArray(); - var result = new ValueTuple[count]; - var expected = Enumerable.Range(0, count).Select(value => ((float)((value + 24) * (value + 2)), (float)((value + 26) * (value + 3)))).ToArray(); + public void AddMultiply_Second_Half_Should_Succeed(int count) + => AddMultiply_Second_Should_Succeed(count); - // act - Tensor.AddMultiply( - MemoryMarshal.Cast, float>(x), - y, - MemoryMarshal.Cast, float>(z), - MemoryMarshal.Cast, float>(result)); + [Theory] + [MemberData(nameof(AddData))] + public void AddMultiply_Both_Half_Should_Succeed(int count) + => AddMultiply_Both_Should_Succeed(count); - // assert - result.Should().Equal(expected); - } + [Theory] + [MemberData(nameof(AddData))] + public void AddMultiply_First_Float_Should_Succeed(int count) + => AddMultiply_First_Should_Succeed(count); [Theory] [MemberData(nameof(AddData))] public void AddMultiply_Second_Float_Should_Succeed(int count) - { - // arrange - var x = Enumerable.Range(0, count).Select(value => ((float)value, (float)(value + 1))).ToArray(); - var y = Enumerable.Range(0, count).Select(value => ((float)(value + 2), (float)(value + 3))).ToArray(); - var z = (42f, 43f); - var result = new ValueTuple[count]; - var expected = Enumerable.Range(0, count).Select(value => ((float)((value + value + 2) * 42), (float)((value + value + 4) * 43))).ToArray(); - - // act - Tensor.AddMultiply( - MemoryMarshal.Cast, float>(x), - MemoryMarshal.Cast, float>(y), - z, - MemoryMarshal.Cast, float>(result)); - - // assert - result.Should().Equal(expected); - } + => AddMultiply_Second_Should_Succeed(count); [Theory] [MemberData(nameof(AddData))] public void AddMultiply_Both_Float_Should_Succeed(int count) - { - // arrange - var x = Enumerable.Range(0, count).Select(value => ((float)value, (float)(value + 1))).ToArray(); - var y = (24f, 25f); - var z = (42f, 43f); - var result = new ValueTuple[count]; - var expected = Enumerable.Range(0, count).Select(value => ((float)((value + 24) * 42), (float)((value + 26) * 43))).ToArray(); - - // act - Tensor.AddMultiply( - MemoryMarshal.Cast, float>(x), - y, - z, - MemoryMarshal.Cast, float>(result)); - - // assert - result.Should().Equal(expected); - } + => AddMultiply_Both_Should_Succeed(count); [Theory] [MemberData(nameof(AddData))] public void AddMultiply_First_Double_Should_Succeed(int count) - { - // arrange - var x = Enumerable.Range(0, count).Select(value => ((double)value, (double)(value + 1))).ToArray(); - var y = (24d, 25d); - var z = Enumerable.Range(0, count).Select(value => ((double)(value + 2), (double)(value + 3))).ToArray(); - var result = new ValueTuple[count]; - var expected = Enumerable.Range(0, count).Select(value => ((double)((value + 24) * (value + 2)), (double)((value + 26) * (value + 3)))).ToArray(); - - // act - Tensor.AddMultiply( - MemoryMarshal.Cast, double>(x), - y, - MemoryMarshal.Cast, double>(z), - MemoryMarshal.Cast, double>(result)); - - // assert - result.Should().Equal(expected); - } + => AddMultiply_First_Should_Succeed(count); [Theory] [MemberData(nameof(AddData))] public void AddMultiply_Second_Double_Should_Succeed(int count) - { - // arrange - var x = Enumerable.Range(0, count).Select(value => ((double)value, (double)(value + 1))).ToArray(); - var y = Enumerable.Range(0, count).Select(value => ((double)(value + 2), (double)(value + 3))).ToArray(); - var z = (42d, 43d); - var result = new ValueTuple[count]; - var expected = Enumerable.Range(0, count).Select(value => ((double)((value + value + 2) * 42), (double)((value + value + 4) * 43))).ToArray(); - - // act - Tensor.AddMultiply( - MemoryMarshal.Cast, double>(x), - MemoryMarshal.Cast, double>(y), - z, - MemoryMarshal.Cast, double>(result)); - - // assert - result.Should().Equal(expected); - } + => AddMultiply_Second_Should_Succeed(count); [Theory] [MemberData(nameof(AddData))] public void AddMultiply_Both_Double_Should_Succeed(int count) - { - // arrange - var x = Enumerable.Range(0, count).Select(value => ((double)value, (double)(value + 1))).ToArray(); - var y = (24d, 25d); - var z = (42d, 43d); - var result = new ValueTuple[count]; - var expected = Enumerable.Range(0, count).Select(value => ((double)((value + 24) * 42), (double)((value + 26) * 43))).ToArray(); - - // act - Tensor.AddMultiply( - MemoryMarshal.Cast, double>(x), - y, - z, - MemoryMarshal.Cast, double>(result)); - - // assert - result.Should().Equal(expected); - } + => AddMultiply_Both_Should_Succeed(count); } diff --git a/src/NetFabric.Numerics.Tensors.UnitTests/AddMultiplyValueTests.cs b/src/NetFabric.Numerics.Tensors.UnitTests/AddMultiplyValueTests.cs index 002487b..015f70e 100644 --- a/src/NetFabric.Numerics.Tensors.UnitTests/AddMultiplyValueTests.cs +++ b/src/NetFabric.Numerics.Tensors.UnitTests/AddMultiplyValueTests.cs @@ -1,22 +1,26 @@ -using System.Linq; - -namespace NetFabric.Numerics.Tensors.UnitTests; +namespace NetFabric.Numerics.Tensors.UnitTests; public class AddMultiplyValueTests { public static TheoryData AddData => 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(AddData))] - public void AddMultiply_First_Short_Should_Succeed(int count) + static void AddMultiply_First_Should_Succeed(int count) + where T : struct, INumber { // arrange - var x = Enumerable.Range(0, count).Select(value => (short)value).ToArray(); - var y = (short)24; - var z = Enumerable.Range(0, count).Select(value => (short)(value + 2)).ToArray(); - var result = new short[count]; - var expected = Enumerable.Range(0, count).Select(value => (short)((value + 24) * (value + 2))).ToArray(); + var source = Enumerable.Range(0, count); + var x = source + .Select(value => T.CreateChecked(value)) + .ToArray(); + var y = T.CreateChecked(24); + var z = source + .Select(value => T.CreateChecked(value + 2)) + .ToArray(); + var result = new T[count]; + var expected = source + .Select(value => T.CreateChecked((value + 24) * (value + 2))) + .ToArray(); // act Tensor.AddMultiply(x, y, z, result); @@ -25,16 +29,22 @@ public void AddMultiply_First_Short_Should_Succeed(int count) result.Should().Equal(expected); } - [Theory] - [MemberData(nameof(AddData))] - public void AddMultiply_Second_Short_Should_Succeed(int count) + static void AddMultiply_Second_Should_Succeed(int count) + where T : struct, INumber { // arrange - var x = Enumerable.Range(0, count).Select(value => (short)value).ToArray(); - var y = Enumerable.Range(0, count).Select(value => (short)(value + 1)).ToArray(); - var z = (short)42; - var result = new short[count]; - var expected = Enumerable.Range(0, count).Select(value => (short)((value + value + 1) * 42)).ToArray(); + var source = Enumerable.Range(0, count); + var x = source + .Select(value => T.CreateChecked(value)) + .ToArray(); + var y = source + .Select(value => T.CreateChecked(value + 1)) + .ToArray(); + var z = T.CreateChecked(42); + var result = new T[count]; + var expected = source + .Select(value => T.CreateChecked((value + value + 1) * 42)) + .ToArray(); // act Tensor.AddMultiply(x, y, z, result); @@ -43,16 +53,20 @@ public void AddMultiply_Second_Short_Should_Succeed(int count) result.Should().Equal(expected); } - [Theory] - [MemberData(nameof(AddData))] - public void AddMultiply_Both_Short_Should_Succeed(int count) + static void AddMultiply_Both_Should_Succeed(int count) + where T : struct, INumber { // arrange - var x = Enumerable.Range(0, count).Select(value => (short)value).ToArray(); - var y = (short)24; - var z = (short)42; - var result = new short[count]; - var expected = Enumerable.Range(0, count).Select(value => (short)((value + 24) * 42)).ToArray(); + var source = Enumerable.Range(0, count); + var x = source + .Select(value => T.CreateChecked(value)) + .ToArray(); + var y = T.CreateChecked(24); + var z = T.CreateChecked(42); + var result = new T[count]; + var expected = source + .Select(value => T.CreateChecked((value + 24) * 42)) + .ToArray(); // act Tensor.AddMultiply(x, y, z, result); @@ -63,218 +77,77 @@ public void AddMultiply_Both_Short_Should_Succeed(int count) [Theory] [MemberData(nameof(AddData))] - public void AddMultiply_First_Int_Should_Succeed(int count) - { - // arrange - var x = Enumerable.Range(0, count).ToArray(); - var y = 24; - var z = Enumerable.Range(0, count).Select(value => value + 2).ToArray(); - var result = new int[count]; - var expected = Enumerable.Range(0, count).Select(value => (value + 24) * (value + 2)).ToArray(); - - // act - Tensor.AddMultiply(x, y, z, result); + public void AddMultiply_First_Short_Should_Succeed(int count) + => AddMultiply_First_Should_Succeed(count); - // assert - result.Should().Equal(expected); - } + [Theory] + [MemberData(nameof(AddData))] + public void AddMultiply_Second_Short_Should_Succeed(int count) + => AddMultiply_Second_Should_Succeed(count); [Theory] [MemberData(nameof(AddData))] - public void AddMultiply_Second_Int_Should_Succeed(int count) - { - // arrange - var x = Enumerable.Range(0, count).ToArray(); - var y = Enumerable.Range(0, count).Select(value => value + 1).ToArray(); - var z = 42; - var result = new int[count]; - var expected = Enumerable.Range(0, count).Select(value => (value + value + 1) * 42).ToArray(); + public void AddMultiply_Both_Short_Should_Succeed(int count) + => AddMultiply_Both_Should_Succeed(count); - // act - Tensor.AddMultiply(x, y, z, result); + [Theory] + [MemberData(nameof(AddData))] + public void AddMultiply_First_Int_Should_Succeed(int count) + => AddMultiply_First_Should_Succeed(count); - // assert - result.Should().Equal(expected); - } + [Theory] + [MemberData(nameof(AddData))] + public void AddMultiply_Second_Int_Should_Succeed(int count) + => AddMultiply_Second_Should_Succeed(count); [Theory] [MemberData(nameof(AddData))] public void AddMultiply_Both_Int_Should_Succeed(int count) - { - // arrange - var x = Enumerable.Range(0, count).ToArray(); - var y = 24; - var z = 42; - var result = new int[count]; - var expected = Enumerable.Range(0, count).Select(value => (value + 24) * 42).ToArray(); - - // act - Tensor.AddMultiply(x, y, z, result); - - // assert - result.Should().Equal(expected); - } + => AddMultiply_Both_Should_Succeed(count); [Theory] [MemberData(nameof(AddData))] public void AddMultiply_First_Long_Should_Succeed(int count) - { - // arrange - var x = Enumerable.Range(0, count).Select(value => (long)value).ToArray(); - var y = 24l; - var z = Enumerable.Range(0, count).Select(value => (long)(value + 2)).ToArray(); - var result = new long[count]; - var expected = Enumerable.Range(0, count).Select(value => (long)((value + 24) * (value + 2))).ToArray(); - - // act - Tensor.AddMultiply(x, y, z, result); - - // assert - result.Should().Equal(expected); - } + => AddMultiply_First_Should_Succeed(count); [Theory] [MemberData(nameof(AddData))] public void AddMultiply_Second_Long_Should_Succeed(int count) - { - // arrange - var x = Enumerable.Range(0, count).Select(value => (long)value).ToArray(); - var y = Enumerable.Range(0, count).Select(value => (long)(value + 1)).ToArray(); - var z = 42l; - var result = new long[count]; - var expected = Enumerable.Range(0, count).Select(value => (long)((value + value + 1) * 42)).ToArray(); - - // act - Tensor.AddMultiply(x, y, z, result); - - // assert - result.Should().Equal(expected); - } + => AddMultiply_Second_Should_Succeed(count); [Theory] [MemberData(nameof(AddData))] public void AddMultiply_Both_Long_Should_Succeed(int count) - { - // arrange - var x = Enumerable.Range(0, count).Select(value => (long)value).ToArray(); - var y = 24l; - var z = 42l; - var result = new long[count]; - var expected = Enumerable.Range(0, count).Select(value => (long)((value + 24) * 42)).ToArray(); - - // act - Tensor.AddMultiply(x, y, z, result); - - // assert - result.Should().Equal(expected); - } + => AddMultiply_Both_Should_Succeed(count); [Theory] [MemberData(nameof(AddData))] public void AddMultiply_First_Float_Should_Succeed(int count) - { - // arrange - var x = Enumerable.Range(0, count).Select(value => (float)value).ToArray(); - var y = 24f; - var z = Enumerable.Range(0, count).Select(value => (float)(value + 2)).ToArray(); - var result = new float[count]; - var expected = Enumerable.Range(0, count).Select(value => (float)((value + 24) * (value + 2))).ToArray(); - - // act - Tensor.AddMultiply(x, y, z, result); - - // assert - result.Should().Equal(expected); - } + => AddMultiply_First_Should_Succeed(count); [Theory] [MemberData(nameof(AddData))] public void AddMultiply_Second_Float_Should_Succeed(int count) - { - // arrange - var x = Enumerable.Range(0, count).Select(value => (float)value).ToArray(); - var y = Enumerable.Range(0, count).Select(value => (float)(value + 1)).ToArray(); - var z = 42f; - var result = new float[count]; - var expected = Enumerable.Range(0, count).Select(value => (float)((value + value + 1) * 42)).ToArray(); - - // act - Tensor.AddMultiply(x, y, z, result); - - // assert - result.Should().Equal(expected); - } + => AddMultiply_Second_Should_Succeed(count); [Theory] [MemberData(nameof(AddData))] public void AddMultiply_Both_Float_Should_Succeed(int count) - { - // arrange - var x = Enumerable.Range(0, count).Select(value => (float)value).ToArray(); - var y = 24f; - var z = 42f; - var result = new float[count]; - var expected = Enumerable.Range(0, count).Select(value => (float)((value + 24) * 42)).ToArray(); - - // act - Tensor.AddMultiply(x, y, z, result); - - // assert - result.Should().Equal(expected); - } + => AddMultiply_Both_Should_Succeed(count); [Theory] [MemberData(nameof(AddData))] public void AddMultiply_First_Double_Should_Succeed(int count) - { - // arrange - var x = Enumerable.Range(0, count).Select(value => (double)value).ToArray(); - var y = 24d; - var z = Enumerable.Range(0, count).Select(value => (double)(value + 2)).ToArray(); - var result = new double[count]; - var expected = Enumerable.Range(0, count).Select(value => (double)((value + 24) * (value + 2))).ToArray(); - - // act - Tensor.AddMultiply(x, y, z, result); - - // assert - result.Should().Equal(expected); - } + => AddMultiply_First_Should_Succeed(count); [Theory] [MemberData(nameof(AddData))] public void AddMultiply_Second_Double_Should_Succeed(int count) - { - // arrange - var x = Enumerable.Range(0, count).Select(value => (double)value).ToArray(); - var y = Enumerable.Range(0, count).Select(value => (double)(value + 1)).ToArray(); - var z = 42d; - var result = new double[count]; - var expected = Enumerable.Range(0, count).Select(value => (double)((value + value + 1) * 42)).ToArray(); - - // act - Tensor.AddMultiply(x, y, z, result); - - // assert - result.Should().Equal(expected); - } + => AddMultiply_Second_Should_Succeed(count); [Theory] [MemberData(nameof(AddData))] public void AddMultiply_Both_Double_Should_Succeed(int count) - { - // arrange - var x = Enumerable.Range(0, count).Select(value => (double)value).ToArray(); - var y = 24d; - var z = 42d; - var result = new double[count]; - var expected = Enumerable.Range(0, count).Select(value => (double)((value + 24) * 42)).ToArray(); - - // act - Tensor.AddMultiply(x, y, z, result); - - // assert - result.Should().Equal(expected); - } + => AddMultiply_Both_Should_Succeed(count); } diff --git a/src/NetFabric.Numerics.Tensors.UnitTests/AddMultiplyValueTripletsTests.cs b/src/NetFabric.Numerics.Tensors.UnitTests/AddMultiplyValueTripletsTests.cs new file mode 100644 index 0000000..4f78290 --- /dev/null +++ b/src/NetFabric.Numerics.Tensors.UnitTests/AddMultiplyValueTripletsTests.cs @@ -0,0 +1,182 @@ +using System.Runtime.InteropServices; + +namespace NetFabric.Numerics.Tensors.UnitTests; + +public class AddMultiplyValueTripletsTests +{ + public static TheoryData AddData + => 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 }; + + static void AddMultiply_First_Should_Succeed(int count) + where T : struct, INumber + { + // arrange + var source = Enumerable.Range(0, count); + var x = source + .Select(value => new MyVector3(T.CreateChecked(value), T.CreateChecked(value + 1), T.CreateChecked(value + 2))) + .ToArray(); + var y = (T.CreateChecked(24), T.CreateChecked(25), T.CreateChecked(26)); + var z = source + .Select(value => new MyVector3(T.CreateChecked(value + 2), T.CreateChecked(value + 3), T.CreateChecked(value + 4))) + .ToArray(); + var result = new MyVector3[count]; + var expected = source + .Select(value => new MyVector3(T.CreateChecked((value + 24) * (value + 2)), T.CreateChecked((value + 26) * (value + 3)), T.CreateChecked((value + 28) * (value + 4)))) + .ToArray(); + + // act + Tensor.AddMultiply( + MemoryMarshal.Cast, T>(x), + y, + MemoryMarshal.Cast, T>(z), + MemoryMarshal.Cast, T>(result)); + + // assert + result.Should().Equal(expected); + } + + static void AddMultiply_Second_Should_Succeed(int count) + where T : struct, INumber + { + // arrange + var source = Enumerable.Range(0, count); + var x = source + .Select(value => new MyVector3(T.CreateChecked(value), T.CreateChecked(value + 1), T.CreateChecked(value + 2))) + .ToArray(); + var y = source + .Select(value => new MyVector3(T.CreateChecked(value + 2), T.CreateChecked(value + 3), T.CreateChecked(value + 4))) + .ToArray(); + var z = (T.CreateChecked(42), T.CreateChecked(43), T.CreateChecked(44)); + var result = new MyVector3[count]; + var expected = source + .Select(value => new MyVector3(T.CreateChecked((value + value + 2) * 42), T.CreateChecked((value + value + 4) * 43), T.CreateChecked((value + value + 6) * 44))) + .ToArray(); + + // act + Tensor.AddMultiply( + MemoryMarshal.Cast, T>(x), + MemoryMarshal.Cast, T>(y), + z, + MemoryMarshal.Cast, T>(result)); + + // assert + result.Should().Equal(expected); + } + + static void AddMultiply_Both_Should_Succeed(int count) + where T : struct, INumber + { + // arrange + var source = Enumerable.Range(0, count); + var x = source + .Select(value => new MyVector3(T.CreateChecked(value), T.CreateChecked(value + 1), T.CreateChecked(value + 2))) + .ToArray(); + var y = (T.CreateChecked(24), T.CreateChecked(25), T.CreateChecked(26)); + var z = (T.CreateChecked(42), T.CreateChecked(43), T.CreateChecked(44)); + var result = new MyVector3[count]; + var expected = source + .Select(value => new MyVector3(T.CreateChecked((value + 24) * 42), T.CreateChecked((value + 26) * 43), T.CreateChecked((value + 28) * 44))) + .ToArray(); + + // act + Tensor.AddMultiply( + MemoryMarshal.Cast, T>(x), + y, + z, + MemoryMarshal.Cast, T>(result)); + + // assert + result.Should().Equal(expected); + } + + [Theory] + [MemberData(nameof(AddData))] + public void AddMultiply_First_Short_Should_Succeed(int count) + => AddMultiply_First_Should_Succeed(count); + + [Theory] + [MemberData(nameof(AddData))] + public void AddMultiply_Second_Short_Should_Succeed(int count) + => AddMultiply_Second_Should_Succeed(count); + + [Theory] + [MemberData(nameof(AddData))] + public void AddMultiply_Both_Short_Should_Succeed(int count) + => AddMultiply_Both_Should_Succeed(count); + + [Theory] + [MemberData(nameof(AddData))] + public void AddMultiply_First_Int_Should_Succeed(int count) + => AddMultiply_First_Should_Succeed(count); + + [Theory] + [MemberData(nameof(AddData))] + public void AddMultiply_Second_Int_Should_Succeed(int count) + => AddMultiply_Second_Should_Succeed(count); + + [Theory] + [MemberData(nameof(AddData))] + public void AddMultiply_Both_Int_Should_Succeed(int count) + => AddMultiply_Both_Should_Succeed(count); + + [Theory] + [MemberData(nameof(AddData))] + public void AddMultiply_First_Long_Should_Succeed(int count) + => AddMultiply_First_Should_Succeed(count); + + [Theory] + [MemberData(nameof(AddData))] + public void AddMultiply_Second_Long_Should_Succeed(int count) + => AddMultiply_Second_Should_Succeed(count); + + [Theory] + [MemberData(nameof(AddData))] + public void AddMultiply_Both_Long_Should_Succeed(int count) + => AddMultiply_Both_Should_Succeed(count); + + [Theory] + [MemberData(nameof(AddData))] + public void AddMultiply_First_Half_Should_Succeed(int count) + => AddMultiply_First_Should_Succeed(count); + + [Theory] + [MemberData(nameof(AddData))] + public void AddMultiply_Second_Half_Should_Succeed(int count) + => AddMultiply_Second_Should_Succeed(count); + + [Theory] + [MemberData(nameof(AddData))] + public void AddMultiply_Both_Half_Should_Succeed(int count) + => AddMultiply_Both_Should_Succeed(count); + + [Theory] + [MemberData(nameof(AddData))] + public void AddMultiply_First_Float_Should_Succeed(int count) + => AddMultiply_First_Should_Succeed(count); + + [Theory] + [MemberData(nameof(AddData))] + public void AddMultiply_Second_Float_Should_Succeed(int count) + => AddMultiply_Second_Should_Succeed(count); + + [Theory] + [MemberData(nameof(AddData))] + public void AddMultiply_Both_Float_Should_Succeed(int count) + => AddMultiply_Both_Should_Succeed(count); + + [Theory] + [MemberData(nameof(AddData))] + public void AddMultiply_First_Double_Should_Succeed(int count) + => AddMultiply_First_Should_Succeed(count); + + [Theory] + [MemberData(nameof(AddData))] + public void AddMultiply_Second_Double_Should_Succeed(int count) + => AddMultiply_Second_Should_Succeed(count); + + [Theory] + [MemberData(nameof(AddData))] + public void AddMultiply_Both_Double_Should_Succeed(int count) + => AddMultiply_Both_Should_Succeed(count); + +} diff --git a/src/NetFabric.Numerics.Tensors.UnitTests/AddPairsTests.cs b/src/NetFabric.Numerics.Tensors.UnitTests/AddPairsTests.cs index 770dcfe..3c12c75 100644 --- a/src/NetFabric.Numerics.Tensors.UnitTests/AddPairsTests.cs +++ b/src/NetFabric.Numerics.Tensors.UnitTests/AddPairsTests.cs @@ -1,5 +1,4 @@ -using System.Linq; -using System.Runtime.InteropServices; +using System.Runtime.InteropServices; namespace NetFabric.Numerics.Tensors.UnitTests; @@ -8,21 +7,27 @@ public class AddPairsTests public static TheoryData AddData => 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(AddData))] - public void Add_Short_Should_Succeed(int count) + static void Add_Should_Succeed(int count) + where T : struct, INumber { // arrange - var x = Enumerable.Range(0, count).Select(value => ((short)value, (short)(value + 1))).ToArray(); - var y = Enumerable.Range(0, count).Select(value => ((short)(value + 2), (short)(value + 3))).ToArray(); - var result = new ValueTuple[count]; - var expected = Enumerable.Range(0, count).Select(value => ((short)(value + value + 2), (short)(value + value + 4))).ToArray(); + var source = Enumerable.Range(0, count); + var x = source + .Select(value => new MyVector2(T.CreateChecked(value), T.CreateChecked(value + 1))) + .ToArray(); + var y = source + .Select(value => new MyVector2(T.CreateChecked(value + 2), T.CreateChecked(value + 3))) + .ToArray(); + var result = new MyVector2[count]; + var expected = source + .Select(value => new MyVector2(T.CreateChecked(value + value + 2), T.CreateChecked(value + value + 4))) + .ToArray(); // act Tensor.Add( - MemoryMarshal.Cast, short>(x), - MemoryMarshal.Cast, short>(y), - MemoryMarshal.Cast, short>(result)); + MemoryMarshal.Cast, T>(x), + MemoryMarshal.Cast, T>(y), + MemoryMarshal.Cast, T>(result)); // assert result.Should().Equal(expected); @@ -30,82 +35,32 @@ public void Add_Short_Should_Succeed(int count) [Theory] [MemberData(nameof(AddData))] - public void Add_Int_Should_Succeed(int count) - { - // arrange - var x = Enumerable.Range(0, count).Select(value => (value, value + 1)).ToArray(); - var y = Enumerable.Range(0, count).Select(value => (value + 2, value + 3)).ToArray(); - var result = new ValueTuple[count]; - var expected = Enumerable.Range(0, count).Select(value => (value + value + 2, value + value + 4)).ToArray(); - - // act - Tensor.Add( - MemoryMarshal.Cast, int>(x), - MemoryMarshal.Cast, int>(y), - MemoryMarshal.Cast, int>(result)); + public void Add_Short_Should_Succeed(int count) + => Add_Should_Succeed(count); - // assert - result.Should().Equal(expected); - } + [Theory] + [MemberData(nameof(AddData))] + public void Add_Int_Should_Succeed(int count) + => Add_Should_Succeed(count); [Theory] [MemberData(nameof(AddData))] public void Add_Long_Should_Succeed(int count) - { - // arrange - var x = Enumerable.Range(0, count).Select(value => ((long)value, (long)(value + 1))).ToArray(); - var y = Enumerable.Range(0, count).Select(value => ((long)(value + 2), (long)(value + 3))).ToArray(); - var result = new ValueTuple[count]; - var expected = Enumerable.Range(0, count).Select(value => ((long)(value + value + 2), (long)(value + value + 4))).ToArray(); + => Add_Should_Succeed(count); - // act - Tensor.Add( - MemoryMarshal.Cast, long>(x), - MemoryMarshal.Cast, long>(y), - MemoryMarshal.Cast, long>(result)); - - // assert - result.Should().Equal(expected); - } + [Theory] + [MemberData(nameof(AddData))] + public void Add_Half_Should_Succeed(int count) + => Add_Should_Succeed(count); [Theory] [MemberData(nameof(AddData))] public void Add_Float_Should_Succeed(int count) - { - // arrange - var x = Enumerable.Range(0, count).Select(value => ((float)value, (float)(value + 1))).ToArray(); - var y = Enumerable.Range(0, count).Select(value => ((float)(value + 2), (float)(value + 3))).ToArray(); - var result = new ValueTuple[count]; - var expected = Enumerable.Range(0, count).Select(value => ((float)(value + value + 2), (float)(value + value + 4))).ToArray(); - - // act - Tensor.Add( - MemoryMarshal.Cast, float>(x), - MemoryMarshal.Cast, float>(y), - MemoryMarshal.Cast, float>(result)); - - // assert - result.Should().Equal(expected); - } + => Add_Should_Succeed(count); [Theory] [MemberData(nameof(AddData))] public void Add_Double_Should_Succeed(int count) - { - // arrange - var x = Enumerable.Range(0, count).Select(value => ((double)value, (double)(value + 1))).ToArray(); - var y = Enumerable.Range(0, count).Select(value => ((double)(value + 2), (double)(value + 3))).ToArray(); - var result = new ValueTuple[count]; - var expected = Enumerable.Range(0, count).Select(value => ((double)(value + value + 2), (double)(value + value + 4))).ToArray(); - - // act - Tensor.Add( - MemoryMarshal.Cast, double>(x), - MemoryMarshal.Cast, double>(y), - MemoryMarshal.Cast, double>(result)); - - // assert - result.Should().Equal(expected); - } + => Add_Should_Succeed(count); } diff --git a/src/NetFabric.Numerics.Tensors.UnitTests/AddTests.cs b/src/NetFabric.Numerics.Tensors.UnitTests/AddTests.cs index 36b3d9b..6a3c1b5 100644 --- a/src/NetFabric.Numerics.Tensors.UnitTests/AddTests.cs +++ b/src/NetFabric.Numerics.Tensors.UnitTests/AddTests.cs @@ -1,24 +1,22 @@ -using System.Linq; - -namespace NetFabric.Numerics.Tensors.UnitTests; +namespace NetFabric.Numerics.Tensors.UnitTests; public class AddTests { public static TheoryData AddData => 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(AddData))] - public void Add_Short_Should_Succeed(int count) + static void Add_Should_Succeed(int count) + where T : struct, INumber { // arrange - var x = Enumerable.Range(0, count).Select(value => (short)value).ToArray(); - var y = Enumerable.Range(0, count).Select(value => (short)(value + 1)).ToArray(); - var result = new short[count]; - var expected = Enumerable.Range(0, count).Select(value => (short)(value + value + 1)).ToArray(); + var source = Enumerable.Range(0, count); + var x = source.Select(value => T.CreateChecked(value)).ToArray(); + var y = source.Select(value => T.CreateChecked(value + 1)).ToArray(); + var result = new T[count]; + var expected = source.Select(value => T.CreateChecked(value + value + 1)).ToArray(); // act - Tensor.Add(x, y, result); + Tensor.Add(x, y, result); // assert result.Should().Equal(expected); @@ -26,70 +24,32 @@ public void Add_Short_Should_Succeed(int count) [Theory] [MemberData(nameof(AddData))] - public void Add_Int_Should_Succeed(int count) - { - // arrange - var x = Enumerable.Range(0, count).ToArray(); - var y = Enumerable.Range(0, count).Select(value => value + 1).ToArray(); - var result = new int[count]; - var expected = Enumerable.Range(0, count).Select(value => value + value + 1).ToArray(); - - // act - Tensor.Add(x, y, result); + public void Add_Short_Should_Succeed(int count) + => Add_Should_Succeed(count); - // assert - result.Should().Equal(expected); - } + [Theory] + [MemberData(nameof(AddData))] + public void Add_Int_Should_Succeed(int count) + => Add_Should_Succeed(count); [Theory] [MemberData(nameof(AddData))] public void Add_Long_Should_Succeed(int count) - { - // arrange - var x = Enumerable.Range(0, count).Select(value => (long)value).ToArray(); - var y = Enumerable.Range(0, count).Select(value => (long)(value + 1)).ToArray(); - var result = new long[count]; - var expected = Enumerable.Range(0, count).Select(value => (long)(value + value + 1)).ToArray(); + => Add_Should_Succeed(count); - // act - Tensor.Add(x, y, result); - - // assert - result.Should().Equal(expected); - } + [Theory] + [MemberData(nameof(AddData))] + public void Add_Half_Should_Succeed(int count) + => Add_Should_Succeed(count); [Theory] [MemberData(nameof(AddData))] public void Add_Float_Should_Succeed(int count) - { - // arrange - var x = Enumerable.Range(0, count).Select(value => (float)value).ToArray(); - var y = Enumerable.Range(0, count).Select(value => (float)(value + 1)).ToArray(); - var result = new float[count]; - var expected = Enumerable.Range(0, count).Select(value => (float)(value + value + 1)).ToArray(); - - // act - Tensor.Add(x, y, result); - - // assert - result.Should().Equal(expected); - } + => Add_Should_Succeed(count); [Theory] [MemberData(nameof(AddData))] public void Add_Double_Should_Succeed(int count) - { - // arrange - var x = Enumerable.Range(0, count).Select(value => (double)value).ToArray(); - var y = Enumerable.Range(0, count).Select(value => (double)(value + 1)).ToArray(); - var result = new double[count]; - var expected = Enumerable.Range(0, count).Select(value => (double)(value + value + 1)).ToArray(); - - // act - Tensor.Add(x, y, result); - - // assert - result.Should().Equal(expected); - } + => Add_Should_Succeed(count); } diff --git a/src/NetFabric.Numerics.Tensors.UnitTests/AddTripletsTests.cs b/src/NetFabric.Numerics.Tensors.UnitTests/AddTripletsTests.cs new file mode 100644 index 0000000..857a51b --- /dev/null +++ b/src/NetFabric.Numerics.Tensors.UnitTests/AddTripletsTests.cs @@ -0,0 +1,65 @@ +using System.Runtime.InteropServices; + +namespace NetFabric.Numerics.Tensors.UnitTests; + +public class AddTripletsTests +{ + public static TheoryData AddData + => 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 }; + + static void Add_Should_Succeed(int count) + where T : struct, INumber + { + // arrange + var source = Enumerable.Range(0, count); + var x = source + .Select(value => new MyVector3(T.CreateChecked(value), T.CreateChecked(value + 1), T.CreateChecked(value + 2))) + .ToArray(); + var y = source + .Select(value => new MyVector3(T.CreateChecked(value + 2), T.CreateChecked(value + 3), T.CreateChecked(value + 4))) + .ToArray(); + var result = new MyVector3[count]; + var expected = source + .Select(value => new MyVector3(T.CreateChecked(value + value + 2), T.CreateChecked(value + value + 4), T.CreateChecked(value + value + 6))) + .ToArray(); + + // act + Tensor.Add( + MemoryMarshal.Cast, T>(x), + MemoryMarshal.Cast, T>(y), + MemoryMarshal.Cast, T>(result)); + + // assert + result.Should().Equal(expected); + } + + [Theory] + [MemberData(nameof(AddData))] + public void Add_Short_Should_Succeed(int count) + => Add_Should_Succeed(count); + + [Theory] + [MemberData(nameof(AddData))] + public void Add_Int_Should_Succeed(int count) + => Add_Should_Succeed(count); + + [Theory] + [MemberData(nameof(AddData))] + public void Add_Long_Should_Succeed(int count) + => Add_Should_Succeed(count); + + [Theory] + [MemberData(nameof(AddData))] + public void Add_Half_Should_Succeed(int count) + => Add_Should_Succeed(count); + + [Theory] + [MemberData(nameof(AddData))] + public void Add_Float_Should_Succeed(int count) + => Add_Should_Succeed(count); + + [Theory] + [MemberData(nameof(AddData))] + public void Add_Double_Should_Succeed(int count) + => Add_Should_Succeed(count); +} diff --git a/src/NetFabric.Numerics.Tensors.UnitTests/AddValuePairsTests.cs b/src/NetFabric.Numerics.Tensors.UnitTests/AddValuePairsTests.cs index 6577a59..c783dd9 100644 --- a/src/NetFabric.Numerics.Tensors.UnitTests/AddValuePairsTests.cs +++ b/src/NetFabric.Numerics.Tensors.UnitTests/AddValuePairsTests.cs @@ -1,26 +1,28 @@ -using System.Linq; -using System.Runtime.InteropServices; +using System.Runtime.InteropServices; namespace NetFabric.Numerics.Tensors.UnitTests; public class AddValuePairsTests { - public static readonly ValueTuple constValue = (42, 24); - public static TheoryData AddData => 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(AddData))] - public void Add_Short_Should_Succeed(int count) + static void Add_Should_Succeed(int count) + where T : struct, INumber { // arrange - var x = Enumerable.Range(0, count).Select(value => ((short)value, (short)(value + 1))).ToArray(); - var result = new ValueTuple[count]; - var expected = Enumerable.Range(0, count).Select(value => ((short)(value + constValue.Item1), (short)(value + 1 + constValue.Item2))).ToArray(); + var source = Enumerable.Range(0, count); + var x = source + .Select(value => new MyVector2(T.CreateChecked(value), T.CreateChecked(value + 1))) + .ToArray(); + var y = (T.CreateChecked(42), T.CreateChecked(24)); + var result = new MyVector2[count]; + var expected = source + .Select(value => new MyVector2(T.CreateChecked(value) + y.Item1, T.CreateChecked(value + 1) + y.Item2)) + .ToArray(); // act - Tensor.Add(MemoryMarshal.Cast, short>(x), constValue, MemoryMarshal.Cast, short>(result)); + Tensor.Add(MemoryMarshal.Cast, T>(x), y, MemoryMarshal.Cast, T>(result)); // assert result.Should().Equal(expected); @@ -28,66 +30,32 @@ public void Add_Short_Should_Succeed(int count) [Theory] [MemberData(nameof(AddData))] - public void Add_Int_Should_Succeed(int count) - { - // arrange - var x = Enumerable.Range(0, count).Select(value => (value, value + 1)).ToArray(); - var result = new ValueTuple[count]; - var expected = Enumerable.Range(0, count).Select(value => (value + constValue.Item1, value + 1 + constValue.Item2)).ToArray(); - - // act - Tensor.Add(MemoryMarshal.Cast, int>(x), constValue, MemoryMarshal.Cast, int>(result)); + public void Add_Short_Should_Succeed(int count) + => Add_Should_Succeed(count); - // assert - result.Should().Equal(expected); - } + [Theory] + [MemberData(nameof(AddData))] + public void Add_Int_Should_Succeed(int count) + => Add_Should_Succeed(count); [Theory] [MemberData(nameof(AddData))] public void Add_Long_Should_Succeed(int count) - { - // arrange - var x = Enumerable.Range(0, count).Select(value => ((long)value, (long)(value + 1))).ToArray(); - var result = new ValueTuple[count]; - var expected = Enumerable.Range(0, count).Select(value => ((long)(value + constValue.Item1), (long)(value + 1 + constValue.Item2))).ToArray(); + => Add_Should_Succeed(count); - // act - Tensor.Add(MemoryMarshal.Cast, long>(x), constValue, MemoryMarshal.Cast, long>(result)); - - // assert - result.Should().Equal(expected); - } + [Theory] + [MemberData(nameof(AddData))] + public void Add_Half_Should_Succeed(int count) + => Add_Should_Succeed(count); [Theory] [MemberData(nameof(AddData))] public void Add_Float_Should_Succeed(int count) - { - // arrange - var x = Enumerable.Range(0, count).Select(value => ((float)value, (float)(value + 1))).ToArray(); - var result = new ValueTuple[count]; - var expected = Enumerable.Range(0, count).Select(value => ((float)(value + constValue.Item1), (float)(value + 1 + constValue.Item2))).ToArray(); - - // act - Tensor.Add(MemoryMarshal.Cast, float>(x), constValue, MemoryMarshal.Cast, float>(result)); - - // assert - result.Should().Equal(expected); - } + => Add_Should_Succeed(count); [Theory] [MemberData(nameof(AddData))] public void Add_Double_Should_Succeed(int count) - { - // arrange - var x = Enumerable.Range(0, count).Select(value => ((double)value, (double)(value + 1))).ToArray(); - var result = new ValueTuple[count]; - var expected = Enumerable.Range(0, count).Select(value => ((double)(value + constValue.Item1), (double)(value + 1 + constValue.Item2))).ToArray(); - - // act - Tensor.Add(MemoryMarshal.Cast, double>(x), constValue, MemoryMarshal.Cast, double>(result)); - - // assert - result.Should().Equal(expected); - } + => Add_Should_Succeed(count); } diff --git a/src/NetFabric.Numerics.Tensors.UnitTests/AddValueTests.cs b/src/NetFabric.Numerics.Tensors.UnitTests/AddValueTests.cs index bb47974..98d6905 100644 --- a/src/NetFabric.Numerics.Tensors.UnitTests/AddValueTests.cs +++ b/src/NetFabric.Numerics.Tensors.UnitTests/AddValueTests.cs @@ -1,25 +1,26 @@ -using System.Linq; - -namespace NetFabric.Numerics.Tensors.UnitTests; +namespace NetFabric.Numerics.Tensors.UnitTests; public class AddValueTests { - public const short constValue = 42; - public static TheoryData AddData => 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(AddData))] - public void Add_Short_Should_Succeed(int count) + static void Add_Should_Succeed(int count) + where T : struct, INumber { // arrange - var x = Enumerable.Range(0, count).Select(value => (short)value).ToArray(); - var result = new short[count]; - var expected = Enumerable.Range(0, count).Select(value => (short)(value + constValue)).ToArray(); + var source = Enumerable.Range(0, count); + var x = source + .Select(value => T.CreateChecked(value)) + .ToArray(); + var y = T.CreateChecked(42); + var result = new T[count]; + var expected = source + .Select(value => T.CreateChecked(value) + y) + .ToArray(); // act - Tensor.Add(x, constValue, result); + Tensor.Add(x, y, result); // assert result.Should().Equal(expected); @@ -27,66 +28,32 @@ public void Add_Short_Should_Succeed(int count) [Theory] [MemberData(nameof(AddData))] - public void Add_Int_Should_Succeed(int count) - { - // arrange - var x = Enumerable.Range(0, count).ToArray(); - var result = new int[count]; - var expected = Enumerable.Range(0, count).Select(value => value + constValue).ToArray(); - - // act - Tensor.Add(x, (int)constValue, result); + public void Add_Short_Should_Succeed(int count) + => Add_Should_Succeed(count); - // assert - result.Should().Equal(expected); - } + [Theory] + [MemberData(nameof(AddData))] + public void Add_Int_Should_Succeed(int count) + => Add_Should_Succeed(count); [Theory] [MemberData(nameof(AddData))] public void Add_Long_Should_Succeed(int count) - { - // arrange - var x = Enumerable.Range(0, count).Select(value => (long)value).ToArray(); - var result = new long[count]; - var expected = Enumerable.Range(0, count).Select(value => (long)(value + constValue)).ToArray(); - - // act - Tensor.Add(x, (long)constValue, result); + => Add_Should_Succeed(count); - // assert - result.Should().Equal(expected); - } + [Theory] + [MemberData(nameof(AddData))] + public void Add_Half_Should_Succeed(int count) + => Add_Should_Succeed(count); [Theory] [MemberData(nameof(AddData))] public void Add_Float_Should_Succeed(int count) - { - // arrange - var x = Enumerable.Range(0, count).Select(value => (float)value).ToArray(); - var result = new float[count]; - var expected = Enumerable.Range(0, count).Select(value => (float)(value + constValue)).ToArray(); - - // act - Tensor.Add(x, (float)constValue, result); - - // assert - result.Should().Equal(expected); - } + => Add_Should_Succeed(count); [Theory] [MemberData(nameof(AddData))] public void Add_Double_Should_Succeed(int count) - { - // arrange - var x = Enumerable.Range(0, count).Select(value => (double)value).ToArray(); - var result = new double[count]; - var expected = Enumerable.Range(0, count).Select(value => (double)(value + constValue)).ToArray(); - - // act - Tensor.Add(x, (double)constValue, result); - - // assert - result.Should().Equal(expected); - } + => Add_Should_Succeed(count); } diff --git a/src/NetFabric.Numerics.Tensors.UnitTests/AddValueTripletsTests.cs b/src/NetFabric.Numerics.Tensors.UnitTests/AddValueTripletsTests.cs new file mode 100644 index 0000000..a160760 --- /dev/null +++ b/src/NetFabric.Numerics.Tensors.UnitTests/AddValueTripletsTests.cs @@ -0,0 +1,61 @@ +using System.Runtime.InteropServices; + +namespace NetFabric.Numerics.Tensors.UnitTests; + +public class AddValueTripletsTests +{ + public static TheoryData AddData + => 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 }; + + static void Add_Should_Succeed(int count) + where T : struct, INumber + { + // arrange + var source = Enumerable.Range(0, count); + var x = source + .Select(value => new MyVector3(T.CreateChecked(value), T.CreateChecked(value + 1), T.CreateChecked(value + 2))) + .ToArray(); + var y = (T.CreateChecked(42), T.CreateChecked(43), T.CreateChecked(44)); + var result = new MyVector3[count]; + var expected = source + .Select(value => new MyVector3(T.CreateChecked(value) + y.Item1, T.CreateChecked(value + 1) + y.Item2, T.CreateChecked(value + 2) + y.Item3)) + .ToArray(); + + // act + Tensor.Add(MemoryMarshal.Cast, T>(x), y, MemoryMarshal.Cast, T>(result)); + + // assert + result.Should().Equal(expected); + } + + [Theory] + [MemberData(nameof(AddData))] + public void Add_Short_Should_Succeed(int count) + => Add_Should_Succeed(count); + + [Theory] + [MemberData(nameof(AddData))] + public void Add_Int_Should_Succeed(int count) + => Add_Should_Succeed(count); + + [Theory] + [MemberData(nameof(AddData))] + public void Add_Long_Should_Succeed(int count) + => Add_Should_Succeed(count); + + [Theory] + [MemberData(nameof(AddData))] + public void Add_Half_Should_Succeed(int count) + => Add_Should_Succeed(count); + + [Theory] + [MemberData(nameof(AddData))] + public void Add_Float_Should_Succeed(int count) + => Add_Should_Succeed(count); + + [Theory] + [MemberData(nameof(AddData))] + public void Add_Double_Should_Succeed(int count) + => Add_Should_Succeed(count); + +} diff --git a/src/NetFabric.Numerics.Tensors.UnitTests/MyVector.cs b/src/NetFabric.Numerics.Tensors.UnitTests/MyVector.cs new file mode 100644 index 0000000..b366ec5 --- /dev/null +++ b/src/NetFabric.Numerics.Tensors.UnitTests/MyVector.cs @@ -0,0 +1,35 @@ +namespace NetFabric.Numerics.Tensors.UnitTests; + +readonly record struct MyVector2(T X, T Y) + : IAdditiveIdentity, MyVector2> + , IAdditionOperators, MyVector2, MyVector2> + where T : struct, IAdditiveIdentity, IAdditionOperators +{ + public MyVector2(ValueTuple tuple) + : this(tuple.Item1, tuple.Item2) + { + } + + public static MyVector2 AdditiveIdentity + => new(T.AdditiveIdentity, T.AdditiveIdentity); + + public static MyVector2 operator +(MyVector2 left, MyVector2 right) + => new(left.X + right.X, left.Y + right.Y); +} + +readonly record struct MyVector3(T X, T Y, T Z) + : IAdditiveIdentity, MyVector3> + , IAdditionOperators, MyVector3, MyVector3> + where T : struct, IAdditiveIdentity, IAdditionOperators +{ + public MyVector3(ValueTuple tuple) + : this(tuple.Item1, tuple.Item2, tuple.Item3) + { + } + + public static MyVector3 AdditiveIdentity + => new(T.AdditiveIdentity, T.AdditiveIdentity, T.AdditiveIdentity); + + public static MyVector3 operator +(MyVector3 left, MyVector3 right) + => new(left.X + right.X, left.Y + right.Y, left.Z + right.Z); +} \ No newline at end of file diff --git a/src/NetFabric.Numerics.Tensors.UnitTests/NetFabric.Numerics.Tensors.UnitTests.csproj b/src/NetFabric.Numerics.Tensors.UnitTests/NetFabric.Numerics.Tensors.UnitTests.csproj index a0b3891..897a6c3 100644 --- a/src/NetFabric.Numerics.Tensors.UnitTests/NetFabric.Numerics.Tensors.UnitTests.csproj +++ b/src/NetFabric.Numerics.Tensors.UnitTests/NetFabric.Numerics.Tensors.UnitTests.csproj @@ -5,10 +5,11 @@ false true - NetFabric.Numerics.Tensors.UnitsTests + NetFabric.Numerics.Tensors.UnitTests + diff --git a/src/NetFabric.Numerics.Tensors.UnitTests/SquareTests.cs b/src/NetFabric.Numerics.Tensors.UnitTests/SquareTests.cs index ac66f08..0c81d14 100644 --- a/src/NetFabric.Numerics.Tensors.UnitTests/SquareTests.cs +++ b/src/NetFabric.Numerics.Tensors.UnitTests/SquareTests.cs @@ -1,23 +1,21 @@ -using System.Linq; - -namespace NetFabric.Numerics.Tensors.UnitTests; +namespace NetFabric.Numerics.Tensors.UnitTests; public class SquareTests { public static TheoryData SquareData => 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(SquareData))] - public void Square_Short_Should_Succeed(int count) + static void Square_Should_Succeed(int count) + where T : struct, INumber { // arrange - var x = Enumerable.Range(0, count).Select(value => (short)value).ToArray(); - var result = new short[count]; - var expected = Enumerable.Range(0, count).Select(value => (short)(value * value)).ToArray(); + var source = Enumerable.Range(0, count); + var x = source.Select(value => T.CreateChecked(value)).ToArray(); + var result = new T[count]; + var expected = source.Select(value => T.CreateChecked(value * value)).ToArray(); // act - Tensor.Square(x, result); + Tensor.Square(x, result); // assert result.Should().Equal(expected); @@ -25,67 +23,32 @@ public void Square_Short_Should_Succeed(int count) [Theory] [MemberData(nameof(SquareData))] - public void Square_Int_Should_Succeed(int count) - { - // arrange - var x = Enumerable.Range(0, count).ToArray(); - var result = new int[count]; - var expected = Enumerable.Range(0, count).Select(value => value * value).ToArray(); - - // act - Tensor.Square(x, result); + public void Square_Short_Should_Succeed(int count) + => Square_Should_Succeed(count); - // assert - result.Should().Equal(expected); - } + [Theory] + [MemberData(nameof(SquareData))] + public void Square_Int_Should_Succeed(int count) + => Square_Should_Succeed(count); [Theory] [MemberData(nameof(SquareData))] public void Square_Long_Should_Succeed(int count) - { - // arrange - var x = Enumerable.Range(0, count).Select(value => (long)value).ToArray(); - var y = Enumerable.Range(0, count).Select(value => (long)(value + 1)).ToArray(); - var result = new long[count]; - var expected = Enumerable.Range(0, count).Select(value => (long)(value * value)).ToArray(); + => Square_Should_Succeed(count); - // act - Tensor.Square(x, result); - - // assert - result.Should().Equal(expected); - } + [Theory] + [MemberData(nameof(SquareData))] + public void Square_Half_Should_Succeed(int count) + => Square_Should_Succeed(count); [Theory] [MemberData(nameof(SquareData))] public void Square_Float_Should_Succeed(int count) - { - // arrange - var x = Enumerable.Range(0, count).Select(value => (float)value).ToArray(); - var result = new float[count]; - var expected = Enumerable.Range(0, count).Select(value => (float)(value * value)).ToArray(); - - // act - Tensor.Square(x, result); - - // assert - result.Should().Equal(expected); - } + => Square_Should_Succeed(count); [Theory] [MemberData(nameof(SquareData))] public void Square_Double_Should_Succeed(int count) - { - // arrange - var x = Enumerable.Range(0, count).Select(value => (double)value).ToArray(); - var result = new double[count]; - var expected = Enumerable.Range(0, count).Select(value => (double)(value * value)).ToArray(); - - // act - Tensor.Square(x, result); - - // assert - result.Should().Equal(expected); - } + => Square_Should_Succeed(count); } diff --git a/src/NetFabric.Numerics.Tensors.UnitTests/SumPairsTests.cs b/src/NetFabric.Numerics.Tensors.UnitTests/SumPairsTests.cs index e2fedc4..8070074 100644 --- a/src/NetFabric.Numerics.Tensors.UnitTests/SumPairsTests.cs +++ b/src/NetFabric.Numerics.Tensors.UnitTests/SumPairsTests.cs @@ -1,5 +1,4 @@ -using System.Linq; -using System.Runtime.InteropServices; +using System.Runtime.InteropServices; namespace NetFabric.Numerics.Tensors.UnitTests; @@ -8,83 +7,50 @@ public class SumPairsTests public static TheoryData 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) + static void SumPairs_Should_Succeed(int count) + where T : struct, INumber { // 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)); + var source = Enumerable.Range(0, count) + .Select(value => new MyVector2(T.CreateChecked(value), T.CreateChecked(value + 1))) + .ToArray(); + var expected = source + .Aggregate(MyVector2.AdditiveIdentity, (sum, value) => sum + value); // act - var result = Tensor.SumPairs(MemoryMarshal.Cast, short>(source)); + var result = new MyVector2(Tensor.SumPairs(MemoryMarshal.Cast, T>(source))); // assert - result.Item1.Should().Be((short)expected.Item1); - result.Item2.Should().Be((short)expected.Item2); + result.Should().Be(expected); } [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(MemoryMarshal.Cast, int>(source)); + public void SumPairs_Short_Should_Succeed(int count) + => SumPairs_Should_Succeed(count); - // assert - result.Item1.Should().Be(expected.Item1); - result.Item2.Should().Be(expected.Item2); - } + [Theory] + [MemberData(nameof(SumData))] + public void SumPairs_Int_Should_Succeed(int count) + => SumPairs_Should_Succeed(count); [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(MemoryMarshal.Cast, long>(source)); + => SumPairs_Should_Succeed(count); - // assert - result.Item1.Should().Be(expected.Item1); - result.Item2.Should().Be(expected.Item2); - } + [Theory] + [MemberData(nameof(SumData))] + public void SumPairs_Half_Should_Succeed(int count) + => SumPairs_Should_Succeed(count); [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(MemoryMarshal.Cast, float>(source)); - - // assert - result.Item1.Should().Be(expected.Item1); - result.Item2.Should().Be(expected.Item2); - } + => SumPairs_Should_Succeed(count); [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(MemoryMarshal.Cast, double>(source)); - - // assert - result.Item1.Should().Be(expected.Item1); - result.Item2.Should().Be(expected.Item2); - } + => SumPairs_Should_Succeed(count); } diff --git a/src/NetFabric.Numerics.Tensors.UnitTests/SumTests.cs b/src/NetFabric.Numerics.Tensors.UnitTests/SumTests.cs index 285d932..a7f3957 100644 --- a/src/NetFabric.Numerics.Tensors.UnitTests/SumTests.cs +++ b/src/NetFabric.Numerics.Tensors.UnitTests/SumTests.cs @@ -1,85 +1,54 @@ -using System.Linq; - -namespace NetFabric.Numerics.Tensors.UnitTests; +namespace NetFabric.Numerics.Tensors.UnitTests; public class SumTests { public static TheoryData 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 Sum_Short_Should_Succeed(int count) + static void Sum_Should_Succeed(int count) + where T : struct, INumber { // arrange - var source = Enumerable.Range(0, count).Select(value => (short)value).ToArray(); - var expected = source.Aggregate(0, (sum, value) => sum + value); + var source = Enumerable.Range(0, count) + .Select(value => T.CreateChecked(value)) + .ToArray(); + var expected = source.Aggregate(T.AdditiveIdentity, (sum, value) => sum + value); // act - var result = Tensor.Sum(source); + var result = Tensor.Sum(source); // assert - result.Should().Be((short)expected); + result.Should().Be(T.CreateChecked(expected)); } [Theory] [MemberData(nameof(SumData))] - public void Sum_Int_Should_Succeed(int count) - { - // arrange - var source = Enumerable.Range(0, count).ToArray(); - var expected = source.Sum(); - - // act - var result = Tensor.Sum(source); + public void Sum_Short_Should_Succeed(int count) + => Sum_Should_Succeed(count); - // assert - result.Should().Be(expected); - } + [Theory] + [MemberData(nameof(SumData))] + public void Sum_Int_Should_Succeed(int count) + => Sum_Should_Succeed(count); [Theory] [MemberData(nameof(SumData))] public void Sum_Long_Should_Succeed(int count) - { - // arrange - var source = Enumerable.Range(0, count).Select(value => (long)value).ToArray(); - var expected = source.Sum(); - - // act - var result = Tensor.Sum(source); + => Sum_Should_Succeed(count); - // assert - result.Should().Be(expected); - } + [Theory] + [MemberData(nameof(SumData))] + public void Sum_Half_Should_Succeed(int count) + => Sum_Should_Succeed(count); [Theory] [MemberData(nameof(SumData))] public void Sum_Float_Should_Succeed(int count) - { - // arrange - var source = Enumerable.Range(0, count).Select(value => (float)value).ToArray(); - var expected = source.Sum(); - - // act - var result = Tensor.Sum(source); - - // assert - result.Should().Be(expected); - } + => Sum_Should_Succeed(count); [Theory] [MemberData(nameof(SumData))] public void Sum_Double_Should_Succeed(int count) - { - // arrange - var source = Enumerable.Range(0, count).Select(value => (double)value).ToArray(); - var expected = source.Sum(); - - // act - var result = Tensor.Sum(source); - - // assert - result.Should().Be(expected); - } + => Sum_Should_Succeed(count); } diff --git a/src/NetFabric.Numerics.Tensors.UnitTests/SumTripletsTests.cs b/src/NetFabric.Numerics.Tensors.UnitTests/SumTripletsTests.cs new file mode 100644 index 0000000..3211fba --- /dev/null +++ b/src/NetFabric.Numerics.Tensors.UnitTests/SumTripletsTests.cs @@ -0,0 +1,56 @@ +using System.Runtime.InteropServices; + +namespace NetFabric.Numerics.Tensors.UnitTests; + +public class SumTripletsTests +{ + public static TheoryData 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 }; + + static void SumTriplets_Should_Succeed(int count) + where T : struct, INumber + { + // arrange + var source = Enumerable.Range(0, count) + .Select(value => new MyVector3(T.CreateChecked(value), T.CreateChecked(value + 1), T.CreateChecked(value + 2))) + .ToArray(); + var expected = source + .Aggregate(MyVector3.AdditiveIdentity, (sum, value) => sum + value); + + // act + var result = new MyVector3(Tensor.SumTriplets(MemoryMarshal.Cast, T>(source))); + + // assert + result.Should().Be(expected); + } + + [Theory] + [MemberData(nameof(SumData))] + public void SumTriplets_Short_Should_Succeed(int count) + => SumTriplets_Should_Succeed(count); + + [Theory] + [MemberData(nameof(SumData))] + public void SumTriplets_Int_Should_Succeed(int count) + => SumTriplets_Should_Succeed(count); + + [Theory] + [MemberData(nameof(SumData))] + public void SumTriplets_Long_Should_Succeed(int count) + => SumTriplets_Should_Succeed(count); + + [Theory] + [MemberData(nameof(SumData))] + public void SumTriplets_Half_Should_Succeed(int count) + => SumTriplets_Should_Succeed(count); + + [Theory] + [MemberData(nameof(SumData))] + public void SumTriplets_Float_Should_Succeed(int count) + => SumTriplets_Should_Succeed(count); + + [Theory] + [MemberData(nameof(SumData))] + public void SumTriplets_Double_Should_Succeed(int count) + => SumTriplets_Should_Succeed(count); +} diff --git a/src/NetFabric.Numerics.Tensors/Add.cs b/src/NetFabric.Numerics.Tensors/Add.cs index 7d12f45..fa14774 100644 --- a/src/NetFabric.Numerics.Tensors/Add.cs +++ b/src/NetFabric.Numerics.Tensors/Add.cs @@ -1,17 +1,61 @@ -using System.Runtime.InteropServices; - namespace NetFabric.Numerics; public static partial class Tensor { + /// + /// Adds a scalar value to each element of the source span and stores the result in the destination span. + /// + /// The type of the elements in the spans. + /// The source span. + /// The scalar value to add. + /// The destination span. + /// Thrown when the source and destination spans have different lengths. + /// Thrown when the addition operation is not defined for the type . public static void Add(ReadOnlySpan x, T y, Span destination) where T : struct, IAdditionOperators => Apply>(x, y, destination); + /// + /// Adds a scalar value to each element of the source span and stores the result in the destination span. + /// + /// The type of the elements in the spans. + /// The source span. + /// The tuple of two values to add. + /// The destination span. + /// Thrown when the source and destination spans have different lengths. + /// Thrown when the addition operation is not defined for the type . + /// + /// This method can be used to calculate the addition of 2D vectors. + /// public static void Add(ReadOnlySpan x, ValueTuple y, Span destination) where T : struct, IAdditionOperators => Apply>(x, y, destination); + /// + /// Adds a scalar value to each element of the source span and stores the result in the destination span. + /// + /// The type of the elements in the spans. + /// The source span. + /// The tuple of three values to add. + /// The destination span. + /// Thrown when the source and destination spans have different lengths. + /// Thrown when the addition operation is not defined for the type . + /// + /// This method can be used to calculate the addition of 3D vectors. + /// + public static void Add(ReadOnlySpan x, ValueTuple y, Span destination) + where T : struct, IAdditionOperators + => Apply>(x, y, destination); + + /// + /// Adds corresponding elements of two source spans and stores the result in the destination span. + /// + /// The type of the elements in the spans. + /// The first source span. + /// The second source span. + /// The destination span. + /// Thrown when the source and destination spans have different lengths. + /// Thrown when the addition operation is not defined for the type . public static void Add(ReadOnlySpan x, ReadOnlySpan y, Span destination) where T : struct, IAdditionOperators => Apply>(x, y, destination); diff --git a/src/NetFabric.Numerics.Tensors/AddMultiply.cs b/src/NetFabric.Numerics.Tensors/AddMultiply.cs index a4c3272..c0475d2 100644 --- a/src/NetFabric.Numerics.Tensors/AddMultiply.cs +++ b/src/NetFabric.Numerics.Tensors/AddMultiply.cs @@ -1,33 +1,161 @@ -using System.Runtime.InteropServices; - namespace NetFabric.Numerics; public static partial class Tensor { + /// + /// Adds each element of a span to a scalar value then multiply by another scalar value and stores the result in the destination span. + /// + /// The type of the elements in the spans. + /// The source span. + /// The scalar value to add. + /// The scalar value to multiply. + /// The destination span. + /// Thrown when the source and destination spans have different lengths. + /// Thrown when the addition operation is not defined for the type . public static void AddMultiply(ReadOnlySpan x, T y, T z, Span destination) where T : struct, IAdditionOperators, IMultiplyOperators => Apply>(x, y, z, destination); + /// + /// Adds each element of a span to a scalar value then multiply by another scalar value and stores the result in the destination span. + /// + /// The type of the elements in the spans. + /// The source span. + /// The tuple of two values to add. + /// The tuple of two values to multiply. + /// The destination span. + /// Thrown when the source and destination spans have different lengths. + /// Thrown when the addition operation is not defined for the type . + /// + /// This method can be used to calculate the addition and multiplication of 2D vectors. + /// public static void AddMultiply(ReadOnlySpan x, ValueTuple y, ValueTuple z, Span destination) where T : struct, IAdditionOperators, IMultiplyOperators => Apply>(x, y, z, destination); + /// + /// Adds each element of a span to a scalar value then multiply by another scalar value and stores the result in the destination span. + /// + /// The type of the elements in the spans. + /// The source span. + /// The tuple of three values to add. + /// The tuple of three values to multiply. + /// The destination span. + /// Thrown when the source and destination spans have different lengths. + /// Thrown when the addition operation is not defined for the type . + /// + /// This method can be used to calculate the addition and multiplication of 3D vectors. + /// + public static void AddMultiply(ReadOnlySpan x, ValueTuple y, ValueTuple z, Span destination) + where T : struct, IAdditionOperators, IMultiplyOperators + => Apply>(x, y, z, destination); + + /// + /// Adds each element of a span to a scalar value then multiply by then respective element of another span and stores the result in the destination span. + /// + /// The type of the elements in the spans. + /// The source span. + /// The scalar value to add. + /// The span of values to multiply. + /// The destination span. + /// Thrown when the source and destination spans have different lengths. + /// Thrown when the addition operation is not defined for the type . public static void AddMultiply(ReadOnlySpan x, T y, ReadOnlySpan z, Span destination) where T : struct, IAdditionOperators, IMultiplyOperators => Apply>(x, y, z, destination); + /// + /// Adds each element of a span to a scalar value then multiply by then respective element of another span and stores the result in the destination span. + /// + /// The type of the elements in the spans. + /// The source span. + /// The tuple of two values to add. + /// The span of values to multiply. + /// The destination span. + /// Thrown when the source and destination spans have different lengths. + /// Thrown when the addition operation is not defined for the type . + /// + /// This method can be used to calculate the addition and multiplication of 2D vectors. + /// public static void AddMultiply(ReadOnlySpan x, ValueTuple y, ReadOnlySpan z, Span destination) where T : struct, IAdditionOperators, IMultiplyOperators => Apply>(x, y, z, destination); + /// + /// Adds each element of a span to a scalar value then multiply by then respective element of another span and stores the result in the destination span. + /// + /// The type of the elements in the spans. + /// The source span. + /// The tuple of three values to add. + /// The span of values to multiply. + /// The destination span. + /// Thrown when the source and destination spans have different lengths. + /// Thrown when the addition operation is not defined for the type . + /// + /// This method can be used to calculate the addition and multiplication of 3D vectors. + /// + public static void AddMultiply(ReadOnlySpan x, ValueTuple y, ReadOnlySpan z, Span destination) + where T : struct, IAdditionOperators, IMultiplyOperators + => Apply>(x, y, z, destination); + + /// + /// Adds each element of a span to the respective element of another span then multiply by a scalar value and stores the result in the destination span. + /// + /// The type of the elements in the spans. + /// The source span. + /// The span of values to add. + /// The scalar value to multiply. + /// The destination span. + /// Thrown when the source and destination spans have different lengths. + /// Thrown when the addition operation is not defined for the type . public static void AddMultiply(ReadOnlySpan x, ReadOnlySpan y, T z, Span destination) where T : struct, IAdditionOperators, IMultiplyOperators => Apply>(x, y, z, destination); + /// + /// Adds each element of a span to the respective element of another span then multiply by a scalar value and stores the result in the destination span. + /// + /// The type of the elements in the spans. + /// The source span. + /// The span of values to add. + /// The tuple of two values to multiply. + /// The destination span. + /// Thrown when the source and destination spans have different lengths. + /// Thrown when the addition operation is not defined for the type . + /// + /// This method can be used to calculate the addition and multiplication of 2D vectors. + /// public static void AddMultiply(ReadOnlySpan x, ReadOnlySpan y, ValueTuple z, Span destination) where T : struct, IAdditionOperators, IMultiplyOperators => Apply>(x, y, z, destination); + /// + /// Adds each element of a span to the respective element of another span then multiply by a scalar value and stores the result in the destination span. + /// + /// The type of the elements in the spans. + /// The source span. + /// The span of values to add. + /// The tuple of three values to multiply. + /// The destination span. + /// Thrown when the source and destination spans have different lengths. + /// Thrown when the addition operation is not defined for the type . + /// + /// This method can be used to calculate the addition and multiplication of 3D vectors. + /// + public static void AddMultiply(ReadOnlySpan x, ReadOnlySpan y, ValueTuple z, Span destination) + where T : struct, IAdditionOperators, IMultiplyOperators + => Apply>(x, y, z, destination); + + /// + /// Adds each element of a span to respective element on a second span then multiply by the respective element of a third element and stores the result in the destination span. + /// + /// The type of the elements in the spans. + /// The source span. + /// The span of values to add. + /// The span of values to multiply. + /// The destination span. + /// Thrown when the source and destination spans have different lengths. + /// Thrown when the addition operation is not defined for the type . public static void AddMultiply(ReadOnlySpan x, ReadOnlySpan y, ReadOnlySpan z, Span destination) where T : struct, IAdditionOperators, IMultiplyOperators => Apply>(x, y, z, destination); diff --git a/src/NetFabric.Numerics.Tensors/Aggregate.cs b/src/NetFabric.Numerics.Tensors/Aggregate.cs index 1c1d466..56ac8e2 100644 --- a/src/NetFabric.Numerics.Tensors/Aggregate.cs +++ b/src/NetFabric.Numerics.Tensors/Aggregate.cs @@ -1,31 +1,28 @@ +namespace NetFabric.Numerics; -using System; -using System.Numerics; -using System.Runtime.CompilerServices; -using System.Runtime.InteropServices; - -namespace NetFabric.Numerics +public static partial class Tensor { - public static partial class Tensor + + /// + /// Aggregates the elements of a using the specified . + /// + /// The type of the elements in the . + /// The type of the aggregation operator. + /// The to aggregate. + /// The aggregated value. + public static T Aggregate(ReadOnlySpan source) + where T : struct + where TOperator : struct, IAggregationOperator { - /// - /// Aggregates the elements in the specified using the specified . - /// - /// The type of the elements in the span. - /// The type of the aggregation operator. - /// The source span. - /// The aggregated value. - public static T Aggregate(ReadOnlySpan source) - where T : struct - where TOperator : struct, IAggregationOperator + var result = TOperator.Identity; + ref var sourceRef = ref MemoryMarshal.GetReference(source); + + if (Vector.IsHardwareAccelerated && Vector.IsSupported) { - var result = TOperator.Seed; - var resultVector = new Vector(TOperator.Seed); + var resultVector = new Vector(TOperator.Identity); nint index = 0; - if (Vector.IsHardwareAccelerated && - Vector.IsSupported && - source.Length >= Vector.Count) + if (source.Length >= Vector.Count) { var sourceVectors = MemoryMarshal.Cast>(source); @@ -33,14 +30,20 @@ public static T Aggregate(ReadOnlySpan source) for (nint indexVector = 0; indexVector < sourceVectors.Length; indexVector++) resultVector = TOperator.Invoke(resultVector, Unsafe.Add(ref sourceVectorsRef, indexVector)); - index = source.Length - source.Length % Vector.Count; + index = source.Length - (source.Length % Vector.Count); } - ref var sourceRef = ref MemoryMarshal.GetReference(source); for (; index < source.Length; index++) result = TOperator.Invoke(result, Unsafe.Add(ref sourceRef, index)); return TOperator.ResultSelector(result, resultVector); } + else + { + for (nint index = 0; index < source.Length; index++) + result = TOperator.Invoke(result, Unsafe.Add(ref sourceRef, index)); + + return result; + } } } \ No newline at end of file diff --git a/src/NetFabric.Numerics.Tensors/AggregatePairs.cs b/src/NetFabric.Numerics.Tensors/AggregatePairs.cs index e49fe5a..3d1f032 100644 --- a/src/NetFabric.Numerics.Tensors/AggregatePairs.cs +++ b/src/NetFabric.Numerics.Tensors/AggregatePairs.cs @@ -1,20 +1,18 @@ - using System; -using System.Numerics; -using System.Runtime.CompilerServices; -using System.Runtime.InteropServices; namespace NetFabric.Numerics { public static partial class Tensor { + /// - /// Aggregates the elements in the specified using the specified . + /// Aggregates pairs of elements in a using the specified . /// - /// The type of the elements in the span. + /// The type of the elements in the . /// The type of the aggregation operator. - /// The source span. - /// The aggregated value. + /// The containing the elements to aggregate. + /// A representing the aggregated pairs. + /// Thrown when the span does not have an even size. public static ValueTuple AggregatePairs(ReadOnlySpan source) where T : struct where TOperator : struct, IAggregationPairsOperator @@ -22,35 +20,46 @@ public static ValueTuple AggregatePairs(ReadOnlySpan sour 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.Zero; - - nint index = 0; + var result = TOperator.Identity; + ref var sourceRef = ref MemoryMarshal.GetReference(source); - if (Vector.IsHardwareAccelerated && - Vector.IsSupported && - Vector.Count > 2 && - Vector.Count % 2 is 0 && - source.Length >= Vector.Count) + if (Vector.IsHardwareAccelerated && Vector.IsSupported) { - var sourceVectors = MemoryMarshal.Cast>(source); - resultVector = GetVector(TOperator.Seed); + var resultVector = GetVector(TOperator.Identity); - ref var sourceVectorsRef = ref MemoryMarshal.GetReference(sourceVectors); - for (nint indexVector = 0; indexVector < sourceVectors.Length; indexVector++) - resultVector = TOperator.Invoke(resultVector, Unsafe.Add(ref sourceVectorsRef, indexVector)); + nint index = 0; - index = source.Length - source.Length % Vector.Count; - } + if (Vector.Count > 2 && + Vector.Count % 2 is 0 && + source.Length >= Vector.Count) + { + var sourceVectors = MemoryMarshal.Cast>(source); - 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)); + 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.Count); + } + + 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); } + else + { + for (nint index = 0; 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); + return result; + } } } } \ No newline at end of file diff --git a/src/NetFabric.Numerics.Tensors/AggregateTriplets.cs b/src/NetFabric.Numerics.Tensors/AggregateTriplets.cs new file mode 100644 index 0000000..69611f1 --- /dev/null +++ b/src/NetFabric.Numerics.Tensors/AggregateTriplets.cs @@ -0,0 +1,33 @@ +namespace NetFabric.Numerics +{ + public static partial class Tensor + { + /// + /// Aggregates triplets of elements from a using the specified . + /// + /// The type of the elements in the . + /// The type of the aggregation operator. + /// The to aggregate triplets from. + /// A containing the aggregated triplets. + /// Thrown when the size of the span is not a multiple of 3. + public static ValueTuple AggregateTriplets(ReadOnlySpan source) + where T : struct + where TOperator : struct, IAggregationTripletsOperator + { + if (source.Length % 3 is not 0) + Throw.ArgumentException(nameof(source), "source span must have a size multiple of 3."); + + var result = TOperator.Seed; + + ref var sourceRef = ref MemoryMarshal.GetReference(source); + for (nint index = 0; index < source.Length; index += 3) + { + result.Item1 = TOperator.Invoke(result.Item1, Unsafe.Add(ref sourceRef, index)); + result.Item2 = TOperator.Invoke(result.Item2, Unsafe.Add(ref sourceRef, index + 1)); + result.Item3 = TOperator.Invoke(result.Item3, Unsafe.Add(ref sourceRef, index + 2)); + } + + return result; + } + } +} \ No newline at end of file diff --git a/src/NetFabric.Numerics.Tensors/ApplyBinary.cs b/src/NetFabric.Numerics.Tensors/ApplyBinary.cs index e482596..020126d 100644 --- a/src/NetFabric.Numerics.Tensors/ApplyBinary.cs +++ b/src/NetFabric.Numerics.Tensors/ApplyBinary.cs @@ -1,10 +1,8 @@ -using System.Runtime.InteropServices; - namespace NetFabric.Numerics; public static partial class Tensor { - public static void Apply(ReadOnlySpan x, ReadOnlySpan y, Span destination, bool useIntrinsics = true) + public static void Apply(ReadOnlySpan x, ReadOnlySpan y, Span destination) where T : struct where TOperator : struct, IBinaryOperator { @@ -22,8 +20,7 @@ public static void Apply(ReadOnlySpan x, ReadOnlySpan y, Spa // Check if hardware acceleration and Vector support are available, // and if the length of the x is greater than the Vector.Count. - if (useIntrinsics && - Vector.IsHardwareAccelerated && + if (Vector.IsHardwareAccelerated && Vector.IsSupported && x.Length >= Vector.Count) { @@ -59,7 +56,7 @@ public static void Apply(ReadOnlySpan x, ReadOnlySpan y, Spa } } - public static void Apply(ReadOnlySpan x, T y, Span destination, bool useIntrinsics = true) + public static void Apply(ReadOnlySpan x, T y, Span destination) where T : struct where TOperator : struct, IBinaryOperator { @@ -73,8 +70,7 @@ public static void Apply(ReadOnlySpan x, T y, Span destinati // Check if hardware acceleration and Vector support are available, // and if the length of the x is greater than the Vector.Count. - if (useIntrinsics && - Vector.IsHardwareAccelerated && + if (Vector.IsHardwareAccelerated && Vector.IsSupported && x.Length >= Vector.Count) { @@ -108,7 +104,7 @@ public static void Apply(ReadOnlySpan x, T y, Span destinati } } - public static void Apply(ReadOnlySpan x, ValueTuple y, Span destination, bool useIntrinsics = true) + public static void Apply(ReadOnlySpan x, ValueTuple y, Span destination) where T : struct where TOperator : struct, IBinaryOperator { @@ -124,8 +120,7 @@ public static void Apply(ReadOnlySpan x, ValueTuple y, Sp // Check if hardware acceleration and Vector support are available, // and if the length of the x is greater than the Vector.Count. - if (useIntrinsics && - Vector.IsHardwareAccelerated && + if (Vector.IsHardwareAccelerated && Vector.IsSupported && Vector.Count > 2 && Vector.Count % 2 is 0 && @@ -164,4 +159,31 @@ public static void Apply(ReadOnlySpan x, ValueTuple y, Sp } } + public static void Apply(ReadOnlySpan x, ValueTuple y, Span destination) + where T : struct + where TOperator : struct, IBinaryOperator + { + if (x.Length % 3 is not 0) + Throw.ArgumentException(nameof(x), "x span must have a size multiple of 3."); + if (x.Length > destination.Length) + Throw.ArgumentException(nameof(destination), "Destination span is too small."); + if (SpansOverlapAndAreNotSame(x, destination)) + Throw.ArgumentException(nameof(destination), "Destination span overlaps with x."); + + ref var xRef = ref MemoryMarshal.GetReference(x); + ref var destinationRef = ref MemoryMarshal.GetReference(destination); + for (nint index = 0; index < x.Length; index += 3) + { + Unsafe.Add(ref destinationRef, index) = TOperator.Invoke( + Unsafe.Add(ref xRef, index), + y.Item1); + Unsafe.Add(ref destinationRef, index + 1) = TOperator.Invoke( + Unsafe.Add(ref xRef, index + 1), + y.Item2); + Unsafe.Add(ref destinationRef, index + 2) = TOperator.Invoke( + Unsafe.Add(ref xRef, index + 2), + y.Item3); + } + } + } \ No newline at end of file diff --git a/src/NetFabric.Numerics.Tensors/ApplyTernary.cs b/src/NetFabric.Numerics.Tensors/ApplyTernary.cs index 39590c0..f8a0e64 100644 --- a/src/NetFabric.Numerics.Tensors/ApplyTernary.cs +++ b/src/NetFabric.Numerics.Tensors/ApplyTernary.cs @@ -1,10 +1,8 @@ -using System.Runtime.InteropServices; - namespace NetFabric.Numerics; public static partial class Tensor { - public static void Apply(ReadOnlySpan x, ReadOnlySpan y, ReadOnlySpan z, Span destination, bool useIntrinsics = true) + public static void Apply(ReadOnlySpan x, ReadOnlySpan y, ReadOnlySpan z, Span destination) where T : struct where TOperator : struct, ITernaryOperator { @@ -24,8 +22,7 @@ public static void Apply(ReadOnlySpan x, ReadOnlySpan y, Rea // Check if hardware acceleration and Vector support are available, // and if the length of the x is greater than the Vector.Count. - if (useIntrinsics && - Vector.IsHardwareAccelerated && + if (Vector.IsHardwareAccelerated && Vector.IsSupported && x.Length >= Vector.Count) { @@ -66,7 +63,7 @@ public static void Apply(ReadOnlySpan x, ReadOnlySpan y, Rea } } - public static void Apply(ReadOnlySpan x, T y, ReadOnlySpan z, Span destination, bool useIntrinsics = true) + public static void Apply(ReadOnlySpan x, T y, ReadOnlySpan z, Span destination) where T : struct where TOperator : struct, ITernaryOperator { @@ -84,8 +81,7 @@ public static void Apply(ReadOnlySpan x, T y, ReadOnlySpan z // Check if hardware acceleration and Vector support are available, // and if the length of the x is greater than the Vector.Count. - if (useIntrinsics && - Vector.IsHardwareAccelerated && + if (Vector.IsHardwareAccelerated && Vector.IsSupported && x.Length >= Vector.Count) { @@ -124,7 +120,7 @@ public static void Apply(ReadOnlySpan x, T y, ReadOnlySpan z } } - public static void Apply(ReadOnlySpan x, ValueTuple y, ReadOnlySpan z, Span destination, bool useIntrinsics = true) + public static void Apply(ReadOnlySpan x, ValueTuple y, ReadOnlySpan z, Span destination) where T : struct where TOperator : struct, ITernaryOperator { @@ -144,8 +140,7 @@ public static void Apply(ReadOnlySpan x, ValueTuple y, Re // Check if hardware acceleration and Vector support are available, // and if the length of the x is greater than the Vector.Count. - if (useIntrinsics && - Vector.IsHardwareAccelerated && + if (Vector.IsHardwareAccelerated && Vector.IsSupported && Vector.Count > 2 && Vector.Count % 2 is 0 && @@ -190,7 +185,42 @@ public static void Apply(ReadOnlySpan x, ValueTuple y, Re } } - public static void Apply(ReadOnlySpan x, ReadOnlySpan y, T z, Span destination, bool useIntrinsics = true) + public static void Apply(ReadOnlySpan x, ValueTuple y, ReadOnlySpan z, Span destination) + where T : struct + where TOperator : struct, ITernaryOperator + { + if (x.Length % 3 is not 0) + Throw.ArgumentException(nameof(x), "x span must have a size multiple of 3."); + if (x.Length != z.Length) + Throw.ArgumentException(nameof(x), "x and z spans must have the same length."); + if (x.Length > destination.Length) + Throw.ArgumentException(nameof(destination), "Destination span is too small."); + if (SpansOverlapAndAreNotSame(x, destination)) + Throw.ArgumentException(nameof(destination), "Destination span overlaps with x."); + if (SpansOverlapAndAreNotSame(z, destination)) + Throw.ArgumentException(nameof(destination), "Destination span overlaps with z."); + + ref var xRef = ref MemoryMarshal.GetReference(x); + ref var zRef = ref MemoryMarshal.GetReference(z); + ref var destinationRef = ref MemoryMarshal.GetReference(destination); + for (nint index = 0; index < x.Length; index += 3) + { + Unsafe.Add(ref destinationRef, index) = TOperator.Invoke( + Unsafe.Add(ref xRef, index), + y.Item1, + Unsafe.Add(ref zRef, index)); + Unsafe.Add(ref destinationRef, index + 1) = TOperator.Invoke( + Unsafe.Add(ref xRef, index + 1), + y.Item2, + Unsafe.Add(ref zRef, index + 1)); + Unsafe.Add(ref destinationRef, index + 2) = TOperator.Invoke( + Unsafe.Add(ref xRef, index + 2), + y.Item3, + Unsafe.Add(ref zRef, index + 2)); + } + } + + public static void Apply(ReadOnlySpan x, ReadOnlySpan y, T z, Span destination) where T : struct where TOperator : struct, ITernaryOperator { @@ -208,8 +238,7 @@ public static void Apply(ReadOnlySpan x, ReadOnlySpan y, T z // Check if hardware acceleration and Vector support are available, // and if the length of the x is greater than the Vector.Count. - if (useIntrinsics && - Vector.IsHardwareAccelerated && + if (Vector.IsHardwareAccelerated && Vector.IsSupported && x.Length >= Vector.Count) { @@ -248,7 +277,7 @@ public static void Apply(ReadOnlySpan x, ReadOnlySpan y, T z } } - public static void Apply(ReadOnlySpan x, ReadOnlySpan y, ValueTuple z, Span destination, bool useIntrinsics = true) + public static void Apply(ReadOnlySpan x, ReadOnlySpan y, ValueTuple z, Span destination) where T : struct where TOperator : struct, ITernaryOperator { @@ -268,8 +297,7 @@ public static void Apply(ReadOnlySpan x, ReadOnlySpan y, Val // Check if hardware acceleration and Vector support are available, // and if the length of the x is greater than the Vector.Count. - if (useIntrinsics && - Vector.IsHardwareAccelerated && + if (Vector.IsHardwareAccelerated && Vector.IsSupported && Vector.Count > 2 && Vector.Count % 2 is 0 && @@ -314,7 +342,42 @@ public static void Apply(ReadOnlySpan x, ReadOnlySpan y, Val } } - public static void Apply(ReadOnlySpan x, T y, T z, Span destination, bool useIntrinsics = true) + public static void Apply(ReadOnlySpan x, ReadOnlySpan y, ValueTuple z, Span destination) + where T : struct + where TOperator : struct, ITernaryOperator + { + if (x.Length % 3 is not 0) + Throw.ArgumentException(nameof(x), "x span must have a size multiple of 3."); + if (x.Length != y.Length) + Throw.ArgumentException(nameof(x), "x and y spans must have the same length."); + if (x.Length > destination.Length) + Throw.ArgumentException(nameof(destination), "Destination span is too small."); + if (SpansOverlapAndAreNotSame(x, destination)) + Throw.ArgumentException(nameof(destination), "Destination span overlaps with x."); + if (SpansOverlapAndAreNotSame(y, destination)) + Throw.ArgumentException(nameof(destination), "Destination span overlaps with y."); + + ref var xRef = ref MemoryMarshal.GetReference(x); + ref var yRef = ref MemoryMarshal.GetReference(y); + ref var destinationRef = ref MemoryMarshal.GetReference(destination); + for (nint index = 0; index < x.Length; index += 3) + { + Unsafe.Add(ref destinationRef, index) = TOperator.Invoke( + Unsafe.Add(ref xRef, index), + Unsafe.Add(ref yRef, index), + z.Item1); + Unsafe.Add(ref destinationRef, index + 1) = TOperator.Invoke( + Unsafe.Add(ref xRef, index + 1), + Unsafe.Add(ref yRef, index + 1), + z.Item2); + Unsafe.Add(ref destinationRef, index + 2) = TOperator.Invoke( + Unsafe.Add(ref xRef, index + 2), + Unsafe.Add(ref yRef, index + 2), + z.Item3); + } + } + + public static void Apply(ReadOnlySpan x, T y, T z, Span destination) where T : struct where TOperator : struct, ITernaryOperator { @@ -328,8 +391,7 @@ public static void Apply(ReadOnlySpan x, T y, T z, Span dest // Check if hardware acceleration and Vector support are available, // and if the length of the x is greater than the Vector.Count. - if (useIntrinsics && - Vector.IsHardwareAccelerated && + if (Vector.IsHardwareAccelerated && Vector.IsSupported && x.Length >= Vector.Count) { @@ -366,7 +428,7 @@ public static void Apply(ReadOnlySpan x, T y, T z, Span dest } } - public static void Apply(ReadOnlySpan x, ValueTuple y, ValueTuple z, Span destination, bool useIntrinsics = true) + public static void Apply(ReadOnlySpan x, ValueTuple y, ValueTuple z, Span destination) where T : struct where TOperator : struct, ITernaryOperator { @@ -382,8 +444,7 @@ public static void Apply(ReadOnlySpan x, ValueTuple y, Va // Check if hardware acceleration and Vector support are available, // and if the length of the x is greater than the Vector.Count. - if (useIntrinsics && - Vector.IsHardwareAccelerated && + if (Vector.IsHardwareAccelerated && Vector.IsSupported && Vector.Count > 2 && Vector.Count % 2 is 0 && @@ -426,4 +487,33 @@ public static void Apply(ReadOnlySpan x, ValueTuple y, Va } } + public static void Apply(ReadOnlySpan x, ValueTuple y, ValueTuple z, Span destination) + where T : struct + where TOperator : struct, ITernaryOperator + { + if (x.Length % 3 is not 0) + Throw.ArgumentException(nameof(x), "x span must have a size multiple of 3."); + if (x.Length > destination.Length) + Throw.ArgumentException(nameof(destination), "Destination span is too small."); + if (SpansOverlapAndAreNotSame(x, destination)) + Throw.ArgumentException(nameof(destination), "Destination span overlaps with x."); + + ref var xRef = ref MemoryMarshal.GetReference(x); + ref var destinationRef = ref MemoryMarshal.GetReference(destination); + for (nint index = 0; index < x.Length; index += 3) + { + Unsafe.Add(ref destinationRef, index) = TOperator.Invoke( + Unsafe.Add(ref xRef, index), + y.Item1, + z.Item1); + Unsafe.Add(ref destinationRef, index + 1) = TOperator.Invoke( + Unsafe.Add(ref xRef, index + 1), + y.Item2, + z.Item2); + Unsafe.Add(ref destinationRef, index + 2) = TOperator.Invoke( + Unsafe.Add(ref xRef, index + 2), + y.Item3, + z.Item3); + } + } } \ No newline at end of file diff --git a/src/NetFabric.Numerics.Tensors/ApplyUnary.cs b/src/NetFabric.Numerics.Tensors/ApplyUnary.cs index ae3ee8e..94cf166 100644 --- a/src/NetFabric.Numerics.Tensors/ApplyUnary.cs +++ b/src/NetFabric.Numerics.Tensors/ApplyUnary.cs @@ -1,10 +1,8 @@ -using System.Runtime.InteropServices; - namespace NetFabric.Numerics; public static partial class Tensor { - public static void Apply(ReadOnlySpan x, Span destination, bool useIntrinsics = true) + public static void Apply(ReadOnlySpan x, Span destination) where T : struct where TOperator : struct, IUnaryOperator { @@ -18,8 +16,7 @@ public static void Apply(ReadOnlySpan x, Span destination, b // Check if hardware acceleration and Vector support are available, // and if the length of the x is greater than the Vector.Count. - if (useIntrinsics && - Vector.IsHardwareAccelerated && + if (Vector.IsHardwareAccelerated && Vector.IsSupported && x.Length >= Vector.Count) { diff --git a/src/NetFabric.Numerics.Tensors/Average.cs b/src/NetFabric.Numerics.Tensors/Average.cs index 6c4e095..2a4dc1a 100644 --- a/src/NetFabric.Numerics.Tensors/Average.cs +++ b/src/NetFabric.Numerics.Tensors/Average.cs @@ -1,5 +1,3 @@ -using System.Runtime.InteropServices; - namespace NetFabric.Numerics; public static partial class Tensor diff --git a/src/NetFabric.Numerics.Tensors/Divide.cs b/src/NetFabric.Numerics.Tensors/Divide.cs index dffc31c..5728446 100644 --- a/src/NetFabric.Numerics.Tensors/Divide.cs +++ b/src/NetFabric.Numerics.Tensors/Divide.cs @@ -1,35 +1,57 @@ -using System.Runtime.InteropServices; - namespace NetFabric.Numerics; public static partial class Tensor { /// - /// Divides a right to each element in the left span and stores the result in the destination span. + /// Divides each element of the left span by the right value and stores the result in the destination span. /// /// The type of the elements in the spans. - /// The left span. - /// The right to divide each element by. - /// The destination span to store the result. - /// Thrown when the left and destination spans have different lengths. - /// Thrown when the type does not implement the interface. + /// The span containing the left operands. + /// The right operand. + /// The span to store the result. + /// Thrown when the right value is zero. public static void Divide(ReadOnlySpan left, T right, Span destination) where T : struct, IDivisionOperators => Apply>(left, right, destination); + /// + /// Divides each element of the left span by the right value and stores the result in the destination span. + /// + /// The type of the elements in the spans. + /// The span containing the left operands. + /// The right operand tuple. + /// The span to store the result. + /// Thrown when any component of the right tuple is zero. /// + /// + /// This method can be used to calculate the division of 2D vectors. + /// public static void Divide(ReadOnlySpan left, ValueTuple right, Span destination) where T : struct, IDivisionOperators => Apply>(left, right, destination); /// - /// Divides corresponding elements in the left and right spans and stores the result in the destination span. + /// Divides each element of the left span by the right value and stores the result in the destination span. + /// + /// The type of the elements in the spans. + /// The span containing the left operands. + /// The right operand tuple. + /// The span to store the result. + /// Thrown when any component of the right tuple is zero. + /// + /// This method can be used to calculate the division of 3D vectors. + /// + public static void Divide(ReadOnlySpan left, ValueTuple right, Span destination) + where T : struct, IDivisionOperators + => Apply>(left, right, destination); + + /// + /// Divides each element of the left span by the corresponding element of the right span and stores the result in the destination span. /// /// The type of the elements in the spans. - /// The left span. - /// The right span. - /// The destination span to store the result. - /// Thrown when the left, right, and destination spans have different lengths. - /// Thrown when the type does not implement the interface. + /// The span containing the left operands. + /// The span containing the right operands. + /// The span to store the result. + /// Thrown when any element of the right span is zero. public static void Divide(ReadOnlySpan left, ReadOnlySpan right, Span destination) where T : struct, IDivisionOperators => Apply>(left, right, destination); diff --git a/src/NetFabric.Numerics.Tensors/ITensorOperation.cs b/src/NetFabric.Numerics.Tensors/ITensorOperation.cs index 3d44304..7eec5db 100644 --- a/src/NetFabric.Numerics.Tensors/ITensorOperation.cs +++ b/src/NetFabric.Numerics.Tensors/ITensorOperation.cs @@ -1,42 +1,141 @@ namespace NetFabric.Numerics; +/// +/// Represents a unary operator that operates on a single value or vector. +/// +/// The type of the value or vector. public interface IUnaryOperator where T : struct { + /// + /// Applies the unary operator to the specified value. + /// + /// The value to apply the operator to. + /// The result of applying the operator to the value. static abstract T Invoke(T x); + + /// + /// Applies the unary operator to the specified vector. + /// + /// The vector to apply the operator to. + /// The result of applying the operator to the vector. static abstract Vector Invoke(Vector x); } -public interface IBinaryOperator +/// +/// Represents a binary operator that operates on two values or vectors. +/// +/// The type of the values or vectors. +public interface IBinaryOperator where T : struct { + /// + /// Applies the binary operator to the specified values. + /// + /// The first value to apply the operator to. + /// The second value to apply the operator to. + /// The result of applying the operator to the values. static abstract T Invoke(T x, T y); + + /// + /// Applies the binary operator to the specified vectors. + /// + /// The first vector to apply the operator to. + /// The second vector to apply the operator to. + /// The result of applying the operator to the vectors. static abstract Vector Invoke(Vector x, Vector y); } +/// +/// Represents a ternary operator that operates on three values or vectors. +/// +/// The type of the values or vectors. +public interface ITernaryOperator + where T : struct +{ + /// + /// Applies the ternary operator to the specified values. + /// + /// The first value to apply the operator to. + /// The second value to apply the operator to. + /// The third value to apply the operator to. + /// The result of applying the operator to the values. + static abstract T Invoke(T x, T y, T z); + + /// + /// Applies the ternary operator to the specified vectors. + /// + /// The first vector to apply the operator to. + /// The second vector to apply the operator to. + /// The third vector to apply the operator to. + /// The result of applying the operator to the vectors. + static abstract Vector Invoke(Vector x, Vector y, Vector z); +} + +/// +/// Represents an aggregation operator that operates on two values or vectors and produces a single result. +/// +/// The type of the values or vectors. public interface IAggregationOperator : IBinaryOperator where T : struct { - static virtual T Seed + /// + /// Gets the identity value for the type and operation to be performed. + /// + static virtual T Identity => Throw.NotSupportedException(); + /// + /// Combines the specified value with the vector to produce a new value. + /// + /// The current value. + /// The vector to combine with the value. + /// The result of combining the value with the vector. static abstract T ResultSelector(T value, Vector vector); } -public interface IAggregationPairsOperator - : IBinaryOperator +/// +/// Represents an aggregation operator that operates on two values or vectors and produces a pair of results. +/// +/// The type of the values or vectors. +public interface IAggregationPairsOperator : IBinaryOperator where T : struct { - static virtual ValueTuple Seed + /// + /// Gets the identity value for the type and operation to be performed. + /// + static virtual ValueTuple Identity => Throw.NotSupportedException>(); + /// + /// Combines the specified values with the vector to produce a new pair of values. + /// + /// The current pair of values. + /// The vector to combine with the values. + /// The result of combining the values with the vector. static abstract ValueTuple ResultSelector(ValueTuple value, Vector vector); } -public interface ITernaryOperator +/// +/// Represents an aggregation operator that operates on two values or vectors and produces a triplet of results. +/// +/// The type of the values or vectors. +public interface IAggregationTripletsOperator + : IBinaryOperator where T : struct { - static abstract T Invoke(T x, T y, T z); - static abstract Vector Invoke(Vector x, Vector y, Vector z); -} + /// + /// Gets the seed value for the aggregation operation. + /// + static virtual ValueTuple Seed + => Throw.NotSupportedException>(); + + /// + /// Combines the specified values with the vector to produce a new triplet of values. + /// + /// The current triplet of values. + /// The vector to combine with the values. + /// The result of combining the values with the vector. + static abstract ValueTuple ResultSelector(ValueTuple value, Vector vector); +} \ No newline at end of file diff --git a/src/NetFabric.Numerics.Tensors/Multiply.cs b/src/NetFabric.Numerics.Tensors/Multiply.cs index ad49f59..919568c 100644 --- a/src/NetFabric.Numerics.Tensors/Multiply.cs +++ b/src/NetFabric.Numerics.Tensors/Multiply.cs @@ -1,35 +1,57 @@ -using System.Runtime.InteropServices; - namespace NetFabric.Numerics; public static partial class Tensor { /// - /// Multiplies a right to each element in the left span and stores the result in the destination span. + /// Multiplies each element of the left span by the right value and stores the result in the destination span. /// /// The type of the elements in the spans. - /// The left span. - /// The right to multiply each element by. - /// The destination span to store the result. - /// Thrown when the left and destination spans have different lengths. - /// Thrown when the type does not implement the interface. + /// The span containing the left operands. + /// The right operand. + /// The span to store the result. + /// Thrown when the spans have different lengths. public static void Multiply(ReadOnlySpan left, T right, Span destination) where T : struct, IMultiplyOperators => Apply>(left, right, destination); + /// + /// Multiplies each element of the left span by the right value and stores the result in the destination span. + /// + /// The type of the elements in the spans. + /// The span containing the left operands. + /// The right tuple operand. + /// The span to store the result. + /// Thrown when the spans have different lengths. + /// + /// This method can be used to calculate the division of 2D vectors. + /// public static void Multiply(ReadOnlySpan left, ValueTuple right, Span destination) where T : struct, IMultiplyOperators => Apply>(left, right, destination); /// - /// Multiplies corresponding elements in the left and right spans and stores the result in the destination span. + /// Multiplies each element of the left span by the right value and stores the result in the destination span. + /// + /// The type of the elements in the spans. + /// The span containing the left operands. + /// The right tuple operand. + /// The span to store the result. + /// Thrown when the spans have different lengths. + /// + /// This method can be used to calculate the division of 3D vectors. + /// + public static void Multiply(ReadOnlySpan left, ValueTuple right, Span destination) + where T : struct, IMultiplyOperators + => Apply>(left, right, destination); + + /// + /// Multiplies each element of the left span by the corresponding element of the right span and stores the result in the destination span. /// /// The type of the elements in the spans. - /// The left span. - /// The right span. - /// The destination span to store the result. - /// Thrown when the left, right, and destination spans have different lengths. - /// Thrown when the type does not implement the interface. + /// The span containing the left operands. + /// The span containing the right operands. + /// The span to store the result. + /// Thrown when the spans have different lengths. public static void Multiply(ReadOnlySpan left, ReadOnlySpan right, Span destination) where T : struct, IMultiplyOperators => Apply>(left, right, destination); diff --git a/src/NetFabric.Numerics.Tensors/Negate.cs b/src/NetFabric.Numerics.Tensors/Negate.cs index c75179e..ce2e44f 100644 --- a/src/NetFabric.Numerics.Tensors/Negate.cs +++ b/src/NetFabric.Numerics.Tensors/Negate.cs @@ -1,9 +1,15 @@ -using System.Runtime.InteropServices; - namespace NetFabric.Numerics; public static partial class Tensor { + /// + /// Negates the each element in the source span and stores the result in the destination span. + /// + /// The type of the elements in the spans. + /// The source span. + /// The destination span. + /// Thrown when the source and destination spans have different lengths. + /// Thrown when the type does not implement the interface. public static void Negate(ReadOnlySpan left, Span destination) where T : struct, IUnaryNegationOperators => Apply>(left, destination); diff --git a/src/NetFabric.Numerics.Tensors/NetFabric.Numerics.Tensors.csproj b/src/NetFabric.Numerics.Tensors/NetFabric.Numerics.Tensors.csproj index c88148c..110689c 100644 --- a/src/NetFabric.Numerics.Tensors/NetFabric.Numerics.Tensors.csproj +++ b/src/NetFabric.Numerics.Tensors/NetFabric.Numerics.Tensors.csproj @@ -8,7 +8,7 @@ Provides methods for performing mathematical operations over tensors represented as spans. These methods are accelerated to use SIMD (Single instruction, multiple data) operations supported by the CPU where available. - 1.0.0-beta01 + 1.0.0 Icon.png LICENSE README.md @@ -25,6 +25,7 @@ + diff --git a/src/NetFabric.Numerics.Tensors/Operators/AddMultiplyOperator.cs b/src/NetFabric.Numerics.Tensors/Operators/AddMultiplyOperator.cs index 6c15f37..123670f 100644 --- a/src/NetFabric.Numerics.Tensors/Operators/AddMultiplyOperator.cs +++ b/src/NetFabric.Numerics.Tensors/Operators/AddMultiplyOperator.cs @@ -1,14 +1,30 @@ -using System.Runtime.InteropServices; - -namespace NetFabric.Numerics; - -public readonly struct AddMultiplyOperator - : ITernaryOperator - where T : struct, IAdditionOperators, IMultiplyOperators +namespace NetFabric.Numerics { - public static T Invoke(T x, T y, T z) - => (x + y) * z; + /// + /// Represents an operator that performs addition and multiplication operations on three operands. + /// + /// The type of the operands. + public readonly struct AddMultiplyOperator : ITernaryOperator + where T : struct, IAdditionOperators, IMultiplyOperators + { + /// + /// Invokes the operator on three operands. + /// + /// The first operand. + /// The second operand. + /// The third operand. + /// The result of the addition and multiplication operations. + public static T Invoke(T x, T y, T z) + => (x + y) * z; - public static Vector Invoke(Vector x, Vector y, Vector z) - => (x + y) * z; + /// + /// Invokes the operator on three vector operands. + /// + /// The first vector operand. + /// The second vector operand. + /// The third vector operand. + /// The result of the addition and multiplication operations. + public static Vector Invoke(Vector x, Vector y, Vector z) + => (x + y) * z; + } } \ No newline at end of file diff --git a/src/NetFabric.Numerics.Tensors/Operators/AddOperator.cs b/src/NetFabric.Numerics.Tensors/Operators/AddOperator.cs index dc5cb9c..b28174a 100644 --- a/src/NetFabric.Numerics.Tensors/Operators/AddOperator.cs +++ b/src/NetFabric.Numerics.Tensors/Operators/AddOperator.cs @@ -1,14 +1,28 @@ -using System.Runtime.InteropServices; - namespace NetFabric.Numerics; +/// +/// Represents an add operator for a specified type. +/// +/// The type of the values to add. public readonly struct AddOperator : IBinaryOperator where T : struct, IAdditionOperators { + /// + /// Adds two values of type . + /// + /// The first value to add. + /// The second value to add. + /// The sum of and . public static T Invoke(T x, T y) => x + y; + /// + /// Adds two vectors of type . + /// + /// The first vector to add. + /// The second vector to add. + /// The sum of and . public static Vector Invoke(Vector x, Vector y) => x + y; } \ No newline at end of file diff --git a/src/NetFabric.Numerics.Tensors/Operators/DivideOperator.cs b/src/NetFabric.Numerics.Tensors/Operators/DivideOperator.cs index 1551a99..3cfc468 100644 --- a/src/NetFabric.Numerics.Tensors/Operators/DivideOperator.cs +++ b/src/NetFabric.Numerics.Tensors/Operators/DivideOperator.cs @@ -1,14 +1,28 @@ -using System.Runtime.InteropServices; - namespace NetFabric.Numerics; +/// +/// Represents a divide operator for a specified type. +/// +/// The type of the operands. public readonly struct DivideOperator : IBinaryOperator where T : struct, IDivisionOperators { + /// + /// Divides two values of type T. + /// + /// The dividend. + /// The divisor. + /// The result of the division. public static T Invoke(T x, T y) => x / y; + /// + /// Divides two vectors of type T. + /// + /// The dividend vector. + /// The divisor vector. + /// The result of the division. public static Vector Invoke(Vector x, Vector y) => x / y; } \ No newline at end of file diff --git a/src/NetFabric.Numerics.Tensors/Operators/MultiplyAddOperator.cs b/src/NetFabric.Numerics.Tensors/Operators/MultiplyAddOperator.cs index 3ae472e..5eca120 100644 --- a/src/NetFabric.Numerics.Tensors/Operators/MultiplyAddOperator.cs +++ b/src/NetFabric.Numerics.Tensors/Operators/MultiplyAddOperator.cs @@ -1,14 +1,30 @@ -using System.Runtime.InteropServices; - namespace NetFabric.Numerics; +/// +/// Represents a multiply-add operator for a given type. +/// +/// The type of the values. public readonly struct MultiplyAddOperator : ITernaryOperator where T : struct, IAdditionOperators, IMultiplyOperators { + /// + /// Computes the result of multiplying two values and adding a third value. + /// + /// The first value to multiply. + /// The second value to multiply. + /// The value to add. + /// The result of multiplying and and adding . public static T Invoke(T x, T y, T z) => (x * y) + z; + /// + /// Computes the result of multiplying two vectors element-wise and adding a third vector element-wise. + /// + /// The first vector to multiply. + /// The second vector to multiply. + /// The vector to add. + /// The result of multiplying and element-wise and adding element-wise. public static Vector Invoke(Vector x, Vector y, Vector z) => (x * y) + z; } \ No newline at end of file diff --git a/src/NetFabric.Numerics.Tensors/Operators/MultiplyOperator.cs b/src/NetFabric.Numerics.Tensors/Operators/MultiplyOperator.cs index 6297671..c0ace69 100644 --- a/src/NetFabric.Numerics.Tensors/Operators/MultiplyOperator.cs +++ b/src/NetFabric.Numerics.Tensors/Operators/MultiplyOperator.cs @@ -1,14 +1,28 @@ -using System.Runtime.InteropServices; - namespace NetFabric.Numerics; +/// +/// Represents a multiply operator for a specified type. +/// +/// The type of the operands and result. public readonly struct MultiplyOperator : IBinaryOperator where T : struct, IMultiplyOperators { + /// + /// Multiplies two values of type . + /// + /// The first value. + /// The second value. + /// The result of multiplying and . public static T Invoke(T x, T y) => x * y; + /// + /// Multiplies two vectors of type . + /// + /// The first vector. + /// The second vector. + /// The result of multiplying and . public static Vector Invoke(Vector x, Vector y) => x * y; } \ No newline at end of file diff --git a/src/NetFabric.Numerics.Tensors/Operators/NegateOperator.cs b/src/NetFabric.Numerics.Tensors/Operators/NegateOperator.cs index 06a1f14..890e371 100644 --- a/src/NetFabric.Numerics.Tensors/Operators/NegateOperator.cs +++ b/src/NetFabric.Numerics.Tensors/Operators/NegateOperator.cs @@ -1,14 +1,26 @@ -using System.Runtime.InteropServices; - namespace NetFabric.Numerics; +/// +/// Represents a unary negate operator. +/// +/// The type of the operand. public readonly struct NegateOperator : IUnaryOperator where T : struct, IUnaryNegationOperators { + /// + /// Negates the specified value. + /// + /// The value to negate. + /// The negated value. public static T Invoke(T x) => -x; + /// + /// Negates the specified vector. + /// + /// The vector to negate. + /// The negated vector. public static Vector Invoke(Vector x) => -x; } \ No newline at end of file diff --git a/src/NetFabric.Numerics.Tensors/Operators/SquareOperator.cs b/src/NetFabric.Numerics.Tensors/Operators/SquareOperator.cs index 9f0f0a2..007ee79 100644 --- a/src/NetFabric.Numerics.Tensors/Operators/SquareOperator.cs +++ b/src/NetFabric.Numerics.Tensors/Operators/SquareOperator.cs @@ -1,14 +1,26 @@ -using System.Runtime.InteropServices; - namespace NetFabric.Numerics; +/// +/// Represents a square operator that performs squaring operations on values of type . +/// +/// The type of values to square. public readonly struct SquareOperator : IUnaryOperator where T : struct, IMultiplyOperators { + /// + /// Squares the specified value. + /// + /// The value to square. + /// The squared value. public static T Invoke(T x) => x * x; + /// + /// Squares each element of the specified vector. + /// + /// The vector to square. + /// A new vector with each element squared. public static Vector Invoke(Vector x) => x * x; } \ No newline at end of file diff --git a/src/NetFabric.Numerics.Tensors/Operators/SubtractOperator.cs b/src/NetFabric.Numerics.Tensors/Operators/SubtractOperator.cs index 5d338d7..93d7358 100644 --- a/src/NetFabric.Numerics.Tensors/Operators/SubtractOperator.cs +++ b/src/NetFabric.Numerics.Tensors/Operators/SubtractOperator.cs @@ -1,14 +1,28 @@ -using System.Runtime.InteropServices; - namespace NetFabric.Numerics; +/// +/// Represents a subtract operator for a specified type. +/// +/// The type of the operands. public readonly struct SubtractOperator : IBinaryOperator where T : struct, ISubtractionOperators { + /// + /// Subtracts two values of type T. + /// + /// The first value. + /// The second value. + /// The result of the subtraction. public static T Invoke(T x, T y) => x - y; + /// + /// Subtracts two vectors of type T. + /// + /// The first vector. + /// The second vector. + /// The result of the subtraction. public static Vector Invoke(Vector x, Vector y) => x - y; } diff --git a/src/NetFabric.Numerics.Tensors/Operators/SumOperator.cs b/src/NetFabric.Numerics.Tensors/Operators/SumOperator.cs index 4a54a25..d5b4b63 100644 --- a/src/NetFabric.Numerics.Tensors/Operators/SumOperator.cs +++ b/src/NetFabric.Numerics.Tensors/Operators/SumOperator.cs @@ -1,44 +1,111 @@ -using System.Runtime.InteropServices; +namespace NetFabric.Numerics +{ + /// + /// Represents an operator that performs the sum aggregation operation on a tensor. + /// + /// The type of the tensor elements. + public readonly struct SumOperator : IAggregationOperator + where T : struct, IAdditiveIdentity, IAdditionOperators + { + /// + /// Gets the seed value for the sum operation. + /// + public static T Identity => T.AdditiveIdentity; -namespace NetFabric.Numerics; + /// + /// Computes the result of the sum operation for a single value and a vector. + /// + /// The value to be added to the sum. + /// The vector to be summed. + /// The result of the sum operation. + public static T ResultSelector(T value, Vector vector) + => Vector.Sum(vector) + value; -public readonly struct SumOperator - : IAggregationOperator - where T : struct, IAdditiveIdentity, IAdditionOperators -{ - public static T Seed - => T.AdditiveIdentity; + /// + /// Computes the sum of two values. + /// + /// The first value to be added. + /// The second value to be added. + /// The sum of the two values. + public static T Invoke(T x, T y) + => x + y; - public static T ResultSelector(T value, Vector vector) - => Vector.Sum(vector) + value; + /// + /// Computes the sum of two vectors. + /// + /// The first vector to be added. + /// The second vector to be added. + /// The sum of the two vectors. + public static Vector Invoke(Vector x, Vector y) + => x + y; + } - public static T Invoke(T x, T y) - => x + y; + /// + /// Represents an operator that performs the sum aggregation operation on pairs of elements in a tensor. + /// + /// The type of the tensor elements. + public readonly struct SumPairsOperator : IAggregationPairsOperator + where T : struct, IAdditiveIdentity, IAdditionOperators + { + /// + /// Gets the seed value for the sum operation on pairs. + /// + public static ValueTuple Identity => (T.AdditiveIdentity, T.AdditiveIdentity); - public static Vector Invoke(Vector x, Vector y) - => x + y; -} + /// + /// Computes the result of the sum operation on pairs for a value and a vector. + /// + /// The value to be added to the sum. + /// The vector to be summed. + /// The result of the sum operation on pairs. + public static ValueTuple ResultSelector(ValueTuple value, Vector vector) + { + for (var index = 0; index < Vector.Count; index += 2) + { + value.Item1 += vector[index]; + value.Item2 += vector[index + 1]; + } + return value; + } -public readonly struct SumPairsOperator - : IAggregationPairsOperator - where T : struct, IAdditiveIdentity, IAdditionOperators -{ - public static ValueTuple Seed - => (T.AdditiveIdentity, T.AdditiveIdentity); + /// + /// Computes the sum of two values. + /// + /// The first value to be added. + /// The second value to be added. + /// The sum of the two values. + public static T Invoke(T x, T y) + => x + y; + + /// + /// Computes the sum of two vectors. + /// + /// The first vector to be added. + /// The second vector to be added. + /// The sum of the two vectors. + public static Vector Invoke(Vector x, Vector y) + => x + y; + } - public static ValueTuple ResultSelector(ValueTuple value, Vector vector) + public readonly struct SumTripletsOperator : IAggregationTripletsOperator + where T : struct, IAdditiveIdentity, IAdditionOperators { - for (var index = 0; index < Vector.Count; index += 2) + public static ValueTuple Seed => (T.AdditiveIdentity, T.AdditiveIdentity, T.AdditiveIdentity); + + public static ValueTuple ResultSelector(ValueTuple value, Vector vector) { - value.Item1 += vector[index]; - value.Item2 += vector[index + 1]; + // for (var index = 0; index < Vector.Count; index += 3) + // { + // value.Item1 += vector[index]; + // value.Item2 += vector[index + 1]; + // } + return value; } - return value; - } - public static T Invoke(T x, T y) - => x + y; + public static T Invoke(T x, T y) + => x + y; - public static Vector Invoke(Vector x, Vector y) - => x + y; + public static Vector Invoke(Vector x, Vector y) + => x + y; + } } \ No newline at end of file diff --git a/src/NetFabric.Numerics.Tensors/README.md b/src/NetFabric.Numerics.Tensors/README.md index 7cf12a3..277730b 100644 --- a/src/NetFabric.Numerics.Tensors/README.md +++ b/src/NetFabric.Numerics.Tensors/README.md @@ -1 +1,3 @@ -# NetFabric.Numerics.Tensors \ No newline at end of file +# NetFabric.Numerics.Tensors + +This project is a part of the NetFabric.Numerics library and focuses on tensors. Tensors are multi-dimensional arrays commonly used in mathematical and scientific computations. diff --git a/src/NetFabric.Numerics.Tensors/Square.cs b/src/NetFabric.Numerics.Tensors/Square.cs index 6dfc8e6..42a7a73 100644 --- a/src/NetFabric.Numerics.Tensors/Square.cs +++ b/src/NetFabric.Numerics.Tensors/Square.cs @@ -1,10 +1,16 @@ -using System.Runtime.InteropServices; - namespace NetFabric.Numerics; public static partial class Tensor { + /// + /// Squares each element in the source span and stores the result in the destination span. + /// + /// The type of the elements in the spans. + /// The source span. + /// The destination span. + /// Thrown when the source and destination spans have different lengths. + /// Thrown when the type does not implement the interface. public static void Square(ReadOnlySpan left, Span destination) where T : struct, IMultiplyOperators => Apply>(left, destination); -} +} \ No newline at end of file diff --git a/src/NetFabric.Numerics.Tensors/Subtract.cs b/src/NetFabric.Numerics.Tensors/Subtract.cs index 4e62435..c7aef97 100644 --- a/src/NetFabric.Numerics.Tensors/Subtract.cs +++ b/src/NetFabric.Numerics.Tensors/Subtract.cs @@ -1,35 +1,57 @@ -using System.Runtime.InteropServices; - namespace NetFabric.Numerics; public static partial class Tensor { /// - /// Subtracts a right from each element in the left span and stores the result in the destination span. + /// Subtracts a scalar value from each element of the left span and stores the result in the destination span. /// /// The type of the elements in the spans. - /// The left span. - /// The right to subtract from each element. - /// The destination span to store the result. - /// Thrown when the left and destination spans have different lengths. - /// Thrown when the type does not implement the interface. + /// The span containing the elements to subtract from. + /// The scalar value to subtract from each element. + /// The span to store the result in. + /// Thrown when the subtraction operation is not supported for the specified type. public static void Subtract(ReadOnlySpan left, T right, Span destination) where T : struct, ISubtractionOperators => Apply>(left, right, destination); + /// + /// Subtracts a scalar value from each element of the left span and stores the result in the destination span. + /// + /// The type of the elements in the spans. + /// The span containing the elements to subtract from. + /// The tuple of two values to subtract from each element. + /// The span to store the result in. + /// Thrown when the subtraction operation is not supported for the specified type. + /// + /// This method can be used to calculate the division of 2D vectors. + /// public static void Subtract(ReadOnlySpan left, ValueTuple right, Span destination) where T : struct, ISubtractionOperators => Apply>(left, right, destination); /// - /// Subtracts corresponding elements in the left and right spans and stores the result in the destination span. + /// Subtracts a scalar value from each element of the left span and stores the result in the destination span. + /// + /// The type of the elements in the spans. + /// The span containing the elements to subtract from. + /// The tuple of three values to subtract from each element. + /// The span to store the result in. + /// Thrown when the subtraction operation is not supported for the specified type. + /// + /// This method can be used to calculate the division of 3D vectors. + /// + public static void Subtract(ReadOnlySpan left, ValueTuple right, Span destination) + where T : struct, ISubtractionOperators + => Apply>(left, right, destination); + + /// + /// Subtracts each element of the right span from the corresponding element of the left span and stores the result in the destination span. /// /// The type of the elements in the spans. - /// The left span. - /// The right span. - /// The destination span to store the result. - /// Thrown when the left, right, and destination spans have different lengths. - /// Thrown when the type does not implement the interface. + /// The span containing the elements to subtract from. + /// The span containing the elements to subtract. + /// The span to store the result in. + /// Thrown when the subtraction operation is not supported for the specified type. public static void Subtract(ReadOnlySpan left, ReadOnlySpan right, Span destination) where T : struct, ISubtractionOperators => Apply>(left, right, destination); diff --git a/src/NetFabric.Numerics.Tensors/Sum.cs b/src/NetFabric.Numerics.Tensors/Sum.cs index a5a8591..7803cb6 100644 --- a/src/NetFabric.Numerics.Tensors/Sum.cs +++ b/src/NetFabric.Numerics.Tensors/Sum.cs @@ -1,5 +1,3 @@ -using System.Runtime.InteropServices; - namespace NetFabric.Numerics; public static partial class Tensor @@ -25,10 +23,23 @@ public static T Sum(ReadOnlySpan source) /// A tuple containing the sum of pairs of elements. /// /// This method can be used to calculate the sum of 2D vectors. - /// /// This method requires the type to implement the and interfaces. /// public static ValueTuple SumPairs(ReadOnlySpan source) where T : struct, IAdditionOperators, IAdditiveIdentity => AggregatePairs>(source); + + /// + /// Computes the sum of triplets of values in a span. + /// + /// The type of the elements in the span. + /// The input span. + /// A tuple containing the sum of triplets of elements. + /// + /// This method can be used to calculate the sum of 3D vectors. + /// This method requires the type to implement the and interfaces. + /// + public static ValueTuple SumTriplets(ReadOnlySpan source) + where T : struct, IAdditionOperators, IAdditiveIdentity + => AggregateTriplets>(source); } diff --git a/src/NetFabric.Numerics.Tensors/Tensor.cs b/src/NetFabric.Numerics.Tensors/Tensor.cs index fe0e5f0..1c26bec 100644 --- a/src/NetFabric.Numerics.Tensors/Tensor.cs +++ b/src/NetFabric.Numerics.Tensors/Tensor.cs @@ -1,8 +1,3 @@ -using System; -using System.Numerics; -using System.Runtime.CompilerServices; -using System.Runtime.InteropServices; - namespace NetFabric.Numerics; public static partial class Tensor diff --git a/src/NetFabric.Numerics.Tensors/VectorExtensions.cs b/src/NetFabric.Numerics.Tensors/VectorExtensions.cs deleted file mode 100644 index 70d9149..0000000 --- a/src/NetFabric.Numerics.Tensors/VectorExtensions.cs +++ /dev/null @@ -1,15 +0,0 @@ -namespace NetFabric.Numerics; - -static class VectorExtensions -{ - public static void Fill(this Vector vector, T value1, T value2) - where T : struct - { - ref var valueArrayRef = ref Unsafe.As, T>(ref Unsafe.AsRef(in vector)); - for (nint index = 0; index < Vector.Count; index += 2) - { - Unsafe.Add(ref valueArrayRef, index) = value1; - Unsafe.Add(ref valueArrayRef, index + 1) = value2; - } - } -} \ No newline at end of file diff --git a/src/NetFabric.Numerics/Rectangular3D/Vector.cs b/src/NetFabric.Numerics/Rectangular3D/Vector.cs index e968f7c..aced971 100644 --- a/src/NetFabric.Numerics/Rectangular3D/Vector.cs +++ b/src/NetFabric.Numerics/Rectangular3D/Vector.cs @@ -353,7 +353,7 @@ public readonly string ToString([StringSyntax(StringSyntaxAttribute.NumericForma /// /// Provides static methods for vector operations. /// -public static class Vector +public static partial class Vector { /// /// Determines whether the specified vector is a zero vector, where all components are zero. diff --git a/src/NetFabric.Numerics/Rectangular3D/VectorSpanOperations.cs b/src/NetFabric.Numerics/Rectangular3D/VectorSpanOperations.cs new file mode 100644 index 0000000..1d20056 --- /dev/null +++ b/src/NetFabric.Numerics/Rectangular3D/VectorSpanOperations.cs @@ -0,0 +1,31 @@ +using System.Runtime.InteropServices; + +namespace NetFabric.Numerics.Rectangular3D; + +public static partial class Vector +{ + public static void Add(ReadOnlySpan> angles, Vector value, Span> result) + where T : struct, INumber, IMinMaxValue + => Tensor.Add(MemoryMarshal.Cast, T>(angles), (value.X, value.Y, value.Z), MemoryMarshal.Cast, T>(result)); + + public static void Add(ReadOnlySpan> left, ReadOnlySpan> right, Span> result) + where T : struct, INumber, IMinMaxValue + => Tensor.Add(MemoryMarshal.Cast, T>(left), MemoryMarshal.Cast, T>(right), MemoryMarshal.Cast, T>(result)); + + public static void Subtract(ReadOnlySpan> angles, Vector value, Span> result) + where T : struct, INumber, IMinMaxValue + => Tensor.Subtract(MemoryMarshal.Cast, T>(angles), (value.X, value.Y, value.Z), MemoryMarshal.Cast, T>(result)); + + public static void Subtract(ReadOnlySpan> left, ReadOnlySpan> right, Span> result) + where T : struct, INumber, IMinMaxValue + => Tensor.Subtract(MemoryMarshal.Cast, T>(left), MemoryMarshal.Cast, T>(right), MemoryMarshal.Cast, T>(result)); + + public static void Multiply(ReadOnlySpan> angles, Vector value, Span> result) + where T : struct, INumber, IMinMaxValue + => Tensor.Multiply(MemoryMarshal.Cast, T>(angles), (value.X, value.Y, value.Z), MemoryMarshal.Cast, T>(result)); + + public static void Divide(ReadOnlySpan> angles, Vector value, Span> result) + where T : struct, INumber, IMinMaxValue + => Tensor.Divide(MemoryMarshal.Cast, T>(angles), (value.X, value.Y, value.Z), MemoryMarshal.Cast, T>(result)); + +}