mirror of
https://github.com/saymrwulf/onnxruntime.git
synced 2026-07-07 17:15:29 +00:00
Support scalar tensors in c# (#4849)
This commit is contained in:
parent
d3cddba8f1
commit
26bd8c2085
4 changed files with 112 additions and 26 deletions
|
|
@ -36,11 +36,6 @@ namespace Microsoft.ML.OnnxRuntime.Tensors
|
|||
|
||||
public static long GetProduct(ReadOnlySpan<int> dimensions, int startIndex = 0)
|
||||
{
|
||||
if (dimensions.Length == 0)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
long product = 1;
|
||||
for (int i = startIndex; i < dimensions.Length; i++)
|
||||
{
|
||||
|
|
@ -96,6 +91,11 @@ namespace Microsoft.ML.OnnxRuntime.Tensors
|
|||
{
|
||||
int[] strides = new int[dimensions.Length];
|
||||
|
||||
if (dimensions.Length == 0)
|
||||
{
|
||||
return strides;
|
||||
}
|
||||
|
||||
int stride = 1;
|
||||
if (reverseStride)
|
||||
{
|
||||
|
|
@ -174,6 +174,12 @@ namespace Microsoft.ML.OnnxRuntime.Tensors
|
|||
Debug.Assert(reverseStride ? IsAscending(strides) : IsDescending(strides), "Index decomposition requires ordered strides");
|
||||
Debug.Assert(strides.Length == indices.Length);
|
||||
|
||||
// scalar tensor - nothing to process
|
||||
if (indices.Length == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
int remainder = index;
|
||||
for (int i = startFromDimension; i < strides.Length; i++)
|
||||
{
|
||||
|
|
@ -199,6 +205,12 @@ namespace Microsoft.ML.OnnxRuntime.Tensors
|
|||
Debug.Assert(reverseStride ? IsAscending(strides) : IsDescending(strides), "Index decomposition requires ordered strides");
|
||||
Debug.Assert(strides.Length == indices.Length);
|
||||
|
||||
// scalar tensor - nothing to process
|
||||
if (indices.Length == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
int remainder = index;
|
||||
for (int i = startFromDimension; i < strides.Length; i++)
|
||||
{
|
||||
|
|
@ -220,6 +232,13 @@ namespace Microsoft.ML.OnnxRuntime.Tensors
|
|||
Debug.Assert(sourceReverseStride ? IsAscending(sourceStrides) : IsDescending(sourceStrides), "Index decomposition requires ordered strides");
|
||||
Debug.Assert(sourceStrides.Length == transformStrides.Length);
|
||||
|
||||
// scalar tensor
|
||||
if (sourceStrides.Length == 0)
|
||||
{
|
||||
Debug.Assert(index == 0, "Index has to be zero for a scalar tensor");
|
||||
return 0;
|
||||
}
|
||||
|
||||
int transformIndex = 0;
|
||||
int remainder = index;
|
||||
|
||||
|
|
|
|||
|
|
@ -93,6 +93,7 @@ namespace Microsoft.ML.OnnxRuntime.Tensors
|
|||
|
||||
/// <summary>
|
||||
/// Gets the value at the specied index, where index is a linearized version of n-dimension indices using strides.
|
||||
/// For a scalar, use index = 0
|
||||
/// </summary>
|
||||
/// <param name="index">An integer index computed as a dot-product of indices.</param>
|
||||
/// <returns>The value at the specified position in this Tensor.</returns>
|
||||
|
|
@ -103,6 +104,7 @@ namespace Microsoft.ML.OnnxRuntime.Tensors
|
|||
|
||||
/// <summary>
|
||||
/// Sets the value at the specied index, where index is a linearized version of n-dimension indices using strides.
|
||||
/// For a scalar, use index = 0
|
||||
/// </summary>
|
||||
/// <param name="index">An integer index computed as a dot-product of indices.</param>
|
||||
/// <param name="value">The new value to set at the specified position in this Tensor.</param>
|
||||
|
|
@ -170,11 +172,7 @@ namespace Microsoft.ML.OnnxRuntime.Tensors
|
|||
/// <returns>A new tensor that reinterprets backing Buffer of this tensor with different dimensions.</returns>
|
||||
public override Tensor<T> Reshape(ReadOnlySpan<int> dimensions)
|
||||
{
|
||||
if (dimensions.Length == 0)
|
||||
{
|
||||
throw new ArgumentException("Dimensions must contain elements.", nameof(dimensions));
|
||||
}
|
||||
|
||||
|
||||
var newSize = ArrayUtilities.GetProduct(dimensions);
|
||||
|
||||
if (newSize != Length)
|
||||
|
|
|
|||
|
|
@ -397,15 +397,15 @@ namespace Microsoft.ML.OnnxRuntime.Tensors
|
|||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initialize an n-dimensional tensor with the specified dimensions and layout. ReverseStride=true gives a stride of 1-element witdth to the first dimension (0). ReverseStride=false gives a stride of 1-element width to the last dimension (n-1).
|
||||
/// Initialize an n-dimensional tensor with the specified dimensions and layout. ReverseStride=true gives a stride of 1-element width to the first dimension (0). ReverseStride=false gives a stride of 1-element width to the last dimension (n-1).
|
||||
/// </summary>
|
||||
/// <param name="dimensions">An span of integers that represent the size of each dimension of the Tensor to create.</param>
|
||||
/// <param name="reverseStride">False (default) to indicate that the first dimension is most major (farthest apart) and the last dimension is most minor (closest together): akin to row-major in a rank-2 tensor. True to indicate that the last dimension is most major (farthest apart) and the first dimension is most minor (closest together): akin to column-major in a rank-2 tensor.</param>
|
||||
protected Tensor(ReadOnlySpan<int> dimensions, bool reverseStride) : base(typeof(T))
|
||||
{
|
||||
if (dimensions.Length == 0)
|
||||
if (dimensions == null)
|
||||
{
|
||||
throw new ArgumentException("Dimensions must contain elements.", nameof(dimensions));
|
||||
throw new ArgumentNullException(nameof(dimensions));
|
||||
}
|
||||
|
||||
this.dimensions = new int[dimensions.Length];
|
||||
|
|
@ -427,7 +427,7 @@ namespace Microsoft.ML.OnnxRuntime.Tensors
|
|||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes tensor with same dimensions as array, content of array is ignored. ReverseStride=true gives a stride of 1-element witdth to the first dimension (0). ReverseStride=false gives a stride of 1-element width to the last dimension (n-1).
|
||||
/// Initializes tensor with same dimensions as array, content of array is ignored. ReverseStride=true gives a stride of 1-element width to the first dimension (0). ReverseStride=false gives a stride of 1-element width to the last dimension (n-1).
|
||||
/// </summary>
|
||||
/// <param name="fromArray">Array from which to derive dimensions.</param>
|
||||
/// <param name="reverseStride">False (default) to indicate that the first dimension is most major (farthest apart) and the last dimension is most minor (closest together): akin to row-major in a rank-2 tensor. True to indicate that the last dimension is most major (farthest apart) and the first dimension is most minor (closest together): akin to column-major in a rank-2 tensor.</param>
|
||||
|
|
@ -438,11 +438,6 @@ namespace Microsoft.ML.OnnxRuntime.Tensors
|
|||
throw new ArgumentNullException(nameof(fromArray));
|
||||
}
|
||||
|
||||
if (fromArray.Rank == 0)
|
||||
{
|
||||
throw new ArgumentException("Array must contain elements.", nameof(fromArray));
|
||||
}
|
||||
|
||||
dimensions = new int[fromArray.Rank];
|
||||
long size = 1;
|
||||
for (int i = 0; i < dimensions.Length; i++)
|
||||
|
|
|
|||
|
|
@ -159,9 +159,7 @@ namespace Microsoft.ML.OnnxRuntime.Tensors.Tests
|
|||
Assert.Equal(24, tensor.Length);
|
||||
Assert.Equal(tensorConstructor.IsReversedStride, tensor.IsReversedStride);
|
||||
|
||||
//Assert.Throws<ArgumentNullException>("dimensions", () => tensorConstructor.CreateFromDimensions<int>(dimensions: null));
|
||||
Assert.Throws<ArgumentException>("dimensions", () => tensorConstructor.CreateFromDimensions<int>(dimensions: new int[0]));
|
||||
|
||||
Assert.Throws<ArgumentNullException>("dimensions", () => tensorConstructor.CreateFromDimensions<int>(dimensions: null));
|
||||
Assert.Throws<ArgumentOutOfRangeException>("dimensions", () => tensorConstructor.CreateFromDimensions<int>(dimensions: new[] { 1, -1 }));
|
||||
|
||||
// ensure dimensions are immutable
|
||||
|
|
@ -224,12 +222,88 @@ namespace Microsoft.ML.OnnxRuntime.Tensors.Tests
|
|||
Assert.True(StructuralComparisons.StructuralEqualityComparer.Equals(emptyTensor1, emptyTensor4));
|
||||
Assert.True(StructuralComparisons.StructuralEqualityComparer.Equals(emptyTensor4, emptyTensor1));
|
||||
|
||||
// create an empty DenseTensor
|
||||
// create an empty DenseTensor from dimensions
|
||||
dimensions = new[] { 0, 2, 1 };
|
||||
var emptyDenseTensor = new DenseTensor<int>(new Span<int>(dimensions));
|
||||
var emptyDenseTensor1 = new DenseTensor<int>(new Span<int>(dimensions));
|
||||
Assert.Equal(0, emptyDenseTensor1.Length);
|
||||
// accessing any index in the underlying buffer should result in an IndexOutOfRangeException
|
||||
Assert.Throws<IndexOutOfRangeException>(() => emptyDenseTensor.GetValue(0));
|
||||
Assert.Throws<IndexOutOfRangeException>(() => emptyDenseTensor.GetValue(5));
|
||||
Assert.Throws<IndexOutOfRangeException>(() => emptyDenseTensor1.GetValue(0));
|
||||
Assert.Throws<IndexOutOfRangeException>(() => emptyDenseTensor1.GetValue(5));
|
||||
|
||||
// create an empty DenseTensor from memory
|
||||
var memory = new Memory<int>(new int[] { });
|
||||
var emptyDenseTensor2 = new DenseTensor<int>(memory, new int[] {2, 0, 2 });
|
||||
Assert.Equal(0, emptyDenseTensor2.Length);
|
||||
// accessing any index in the underlying buffer should result in an IndexOutOfRangeException
|
||||
Assert.Throws<IndexOutOfRangeException>(() => emptyDenseTensor2.GetValue(0));
|
||||
Assert.Throws<IndexOutOfRangeException>(() => emptyDenseTensor2.GetValue(5));
|
||||
|
||||
}
|
||||
|
||||
[Theory()]
|
||||
[MemberData(nameof(GetSingleTensorConstructors))]
|
||||
public void ConstructScalarTensors(TensorConstructor tensorConstructor)
|
||||
{
|
||||
// tests associated with scalar tensors (i.e.) tensors with no dimensions
|
||||
|
||||
// test creation of scalar tensors (from dimensions)
|
||||
var dimensions = new int[] { };
|
||||
var scalarTensor1 = tensorConstructor.CreateFromDimensions<int>(dimensions: dimensions);
|
||||
Assert.Equal(0, scalarTensor1.Dimensions.Length);
|
||||
Assert.Equal(0, scalarTensor1.Rank);
|
||||
|
||||
dimensions = new int[0];
|
||||
var scalarTensor2 = tensorConstructor.CreateFromDimensions<int>(dimensions: dimensions);
|
||||
Assert.Equal(0, scalarTensor2.Dimensions.Length);
|
||||
Assert.Equal(0, scalarTensor2.Rank);
|
||||
|
||||
// TODO: Create from Array ?
|
||||
|
||||
// ensure the lengths of the scalar tensors is 1
|
||||
Assert.Equal(1, scalarTensor1.Length);
|
||||
Assert.Equal(1, scalarTensor2.Length);
|
||||
|
||||
// equality comparison of scalar tensors with non-scalar tensors throws an ArgumentException
|
||||
dimensions = new int[] { 2, 2 };
|
||||
var nonScalarTensor1 = tensorConstructor.CreateFromDimensions<int>(dimensions: dimensions);
|
||||
|
||||
dimensions = new int[] { 0, 2, 4 };
|
||||
var nonScalarTensor2 = tensorConstructor.CreateFromDimensions<int>(dimensions: dimensions);
|
||||
|
||||
Assert.Throws<ArgumentException>("other", () => StructuralComparisons.StructuralComparer.Compare(nonScalarTensor1, scalarTensor1));
|
||||
Assert.Throws<ArgumentException>("other", () => StructuralComparisons.StructuralComparer.Compare(scalarTensor1, nonScalarTensor1));
|
||||
Assert.Throws<ArgumentException>("other", () => StructuralComparisons.StructuralComparer.Compare(nonScalarTensor2, scalarTensor1));
|
||||
Assert.Throws<ArgumentException>("other", () => StructuralComparisons.StructuralComparer.Compare(scalarTensor1, nonScalarTensor2));
|
||||
|
||||
// equality comparison of scalar tensors is true
|
||||
// equality comparison of a scalar tensor with itself
|
||||
Assert.Equal(0, StructuralComparisons.StructuralComparer.Compare(scalarTensor1, scalarTensor1));
|
||||
Assert.True(StructuralComparisons.StructuralEqualityComparer.Equals(scalarTensor1, scalarTensor1));
|
||||
// equality comparison of a scalar tensor with another scalar tensor
|
||||
Assert.Equal(0, StructuralComparisons.StructuralComparer.Compare(scalarTensor1, scalarTensor2));
|
||||
Assert.Equal(0, StructuralComparisons.StructuralComparer.Compare(scalarTensor2, scalarTensor1));
|
||||
Assert.True(StructuralComparisons.StructuralEqualityComparer.Equals(scalarTensor1, scalarTensor2));
|
||||
Assert.True(StructuralComparisons.StructuralEqualityComparer.Equals(scalarTensor2, scalarTensor1));
|
||||
|
||||
// create a scalar DenseTensor from dimensions
|
||||
dimensions = new int[] { };
|
||||
var scalarDenseTensor1 = new DenseTensor<int>(new Span<int>(dimensions));
|
||||
Assert.Equal(1, scalarDenseTensor1.Length);
|
||||
// set and get values in the scalar tensor
|
||||
scalarDenseTensor1.SetValue(0, 100);
|
||||
Assert.Equal(100, scalarDenseTensor1.GetValue(0));
|
||||
|
||||
// setting a non-zero in the underlying buffer should result in an IndexOutOfRangeException
|
||||
Assert.Throws<IndexOutOfRangeException>(() => scalarDenseTensor1.SetValue(6, 100));
|
||||
|
||||
// accessing a non-zero index in the underlying buffer should result in an IndexOutOfRangeException
|
||||
Assert.Throws<IndexOutOfRangeException>(() => scalarDenseTensor1.GetValue(5));
|
||||
|
||||
// create a scalar DenseTensor from memory
|
||||
var memory = new Memory<int>(new int[] { 1 });
|
||||
var scalarDenseTensor2 = new DenseTensor<int>(memory, new int[] { });
|
||||
Assert.Equal(1, scalarDenseTensor2.Length);
|
||||
Assert.Equal(1, scalarDenseTensor2.GetValue(0));
|
||||
}
|
||||
|
||||
[Theory()]
|
||||
|
|
|
|||
Loading…
Reference in a new issue