From 631908eda0d7dddc69346695f36637382306e6e8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ant=C3=A3o=20Almada?= Date: Sun, 10 Dec 2023 23:04:18 +0000 Subject: [PATCH] Add unit tests for tensors --- NetFabric.Numerics.sln | 16 +++ .../AddBenchmarks.cs} | 4 +- .../AddValueBenchmarks.cs} | 4 +- ...tFabric.Numerics.Tensors.Benchmarks.csproj | 19 ++++ .../Program.cs | 36 +++++++ .../SumBenchmarks.cs} | 4 +- .../AddMultiplyTests.cs | 100 ++++++++++++++++++ .../AddTests.cs | 95 +++++++++++++++++ .../AddValueTests.cs | 92 ++++++++++++++++ ...etFabric.Numerics.Tensors.UnitTests.csproj | 34 ++++++ .../SquareTests.cs | 91 ++++++++++++++++ .../SumTests.cs | 85 +++++++++++++++ src/NetFabric.Numerics.Tensors/Add.cs | 26 +---- src/NetFabric.Numerics.Tensors/AddMultiply.cs | 22 ++++ src/NetFabric.Numerics.Tensors/Apply.cs | 51 +++++++++ src/NetFabric.Numerics.Tensors/Negate.cs | 10 ++ .../Operators/AddMultiplyOperator.cs | 14 +++ .../Operators/MultiplyAddOperator.cs | 14 +++ .../Operators/NegateOperator.cs | 14 +++ .../Operators/SquareOperator.cs | 14 +++ src/NetFabric.Numerics.Tensors/Square.cs | 10 ++ 21 files changed, 727 insertions(+), 28 deletions(-) rename src/{NetFabric.Numerics.Benchmarks/TensorAddBenchmarks.cs => NetFabric.Numerics.Tensors.Benchmarks/AddBenchmarks.cs} (94%) rename src/{NetFabric.Numerics.Benchmarks/TensorAddValueBenchmarks.cs => NetFabric.Numerics.Tensors.Benchmarks/AddValueBenchmarks.cs} (94%) create mode 100644 src/NetFabric.Numerics.Tensors.Benchmarks/NetFabric.Numerics.Tensors.Benchmarks.csproj create mode 100644 src/NetFabric.Numerics.Tensors.Benchmarks/Program.cs rename src/{NetFabric.Numerics.Benchmarks/TensorSumBenchmarks.cs => NetFabric.Numerics.Tensors.Benchmarks/SumBenchmarks.cs} (94%) create mode 100644 src/NetFabric.Numerics.Tensors.UnitTests/AddMultiplyTests.cs create mode 100644 src/NetFabric.Numerics.Tensors.UnitTests/AddTests.cs create mode 100644 src/NetFabric.Numerics.Tensors.UnitTests/AddValueTests.cs create mode 100644 src/NetFabric.Numerics.Tensors.UnitTests/NetFabric.Numerics.Tensors.UnitTests.csproj create mode 100644 src/NetFabric.Numerics.Tensors.UnitTests/SquareTests.cs create mode 100644 src/NetFabric.Numerics.Tensors.UnitTests/SumTests.cs create mode 100644 src/NetFabric.Numerics.Tensors/AddMultiply.cs create mode 100644 src/NetFabric.Numerics.Tensors/Negate.cs create mode 100644 src/NetFabric.Numerics.Tensors/Operators/AddMultiplyOperator.cs create mode 100644 src/NetFabric.Numerics.Tensors/Operators/MultiplyAddOperator.cs create mode 100644 src/NetFabric.Numerics.Tensors/Operators/NegateOperator.cs create mode 100644 src/NetFabric.Numerics.Tensors/Operators/SquareOperator.cs create mode 100644 src/NetFabric.Numerics.Tensors/Square.cs diff --git a/NetFabric.Numerics.sln b/NetFabric.Numerics.sln index 4e84bf1..6e45bd8 100644 --- a/NetFabric.Numerics.sln +++ b/NetFabric.Numerics.sln @@ -58,6 +58,12 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "NetFabric.Numerics.Geodesy. EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "NetFabric.Numerics.Tensors", "src\NetFabric.Numerics.Tensors\NetFabric.Numerics.Tensors.csproj", "{8D2EE0A5-32A2-4019-AD55-7E9B70B76D67}" EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{EC0C1351-970E-48A9-BDA3-3E5E2B095402}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "NetFabric.Numerics.Tensors.UnitTests", "src\NetFabric.Numerics.Tensors.UnitTests\NetFabric.Numerics.Tensors.UnitTests.csproj", "{31E6CCB0-E04A-4947-BF74-E8E70D5594E5}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "NetFabric.Numerics.Tensors.Benchmarks", "src\NetFabric.Numerics.Tensors.Benchmarks\NetFabric.Numerics.Tensors.Benchmarks.csproj", "{A8467A84-F9DD-4B6C-91BB-CF39029331A4}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -96,6 +102,14 @@ Global {8D2EE0A5-32A2-4019-AD55-7E9B70B76D67}.Debug|Any CPU.Build.0 = Debug|Any CPU {8D2EE0A5-32A2-4019-AD55-7E9B70B76D67}.Release|Any CPU.ActiveCfg = Release|Any CPU {8D2EE0A5-32A2-4019-AD55-7E9B70B76D67}.Release|Any CPU.Build.0 = Release|Any CPU + {31E6CCB0-E04A-4947-BF74-E8E70D5594E5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {31E6CCB0-E04A-4947-BF74-E8E70D5594E5}.Debug|Any CPU.Build.0 = Debug|Any CPU + {31E6CCB0-E04A-4947-BF74-E8E70D5594E5}.Release|Any CPU.ActiveCfg = Release|Any CPU + {31E6CCB0-E04A-4947-BF74-E8E70D5594E5}.Release|Any CPU.Build.0 = Release|Any CPU + {A8467A84-F9DD-4B6C-91BB-CF39029331A4}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {A8467A84-F9DD-4B6C-91BB-CF39029331A4}.Debug|Any CPU.Build.0 = Debug|Any CPU + {A8467A84-F9DD-4B6C-91BB-CF39029331A4}.Release|Any CPU.ActiveCfg = Release|Any CPU + {A8467A84-F9DD-4B6C-91BB-CF39029331A4}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -104,6 +118,8 @@ Global {A04C4E36-5F33-43F8-A7EE-15D81C7F8A58} = {762517CA-3F22-4DC6-955C-4F4FC69EB670} {BA0837F2-730C-4537-B08B-5FFD2D91AF30} = {762517CA-3F22-4DC6-955C-4F4FC69EB670} {83BDB68A-4F4B-4D7D-A7C7-55CC9F03657B} = {762517CA-3F22-4DC6-955C-4F4FC69EB670} + {31E6CCB0-E04A-4947-BF74-E8E70D5594E5} = {EC0C1351-970E-48A9-BDA3-3E5E2B095402} + {A8467A84-F9DD-4B6C-91BB-CF39029331A4} = {EC0C1351-970E-48A9-BDA3-3E5E2B095402} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {AACC30AE-BE57-461F-817B-2AE0F001A33F} diff --git a/src/NetFabric.Numerics.Benchmarks/TensorAddBenchmarks.cs b/src/NetFabric.Numerics.Tensors.Benchmarks/AddBenchmarks.cs similarity index 94% rename from src/NetFabric.Numerics.Benchmarks/TensorAddBenchmarks.cs rename to src/NetFabric.Numerics.Tensors.Benchmarks/AddBenchmarks.cs index 2a98d16..ede20ab 100644 --- a/src/NetFabric.Numerics.Benchmarks/TensorAddBenchmarks.cs +++ b/src/NetFabric.Numerics.Tensors.Benchmarks/AddBenchmarks.cs @@ -1,8 +1,8 @@ using BenchmarkDotNet.Attributes; -namespace NetFabric.Numerics.Rectangular2D.Benchmarks; +namespace NetFabric.Numerics.Tensors.Benchmarks; -public class TensorAddBenchmarks +public class AddBenchmarks { short[]? arrayShort; int[]? arrayInt; diff --git a/src/NetFabric.Numerics.Benchmarks/TensorAddValueBenchmarks.cs b/src/NetFabric.Numerics.Tensors.Benchmarks/AddValueBenchmarks.cs similarity index 94% rename from src/NetFabric.Numerics.Benchmarks/TensorAddValueBenchmarks.cs rename to src/NetFabric.Numerics.Tensors.Benchmarks/AddValueBenchmarks.cs index 8d7f0ec..c559135 100644 --- a/src/NetFabric.Numerics.Benchmarks/TensorAddValueBenchmarks.cs +++ b/src/NetFabric.Numerics.Tensors.Benchmarks/AddValueBenchmarks.cs @@ -1,8 +1,8 @@ using BenchmarkDotNet.Attributes; -namespace NetFabric.Numerics.Rectangular2D.Benchmarks; +namespace NetFabric.Numerics.Tensors.Benchmarks; -public class TensorAddValueBenchmarks +public class AddValueBenchmarks { short[]? arrayShort; int[]? arrayInt; diff --git a/src/NetFabric.Numerics.Tensors.Benchmarks/NetFabric.Numerics.Tensors.Benchmarks.csproj b/src/NetFabric.Numerics.Tensors.Benchmarks/NetFabric.Numerics.Tensors.Benchmarks.csproj new file mode 100644 index 0000000..f35cf89 --- /dev/null +++ b/src/NetFabric.Numerics.Tensors.Benchmarks/NetFabric.Numerics.Tensors.Benchmarks.csproj @@ -0,0 +1,19 @@ + + + + Exe + net8.0 + + false + NetFabric.Numerics.Tensors.Benchmarks + + + + + + + + + + + diff --git a/src/NetFabric.Numerics.Tensors.Benchmarks/Program.cs b/src/NetFabric.Numerics.Tensors.Benchmarks/Program.cs new file mode 100644 index 0000000..e9c9373 --- /dev/null +++ b/src/NetFabric.Numerics.Tensors.Benchmarks/Program.cs @@ -0,0 +1,36 @@ +using BenchmarkDotNet.Columns; +using BenchmarkDotNet.Configs; +using BenchmarkDotNet.Jobs; +using BenchmarkDotNet.Reports; +using BenchmarkDotNet.Running; +using System.Runtime.Intrinsics; + +var config = DefaultConfig.Instance + .WithSummaryStyle(SummaryStyle.Default.WithRatioStyle(RatioStyle.Trend)) + .HideColumns(Column.EnvironmentVariables, Column.RatioSD, Column.Error) + // .AddDiagnoser(new DisassemblyDiagnoser(new DisassemblyDiagnoserConfig + // (exportGithubMarkdown: true, printInstructionAddresses: false))) + .AddJob(Job.Default.WithId("Scalar") + .WithEnvironmentVariable("DOTNET_EnableHWIntrinsic", "0") + .AsBaseline()); + +if (Vector128.IsHardwareAccelerated) +{ + config = config + .AddJob(Job.Default.WithId("Vector128") + .WithEnvironmentVariable("DOTNET_EnableAVX2", "0") + .WithEnvironmentVariable("DOTNET_EnableAVX512F", "0")); +} +if (Vector256.IsHardwareAccelerated) +{ + config = config + .AddJob(Job.Default.WithId("Vector256") + .WithEnvironmentVariable("DOTNET_EnableAVX512F", "0")); +} +if (Vector512.IsHardwareAccelerated) +{ + config = config + .AddJob(Job.Default.WithId("Vector512")); +} + +BenchmarkSwitcher.FromAssembly(typeof(Program).Assembly).Run(args, config); \ No newline at end of file diff --git a/src/NetFabric.Numerics.Benchmarks/TensorSumBenchmarks.cs b/src/NetFabric.Numerics.Tensors.Benchmarks/SumBenchmarks.cs similarity index 94% rename from src/NetFabric.Numerics.Benchmarks/TensorSumBenchmarks.cs rename to src/NetFabric.Numerics.Tensors.Benchmarks/SumBenchmarks.cs index 9e1afd9..6c8d6bc 100644 --- a/src/NetFabric.Numerics.Benchmarks/TensorSumBenchmarks.cs +++ b/src/NetFabric.Numerics.Tensors.Benchmarks/SumBenchmarks.cs @@ -1,8 +1,8 @@ using BenchmarkDotNet.Attributes; -namespace NetFabric.Numerics.Rectangular2D.Benchmarks; +namespace NetFabric.Numerics.Tensors.Benchmarks; -public class TensorSumBenchmarks +public class SumBenchmarks { short[]? arrayShort; int[]? arrayInt; diff --git a/src/NetFabric.Numerics.Tensors.UnitTests/AddMultiplyTests.cs b/src/NetFabric.Numerics.Tensors.UnitTests/AddMultiplyTests.cs new file mode 100644 index 0000000..9c584d9 --- /dev/null +++ b/src/NetFabric.Numerics.Tensors.UnitTests/AddMultiplyTests.cs @@ -0,0 +1,100 @@ +using System.Linq; + +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) + { + // 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(); + + // act + Tensor.AddMultiply(x, y, z, result); + + // assert + result.Should().Equal(expected); + } + + [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); + + // assert + result.Should().Equal(expected); + } + + [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(); + + // act + Tensor.AddMultiply(x, y, z, result); + + // assert + result.Should().Equal(expected); + } + + [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); + } + + [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); + } + +} diff --git a/src/NetFabric.Numerics.Tensors.UnitTests/AddTests.cs b/src/NetFabric.Numerics.Tensors.UnitTests/AddTests.cs new file mode 100644 index 0000000..36b3d9b --- /dev/null +++ b/src/NetFabric.Numerics.Tensors.UnitTests/AddTests.cs @@ -0,0 +1,95 @@ +using System.Linq; + +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) + { + // 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(); + + // act + Tensor.Add(x, y, result); + + // assert + result.Should().Equal(expected); + } + + [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); + + // assert + result.Should().Equal(expected); + } + + [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(); + + // act + Tensor.Add(x, y, result); + + // assert + result.Should().Equal(expected); + } + + [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); + } + + [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); + } + +} diff --git a/src/NetFabric.Numerics.Tensors.UnitTests/AddValueTests.cs b/src/NetFabric.Numerics.Tensors.UnitTests/AddValueTests.cs new file mode 100644 index 0000000..56aca90 --- /dev/null +++ b/src/NetFabric.Numerics.Tensors.UnitTests/AddValueTests.cs @@ -0,0 +1,92 @@ +using System.Linq; + +namespace NetFabric.Numerics.Tensors.UnitTests; + +public class AddValueTests +{ + public const int 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_Value_Short_Should_Succeed(int count) + { + // 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(); + + // act + Tensor.Add(x, constValue, result); + + // assert + result.Should().Equal(expected); + } + + [Theory] + [MemberData(nameof(AddData))] + public void Add_Value_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, constValue, result); + + // assert + result.Should().Equal(expected); + } + + [Theory] + [MemberData(nameof(AddData))] + public void Add_Value_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, constValue, result); + + // assert + result.Should().Equal(expected); + } + + [Theory] + [MemberData(nameof(AddData))] + public void Add_Value_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, constValue, result); + + // assert + result.Should().Equal(expected); + } + + [Theory] + [MemberData(nameof(AddData))] + public void Add_Value_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, constValue, result); + + // assert + result.Should().Equal(expected); + } + +} diff --git a/src/NetFabric.Numerics.Tensors.UnitTests/NetFabric.Numerics.Tensors.UnitTests.csproj b/src/NetFabric.Numerics.Tensors.UnitTests/NetFabric.Numerics.Tensors.UnitTests.csproj new file mode 100644 index 0000000..928ffb5 --- /dev/null +++ b/src/NetFabric.Numerics.Tensors.UnitTests/NetFabric.Numerics.Tensors.UnitTests.csproj @@ -0,0 +1,34 @@ + + + + net8.0 + + false + true + NetFabric.Numerics.Tensors.UnitsTests + + + + + + + + + + + + + runtime; build; native; contentfiles; analyzers; buildtransitive + all + + + runtime; build; native; contentfiles; analyzers; buildtransitive + all + + + + + + + + diff --git a/src/NetFabric.Numerics.Tensors.UnitTests/SquareTests.cs b/src/NetFabric.Numerics.Tensors.UnitTests/SquareTests.cs new file mode 100644 index 0000000..ac66f08 --- /dev/null +++ b/src/NetFabric.Numerics.Tensors.UnitTests/SquareTests.cs @@ -0,0 +1,91 @@ +using System.Linq; + +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) + { + // 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(); + + // act + Tensor.Square(x, result); + + // assert + result.Should().Equal(expected); + } + + [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); + + // assert + result.Should().Equal(expected); + } + + [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(); + + // act + Tensor.Square(x, result); + + // assert + result.Should().Equal(expected); + } + + [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); + } + + [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); + } + +} diff --git a/src/NetFabric.Numerics.Tensors.UnitTests/SumTests.cs b/src/NetFabric.Numerics.Tensors.UnitTests/SumTests.cs new file mode 100644 index 0000000..b5d1d89 --- /dev/null +++ b/src/NetFabric.Numerics.Tensors.UnitTests/SumTests.cs @@ -0,0 +1,85 @@ +using System.Linq; + +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) + { + // arrange + var source = Enumerable.Range(0, count).Select(value => (short)value).ToArray(); + var expected = Enumerable.Range(0, count).Sum(); + + // act + var result = Tensor.Sum(source); + + // assert + result.Should().Be((short)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); + + // assert + result.Should().Be(expected); + } + + [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); + + // assert + result.Should().Be(expected); + } + + [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); + } + + [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); + } + +} diff --git a/src/NetFabric.Numerics.Tensors/Add.cs b/src/NetFabric.Numerics.Tensors/Add.cs index 7734fd4..54de76f 100644 --- a/src/NetFabric.Numerics.Tensors/Add.cs +++ b/src/NetFabric.Numerics.Tensors/Add.cs @@ -4,29 +4,11 @@ namespace NetFabric.Numerics; public static partial class Tensor { - /// - /// Adds a value to 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 value to add to each element. - /// The destination span to store the result. - /// Thrown when the source and destination spans have different lengths. - /// Thrown when the type does not implement the interface. - public static void Add(ReadOnlySpan left, T right, Span destination) + public static void Add(ReadOnlySpan x, T y, Span destination) where T : struct, IAdditionOperators - => Apply>(left, right, destination); + => Apply>(x, y, destination); - /// - /// Adds corresponding elements in the left and right spans 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. - public static void Add(ReadOnlySpan left, ReadOnlySpan right, Span destination) + public static void Add(ReadOnlySpan x, ReadOnlySpan y, Span destination) where T : struct, IAdditionOperators - => Apply>(left, right, destination); + => Apply>(x, y, destination); } diff --git a/src/NetFabric.Numerics.Tensors/AddMultiply.cs b/src/NetFabric.Numerics.Tensors/AddMultiply.cs new file mode 100644 index 0000000..d3ef772 --- /dev/null +++ b/src/NetFabric.Numerics.Tensors/AddMultiply.cs @@ -0,0 +1,22 @@ +using System.Runtime.InteropServices; + +namespace NetFabric.Numerics; + +public static partial class Tensor +{ + public static void AddMultiply(ReadOnlySpan x, T y, T z, Span destination) + where T : struct, IAdditionOperators, IMultiplyOperators + => Apply>(x, y, z, destination); + + public static void AddMultiply(ReadOnlySpan x, T y, ReadOnlySpan z, Span destination) + where T : struct, IAdditionOperators, IMultiplyOperators + => Apply>(x, y, z, destination); + + public static void AddMultiply(ReadOnlySpan x, ReadOnlySpan y, T z, Span destination) + where T : struct, IAdditionOperators, IMultiplyOperators + => Apply>(x, y, z, destination); + + 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/Apply.cs b/src/NetFabric.Numerics.Tensors/Apply.cs index 122fce2..d510c42 100644 --- a/src/NetFabric.Numerics.Tensors/Apply.cs +++ b/src/NetFabric.Numerics.Tensors/Apply.cs @@ -333,4 +333,55 @@ public static void Apply(ReadOnlySpan x, ReadOnlySpan y, T z } } + public static void Apply(ReadOnlySpan x, T y, T z, Span destination) + where T : struct + where TOperator : struct, ITernaryOperator + { + 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."); + + // Initialize the index to 0. + nint index = 0; + + // Check if hardware acceleration and Vector support are available, + // and if the length of the x is greater than the Vector.Count. + if (Vector.IsHardwareAccelerated && + Vector.IsSupported && + x.Length >= Vector.Count) + { + // Cast the spans to vectors for hardware acceleration. + var xVectors = MemoryMarshal.Cast>(x); + var yVector = new Vector(y); + var zVector = new Vector(z); + var destinationVectors = MemoryMarshal.Cast>(destination); + + // Iterate through the vectors. + ref var xVectorsRef = ref MemoryMarshal.GetReference(xVectors); + ref var destinationVectorsRef = ref MemoryMarshal.GetReference(destinationVectors); + for (nint indexVector = 0; indexVector < xVectors.Length; indexVector++) + { + Unsafe.Add(ref destinationVectorsRef, indexVector) = TOperator.Invoke( + Unsafe.Add(ref xVectorsRef, indexVector), + yVector, + zVector); + } + + // Update the index to the end of the last complete vector. + index = x.Length - x.Length % Vector.Count; + } + + // Iterate through the remaining elements. + ref var xRef = ref MemoryMarshal.GetReference(x); + ref var destinationRef = ref MemoryMarshal.GetReference(destination); + for (; index < x.Length; index++) + { + Unsafe.Add(ref destinationRef, index) = TOperator.Invoke( + Unsafe.Add(ref xRef, index), + y, + z); + } + } + } \ No newline at end of file diff --git a/src/NetFabric.Numerics.Tensors/Negate.cs b/src/NetFabric.Numerics.Tensors/Negate.cs new file mode 100644 index 0000000..c75179e --- /dev/null +++ b/src/NetFabric.Numerics.Tensors/Negate.cs @@ -0,0 +1,10 @@ +using System.Runtime.InteropServices; + +namespace NetFabric.Numerics; + +public static partial class Tensor +{ + public static void Negate(ReadOnlySpan left, Span destination) + where T : struct, IUnaryNegationOperators + => Apply>(left, destination); +} diff --git a/src/NetFabric.Numerics.Tensors/Operators/AddMultiplyOperator.cs b/src/NetFabric.Numerics.Tensors/Operators/AddMultiplyOperator.cs new file mode 100644 index 0000000..6c15f37 --- /dev/null +++ b/src/NetFabric.Numerics.Tensors/Operators/AddMultiplyOperator.cs @@ -0,0 +1,14 @@ +using System.Runtime.InteropServices; + +namespace NetFabric.Numerics; + +public readonly struct AddMultiplyOperator + : ITernaryOperator + where T : struct, IAdditionOperators, IMultiplyOperators +{ + 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; +} \ No newline at end of file diff --git a/src/NetFabric.Numerics.Tensors/Operators/MultiplyAddOperator.cs b/src/NetFabric.Numerics.Tensors/Operators/MultiplyAddOperator.cs new file mode 100644 index 0000000..3ae472e --- /dev/null +++ b/src/NetFabric.Numerics.Tensors/Operators/MultiplyAddOperator.cs @@ -0,0 +1,14 @@ +using System.Runtime.InteropServices; + +namespace NetFabric.Numerics; + +public readonly struct MultiplyAddOperator + : ITernaryOperator + where T : struct, IAdditionOperators, IMultiplyOperators +{ + 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; +} \ No newline at end of file diff --git a/src/NetFabric.Numerics.Tensors/Operators/NegateOperator.cs b/src/NetFabric.Numerics.Tensors/Operators/NegateOperator.cs new file mode 100644 index 0000000..06a1f14 --- /dev/null +++ b/src/NetFabric.Numerics.Tensors/Operators/NegateOperator.cs @@ -0,0 +1,14 @@ +using System.Runtime.InteropServices; + +namespace NetFabric.Numerics; + +public readonly struct NegateOperator + : IUnaryOperator + where T : struct, IUnaryNegationOperators +{ + public static T Invoke(T x) + => -x; + + 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 new file mode 100644 index 0000000..9f0f0a2 --- /dev/null +++ b/src/NetFabric.Numerics.Tensors/Operators/SquareOperator.cs @@ -0,0 +1,14 @@ +using System.Runtime.InteropServices; + +namespace NetFabric.Numerics; + +public readonly struct SquareOperator + : IUnaryOperator + where T : struct, IMultiplyOperators +{ + public static T Invoke(T x) + => x * x; + + public static Vector Invoke(Vector x) + => x * x; +} \ No newline at end of file diff --git a/src/NetFabric.Numerics.Tensors/Square.cs b/src/NetFabric.Numerics.Tensors/Square.cs new file mode 100644 index 0000000..6dfc8e6 --- /dev/null +++ b/src/NetFabric.Numerics.Tensors/Square.cs @@ -0,0 +1,10 @@ +using System.Runtime.InteropServices; + +namespace NetFabric.Numerics; + +public static partial class Tensor +{ + public static void Square(ReadOnlySpan left, Span destination) + where T : struct, IMultiplyOperators + => Apply>(left, destination); +}