未验证 提交 7be39130 编写于 作者: M Michael Sharp 提交者: GitHub

6 more naive methods for Tensor Primitives. (#92142)

* 6 more naive methods

* updates from pr comments
上级 5ab45c79
......@@ -6,27 +6,33 @@
namespace System.Numerics.Tensors
{
public static class TensorPrimitives
public static partial class TensorPrimitives
{
public static void Add(System.ReadOnlySpan<float> x, float y, System.Span<float> destination) { throw null; }
public static void Add(System.ReadOnlySpan<float> x, System.ReadOnlySpan<float> y, System.Span<float> destination) { throw null; }
public static void AddMultiply(System.ReadOnlySpan<float> x, float y, System.ReadOnlySpan<float> multiplier, System.Span<float> destination) { throw null; }
public static void AddMultiply(System.ReadOnlySpan<float> x, System.ReadOnlySpan<float> y, float multiplier, System.Span<float> destination) { throw null; }
public static void AddMultiply(System.ReadOnlySpan<float> x, System.ReadOnlySpan<float> y, System.ReadOnlySpan<float> multiplier, System.Span<float> destination) { throw null; }
public static void Cosh(System.ReadOnlySpan<float> x, System.Span<float> destination) { throw null; }
public static void Divide(System.ReadOnlySpan<float> x, float y, System.Span<float> destination) { throw null; }
public static void Divide(System.ReadOnlySpan<float> x, System.ReadOnlySpan<float> y, System.Span<float> destination) { throw null; }
public static void Exp(System.ReadOnlySpan<float> x, System.Span<float> destination) { throw null; }
public static void Log(System.ReadOnlySpan<float> x, System.Span<float> destination) { throw null; }
public static void Multiply(System.ReadOnlySpan<float> x, float y, System.Span<float> destination) { throw null; }
public static void Multiply(System.ReadOnlySpan<float> x, System.ReadOnlySpan<float> y, System.Span<float> destination) { throw null; }
public static void MultiplyAdd(System.ReadOnlySpan<float> x, float y, System.ReadOnlySpan<float> addend, System.Span<float> destination) { throw null; }
public static void MultiplyAdd(System.ReadOnlySpan<float> x, System.ReadOnlySpan<float> y, float addend, System.Span<float> destination) { throw null; }
public static void MultiplyAdd(System.ReadOnlySpan<float> x, System.ReadOnlySpan<float> y, System.ReadOnlySpan<float> addend, System.Span<float> destination) { throw null; }
public static void Negate(System.ReadOnlySpan<float> x, System.Span<float> destination) { throw null; }
public static void Subtract(System.ReadOnlySpan<float> x, float y, System.Span<float> destination) { throw null; }
public static void Subtract(System.ReadOnlySpan<float> x, System.ReadOnlySpan<float> y, System.Span<float> destination) { throw null; }
public static void Sinh(System.ReadOnlySpan<float> x, System.Span<float> destination) { throw null; }
public static void Tanh(System.ReadOnlySpan<float> x, System.Span<float> destination) { throw null; }
public static void Add(System.ReadOnlySpan<float> x, System.ReadOnlySpan<float> y, System.Span<float> destination) { }
public static void Add(System.ReadOnlySpan<float> x, float y, System.Span<float> destination) { }
public static void AddMultiply(System.ReadOnlySpan<float> x, System.ReadOnlySpan<float> y, System.ReadOnlySpan<float> multiplier, System.Span<float> destination) { }
public static void AddMultiply(System.ReadOnlySpan<float> x, System.ReadOnlySpan<float> y, float multiplier, System.Span<float> destination) { }
public static void AddMultiply(System.ReadOnlySpan<float> x, float y, System.ReadOnlySpan<float> multiplier, System.Span<float> destination) { }
public static void Cosh(System.ReadOnlySpan<float> x, System.Span<float> destination) { }
public static float CosineSimilarity(System.ReadOnlySpan<float> x, System.ReadOnlySpan<float> y) { throw null; }
public static float Distance(System.ReadOnlySpan<float> x, System.ReadOnlySpan<float> y) { throw null; }
public static void Divide(System.ReadOnlySpan<float> x, System.ReadOnlySpan<float> y, System.Span<float> destination) { }
public static void Divide(System.ReadOnlySpan<float> x, float y, System.Span<float> destination) { }
public static float Dot(System.ReadOnlySpan<float> x, System.ReadOnlySpan<float> y) { throw null; }
public static void Exp(System.ReadOnlySpan<float> x, System.Span<float> destination) { }
public static float L2Normalize(System.ReadOnlySpan<float> x) { throw null; }
public static void Log(System.ReadOnlySpan<float> x, System.Span<float> destination) { }
public static void Multiply(System.ReadOnlySpan<float> x, System.ReadOnlySpan<float> y, System.Span<float> destination) { }
public static void Multiply(System.ReadOnlySpan<float> x, float y, System.Span<float> destination) { }
public static void MultiplyAdd(System.ReadOnlySpan<float> x, System.ReadOnlySpan<float> y, System.ReadOnlySpan<float> addend, System.Span<float> destination) { }
public static void MultiplyAdd(System.ReadOnlySpan<float> x, System.ReadOnlySpan<float> y, float addend, System.Span<float> destination) { }
public static void MultiplyAdd(System.ReadOnlySpan<float> x, float y, System.ReadOnlySpan<float> addend, System.Span<float> destination) { }
public static void Negate(System.ReadOnlySpan<float> x, System.Span<float> destination) { }
public static void Sigmoid(System.ReadOnlySpan<float> x, System.Span<float> destination) { }
public static void Sinh(System.ReadOnlySpan<float> x, System.Span<float> destination) { }
public static void SoftMax(System.ReadOnlySpan<float> x, System.Span<float> destination) { }
public static void Subtract(System.ReadOnlySpan<float> x, System.ReadOnlySpan<float> y, System.Span<float> destination) { }
public static void Subtract(System.ReadOnlySpan<float> x, float y, System.Span<float> destination) { }
public static void Tanh(System.ReadOnlySpan<float> x, System.Span<float> destination) { }
}
}
......@@ -120,7 +120,10 @@
<data name="Argument_DestinationTooShort" xml:space="preserve">
<value>Destination is too short.</value>
</data>
<data name="Argument_SpansMustBeNonEmpty" xml:space="preserve">
<value>Input span arguments must not be empty.</value>
</data>
<data name="Argument_SpansMustHaveSameLength" xml:space="preserve">
<value>Input span arguments must all have the same length.</value>
</data>
</root>
</root>
\ No newline at end of file
......@@ -253,5 +253,162 @@ public static void Tanh(ReadOnlySpan<float> x, Span<float> destination)
destination[i] = MathF.Tanh(x[i]);
}
}
/// <summary>Computes the cosine similarity between two non-zero vectors.</summary>
/// <param name="x">The first tensor, represented as a span.</param>
/// <param name="y">The second tensor, represented as a span.</param>
/// <returns>The cosine similarity between the two vectors.</returns>
/// <exception cref="ArgumentException">Length of '<paramref name="x" />' must be same as length of '<paramref name="y" />'.</exception>
/// <exception cref="ArgumentException">'<paramref name="x" />' and '<paramref name="y" />' must not be empty.</exception>
public static float CosineSimilarity(ReadOnlySpan<float> x, ReadOnlySpan<float> y)
{
if (x.Length != y.Length)
{
ThrowHelper.ThrowArgument_SpansMustHaveSameLength();
}
if (x.Length == 0 || y.Length == 0)
{
ThrowHelper.ThrowArgument_SpansMustBeNonEmpty();
}
float dotprod = 0f;
float magx = 0f;
float magy = 0f;
for (int i = 0; i < x.Length; i++)
{
dotprod += x[i] * y[i];
magx += x[i] * x[i];
magy += y[i] * y[i];
}
return dotprod / (MathF.Sqrt(magx) * MathF.Sqrt(magy));
}
/// <summary>
/// Compute the distance between two points in Euclidean space.
/// </summary>
/// <param name="x">The first tensor, represented as a span.</param>
/// <param name="y">The second tensor, represented as a span.</param>
/// <returns>The Euclidean distance.</returns>
/// <exception cref="ArgumentException">Length of '<paramref name="x" />' must be same as length of '<paramref name="y" />'.</exception>
/// <exception cref="ArgumentException">'<paramref name="x" />' and '<paramref name="y" />' must not be empty.</exception>
public static float Distance(ReadOnlySpan<float> x, ReadOnlySpan<float> y)
{
if (x.Length != y.Length)
{
ThrowHelper.ThrowArgument_SpansMustHaveSameLength();
}
if (x.Length == 0 || y.Length == 0)
{
ThrowHelper.ThrowArgument_SpansMustBeNonEmpty();
}
float distance = 0f;
for (int i = 0; i < x.Length; i++)
{
float dist = x[i] - y[i];
distance += dist * dist;
}
return MathF.Sqrt(distance);
}
/// <summary>
/// A mathematical operation that takes two vectors and returns a scalar.
/// </summary>
/// <param name="x">The first tensor, represented as a span.</param>
/// <param name="y">The second tensor, represented as a span.</param>
/// <returns>The dot product.</returns>
/// <exception cref="ArgumentException">Length of '<paramref name="x" />' must be same as length of '<paramref name="y" />'.</exception>
public static float Dot(ReadOnlySpan<float> x, ReadOnlySpan<float> y) // BLAS1: dot
{
if (x.Length != y.Length)
{
ThrowHelper.ThrowArgument_SpansMustHaveSameLength();
}
float dotprod = 0f;
for (int i = 0; i < x.Length; i++)
{
dotprod += x[i] * y[i];
}
return dotprod;
}
/// <summary>
/// A mathematical operation that takes a vector and returns the L2 norm.
/// </summary>
/// <param name="x">The first tensor, represented as a span.</param>
/// <returns>The L2 norm.</returns>
public static float L2Normalize(ReadOnlySpan<float> x) // BLAS1: nrm2
{
float magx = 0f;
for (int i = 0; i < x.Length; i++)
{
magx += x[i] * x[i];
}
return MathF.Sqrt(magx);
}
/// <summary>
/// A function that takes a collection of real numbers and returns a probability distribution.
/// </summary>
/// <param name="x">The first tensor, represented as a span.</param>
/// <param name="destination">The destination tensor.</param>
/// <exception cref="ArgumentException">Destination is too short.</exception>
/// <exception cref="ArgumentException">'<paramref name="x" />' must not be empty.</exception>
public static void SoftMax(ReadOnlySpan<float> x, Span<float> destination)
{
if (x.Length > destination.Length)
{
ThrowHelper.ThrowArgument_DestinationTooShort();
}
if (x.Length == 0)
{
ThrowHelper.ThrowArgument_SpansMustBeNonEmpty();
}
float expSum = 0f;
for (int i = 0; i < x.Length; i++)
{
expSum += MathF.Pow((float)Math.E, x[i]);
}
for (int i = 0; i < x.Length; i++)
{
destination[i] = MathF.Exp(x[i]) / expSum;
}
}
/// <summary>
/// A function that takes a real number and returns a value between 0 and 1.
/// </summary>
/// <param name="x">The first tensor, represented as a span.</param>
/// <param name="destination">The destination tensor.</param>
/// <exception cref="ArgumentException">Destination is too short.</exception>
/// <exception cref="ArgumentException">'<paramref name="x" />' must not be empty.</exception>
public static void Sigmoid(ReadOnlySpan<float> x, Span<float> destination)
{
if (x.Length > destination.Length)
{
ThrowHelper.ThrowArgument_DestinationTooShort();
}
if (x.Length == 0)
{
ThrowHelper.ThrowArgument_SpansMustBeNonEmpty();
}
for (int i = 0; i < x.Length; i++)
{
destination[i] = 1f / (1 + MathF.Exp(-x[i]));
}
}
}
}
......@@ -14,5 +14,9 @@ internal static class ThrowHelper
[DoesNotReturn]
public static void ThrowArgument_SpansMustHaveSameLength() =>
throw new ArgumentException(SR.Argument_SpansMustHaveSameLength);
[DoesNotReturn]
public static void ThrowArgument_SpansMustBeNonEmpty() =>
throw new ArgumentException(SR.Argument_SpansMustBeNonEmpty);
}
}
......@@ -702,5 +702,189 @@ public static void TanhTensor_ThrowsForTooShortDestination(int tensorLength)
AssertExtensions.Throws<ArgumentException>("destination", () => TensorPrimitives.Tanh(x, destination));
}
[Theory]
[MemberData(nameof(TensorLengths))]
public static void CosineSimilarity_ThrowsForMismatchedLengths_x_y(int tensorLength)
{
float[] x = CreateAndFillTensor(tensorLength);
float[] y = CreateAndFillTensor(tensorLength - 1);
Assert.Throws<ArgumentException>(() => TensorPrimitives.CosineSimilarity(x, y));
}
[Fact]
public static void CosineSimilarity_ThrowsForEmpty_x_y()
{
float[] x = [];
float[] y = [];
Assert.Throws<ArgumentException>(() => TensorPrimitives.CosineSimilarity(x, y));
}
[Theory]
[InlineData(new float[] { 3, 2, 0, 5 }, new float[] { 1, 0, 0, 0 }, 0.49f)]
[InlineData(new float[] { 1, 1, 1, 1, 1, 0 }, new float[] { 1, 1, 1, 1, 0, 1 }, 0.80f)]
public static void CosineSimilarity(float[] x, float[] y, float expectedResult)
{
Assert.Equal(expectedResult, TensorPrimitives.CosineSimilarity(x, y), .01f);
}
[Fact]
public static void Distance_ThrowsForEmpty_x_y()
{
float[] x = [];
float[] y = [];
Assert.Throws<ArgumentException>(() => TensorPrimitives.Distance(x, y));
}
[Theory]
[MemberData(nameof(TensorLengths))]
public static void Distance_ThrowsForMismatchedLengths_x_y(int tensorLength)
{
float[] x = CreateAndFillTensor(tensorLength);
float[] y = CreateAndFillTensor(tensorLength - 1);
Assert.Throws<ArgumentException>(() => TensorPrimitives.Distance(x, y));
}
[Theory]
[InlineData(new float[] { 3, 2 }, new float[] { 4, 1 }, 1.4142f)]
[InlineData(new float[] { 0, 4 }, new float[] { 6, 2 }, 6.3245f)]
[InlineData(new float[] { 1, 2, 3 }, new float[] { 4, 5, 6 }, 5.1961f)]
[InlineData(new float[] { 5, 1, 6, 10 }, new float[] { 7, 2, 8, 4 }, 6.7082f)]
public static void Distance(float[] x, float[] y, float expectedResult)
{
Assert.Equal(expectedResult, TensorPrimitives.Distance(x, y), .001f);
}
[Theory]
[MemberData(nameof(TensorLengths))]
public static void Dot_ThrowsForMismatchedLengths_x_y(int tensorLength)
{
float[] x = CreateAndFillTensor(tensorLength);
float[] y = CreateAndFillTensor(tensorLength - 1);
Assert.Throws<ArgumentException>(() => TensorPrimitives.Dot(x, y));
}
[Theory]
[InlineData(new float[] { 1, 3, -5 }, new float[] { 4, -2, -1 }, 3)]
[InlineData(new float[] { 1, 2, 3 }, new float[] { 4, 5, 6 }, 32)]
[InlineData(new float[] { 1, 2, 3, 10, 8 }, new float[] { 4, 5, 6, -2, 7 }, 68)]
[InlineData(new float[] { }, new float[] { }, 0)]
public static void Dot(float[] x, float[] y, float expectedResult)
{
Assert.Equal(expectedResult, TensorPrimitives.Dot(x, y), .001f);
}
[Theory]
[InlineData(new float[] { 1, 2, 3 }, 3.7416)]
[InlineData(new float[] { 3, 4 }, 5)]
[InlineData(new float[] { 3 }, 3)]
[InlineData(new float[] { 3, 4, 1, 2 }, 5.477)]
[InlineData(new float[] { }, 0f)]
public static void L2Normalize(float[] x, float expectedResult)
{
Assert.Equal(expectedResult, TensorPrimitives.L2Normalize(x), .001f);
}
[Theory]
[MemberData(nameof(TensorLengths))]
public static void SoftMax_ThrowsForTooShortDestination(int tensorLength)
{
float[] x = CreateAndFillTensor(tensorLength);
float[] destination = CreateTensor(tensorLength - 1);
AssertExtensions.Throws<ArgumentException>("destination", () => TensorPrimitives.SoftMax(x, destination));
}
[Theory]
[InlineData(new float[] { 3, 1, .2f }, new float[] { 0.8360188f, 0.11314284f, 0.05083836f })]
[InlineData(new float[] { 3, 4, 1 }, new float[] { 0.2594f, 0.7052f, 0.0351f })]
[InlineData(new float[] { 5, 3 }, new float[] { 0.8807f, 0.1192f })]
[InlineData(new float[] { 4, 2, 1, 9 }, new float[] { 0.0066f, 9.04658e-4f, 3.32805e-4f, 0.9920f})]
public static void SoftMax(float[] x, float[] expectedResult)
{
var dest = new float[x.Length];
TensorPrimitives.SoftMax(x, dest);
for (int i = 0; i < x.Length; i++)
{
Assert.Equal(expectedResult[i], dest[i], .001f);
}
}
[Fact]
public static void SoftMax_DestinationLongerThanSource()
{
var x = new float[] { 3, 1, .2f };
var expectedResult = new float[] { 0.8360188f, 0.11314284f, 0.05083836f };
var dest = new float[x.Length + 1];
TensorPrimitives.SoftMax(x, dest);
for (int i = 0; i < x.Length; i++)
{
Assert.Equal(expectedResult[i], dest[i], .001f);
}
}
[Fact]
public static void SoftMax_ThrowsForEmpty_x_y()
{
var x = new float[] { };
var dest = new float[x.Length];
AssertExtensions.Throws<ArgumentException>(() => TensorPrimitives.SoftMax(x, dest));
}
[Theory]
[MemberData(nameof(TensorLengths))]
public static void Sigmoid_ThrowsForTooShortDestination(int tensorLength)
{
float[] x = CreateAndFillTensor(tensorLength);
float[] destination = CreateTensor(tensorLength - 1);
AssertExtensions.Throws<ArgumentException>("destination", () => TensorPrimitives.Sigmoid(x, destination));
}
[Theory]
[InlineData(new float[] { -5, -4.5f, -4 }, new float[] { 0.0066f, 0.0109f, 0.0179f })]
[InlineData(new float[] { 4.5f, 5 }, new float[] { 0.9890f, 0.9933f })]
[InlineData(new float[] { 0, -3, 3, .5f }, new float[] { 0.5f, 0.0474f, 0.9525f, 0.6224f })]
public static void Sigmoid(float[] x, float[] expectedResult)
{
var dest = new float[x.Length];
TensorPrimitives.Sigmoid(x, dest);
for (int i = 0; i < x.Length; i++)
{
Assert.Equal(expectedResult[i], dest[i], .001f);
}
}
[Fact]
public static void Sigmoid_DestinationLongerThanSource()
{
var x = new float[] { -5, -4.5f, -4 };
var expectedResult = new float[] { 0.0066f, 0.0109f, 0.0179f };
var dest = new float[x.Length + 1];
TensorPrimitives.Sigmoid(x, dest);
for (int i = 0; i < x.Length; i++)
{
Assert.Equal(expectedResult[i], dest[i], .001f);
}
}
[Fact]
public static void Sigmoid_ThrowsForEmpty_x_y()
{
var x = new float[] { };
var dest = new float[x.Length];
AssertExtensions.Throws<ArgumentException>(() => TensorPrimitives.Sigmoid(x, dest));
}
}
}
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册