mirror of
https://github.com/saymrwulf/onnxruntime.git
synced 2026-07-28 20:11:22 +00:00
[C#] Rename unreleased API, add utilities (#16806)
### Description 1. rename OrtValue.FillStringTensorElement to StringTensorSetElementAt . To the API user I think we're conceptually setting the string at an offset in the tensor with is roughly equivalent to `List<string> list ... list[index] = "value"`. 2. While working on new inference examples, I noticed that I am still inclined to use `DenseTensor` for N-D indexing. Added `GetStrides()` and `GetIndex()` from strides for long dims, so the user can obtain strides and translate N-D indices into a flat index to operate directly on the native `OrtValue` buffers. Expose these functions to the user. 3. Make sure we generate docs for C# public static functions.
This commit is contained in:
parent
f4faceab28
commit
bd4d011142
11 changed files with 153 additions and 45 deletions
|
|
@ -1,8 +1,8 @@
|
|||
using System;
|
||||
using Microsoft.ML.OnnxRuntime.Tensors;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
|
||||
namespace Microsoft.ML.OnnxRuntime.InferenceSample
|
||||
{
|
||||
|
|
@ -30,7 +30,7 @@ namespace Microsoft.ML.OnnxRuntime.InferenceSample
|
|||
// We create an OrtValue in this case over the buffer of potentially different shapes.
|
||||
// It is Okay as long as the specified shape does not exceed the actual length of the buffer
|
||||
var shape = Array.ConvertAll<int, long>(inputMeta[name].Dimensions, Convert.ToInt64);
|
||||
Debug.Assert(shape.Aggregate(1L, (a, v) => a * v) <= inputData.LongLength);
|
||||
Debug.Assert(ShapeUtils.GetSizeForShape(shape) <= inputData.LongLength);
|
||||
|
||||
var ortValue = OrtValue.CreateTensorValueFromMemory(inputData, shape);
|
||||
_inputData.Add(ortValue);
|
||||
|
|
|
|||
|
|
@ -342,7 +342,7 @@ namespace Microsoft.ML.OnnxRuntime
|
|||
"Strings are not supported by this API");
|
||||
}
|
||||
|
||||
var shapeSize = ArrayUtilities.GetSizeForShape(shape);
|
||||
var shapeSize = ShapeUtils.GetSizeForShape(shape);
|
||||
var requiredBufferSize = shapeSize * typeInfo.TypeSize;
|
||||
if (requiredBufferSize > sizeInBytes)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -5,9 +5,7 @@ using Microsoft.ML.OnnxRuntime.Tensors;
|
|||
using System;
|
||||
using System.Buffers;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.Diagnostics;
|
||||
using System.Linq;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Text;
|
||||
|
||||
|
|
@ -506,7 +504,7 @@ namespace Microsoft.ML.OnnxRuntime
|
|||
"Cannot map managed strings buffer to native OrtValue. Use string specific interfaces");
|
||||
}
|
||||
|
||||
var shapeSize = ArrayUtilities.GetSizeForShape(shape);
|
||||
var shapeSize = ShapeUtils.GetSizeForShape(shape);
|
||||
var requiredBufferSizeInBytes = shapeSize * typeInfo.TypeSize;
|
||||
|
||||
// We allow creating a tensor over part of the buffer
|
||||
|
|
@ -552,7 +550,7 @@ namespace Microsoft.ML.OnnxRuntime
|
|||
"Cannot map managed strings buffer to native OrtValue. Use string specific interfaces.");
|
||||
}
|
||||
|
||||
var shapeSize = ArrayUtilities.GetSizeForShape(shape);
|
||||
var shapeSize = ShapeUtils.GetSizeForShape(shape);
|
||||
// We allow creating a tensor over part of the buffer
|
||||
if (shapeSize > memory.Length)
|
||||
{
|
||||
|
|
@ -778,7 +776,7 @@ namespace Microsoft.ML.OnnxRuntime
|
|||
/// <param name="str">ReadOnlySpan over chars</param>
|
||||
/// <param name="index">index of the string element within the tensor
|
||||
/// must be within bounds of [0, N)</param>
|
||||
public void FillStringTensorElement(ReadOnlySpan<char> str, int index)
|
||||
public void StringTensorSetElementAt(ReadOnlySpan<char> str, int index)
|
||||
{
|
||||
unsafe
|
||||
{
|
||||
|
|
@ -805,9 +803,9 @@ namespace Microsoft.ML.OnnxRuntime
|
|||
/// <param name="rom">ReadOnlyMemory instance over an array of chars</param>
|
||||
/// <param name="index">index of the string element within the tensor
|
||||
/// must be within bounds of [0, N)</param>
|
||||
public void FillStringTensorElement(ReadOnlyMemory<char> rom, int index)
|
||||
public void StringTensorSetElementAt(ReadOnlyMemory<char> rom, int index)
|
||||
{
|
||||
FillStringTensorElement(rom.Span, index);
|
||||
StringTensorSetElementAt(rom.Span, index);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
|
@ -818,7 +816,7 @@ namespace Microsoft.ML.OnnxRuntime
|
|||
/// </summary>
|
||||
/// <param name="utf8Bytes">read only span of bytes</param>
|
||||
/// <param name="index">flat index of the element in the string tensor</param>
|
||||
public void FillStringTensorElement(ReadOnlySpan<byte> utf8Bytes, int index)
|
||||
public void StringTensorSetElementAt(ReadOnlySpan<byte> utf8Bytes, int index)
|
||||
{
|
||||
NativeApiStatus.VerifySuccess(NativeMethods.OrtGetResizedStringTensorElementBuffer(Handle,
|
||||
(UIntPtr)index, (UIntPtr)utf8Bytes.Length, out IntPtr buffer));
|
||||
|
|
@ -1111,7 +1109,7 @@ namespace Microsoft.ML.OnnxRuntime
|
|||
int count = 0;
|
||||
foreach (var key in keys)
|
||||
{
|
||||
ortValues[0].FillStringTensorElement(key.AsSpan(), count++);
|
||||
ortValues[0].StringTensorSetElementAt(key.AsSpan(), count++);
|
||||
}
|
||||
|
||||
ortValues[1] = CreateTensorValueFromMemory(values, shape);
|
||||
|
|
@ -1163,7 +1161,7 @@ namespace Microsoft.ML.OnnxRuntime
|
|||
int count = 0;
|
||||
foreach (var value in values)
|
||||
{
|
||||
ortValues[1].FillStringTensorElement(value.AsSpan(), count++);
|
||||
ortValues[1].StringTensorSetElementAt(value.AsSpan(), count++);
|
||||
}
|
||||
return CreateMap(ref ortValues[0], ref ortValues[1]);
|
||||
}
|
||||
|
|
|
|||
82
csharp/src/Microsoft.ML.OnnxRuntime/ShapeUtils.shared.cs
Normal file
82
csharp/src/Microsoft.ML.OnnxRuntime/ShapeUtils.shared.cs
Normal file
|
|
@ -0,0 +1,82 @@
|
|||
using System.Diagnostics;
|
||||
using System;
|
||||
|
||||
namespace Microsoft.ML.OnnxRuntime.Tensors
|
||||
{
|
||||
/// <summary>
|
||||
/// This class contains utilities for useful calculations with shape.
|
||||
/// </summary>
|
||||
public static class ShapeUtils
|
||||
{
|
||||
/// <summary>
|
||||
/// Returns a number of elements in the tensor from the given shape
|
||||
/// </summary>
|
||||
/// <param name="shape"></param>
|
||||
/// <returns>size</returns>
|
||||
/// <exception cref="ArgumentOutOfRangeException"></exception>
|
||||
public static long GetSizeForShape(ReadOnlySpan<long> shape)
|
||||
{
|
||||
long product = 1;
|
||||
foreach (var dim in shape)
|
||||
{
|
||||
if (dim < 0)
|
||||
{
|
||||
throw new ArgumentOutOfRangeException($"Shape must not have negative elements: {dim}");
|
||||
}
|
||||
checked
|
||||
{
|
||||
product *= dim;
|
||||
}
|
||||
}
|
||||
return product;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the set of strides that can be used to calculate the offset of n-dimensions in a 1-dimensional layout
|
||||
/// </summary>
|
||||
/// <param name="dimensions"></param>
|
||||
/// <returns>an array of strides</returns>
|
||||
public static long[] GetStrides(ReadOnlySpan<long> dimensions)
|
||||
{
|
||||
long[] strides = new long[dimensions.Length];
|
||||
|
||||
if (dimensions.Length == 0)
|
||||
{
|
||||
return strides;
|
||||
}
|
||||
|
||||
long stride = 1;
|
||||
for (int i = strides.Length - 1; i >= 0; i--)
|
||||
{
|
||||
strides[i] = stride;
|
||||
if (dimensions[i] < 0)
|
||||
{
|
||||
throw new ArgumentException($"Dimension {i} is negative");
|
||||
}
|
||||
stride *= dimensions[i];
|
||||
}
|
||||
|
||||
return strides;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Calculates the 1-d index for n-d indices in layout specified by strides.
|
||||
/// </summary>
|
||||
/// <param name="strides">pre-calculated strides</param>
|
||||
/// <param name="indices">Indices. Must have the same length as strides</param>
|
||||
/// <param name="startFromDimension"></param>
|
||||
/// <returns>A 1-d index into the tensor buffer</returns>
|
||||
public static long GetIndex(ReadOnlySpan<long> strides, ReadOnlySpan<long> indices, int startFromDimension = 0)
|
||||
{
|
||||
Debug.Assert(strides.Length == indices.Length);
|
||||
|
||||
long index = 0;
|
||||
for (int i = startFromDimension; i < indices.Length; i++)
|
||||
{
|
||||
index += strides[i] * indices[i];
|
||||
}
|
||||
|
||||
return index;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -11,8 +11,8 @@
|
|||
// The .NET Foundation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using System.Diagnostics;
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
|
||||
namespace Microsoft.ML.OnnxRuntime.Tensors
|
||||
{
|
||||
|
|
@ -20,20 +20,6 @@ namespace Microsoft.ML.OnnxRuntime.Tensors
|
|||
{
|
||||
public const int StackallocMax = 16;
|
||||
|
||||
public static long GetSizeForShape(long[] shape)
|
||||
{
|
||||
long product = 1;
|
||||
foreach (var dim in shape)
|
||||
{
|
||||
if (dim < 0)
|
||||
{
|
||||
throw new ArgumentOutOfRangeException("Shape must not have negative elements:" + dim);
|
||||
}
|
||||
product *= dim;
|
||||
}
|
||||
return product;
|
||||
}
|
||||
|
||||
public static long GetProduct(ReadOnlySpan<int> dimensions, int startIndex = 0)
|
||||
{
|
||||
long product = 1;
|
||||
|
|
@ -162,7 +148,7 @@ namespace Microsoft.ML.OnnxRuntime.Tensors
|
|||
}
|
||||
|
||||
/// <summary>
|
||||
/// Calculates the n-d indices from the 1-d index in a layout specificed by strides
|
||||
/// Calculates the n-d indices from the 1-d index in a layout specified by strides
|
||||
/// </summary>
|
||||
/// <param name="strides"></param>
|
||||
/// <param name="reverseStride"></param>
|
||||
|
|
|
|||
|
|
@ -363,7 +363,7 @@ namespace Microsoft.ML.OnnxRuntime.Tests
|
|||
Assert.Equal(typeof(float), inputMeta[inputName].ElementType);
|
||||
Assert.True(inputMeta[inputName].IsTensor);
|
||||
var longShape = Array.ConvertAll<int, long>(inputMeta[inputName].Dimensions, Convert.ToInt64);
|
||||
var byteSize = longShape.Aggregate(1L, (a, b) => a * b) * sizeof(float);
|
||||
var byteSize = ShapeUtils.GetSizeForShape(longShape);
|
||||
pinnedInputs.Add(FixedBufferOnnxValue.CreateFromMemory<float>(memInfo, inputData,
|
||||
TensorElementType.Float, longShape, byteSize));
|
||||
|
||||
|
|
@ -375,7 +375,7 @@ namespace Microsoft.ML.OnnxRuntime.Tests
|
|||
Assert.Equal(typeof(float), outputMeta[outputName].ElementType);
|
||||
Assert.True(outputMeta[outputName].IsTensor);
|
||||
longShape = Array.ConvertAll<int, long>(outputMeta[outputName].Dimensions, Convert.ToInt64);
|
||||
byteSize = longShape.Aggregate(1L, (a, b) => a * b) * sizeof(float);
|
||||
byteSize = ShapeUtils.GetSizeForShape(longShape);
|
||||
float[] outputBuffer = new float[expectedOutput.Length];
|
||||
pinnedOutputs.Add(FixedBufferOnnxValue.CreateFromMemory<float>(memInfo, outputBuffer,
|
||||
TensorElementType.Float, longShape, byteSize));
|
||||
|
|
|
|||
|
|
@ -51,10 +51,10 @@ namespace Microsoft.ML.OnnxRuntime.Tests
|
|||
_inputShape = Array.ConvertAll<int, long>(inputMeta[_inputName].Dimensions, Convert.ToInt64);
|
||||
_outputShape = Array.ConvertAll<int, long>(outputMeta[_outputName].Dimensions, Convert.ToInt64);
|
||||
|
||||
var inputShapeSize = ArrayUtilities.GetSizeForShape(_inputShape);
|
||||
var inputShapeSize = ShapeUtils.GetSizeForShape(_inputShape);
|
||||
Assert.Equal(inputShapeSize, _inputData.Length);
|
||||
|
||||
var outputShapeSize = ArrayUtilities.GetSizeForShape(_outputShape);
|
||||
var outputShapeSize = ShapeUtils.GetSizeForShape(_outputShape);
|
||||
Assert.Equal(outputShapeSize, _outputData.Length);
|
||||
|
||||
_inputSizeInBytes = inputShapeSize * sizeof(float);
|
||||
|
|
@ -297,7 +297,7 @@ namespace Microsoft.ML.OnnxRuntime.Tests
|
|||
|
||||
long[] inputOutputShape = { 3, 2 };
|
||||
float[] input = { 1.0F, 2.0F, 3.0F, 4.0F, 5.0F, 6.0F };
|
||||
var inputOutputShapeSize = ArrayUtilities.GetSizeForShape(inputOutputShape);
|
||||
var inputOutputShapeSize = ShapeUtils.GetSizeForShape(inputOutputShape);
|
||||
Assert.Equal(inputOutputShapeSize, input.LongLength);
|
||||
|
||||
var memInput = new Memory<float>(input);
|
||||
|
|
|
|||
|
|
@ -1,6 +1,5 @@
|
|||
using Microsoft.ML.OnnxRuntime.Tensors;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Text;
|
||||
using Xunit;
|
||||
|
|
@ -22,7 +21,7 @@ namespace Microsoft.ML.OnnxRuntime.Tests
|
|||
string[] strsRom = { "HelloR", "OrtR", "WorldR" };
|
||||
string[] strs = { "Hello", "Ort", "World" };
|
||||
long[] shape = { 1, 1, 3 };
|
||||
var elementsNum = ArrayUtilities.GetSizeForShape(shape);
|
||||
var elementsNum = ShapeUtils.GetSizeForShape(shape);
|
||||
Assert.Equal(elementsNum, strs.Length);
|
||||
Assert.Equal(elementsNum, strsRom.Length);
|
||||
|
||||
|
|
@ -65,13 +64,13 @@ namespace Microsoft.ML.OnnxRuntime.Tests
|
|||
for (int i = 0; i < elementsNum; ++i)
|
||||
{
|
||||
// First populate via ROM
|
||||
strTensor.FillStringTensorElement(strsRom[i].AsMemory(), i);
|
||||
strTensor.StringTensorSetElementAt(strsRom[i].AsMemory(), i);
|
||||
Assert.Equal(strsRom[i], strTensor.GetStringElement(i));
|
||||
Assert.Equal(strsRom[i], strTensor.GetStringElementAsMemory(i).ToString());
|
||||
Assert.Equal(Encoding.UTF8.GetBytes(strsRom[i]), strTensor.GetStringElementAsSpan(i).ToArray());
|
||||
|
||||
// Fill via Span
|
||||
strTensor.FillStringTensorElement(strs[i].AsSpan(), i);
|
||||
strTensor.StringTensorSetElementAt(strs[i].AsSpan(), i);
|
||||
Assert.Equal(strs[i], strTensor.GetStringElement(i));
|
||||
Assert.Equal(strs[i], strTensor.GetStringElementAsMemory(i).ToString());
|
||||
Assert.Equal(Encoding.UTF8.GetBytes(strs[i]), strTensor.GetStringElementAsSpan(i).ToArray());
|
||||
|
|
@ -127,7 +126,7 @@ namespace Microsoft.ML.OnnxRuntime.Tests
|
|||
Assert.NotNull(dataTypeInfo);
|
||||
Assert.Equal(dataType, dataTypeInfo.ElementType);
|
||||
|
||||
var elementsNum = ArrayUtilities.GetSizeForShape(shape);
|
||||
var elementsNum = ShapeUtils.GetSizeForShape(shape);
|
||||
|
||||
Assert.True(tensor.IsTensor);
|
||||
Assert.False(tensor.IsSparseTensor);
|
||||
|
|
@ -162,7 +161,7 @@ namespace Microsoft.ML.OnnxRuntime.Tests
|
|||
int[] data = { 1, 2, 3 };
|
||||
var mem = new Memory<int>(data);
|
||||
long[] shape = { 1, 1, 3 };
|
||||
var elementsNum = ArrayUtilities.GetSizeForShape(shape);
|
||||
var elementsNum = ShapeUtils.GetSizeForShape(shape);
|
||||
Assert.Equal(elementsNum, data.Length);
|
||||
|
||||
|
||||
|
|
@ -200,7 +199,7 @@ namespace Microsoft.ML.OnnxRuntime.Tests
|
|||
data[2] = 3;
|
||||
|
||||
long[] shape = { 1, 1, 3 };
|
||||
var elementsNum = ArrayUtilities.GetSizeForShape(shape);
|
||||
var elementsNum = ShapeUtils.GetSizeForShape(shape);
|
||||
Assert.Equal(elementsNum, Elements);
|
||||
|
||||
using (var tensor = OrtValue.CreateTensorValueWithData(OrtMemoryInfo.DefaultInstance, TensorElementType.Int32,
|
||||
|
|
|
|||
|
|
@ -101,5 +101,48 @@ namespace Microsoft.ML.OnnxRuntime.Tests.ArrayTensorExtensions
|
|||
Assert.Equal(expectedDims, tensor.Dimensions.ToArray());
|
||||
CheckValues(array.Cast<int>(), tensor);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void TestLongStrides()
|
||||
{
|
||||
long[] emptyStrides = ShapeUtils.GetStrides(Array.Empty<long>());
|
||||
Assert.Empty(emptyStrides);
|
||||
|
||||
long[] negativeDims = { 2, -3, 4, 5 };
|
||||
Assert.Throws<ArgumentException>(() => ShapeUtils.GetStrides(negativeDims));
|
||||
|
||||
ReadOnlySpan<long> goodDims = stackalloc long[] { 2, 3, 4, 5 };
|
||||
long[] expectedStrides = { 60, 20, 5, 1 };
|
||||
Assert.Equal(expectedStrides, ShapeUtils.GetStrides(goodDims));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void TestLongGetIndex()
|
||||
{
|
||||
ReadOnlySpan<long> dims = stackalloc long[] { 2, 3, 4, 5 };
|
||||
long size = ShapeUtils.GetSizeForShape(dims);
|
||||
Assert.Equal(120, size);
|
||||
|
||||
ReadOnlySpan<long> strides = ShapeUtils.GetStrides(dims);
|
||||
|
||||
static void IncDims(ReadOnlySpan<long> dims, Span<long> indices)
|
||||
{
|
||||
for (int i = dims.Length - 1; i >= 0; i--)
|
||||
{
|
||||
indices[i]++;
|
||||
if (indices[i] < dims[i])
|
||||
break;
|
||||
indices[i] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
Span<long> indices = stackalloc long[] { 0, 0, 0, 0 };
|
||||
for (long i = 0; i < size; i++)
|
||||
{
|
||||
long index = ShapeUtils.GetIndex(strides, indices);
|
||||
Assert.Equal(i, index);
|
||||
IncDims(dims, indices);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -542,7 +542,7 @@ namespace Microsoft.ML.OnnxRuntime.Tests
|
|||
Assert.NotNull(typeInfo);
|
||||
|
||||
// ArrayUtilities not accessible in all builds
|
||||
var shapeSize = shape.Aggregate(1L, (a, v) => a * v);
|
||||
var shapeSize = ShapeUtils.GetSizeForShape(shape);
|
||||
var inferredSize = rawData.Length / typeInfo.TypeSize;
|
||||
Assert.Equal(shapeSize, inferredSize);
|
||||
Assert.Equal(0, rawData.Length % typeInfo.TypeSize);
|
||||
|
|
@ -588,7 +588,7 @@ namespace Microsoft.ML.OnnxRuntime.Tests
|
|||
{
|
||||
for (int i = 0; i < strings.Count; ++i)
|
||||
{
|
||||
ortValue.FillStringTensorElement(strings[i].Span, i);
|
||||
ortValue.StringTensorSetElementAt(strings[i].Span, i);
|
||||
}
|
||||
return ortValue;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -524,7 +524,7 @@ EXTRACT_PACKAGE = NO
|
|||
# included in the documentation.
|
||||
# The default value is: NO.
|
||||
|
||||
EXTRACT_STATIC = NO
|
||||
EXTRACT_STATIC = YES
|
||||
|
||||
# If the EXTRACT_LOCAL_CLASSES tag is set to YES, classes (and structs) defined
|
||||
# locally in source files will be included in the documentation. If set to NO,
|
||||
|
|
|
|||
Loading…
Reference in a new issue