2018-11-20 00:48:22 +00:00
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
2023-06-29 15:59:23 +00:00
using Microsoft.ML.OnnxRuntime.Tensors ;
2018-11-20 00:48:22 +00:00
using System ;
using System.Collections.Generic ;
2023-04-11 16:41:59 +00:00
using System.Diagnostics ;
2023-06-29 15:59:23 +00:00
using System.Linq ;
2023-08-08 05:19:38 +00:00
using System.Net.NetworkInformation ;
2023-06-29 15:59:23 +00:00
using System.Runtime.InteropServices ;
2023-08-08 05:19:38 +00:00
using System.Threading.Tasks ;
2018-11-20 00:48:22 +00:00
namespace Microsoft.ML.OnnxRuntime
{
/// <summary>
2020-11-20 22:03:55 +00:00
/// Represents an Inference Session on an ONNX Model.
/// This is a IDisposable class and it must be disposed of
/// using either a explicit call to Dispose() method or
/// a pattern of using() block. If this is a member of another
/// class that class must also become IDisposable and it must
2023-06-29 15:59:23 +00:00
/// dispose of InferenceSession in its Dispose() method.
2018-11-20 00:48:22 +00:00
/// </summary>
2019-05-20 22:48:14 +00:00
public class InferenceSession : IDisposable
2018-11-20 00:48:22 +00:00
{
2020-11-20 22:03:55 +00:00
/// <summary>
/// A pointer to a underlying native instance of OrtSession
/// </summary>
2021-08-26 03:59:40 +00:00
private IntPtr _nativeHandle ;
2021-08-23 19:52:15 +00:00
/// <summary>
/// Dictionary that represents input metadata
/// </summary>
2021-08-26 03:59:40 +00:00
private Dictionary < string , NodeMetadata > _inputMetadata ;
2021-08-23 19:52:15 +00:00
2023-04-11 16:41:59 +00:00
/// <summary>
/// Ordered list of input names
/// </summary>
private List < string > _inputNames ;
2021-08-23 19:52:15 +00:00
/// <summary>
/// Dictionary that represent output metadata
/// </summary>
2021-08-26 03:59:40 +00:00
private Dictionary < string , NodeMetadata > _outputMetadata ;
2021-08-23 19:52:15 +00:00
2023-04-11 16:41:59 +00:00
/// <summary>
/// Ordered list of output names
/// </summary>
private List < string > _outputNames ;
2020-11-20 22:03:55 +00:00
/// <summary>
2021-08-23 19:52:15 +00:00
/// Dictionary that represents overridableInitializers metadata
2020-11-20 22:03:55 +00:00
/// </summary>
2021-08-26 03:59:40 +00:00
private Dictionary < string , NodeMetadata > _overridableInitializerMetadata ;
2022-02-02 02:09:24 +00:00
2023-04-20 22:12:51 +00:00
/// <summary>
/// This list holds Utf-8 converted input/output names allocated from a native heap
/// and as such do not require pinning. It must be disposed of (freed).
///
/// Introduced to reduce the GC burden as the names are used in every Run() call.
/// </summary>
private List < IntPtr > _namesMemoryPtrs ;
2019-08-14 19:02:02 +00:00
private SessionOptions _builtInSessionOptions = null ;
private RunOptions _builtInRunOptions = null ;
2020-08-25 18:13:49 +00:00
private ModelMetadata _modelMetadata = null ;
2020-08-27 16:17:42 +00:00
private bool _disposed = false ;
2020-10-14 12:32:43 +00:00
private ulong _profilingStartTimeNs = 0 ;
2018-11-20 00:48:22 +00:00
#region Public API
2018-11-26 06:46:21 +00:00
/// <summary>
/// Constructs an InferenceSession from a model file
/// </summary>
/// <param name="modelPath"></param>
2018-11-20 00:48:22 +00:00
public InferenceSession ( string modelPath )
{
2019-08-14 19:02:02 +00:00
_builtInSessionOptions = new SessionOptions ( ) ; // need to be disposed
Init ( modelPath , _builtInSessionOptions ) ;
2018-11-20 00:48:22 +00:00
}
2021-05-15 03:44:42 +00:00
/// <summary>
/// Constructs an InferenceSession from a model file and it will use
/// the provided pre-packed weights container to store and share pre-packed buffers
/// of shared initializers across sessions if any.
/// </summary>
/// <param name="modelPath">Model path</param>
/// <param name="prepackedWeightsContainer">Instance of PrepackedWeightsContainer.
/// Lifetime of 'prepackedWeightsContainer' must be
/// managed by the user and it must outlive any sessions reliant on it</param>
public InferenceSession ( string modelPath , PrePackedWeightsContainer prepackedWeightsContainer )
{
_builtInSessionOptions = new SessionOptions ( ) ; // need to be disposed
Init ( modelPath , _builtInSessionOptions , prepackedWeightsContainer ) ;
}
2019-08-14 19:02:02 +00:00
2018-11-26 06:46:21 +00:00
/// <summary>
/// Constructs an InferenceSession from a model file, with some additional session options
/// </summary>
/// <param name="modelPath"></param>
/// <param name="options"></param>
2018-11-20 00:48:22 +00:00
public InferenceSession ( string modelPath , SessionOptions options )
{
2019-08-14 19:02:02 +00:00
Init ( modelPath , options ) ;
2018-11-20 00:48:22 +00:00
}
2021-05-15 03:44:42 +00:00
/// <summary>
/// Constructs an InferenceSession from a model file, with some additional session options
/// and it will use the provided pre-packed weights container to store and share pre-packed buffers
/// of shared initializers across sessions if any.
/// </summary>
/// <param name="modelPath">Model path</param>
/// <param name="options">Session options</param>
/// <param name="prepackedWeightsContainer">Instance of PrepackedWeightsContainer.
/// Lifetime of 'prepackedWeightsContainer' must be
/// managed by the user and it must outlive any sessions reliant on it</param>
public InferenceSession ( string modelPath , SessionOptions options ,
PrePackedWeightsContainer prepackedWeightsContainer )
{
Init ( modelPath , options , prepackedWeightsContainer ) ;
}
2019-09-24 18:59:04 +00:00
/// <summary>
/// Constructs an InferenceSession from a model data in byte array
/// </summary>
/// <param name="model"></param>
public InferenceSession ( byte [ ] model )
{
_builtInSessionOptions = new SessionOptions ( ) ; // need to be disposed
Init ( model , _builtInSessionOptions ) ;
}
2021-05-15 03:44:42 +00:00
/// <summary>
/// Constructs an InferenceSession from a model data (in byte array) and it will use
/// the provided pre-packed weights container to store and share pre-packed buffers
/// of shared initializers across sessions if any.
/// </summary>
/// <param name="model">Model as byte array</param>
/// <param name="prepackedWeightsContainer">Instance of PrepackedWeightsContainer.
/// Lifetime of 'prepackedWeightsContainer' must be
/// managed by the user and it must outlive any sessions reliant on it</param>
public InferenceSession ( byte [ ] model , PrePackedWeightsContainer prepackedWeightsContainer )
{
_builtInSessionOptions = new SessionOptions ( ) ; // need to be disposed
Init ( model , _builtInSessionOptions , prepackedWeightsContainer ) ;
}
2019-09-24 18:59:04 +00:00
/// <summary>
/// Constructs an InferenceSession from a model data in byte array, with some additional session options
/// </summary>
/// <param name="model"></param>
/// <param name="options"></param>
public InferenceSession ( byte [ ] model , SessionOptions options )
{
Init ( model , options ) ;
}
2019-08-14 19:02:02 +00:00
2021-05-15 03:44:42 +00:00
/// <summary>
/// Constructs an InferenceSession from a model data (in byte array) with some additional
/// session options and it will use the provided pre-packed weights container to store
/// and share pre-packed buffers of shared initializers across sessions if any.
/// </summary>
/// <param name="model">Model as byte array</param>
/// <param name="options">Session Options</param>
/// <param name="prepackedWeightsContainer">Instance of PrepackedWeightsContainer.
/// Lifetime of 'prepackedWeightsContainer' must be
/// managed by the user and it must outlive any sessions reliant on it</param>
public InferenceSession ( byte [ ] model , SessionOptions options ,
PrePackedWeightsContainer prepackedWeightsContainer )
{
Init ( model , options , prepackedWeightsContainer ) ;
}
2019-08-14 19:02:02 +00:00
/// <summary>
/// Meta data regarding the input nodes, keyed by input names
/// </summary>
2018-11-20 00:48:22 +00:00
public IReadOnlyDictionary < string , NodeMetadata > InputMetadata
{
get
{
2019-05-20 22:48:14 +00:00
return _inputMetadata ;
2018-11-20 00:48:22 +00:00
}
}
2023-04-11 16:41:59 +00:00
/// <summary>
/// Ordered list of input names that can be accessed by index;
/// </summary>
public IReadOnlyList < string > InputNames { get { return _inputNames ; } }
2019-08-14 19:02:02 +00:00
/// <summary>
/// Metadata regarding the output nodes, keyed by output names
/// </summary>
2018-11-20 00:48:22 +00:00
public IReadOnlyDictionary < string , NodeMetadata > OutputMetadata
{
get
{
2019-05-20 22:48:14 +00:00
return _outputMetadata ;
2018-11-20 00:48:22 +00:00
}
}
2023-04-11 16:41:59 +00:00
/// <summary>
/// Ordered list of output names that can be accessed by index.
/// </summary>
public IReadOnlyList < string > OutputNames { get { return _outputNames ; } }
2019-10-04 23:38:00 +00:00
/// <summary>
/// Metadata regarding the overridable initializers, keyed by node names
/// </summary>
public IReadOnlyDictionary < string , NodeMetadata > OverridableInitializerMetadata
{
get
{
return _overridableInitializerMetadata ;
}
}
2019-08-14 19:02:02 +00:00
2018-11-26 06:46:21 +00:00
/// <summary>
/// Runs the loaded model for the given inputs, and fetches all the outputs.
/// </summary>
2020-04-08 18:57:40 +00:00
/// <param name="inputs">specify a collection of <see cref="NamedOnnxValue"/> that indicates the input values.</param>
2019-08-14 19:02:02 +00:00
/// <returns>Output Tensors in a Collection of NamedOnnxValue. User must dispose the output.</returns>
2019-01-29 05:40:19 +00:00
public IDisposableReadOnlyCollection < DisposableNamedOnnxValue > Run ( IReadOnlyCollection < NamedOnnxValue > inputs )
2018-11-23 04:56:43 +00:00
{
2023-04-20 22:12:51 +00:00
return Run ( inputs , _outputNames ) ;
2018-11-23 04:56:43 +00:00
}
2018-11-26 06:46:21 +00:00
/// <summary>
/// Runs the loaded model for the given inputs, and fetches the outputs specified in <paramref name="outputNames"/>.
/// </summary>
2020-04-08 18:57:40 +00:00
/// <param name="inputs">Specify a collection of <see cref="NamedOnnxValue"/> that indicates the input values.</param>
/// <param name="outputNames">Specify a collection of string that indicates the output names to fetch.</param>
2019-08-14 19:02:02 +00:00
/// <returns>Output Tensors in a Collection of NamedOnnxValue. User must dispose the output.</returns>
2023-04-11 16:41:59 +00:00
public IDisposableReadOnlyCollection < DisposableNamedOnnxValue > Run ( IReadOnlyCollection < NamedOnnxValue > inputs ,
IReadOnlyCollection < string > outputNames )
2018-11-20 00:48:22 +00:00
{
2020-04-08 18:57:40 +00:00
return Run ( inputs , outputNames , _builtInRunOptions ) ;
2018-11-20 00:48:22 +00:00
}
/// <summary>
2020-04-08 18:57:40 +00:00
/// Runs the loaded model for the given inputs, and fetches the specified outputs in <paramref name="outputNames"/>. Uses the given RunOptions for this run.
2018-11-20 00:48:22 +00:00
/// </summary>
2020-04-08 18:57:40 +00:00
/// <param name="inputs">Specify a collection of <see cref="NamedOnnxValue"/> that indicates the input values.</param>
/// <param name="outputNames">Specify a collection of string that indicates the output names to fetch.</param>
2018-11-20 00:48:22 +00:00
/// <param name="options"></param>
2019-08-14 19:02:02 +00:00
/// <returns>Output Tensors in a Collection of NamedOnnxValue. User must dispose the output.</returns>
2023-04-11 16:41:59 +00:00
public IDisposableReadOnlyCollection < DisposableNamedOnnxValue > Run ( IReadOnlyCollection < NamedOnnxValue > inputs ,
IReadOnlyCollection < string > outputNames ,
RunOptions options )
2018-11-20 00:48:22 +00:00
{
2023-06-29 15:59:23 +00:00
var inputNamesArray = LookupUtf8Names ( inputs , v = > v . Name , LookupInputMetadata ) ;
var outputNamesArray = LookupUtf8Names ( outputNames , n = > n , LookupOutputMetadata ) ;
2018-11-20 00:48:22 +00:00
2023-06-29 15:59:23 +00:00
var inputValuesArray = GetOrtValuesHandles ( inputs , LookupInputMetadata ,
ExtractOrtValueHandleForInput , out DisposableArray < IDisposable > inputsDisposer ) ;
try
{
var outputsDisposer = RunImpl ( options , inputNamesArray , inputValuesArray , outputNamesArray ) ;
try
{
return CreateDisposableResult ( outputsDisposer . Span , outputNames ) ;
}
finally
{
outputsDisposer . Dispose ( ) ;
}
}
finally
{
inputsDisposer . Dispose ( ) ;
2018-11-20 00:48:22 +00:00
}
2020-04-08 18:57:40 +00:00
}
/// <summary>
/// Runs the loaded model for the given inputs, and fetches all the outputs.
/// </summary>
/// <param name="inputNames">Specify a collection of string that indicates the input names. Should match <paramref name="inputValues"/>.</param>
/// <param name="inputValues">Specify a collection of <see cref="FixedBufferOnnxValue"/> that indicates the input values.</param>
/// <returns>Output Tensors in a Collection of NamedOnnxValue. User must dispose the output.</returns>
public IDisposableReadOnlyCollection < DisposableNamedOnnxValue > Run (
IReadOnlyCollection < string > inputNames ,
IReadOnlyCollection < FixedBufferOnnxValue > inputValues )
{
2023-04-11 16:41:59 +00:00
return Run ( inputNames , inputValues , _outputNames , _builtInRunOptions ) ;
2020-04-08 18:57:40 +00:00
}
/// <summary>
/// Runs the loaded model for the given inputs, and fetches the outputs specified in <paramref name="outputNames"/>.
/// </summary>
/// <param name="inputNames">Specify a collection of string that indicates the input names. Should match <paramref name="inputValues"/>.</param>
/// <param name="inputValues">Specify a collection of <see cref="FixedBufferOnnxValue"/> that indicates the input values.</param>
/// <param name="outputNames">Specify a collection of string that indicates the output names to fetch.</param>
/// <returns>Output Tensors in a Collection of NamedOnnxValue. User must dispose the output.</returns>
public IDisposableReadOnlyCollection < DisposableNamedOnnxValue > Run (
IReadOnlyCollection < string > inputNames ,
IReadOnlyCollection < FixedBufferOnnxValue > inputValues ,
IReadOnlyCollection < string > outputNames )
{
return Run ( inputNames , inputValues , outputNames , _builtInRunOptions ) ;
}
/// <summary>
/// Runs the loaded model for the given inputs, and fetches the specified outputs in <paramref name="outputNames"/>. Uses the given RunOptions for this run.
/// </summary>
/// <param name="inputNames">Specify a collection of string that indicates the input names. Should match <paramref name="inputValues"/>.</param>
/// <param name="inputValues">Specify a collection of <see cref="FixedBufferOnnxValue"/> that indicates the input values.</param>
/// <param name="outputNames">Specify a collection of string that indicates the output names to fetch.</param>
/// <param name="options"></param>
/// <returns>Output Tensors in a Collection of NamedOnnxValue. User must dispose the output.</returns>
public IDisposableReadOnlyCollection < DisposableNamedOnnxValue > Run (
IReadOnlyCollection < string > inputNames ,
IReadOnlyCollection < FixedBufferOnnxValue > inputValues ,
IReadOnlyCollection < string > outputNames ,
RunOptions options )
{
if ( inputNames . Count ! = inputValues . Count )
{
throw new ArgumentException ( $"Length of {nameof(inputNames)} ({inputNames.Count}) must match that of {nameof(inputValues)} ({inputValues.Count})." ) ;
}
2023-06-29 15:59:23 +00:00
var inputNamesArray = LookupUtf8Names ( inputNames , n = > n , LookupInputMetadata ) ;
IntPtr [ ] inputValuesArray = GetOrtValuesHandles ( inputValues , true ) ;
var outputNamesArray = LookupUtf8Names ( outputNames , n = > n , LookupOutputMetadata ) ;
2020-04-08 18:57:40 +00:00
2023-06-29 15:59:23 +00:00
var disposableHandles = RunImpl ( options , inputNamesArray , inputValuesArray , outputNamesArray ) ;
try
{
return CreateDisposableResult ( disposableHandles . Span , outputNames ) ;
}
finally
{
disposableHandles . Dispose ( ) ;
2020-04-08 18:57:40 +00:00
}
}
/// <summary>
/// Runs the loaded model for the given inputs and outputs.
///
/// Outputs need to be created with correct type and dimension to accept the fetched data.
/// </summary>
/// <param name="inputNames">Specify a collection of string that indicates the input names. Should match <paramref name="inputValues"/>.</param>
/// <param name="inputValues">Specify a collection of <see cref="FixedBufferOnnxValue"/> that indicates the input values.</param>
/// <param name="outputNames">Specify a collection of string that indicates the output names. Should match <paramref name="outputValues"/>.</param>
/// <param name="outputValues">Specify a collection of <see cref="FixedBufferOnnxValue"/> that indicates the output values.</param>
public void Run (
IReadOnlyCollection < string > inputNames ,
IReadOnlyCollection < FixedBufferOnnxValue > inputValues ,
IReadOnlyCollection < string > outputNames ,
IReadOnlyCollection < FixedBufferOnnxValue > outputValues )
{
Run ( inputNames , inputValues , outputNames , outputValues , _builtInRunOptions ) ;
}
/// <summary>
/// Runs the loaded model for the given inputs and outputs. Uses the given RunOptions for this run.
///
/// Outputs need to be created with correct type and dimension to accept the fetched data.
/// </summary>
/// <param name="inputNames">Specify a collection of string that indicates the input names. Should match <paramref name="inputValues"/>.</param>
/// <param name="inputValues">Specify a collection of <see cref="FixedBufferOnnxValue"/> that indicates the input values.</param>
/// <param name="outputNames">Specify a collection of string that indicates the output names. Should match <paramref name="outputValues"/>.</param>
/// <param name="outputValues">Specify a collection of <see cref="FixedBufferOnnxValue"/> that indicates the output values.</param>
/// <param name="options"></param>
public void Run (
IReadOnlyCollection < string > inputNames ,
IReadOnlyCollection < FixedBufferOnnxValue > inputValues ,
IReadOnlyCollection < string > outputNames ,
IReadOnlyCollection < FixedBufferOnnxValue > outputValues ,
RunOptions options )
{
if ( inputNames . Count ! = inputValues . Count )
{
throw new ArgumentException ( $"Length of {nameof(inputNames)} ({inputNames.Count}) must match that of {nameof(inputValues)} ({inputValues.Count})." ) ;
}
if ( outputNames . Count ! = outputValues . Count )
{
throw new ArgumentException ( $"Length of {nameof(outputNames)} ({outputNames.Count}) must match that of {nameof(outputValues)} ({outputValues.Count})." ) ;
}
2023-06-29 15:59:23 +00:00
// prepare inputs
var inputNamesArray = LookupUtf8Names ( inputNames , n = > n , LookupInputMetadata ) ;
IntPtr [ ] inputValuesArray = GetOrtValuesHandles ( inputValues , true ) ;
2020-06-16 18:06:11 +00:00
2023-06-29 15:59:23 +00:00
// prepare outputs
var outputNamesArray = LookupUtf8Names ( outputNames , n = > n , LookupOutputMetadata ) ;
IntPtr [ ] outputValuesArray = GetOrtValuesHandles ( outputValues , false ) ;
2020-04-08 18:57:40 +00:00
2023-06-29 15:59:23 +00:00
NativeApiStatus . VerifySuccess ( NativeMethods . OrtRun (
_nativeHandle ,
options . Handle ,
inputNamesArray ,
inputValuesArray ,
( UIntPtr ) inputNames . Count ,
outputNamesArray ,
( UIntPtr ) outputNames . Count ,
outputValuesArray /* pointers to Pre-allocated OrtValue instances */
) ) ;
2020-04-08 18:57:40 +00:00
}
/// <summary>
/// Runs the loaded model for the given inputs and outputs.
///
/// Outputs need to be created with correct type and dimension to receive the fetched data.
/// </summary>
/// <param name="inputs">Specify a collection of <see cref="NamedOnnxValue"/> that indicates the input values.</param>
2020-11-20 22:03:55 +00:00
/// <param name="outputs">Specify a collection of <see cref="NamedOnnxValue"/> that indicates the output values.</param>
2020-04-08 18:57:40 +00:00
public void Run (
IReadOnlyCollection < NamedOnnxValue > inputs ,
IReadOnlyCollection < NamedOnnxValue > outputs )
{
Run ( inputs , outputs , _builtInRunOptions ) ;
}
/// <summary>
2023-04-11 16:41:59 +00:00
///
2020-04-08 18:57:40 +00:00
/// Runs the loaded model for the given inputs and outputs. Uses the given RunOptions for this run.
2020-08-10 20:33:49 +00:00
///
2020-04-08 18:57:40 +00:00
/// Outputs need to be created with correct type and dimension to receive the fetched data.
/// </summary>
/// <param name="inputs">Specify a collection of <see cref="NamedOnnxValue"/> that indicates the input values.</param>
2020-11-20 22:03:55 +00:00
/// <param name="outputs">Specify a collection of <see cref="NamedOnnxValue"/> that indicates the output values.</param>
2020-04-08 18:57:40 +00:00
/// <param name="options"></param>
public void Run (
IReadOnlyCollection < NamedOnnxValue > inputs ,
IReadOnlyCollection < NamedOnnxValue > outputs ,
RunOptions options )
{
2023-06-29 15:59:23 +00:00
var inputNamesArray = LookupUtf8Names ( inputs , i = > i . Name , LookupInputMetadata ) ;
var outputNamesArray = LookupUtf8Names ( outputs , o = > o . Name , LookupOutputMetadata ) ;
2020-04-08 18:57:40 +00:00
2023-06-29 15:59:23 +00:00
var inputValuesArray = GetOrtValuesHandles ( inputs , LookupInputMetadata , ExtractOrtValueHandleForInput ,
out DisposableArray < IDisposable > inputDisposer ) ;
2020-04-08 18:57:40 +00:00
2023-06-29 15:59:23 +00:00
try
{
var outputValuesArray = GetOrtValuesHandles ( outputs , LookupOutputMetadata , ExtractOrtValueHandleForOutput ,
out DisposableArray < IDisposable > outputDisposer ) ;
try
{
NativeApiStatus . VerifySuccess ( NativeMethods . OrtRun (
_nativeHandle ,
options . Handle ,
inputNamesArray ,
inputValuesArray ,
( UIntPtr ) inputs . Count ,
outputNamesArray ,
( UIntPtr ) outputs . Count ,
outputValuesArray /* pointers to Pre-allocated OrtValue instances */
) ) ;
}
finally
{
outputDisposer . Dispose ( ) ;
}
}
finally
{
inputDisposer . Dispose ( ) ;
2020-04-08 18:57:40 +00:00
}
}
/// <summary>
/// Runs the loaded model for the given inputs and outputs.
///
/// Outputs need to be created with correct type and dimension to receive the fetched data.
/// </summary>
/// <param name="inputs">Specify a collection of <see cref="NamedOnnxValue"/> that indicates the input values.</param>
/// <param name="outputNames">Specify a collection of string that indicates the output names. Should match <paramref name="outputValues"/>.</param>
/// <param name="outputValues">Specify a collection of <see cref="FixedBufferOnnxValue"/> that indicates the output values.</param>
public void Run (
IReadOnlyCollection < NamedOnnxValue > inputs ,
IReadOnlyCollection < string > outputNames ,
IReadOnlyCollection < FixedBufferOnnxValue > outputValues )
{
Run ( inputs , outputNames , outputValues , _builtInRunOptions ) ;
}
/// <summary>
/// Runs the loaded model for the given inputs and outputs. Uses the given RunOptions for this run.
///
/// Outputs need to be created with correct type and dimension to receive the fetched data.
/// </summary>
/// <param name="inputs">Specify a collection of <see cref="NamedOnnxValue"/> that indicates the input values.</param>
/// <param name="outputNames">Specify a collection of string that indicates the output names. Should match <paramref name="outputValues"/>.</param>
/// <param name="outputValues">Specify a collection of <see cref="FixedBufferOnnxValue"/> that indicates the output values.</param>
/// <param name="options"></param>
public void Run (
IReadOnlyCollection < NamedOnnxValue > inputs ,
IReadOnlyCollection < string > outputNames ,
IReadOnlyCollection < FixedBufferOnnxValue > outputValues ,
RunOptions options )
{
if ( outputNames . Count ! = outputValues . Count )
{
throw new ArgumentException ( $"Length of {nameof(outputNames)} ({outputNames.Count}) must match that of {nameof(outputValues)} ({outputValues.Count})." ) ;
}
2023-06-29 15:59:23 +00:00
var inputNamesArray = LookupUtf8Names ( inputs , i = > i . Name , LookupInputMetadata ) ;
var outputNamesArray = LookupUtf8Names ( outputNames , n = > n , LookupOutputMetadata ) ;
var outputValuesArray = GetOrtValuesHandles ( outputValues , false ) ;
2020-04-08 18:57:40 +00:00
2023-06-29 15:59:23 +00:00
var inputValuesArray = GetOrtValuesHandles ( inputs , LookupInputMetadata , ExtractOrtValueHandleForInput ,
out DisposableArray < IDisposable > inputsDisposer ) ;
2020-04-08 18:57:40 +00:00
2023-06-29 15:59:23 +00:00
try
{
2020-08-10 20:33:49 +00:00
NativeApiStatus . VerifySuccess ( NativeMethods . OrtRun (
2020-04-08 18:57:40 +00:00
_nativeHandle ,
options . Handle ,
inputNamesArray ,
inputValuesArray ,
( UIntPtr ) inputs . Count ,
outputNamesArray ,
( UIntPtr ) outputNames . Count ,
outputValuesArray /* pointers to Pre-allocated OrtValue instances */
2020-08-10 20:33:49 +00:00
) ) ;
2020-04-08 18:57:40 +00:00
}
2023-06-29 15:59:23 +00:00
finally
{
inputsDisposer . Dispose ( ) ;
}
2020-04-08 18:57:40 +00:00
}
/// <summary>
2020-08-10 20:33:49 +00:00
///
2020-04-08 18:57:40 +00:00
/// Runs the loaded model for the given inputs and outputs.
2020-08-10 20:33:49 +00:00
///
2020-04-08 18:57:40 +00:00
/// Outputs need to be created with correct type and dimension to receive the fetched data.
/// </summary>
/// <param name="inputNames">Specify a collection of string that indicates the input names. Should match <paramref name="inputValues"/>.</param>
/// <param name="inputValues">Specify a collection of <see cref="FixedBufferOnnxValue"/> that indicates the input values.</param>
2020-11-20 22:03:55 +00:00
/// <param name="outputs">Specify a collection of <see cref="NamedOnnxValue"/> that indicates the output values.</param>
2020-04-08 18:57:40 +00:00
public void Run (
IReadOnlyCollection < string > inputNames ,
IReadOnlyCollection < FixedBufferOnnxValue > inputValues ,
IReadOnlyCollection < NamedOnnxValue > outputs )
{
Run ( inputNames , inputValues , outputs , _builtInRunOptions ) ;
}
/// <summary>
2023-04-11 16:41:59 +00:00
///
2020-04-08 18:57:40 +00:00
/// Runs the loaded model for the given inputs and outputs. Uses the given RunOptions for this run.
2020-08-10 20:33:49 +00:00
///
2020-04-08 18:57:40 +00:00
/// Outputs need to be created with correct type and dimension to receive the fetched data.
/// </summary>
/// <param name="inputNames">Specify a collection of string that indicates the input names. Should match <paramref name="inputValues"/>.</param>
/// <param name="inputValues">Specify a collection of <see cref="FixedBufferOnnxValue"/> that indicates the input values.</param>
2020-11-20 22:03:55 +00:00
/// <param name="outputs">Specify a collection of <see cref="NamedOnnxValue"/> that indicates the output values.</param>
2020-04-08 18:57:40 +00:00
/// <param name="options"></param>
public void Run (
IReadOnlyCollection < string > inputNames ,
IReadOnlyCollection < FixedBufferOnnxValue > inputValues ,
IReadOnlyCollection < NamedOnnxValue > outputs ,
RunOptions options )
{
if ( inputNames . Count ! = inputValues . Count )
{
throw new ArgumentException ( $"Length of {nameof(inputNames)} ({inputNames.Count}) must match that of {nameof(inputValues)} ({inputValues.Count})." ) ;
2018-11-20 00:48:22 +00:00
}
2019-05-20 22:48:14 +00:00
2023-06-29 15:59:23 +00:00
// prepare inputs
var inputNamesArray = LookupUtf8Names ( inputNames , n = > n , LookupInputMetadata ) ;
var inputValuesArray = GetOrtValuesHandles ( inputValues , true ) ;
2020-04-08 18:57:40 +00:00
2023-06-29 15:59:23 +00:00
// prepare outputs
var outputNamesArray = LookupUtf8Names ( outputs , o = > o . Name , LookupOutputMetadata ) ;
var outputValuesArray = GetOrtValuesHandles ( outputs , LookupOutputMetadata , ExtractOrtValueHandleForOutput ,
out DisposableArray < IDisposable > outputsDisposer ) ;
2020-04-08 18:57:40 +00:00
2023-06-29 15:59:23 +00:00
try
{
2020-08-10 20:33:49 +00:00
NativeApiStatus . VerifySuccess ( NativeMethods . OrtRun (
2020-04-08 18:57:40 +00:00
_nativeHandle ,
options . Handle ,
inputNamesArray ,
inputValuesArray ,
( UIntPtr ) inputNames . Count ,
outputNamesArray ,
( UIntPtr ) outputs . Count ,
outputValuesArray /* pointers to Pre-allocated OrtValue instances */
2020-08-10 20:33:49 +00:00
) ) ;
}
2023-06-29 15:59:23 +00:00
finally
{
outputsDisposer . Dispose ( ) ;
}
}
/// <summary>
/// The API runs the inference taking a collection of OrtValues as input and
/// returning a collection of output OrtValues.
/// </summary>
/// <param name="runOptions">runOptions</param>
/// <param name="inputNames">A collection of input names.
/// To supply all names, use InputNames property</param>
/// <param name="inputValues">Input OrtValues. The size of the collection must match the size and the order of the inputNames</param>
/// <param name="outputNames">Output names requested. To supply all names, use OutputNames property.</param>
/// <returns>A disposable collection of disposable OrtValues</returns>
/// <exception cref="ArgumentException"></exception>
public IDisposableReadOnlyCollection < OrtValue > Run ( RunOptions runOptions , IReadOnlyCollection < string > inputNames ,
IReadOnlyCollection < OrtValue > inputValues , IReadOnlyCollection < string > outputNames )
{
if ( inputNames . Count ! = inputValues . Count )
{
throw new ArgumentException ( $"Length of {nameof(inputNames)} ({inputNames.Count}) must match that of {nameof(inputValues)} ({inputValues.Count})." ) ;
}
2023-08-08 05:19:38 +00:00
var inputNamesArray = LookupUtf8Names ( inputNames , n = > n , LookupInputMetadata ) ;
2023-06-29 15:59:23 +00:00
var inputHandlesArray = inputValues . Select ( v = > v . Handle ) . ToArray ( ) ;
var outputNamesArray = LookupUtf8Names ( outputNames , n = > n , LookupOutputMetadata ) ;
var disposableHandles = RunImpl ( runOptions , inputNamesArray , inputHandlesArray , outputNamesArray ) ;
try
{
return CreateDisposableResult ( disposableHandles ) ;
}
finally
{
disposableHandles . Dispose ( ) ;
}
}
/// <summary>
/// This API takes inputs as a dictionary of input names paired with input OrtValues
///
/// It returns a disposable collection of OrtValues for outputs that were designated by outputNames
/// </summary>
/// <param name="runOptions"></param>
/// <param name="inputs">Dictionary of name/value pairs</param>
/// <param name="outputNames">requested outputs. To request all outputs, use OutputNames property of this sessions</param>
/// <returns>A disposable collection of outputs</returns>
public IDisposableReadOnlyCollection < OrtValue > Run ( RunOptions runOptions , IReadOnlyDictionary < string , OrtValue > inputs ,
IReadOnlyCollection < string > outputNames )
{
IntPtr [ ] inputNamesArray = new IntPtr [ inputs . Count ] ;
IntPtr [ ] inputHandlesArray = new IntPtr [ inputs . Count ] ;
int count = 0 ;
2023-08-08 05:19:38 +00:00
foreach ( var input in inputs )
2023-06-29 15:59:23 +00:00
{
inputNamesArray [ count ] = LookupInputMetadata ( input . Key ) . ZeroTerminatedName ;
inputHandlesArray [ count ] = input . Value . Handle ;
+ + count ;
}
var outputNamesArray = LookupUtf8Names ( outputNames , n = > n , LookupOutputMetadata ) ;
var disposableHandles = RunImpl ( runOptions , inputNamesArray , inputHandlesArray , outputNamesArray ) ;
try
{
return CreateDisposableResult ( disposableHandles ) ;
}
finally
{
disposableHandles . Dispose ( ) ;
}
}
private static IDisposableReadOnlyCollection < OrtValue > CreateDisposableResult ( DisposableOrtValueHandleArray disposableHandles )
{
var outputValues = new DisposableList < OrtValue > ( disposableHandles . Span . Length ) ;
try
{
for ( int i = 0 ; i < disposableHandles . Span . Length ; i + + )
{
outputValues . Add ( new OrtValue ( disposableHandles . Span [ i ] ) ) ;
disposableHandles . Span [ i ] = IntPtr . Zero ;
}
return outputValues ;
}
catch ( Exception )
{
outputValues . Dispose ( ) ;
throw ;
}
}
/// <summary>
/// The API takes collections of inputNames/inputValues and collections of outputNames/outputValues.
/// The sizes of the corresponding collections must match.
///
/// The output OrtValues are pre-allocated and the API will fill the data into the OrtValues.
/// These MUST be tensors. The API does not support non-tensor types for output values.
///
/// The API is useful when the output values are tensors and their shapes are known, and you
/// prefer the output to go to the pre-allocated memory. In such a case, you create
/// output OrtValues over those pre-allocated buffers and pass them to the API.
/// </summary>
/// <param name="runOptions">runOptions, if null the defaults are used</param>
/// <param name="inputNames">collection of input names.</param>
/// <param name="inputValues">collection of input OrtValues. Must match the order and the number of input names.</param>
/// <param name="outputNames">Requested output names.</param>
/// <param name="outputValues">Pre-allocated output values.
/// The order and the number must match the specified output names. Shapes must match actual output values.</param>
/// <exception cref="ArgumentException"></exception>
public void Run ( RunOptions runOptions , IReadOnlyCollection < string > inputNames , IReadOnlyCollection < OrtValue > inputValues ,
IReadOnlyCollection < string > outputNames , IReadOnlyCollection < OrtValue > outputValues )
{
if ( inputNames . Count ! = inputValues . Count )
{
throw new ArgumentException (
$"Length of {nameof(inputNames)} ({inputNames.Count}) must match that of {nameof(inputValues)} ({inputValues.Count})." ) ;
}
if ( outputNames . Count ! = outputValues . Count )
{
throw new ArgumentException (
$"Length of {nameof(outputNames)} ({outputNames.Count}) must match that of {nameof(outputValues)} ({outputValues.Count})." ) ;
}
if ( runOptions is null )
{
runOptions = _builtInRunOptions ;
}
var inputNamesArray = LookupUtf8Names ( inputNames , n = > n , LookupInputMetadata ) ;
var inputHandlesArray = inputValues . Select ( v = > v . Handle ) . ToArray ( ) ;
var outputNamesArray = LookupUtf8Names ( outputNames , n = > n , LookupOutputMetadata ) ;
var outputHandlesArray = outputValues . Select ( v = > v . Handle ) . ToArray ( ) ;
NativeApiStatus . VerifySuccess ( NativeMethods . OrtRun (
_nativeHandle ,
runOptions . Handle ,
inputNamesArray ,
inputHandlesArray ,
( UIntPtr ) inputNames . Count ,
outputNamesArray ,
( UIntPtr ) outputNames . Count ,
outputHandlesArray /* pointers to Pre-allocated OrtValue instances */
) ) ;
2020-08-10 20:33:49 +00:00
}
/// <summary>
/// Create OrtIoBinding instance to bind pre-allocated buffers
/// to input/output
/// </summary>
2020-11-24 22:10:14 +00:00
/// <returns>A new instance of OrtIoBinding</returns>
2020-08-10 20:33:49 +00:00
public OrtIoBinding CreateIoBinding ( )
{
return new OrtIoBinding ( this ) ;
}
2020-04-08 18:57:40 +00:00
2020-08-10 20:33:49 +00:00
/// <summary>
/// This method runs inference on the OrtIoBinding instance
/// The method does not return anything. This is a lightweight version of
/// RunWithBindingAndNames(). When you bind pre-allocated buffers to the output values
/// you may not want to fetch the outputs since you already have access to them so you can spare
/// the expense of fetching them and pairing with names.
/// You can still fetch the outputs by calling OrtIOBinding.GetOutputValues()
/// </summary>
2020-11-24 22:10:14 +00:00
/// <param name="runOptions">runOptions</param>
/// <param name="ioBinding">ioBinding instance to use</param>
2020-08-10 20:33:49 +00:00
public void RunWithBinding ( RunOptions runOptions , OrtIoBinding ioBinding )
{
NativeApiStatus . VerifySuccess ( NativeMethods . OrtRunWithBinding ( Handle , runOptions . Handle , ioBinding . Handle ) ) ;
}
2020-04-08 18:57:40 +00:00
2023-06-29 15:59:23 +00:00
/// <summary>
/// This method runs inference on the OrtIoBinding instance. It returns a collection of OrtValues.
/// This method is useful when it is impossible to bind outputs to pre-allocated buffers, because
/// the output shape is not known in advance. In this case, the OrtValues returned by this method
/// are allocated and owned by ORT. The caller is responsible for disposing the collection.
/// </summary>
/// <param name="runOptions">RunOptions</param>
/// <param name="ioBinding">Binding instance</param>
/// <returns>A disposable collection of OrtValues</returns>
public IDisposableReadOnlyCollection < OrtValue > RunWithBoundResults ( RunOptions runOptions , OrtIoBinding ioBinding )
{
NativeApiStatus . VerifySuccess ( NativeMethods . OrtRunWithBinding ( Handle , runOptions . Handle , ioBinding . Handle ) ) ;
return ioBinding . GetOutputValues ( ) ;
}
2020-08-10 20:33:49 +00:00
/// <summary>
/// This method return a collection of DisposableNamedOnnxValue as in other interfaces
/// Query names from OrtIoBinding object and pair then with the array of OrtValues returned
2023-06-29 15:59:23 +00:00
/// from OrtIoBinding.GetOutputValues().
///
/// This API will be deprecated in favor of the API that returns a collection of OrtValues.
2020-08-10 20:33:49 +00:00
///
/// </summary>
/// <param name="runOptions">RunOptions</param>
/// <param name="ioBinding">OrtIoBinding instance with bindings</param>
/// <param name="names">optional parameter. If you already know the names of the outputs you can save a native
/// call to retrieve output names. They will be paired with the returned OrtValues and combined into DisposbleNamedOnnxValues.
/// Otherwise, the method will retrieve output names from the OrtIoBinding instance.
/// It is an error if you supply a different number of names than the returned outputs</param>
2020-11-20 22:03:55 +00:00
/// <returns>A disposable collection of DisposableNamedOnnxValue that encapsulate output OrtValues</returns>
2020-08-10 20:33:49 +00:00
public IDisposableReadOnlyCollection < DisposableNamedOnnxValue > RunWithBindingAndNames ( RunOptions runOptions , OrtIoBinding ioBinding , string [ ] names = null )
{
2023-06-29 15:59:23 +00:00
string [ ] outputNames = names ;
if ( outputNames = = null | | names . Length = = 0 )
2020-08-10 20:33:49 +00:00
{
2023-06-29 15:59:23 +00:00
outputNames = ioBinding . GetOutputNames ( ) ;
}
2020-08-10 20:33:49 +00:00
2023-06-29 15:59:23 +00:00
NativeApiStatus . VerifySuccess ( NativeMethods . OrtRunWithBinding ( Handle , runOptions . Handle , ioBinding . Handle ) ) ;
var ortValues = ioBinding . GetOutputOrtValues ( ) ;
var dispValues = new DisposableArray < OrtValue > ( ortValues ) ;
try
{
var result = new DisposableList < DisposableNamedOnnxValue > ( ortValues . Length ) ;
2020-08-10 20:33:49 +00:00
try
{
for ( int i = 0 ; i < outputNames . Length ; + + i )
2020-04-08 18:57:40 +00:00
{
2023-06-29 15:59:23 +00:00
result . Add ( DisposableNamedOnnxValue . CreateFromOrtValue ( outputNames [ i ] , ref ortValues [ i ] ) ) ;
2020-04-08 18:57:40 +00:00
}
2023-06-29 15:59:23 +00:00
return result ;
2021-05-15 03:44:42 +00:00
}
2021-10-27 10:07:07 +00:00
catch ( Exception )
2020-08-10 20:33:49 +00:00
{
result . Dispose ( ) ;
2021-10-27 10:07:07 +00:00
throw ;
2020-04-08 18:57:40 +00:00
}
2023-06-29 15:59:23 +00:00
}
finally
{
// On success ortValues would contain nulls that will be
// ignored. On failure, ortValues would contain at least
// some valid OrtValue instances that need to be disposed.
dispValues . Dispose ( ) ;
2020-04-08 18:57:40 +00:00
}
2018-11-20 00:48:22 +00:00
}
2020-05-19 02:47:52 +00:00
/// <summary>
2020-11-20 22:03:55 +00:00
/// Ends profiling for the session.
/// </summary>
/// <returns> Returns the profile file name.</returns>
2020-05-19 02:47:52 +00:00
public string EndProfiling ( )
{
2020-08-10 20:33:49 +00:00
var allocator = OrtAllocator . DefaultInstance ;
NativeApiStatus . VerifySuccess ( NativeMethods . OrtSessionEndProfiling ( _nativeHandle ,
allocator . Pointer ,
2023-04-20 22:12:51 +00:00
out IntPtr nameHandle ) ) ;
return NativeOnnxValueHelper . StringFromNativeUtf8 ( nameHandle , allocator ) ;
2020-08-10 20:33:49 +00:00
}
2020-05-19 02:47:52 +00:00
2020-08-10 20:33:49 +00:00
// Delegate for string extraction from an arbitrary input/output object
private delegate string NameExtractor < in TInput > ( TInput input ) ;
2020-05-19 02:47:52 +00:00
2023-04-11 16:41:59 +00:00
// delegate to fetch input/output OrtValue
2023-06-29 15:59:23 +00:00
private delegate IntPtr OrtValueHandleExtractor ( NamedOnnxValue value , NodeMetadata metadata , out IDisposable memOwner ) ;
2023-04-11 16:41:59 +00:00
// Delegate to lookup metadata for input/initializers/output
private delegate NodeMetadata MetadataLookup ( string nodeName ) ;
/// <summary>
/// Checks if the name is a known input or overridable initializer name
/// and if so, returns metadata for it.
/// metadata
/// </summary>
/// <param name="nodeName"></param>
/// <returns>NodeMetadata for the nodeName</returns>
/// <exception cref="OnnxRuntimeException"></exception>
private NodeMetadata LookupInputMetadata ( string nodeName )
{
2023-06-29 15:59:23 +00:00
if ( ! _inputMetadata . TryGetValue ( nodeName , out NodeMetadata meta ) & &
2023-04-11 16:41:59 +00:00
! _overridableInitializerMetadata . TryGetValue ( nodeName , out meta ) )
{
throw new OnnxRuntimeException ( ErrorCode . InvalidArgument , $"Input name: '{nodeName}' is not in the metadata" ) ;
}
return meta ;
}
/// <summary>
/// Checks if the nodeName is a known output name and if so returns metadata for it.
/// </summary>
/// <param name="nodeName"></param>
/// <returns></returns>
/// <exception cref="OnnxRuntimeException"></exception>
private NodeMetadata LookupOutputMetadata ( string nodeName )
{
2023-06-29 15:59:23 +00:00
if ( ! _outputMetadata . TryGetValue ( nodeName , out NodeMetadata meta ) )
2023-04-11 16:41:59 +00:00
{
throw new OnnxRuntimeException ( ErrorCode . InvalidArgument , $"Output name: '{nodeName}' is not in the metadata" ) ;
}
return meta ;
}
/// <summary>
/// Fetches/creates OrtValue for the content of the input
/// </summary>
/// <param name="input"></param>
/// <param name="metadata"></param>
/// <param name="memOwner"></param>
/// <returns></returns>
2023-06-29 15:59:23 +00:00
private static IntPtr ExtractOrtValueHandleForInput ( NamedOnnxValue input , NodeMetadata metadata , out IDisposable memOwner )
2023-04-11 16:41:59 +00:00
{
2023-06-29 15:59:23 +00:00
return input . InputToOrtValueHandle ( metadata , out memOwner ) ;
2023-04-11 16:41:59 +00:00
}
/// <summary>
/// Fetches/Creates OrtValue for output
/// </summary>
/// <param name="output"></param>
/// <param name="metadata"></param>
/// <param name="memOwner"></param>
/// <returns>May return null if the onnx value type does not support pre-creation of output OrtValues</returns>
2023-06-29 15:59:23 +00:00
private static IntPtr ExtractOrtValueHandleForOutput ( NamedOnnxValue output , NodeMetadata metadata , out IDisposable memOwner )
2023-04-11 16:41:59 +00:00
{
2023-06-29 15:59:23 +00:00
return output . OutputToOrtValueHandle ( metadata , out memOwner ) ;
2023-04-11 16:41:59 +00:00
}
2020-08-10 20:33:49 +00:00
/// <summary>
/// Run helper
/// </summary>
2023-04-11 16:41:59 +00:00
/// <param name="values">names to convert to zero terminated utf8 and pin</param>
/// <param name="nameExtractor">extractor functor that helps extracting names from inputs</param>
/// <param name="metaDict">inputs/outputs metadata</param>
2020-08-10 20:33:49 +00:00
/// <returns></returns>
2023-06-29 15:59:23 +00:00
private static IntPtr [ ] LookupUtf8Names < T > ( IReadOnlyCollection < T > values , NameExtractor < T > nameExtractor ,
2023-04-20 22:12:51 +00:00
MetadataLookup metaLookup )
2020-08-10 20:33:49 +00:00
{
2023-04-11 16:41:59 +00:00
var result = new IntPtr [ values . Count ] ;
for ( int i = 0 ; i < values . Count ; + + i )
2020-05-19 02:47:52 +00:00
{
2023-04-11 16:41:59 +00:00
var name = nameExtractor ( values . ElementAt ( i ) ) ;
NodeMetadata meta = metaLookup ( name ) ;
2023-04-20 22:12:51 +00:00
result [ i ] = meta . ZeroTerminatedName ;
2020-05-19 02:47:52 +00:00
}
2020-08-10 20:33:49 +00:00
return result ;
}
/// <summary>
/// This function obtains ortValues for NamedOnnxValue.
2023-04-11 16:41:59 +00:00
/// The problem with NamedOnnxValue is that it is not disposable and can not contain any disposable items.
/// so calling InputToOrtValue creates a new instance of OrtValue that needs to be disposed.
2020-08-10 20:33:49 +00:00
/// The deriving object DisposableNamedValue actually contains and owns OrtValue and it returns
/// it.
/// </summary>
2023-04-11 16:41:59 +00:00
/// <param name="values">a collection of NamedOnnxValues</param>
/// <param name="metaLookup">Metadata lookup function (input/initializers/output)</param>
2020-08-10 20:33:49 +00:00
/// <returns></returns>
2023-06-29 15:59:23 +00:00
private static IntPtr [ ] GetOrtValuesHandles ( IReadOnlyCollection < NamedOnnxValue > values , MetadataLookup metaLookup ,
OrtValueHandleExtractor ortValueExtractor , out DisposableArray < IDisposable > disposer )
{
IDisposable [ ] memHolders = new IDisposable [ values . Count ] ;
var disp = new DisposableArray < IDisposable > ( memHolders ) ;
try
{
IntPtr [ ] result = new IntPtr [ values . Count ] ;
for ( int valueIndex = 0 ; valueIndex < values . Count ; + + valueIndex )
2020-08-10 20:33:49 +00:00
{
2023-06-29 15:59:23 +00:00
var value = values . ElementAt ( valueIndex ) ;
var meta = metaLookup ( value . Name ) ;
result [ valueIndex ] = ortValueExtractor ( value , meta , out IDisposable memHolder ) ;
if ( memHolder ! = null )
{
memHolders [ valueIndex ] = memHolder ;
}
2020-08-10 20:33:49 +00:00
}
2023-06-29 15:59:23 +00:00
disposer = disp ;
return result ;
}
catch ( Exception )
{
disp . Dispose ( ) ;
throw ;
2020-08-10 20:33:49 +00:00
}
}
2023-06-29 15:59:23 +00:00
private static IntPtr [ ] GetOrtValuesHandles ( IReadOnlyCollection < FixedBufferOnnxValue > values , bool input )
2020-08-10 20:33:49 +00:00
{
var valuesArray = new IntPtr [ values . Count ] ;
for ( int index = 0 ; index < values . Count ; + + index )
2020-05-19 02:47:52 +00:00
{
2020-08-10 20:33:49 +00:00
var v = values . ElementAt ( index ) ;
if ( ! input & & v . ElementType = = Tensors . TensorElementType . String )
2020-05-19 02:47:52 +00:00
{
2020-08-10 20:33:49 +00:00
throw new NotSupportedException ( "Using string type FixedBufferOnnxValue in outputs is not supported." ) ;
2020-05-19 02:47:52 +00:00
}
2020-08-10 20:33:49 +00:00
valuesArray [ index ] = v . Value . Handle ;
2020-05-19 02:47:52 +00:00
}
2020-08-10 20:33:49 +00:00
return valuesArray ;
}
2020-05-19 02:47:52 +00:00
2023-06-29 15:59:23 +00:00
private DisposableOrtValueHandleArray RunImpl ( RunOptions options , IntPtr [ ] inputNames , IntPtr [ ] inputValues , IntPtr [ ] outputNames )
2020-08-10 20:33:49 +00:00
{
IntPtr [ ] outputValuesArray = new IntPtr [ outputNames . Length ] ;
NativeApiStatus . VerifySuccess ( NativeMethods . OrtRun (
_nativeHandle ,
options . Handle ,
inputNames ,
inputValues ,
( UIntPtr ) inputNames . Length ,
outputNames ,
( UIntPtr ) outputNames . Length ,
outputValuesArray /* Empty array is passed in to receive output OrtValue pointers */
) ) ;
2023-06-29 15:59:23 +00:00
return new DisposableOrtValueHandleArray ( outputValuesArray ) ;
2020-08-10 20:33:49 +00:00
}
2023-06-29 15:59:23 +00:00
private static IDisposableReadOnlyCollection < DisposableNamedOnnxValue > CreateDisposableResult ( Span < IntPtr > valueHandles ,
2020-08-10 20:33:49 +00:00
IReadOnlyCollection < string > outputNames )
{
2023-06-29 15:59:23 +00:00
Debug . Assert ( valueHandles . Length = = outputNames . Count , "Handles and names sizes must match" ) ;
var result = new DisposableList < DisposableNamedOnnxValue > ( valueHandles . Length ) ;
2020-08-10 20:33:49 +00:00
try
{
2023-06-29 15:59:23 +00:00
for ( int i = 0 ; i < valueHandles . Length ; i + + )
2020-08-10 20:33:49 +00:00
{
2023-06-29 15:59:23 +00:00
var ortValue = new OrtValue ( valueHandles [ i ] ) ;
result . Add ( DisposableNamedOnnxValue . CreateFromOrtValue ( outputNames . ElementAt ( i ) , ref ortValue ) ) ;
valueHandles [ i ] = IntPtr . Zero ; // Prevent double disposal
2020-08-10 20:33:49 +00:00
}
}
2021-10-27 10:07:07 +00:00
catch ( OnnxRuntimeException )
2020-08-10 20:33:49 +00:00
{
result . Dispose ( ) ;
2021-10-27 10:07:07 +00:00
throw ;
2020-08-10 20:33:49 +00:00
}
return result ;
2020-05-19 02:47:52 +00:00
}
2020-04-08 18:57:40 +00:00
2020-11-20 22:03:55 +00:00
/// <summary>
/// This property queries model metadata, constructs
/// an instance of ModelMetadata and caches it
/// </summary>
/// <returns>Instance of ModelMetdata</returns>
2020-08-25 18:13:49 +00:00
public ModelMetadata ModelMetadata
2018-11-26 06:46:21 +00:00
{
get
{
2020-08-25 18:13:49 +00:00
if ( _modelMetadata ! = null )
{
return _modelMetadata ;
}
_modelMetadata = new ModelMetadata ( this ) ;
return _modelMetadata ;
2018-11-26 06:46:21 +00:00
}
}
2018-11-20 00:48:22 +00:00
2020-10-14 12:32:43 +00:00
/// <summary>
/// Return the nanoseconds of profiling's start time
/// On some platforms, this timer may not be as precise as nanoseconds
/// For instance, on Windows and MacOS, the precision will be ~100ns
/// </summary>
public ulong ProfilingStartTimeNs
{
2021-05-15 03:44:42 +00:00
get
{
return _profilingStartTimeNs ;
}
}
2020-10-14 12:32:43 +00:00
2023-08-08 05:19:38 +00:00
private static void OrtCallback ( IntPtr userData , IntPtr [ ] ouputs , uint numOutputs , IntPtr status )
{
var hostHdl = GCHandle . FromIntPtr ( userData ) ;
CallbackHost host = ( CallbackHost ) hostHdl . Target ;
try
{
host . callback ( host . outputValues , status ) ;
}
finally
{
hostHdl . Free ( ) ;
}
}
2023-08-14 13:04:37 +00:00
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
2023-08-08 05:19:38 +00:00
private delegate void OrtCallbackDelegate ( IntPtr userData , IntPtr [ ] outputs , uint numOutputs , IntPtr status ) ;
private static OrtCallbackDelegate ortCallback = new OrtCallbackDelegate ( OrtCallback ) ;
private delegate void UserCallbackDelegate ( IReadOnlyCollection < OrtValue > outputs , IntPtr status ) ;
private class CallbackHost
{
public IReadOnlyCollection < string > inputNames { get ; }
public IReadOnlyCollection < OrtValue > inputValues { get ; }
public IReadOnlyCollection < string > outputNames { get ; }
public IReadOnlyCollection < OrtValue > outputValues { get ; }
public UserCallbackDelegate callback { get ; }
public IntPtr [ ] rawInputNames { get ; }
public IntPtr [ ] rawInputValues { get ; }
public IntPtr [ ] rawOutputNames { get ; }
public IntPtr [ ] rawOutputValues { get ; }
public CallbackHost ( InferenceSession session ,
IReadOnlyCollection < string > cbInputNames ,
IReadOnlyCollection < OrtValue > cbinputValues ,
IReadOnlyCollection < string > cbOutputNames ,
IReadOnlyCollection < OrtValue > cbOutputValues ,
UserCallbackDelegate userCallback )
{
inputNames = cbInputNames ;
inputValues = cbinputValues ;
outputNames = cbOutputNames ;
outputValues = cbOutputValues ;
callback = userCallback ;
rawInputNames = LookupUtf8Names ( inputNames , n = > n , session . LookupInputMetadata ) ;
rawInputValues = inputValues . Select ( v = > v . Handle ) . ToArray ( ) ;
rawOutputNames = LookupUtf8Names ( outputNames , n = > n , session . LookupOutputMetadata ) ;
rawOutputValues = outputValues . Select ( v = > v . Handle ) . ToArray ( ) ;
}
}
private void RunAsyncInternal ( RunOptions options ,
IReadOnlyCollection < string > inputNames ,
IReadOnlyCollection < OrtValue > inputValues ,
IReadOnlyCollection < string > outputNames ,
IReadOnlyCollection < OrtValue > outputValues ,
UserCallbackDelegate callback )
{
CallbackHost host = new CallbackHost ( this , inputNames , inputValues , outputNames , outputValues , callback ) ;
var host_hdl = GCHandle . Alloc ( host , GCHandleType . Normal ) ;
try
{
NativeApiStatus . VerifySuccess ( NativeMethods . OrtRunAsync (
_nativeHandle ,
options = = null ? ( IntPtr ) null : options . Handle ,
host . rawInputNames ,
host . rawInputValues ,
( UIntPtr ) host . rawInputNames . Length ,
host . rawOutputNames ,
( UIntPtr ) host . rawOutputNames . Length ,
host . rawOutputValues ,
Marshal . GetFunctionPointerForDelegate ( ortCallback ) ,
GCHandle . ToIntPtr ( host_hdl )
) ) ;
}
catch ( OnnxRuntimeException )
{
host_hdl . Free ( ) ;
throw ;
}
}
/// <summary>
/// Run inference asynchronous in a thread of intra-op thread pool
/// </summary>
/// <param name="options">run option, can be null</param>
/// <param name="inputNames">name of inputs</param>
/// <param name="inputValues">input ort values</param>
/// <param name="outputNames">name of outputs</param>
/// <param name="outputValues">output of ort values</param>
/// <returns>task to be awaited</returns>
/// <exception cref="OnnxRuntimeException"></exception>
public async Task < IReadOnlyCollection < OrtValue > > RunAsync ( RunOptions options ,
IReadOnlyCollection < string > inputNames ,
IReadOnlyCollection < OrtValue > inputValues ,
IReadOnlyCollection < string > outputNames ,
IReadOnlyCollection < OrtValue > outputValues )
{
var promise = new TaskCompletionSource < IReadOnlyCollection < OrtValue > > ( ) ;
RunAsyncInternal ( options ,
inputNames ,
inputValues ,
outputNames ,
outputValues ,
( IReadOnlyCollection < OrtValue > outputs , IntPtr status ) = >
{
try
{
NativeApiStatus . VerifySuccess ( status ) ;
promise . SetResult ( outputs ) ;
}
catch ( Exception ex )
{
promise . SetException ( ex ) ;
}
} ) ;
return await promise . Task ;
}
2018-11-20 00:48:22 +00:00
#endregion
#region private methods
2019-08-14 19:02:02 +00:00
2021-05-15 03:44:42 +00:00
private void Init ( string modelPath , SessionOptions options ,
PrePackedWeightsContainer prepackedWeightsContainer = null )
2019-08-14 19:02:02 +00:00
{
2023-04-19 04:49:55 +00:00
var envHandle = OrtEnv . Instance ( ) . Handle ;
2023-04-11 16:41:59 +00:00
IntPtr session ;
2021-05-15 03:44:42 +00:00
if ( prepackedWeightsContainer = = null )
{
2023-04-11 16:41:59 +00:00
NativeApiStatus . VerifySuccess ( NativeMethods . OrtCreateSession ( envHandle , NativeOnnxValueHelper . GetPlatformSerializedString ( modelPath ) ,
options . Handle , out session ) ) ;
2021-05-15 03:44:42 +00:00
}
else
{
NativeApiStatus . VerifySuccess ( NativeMethods . OrtCreateSessionWithPrepackedWeightsContainer (
2022-09-02 20:13:48 +00:00
envHandle , NativeOnnxValueHelper . GetPlatformSerializedString ( modelPath ) ,
2021-05-15 03:44:42 +00:00
options . Handle , prepackedWeightsContainer . Pointer , out session ) ) ;
}
2019-08-14 19:02:02 +00:00
2023-04-20 22:12:51 +00:00
InitWithSessionHandle ( session ) ;
2019-09-24 18:59:04 +00:00
}
2021-05-15 03:44:42 +00:00
private void Init ( byte [ ] modelData , SessionOptions options ,
PrePackedWeightsContainer prepackedWeightsContainer = null )
2019-09-24 18:59:04 +00:00
{
2023-04-19 04:49:55 +00:00
var envHandle = OrtEnv . Instance ( ) . Handle ;
2023-04-11 16:41:59 +00:00
IntPtr session ;
2021-05-15 03:44:42 +00:00
if ( prepackedWeightsContainer = = null )
{
NativeApiStatus . VerifySuccess ( NativeMethods . OrtCreateSessionFromArray ( envHandle , modelData , ( UIntPtr ) modelData . Length , options . Handle , out session ) ) ;
}
else
{
NativeApiStatus . VerifySuccess ( NativeMethods . OrtCreateSessionFromArrayWithPrepackedWeightsContainer (
envHandle , modelData , ( UIntPtr ) modelData . Length , options . Handle , prepackedWeightsContainer . Pointer ,
out session ) ) ;
}
2019-09-24 18:59:04 +00:00
2023-04-20 22:12:51 +00:00
InitWithSessionHandle ( session ) ;
2019-09-24 18:59:04 +00:00
}
/// <summary>
/// Initializes the session object with a native session handle
/// </summary>
2020-11-11 18:21:22 +00:00
/// <param name="session">Value of a native session object</param>
2019-09-24 18:59:04 +00:00
/// <param name="options">Session options</param>
2023-04-20 22:12:51 +00:00
private void InitWithSessionHandle ( IntPtr session )
2019-09-24 18:59:04 +00:00
{
_nativeHandle = session ;
2019-08-14 19:02:02 +00:00
try
{
// Initialize input/output metadata
// get input count
2023-04-11 16:41:59 +00:00
NativeApiStatus . VerifySuccess ( NativeMethods . OrtSessionGetInputCount ( _nativeHandle , out UIntPtr inputCount ) ) ;
2023-04-20 22:12:51 +00:00
// get output count
NativeApiStatus . VerifySuccess ( NativeMethods . OrtSessionGetOutputCount ( _nativeHandle , out UIntPtr outputCount ) ) ;
// get overridable initializer count
NativeApiStatus . VerifySuccess ( NativeMethods . OrtSessionGetOverridableInitializerCount ( _nativeHandle ,
out UIntPtr initializerCount ) ) ;
int totalNameCount = ( int ) inputCount + ( int ) outputCount + ( int ) initializerCount ;
_namesMemoryPtrs = new List < IntPtr > ( totalNameCount ) ;
2019-08-14 19:02:02 +00:00
2019-10-04 23:38:00 +00:00
// get all the input names and metadata
2023-04-11 16:41:59 +00:00
_inputMetadata = new Dictionary < string , NodeMetadata > ( ( int ) inputCount ) ;
_inputNames = new List < string > ( ( int ) inputCount ) ;
2019-08-14 19:02:02 +00:00
for ( ulong i = 0 ; i < ( ulong ) inputCount ; i + + )
{
2023-04-11 16:41:59 +00:00
var inputMeta = GetInputMetadata ( i ) ;
2023-04-20 22:12:51 +00:00
var iname = GetInputName ( i , out IntPtr utf8 ) ;
_namesMemoryPtrs . Add ( utf8 ) ;
inputMeta . ZeroTerminatedName = utf8 ;
2023-04-11 16:41:59 +00:00
_inputNames . Add ( iname ) ;
_inputMetadata [ iname ] = inputMeta ;
2019-08-14 19:02:02 +00:00
}
2019-10-04 23:38:00 +00:00
// get all the output names and metadata
2023-04-11 16:41:59 +00:00
_outputMetadata = new Dictionary < string , NodeMetadata > ( ( int ) outputCount ) ;
_outputNames = new List < string > ( ( int ) outputCount ) ;
2019-08-14 19:02:02 +00:00
for ( ulong i = 0 ; i < ( ulong ) outputCount ; i + + )
{
2023-04-11 16:41:59 +00:00
var outputMeta = GetOutputMetadata ( i ) ;
2023-04-20 22:12:51 +00:00
var oname = GetOutputName ( i , out IntPtr utf8 ) ;
_namesMemoryPtrs . Add ( utf8 ) ;
outputMeta . ZeroTerminatedName = utf8 ;
2023-04-11 16:41:59 +00:00
_outputNames . Add ( oname ) ;
_outputMetadata [ oname ] = outputMeta ;
2019-08-14 19:02:02 +00:00
}
2023-04-20 22:12:51 +00:00
_overridableInitializerMetadata = new Dictionary < string , NodeMetadata > ( ( int ) initializerCount ) ;
2019-10-04 23:38:00 +00:00
// get all the overridable initializer names and metadata
2023-04-20 22:12:51 +00:00
for ( ulong i = 0 ; i < ( ulong ) initializerCount ; i + + )
2019-10-04 23:38:00 +00:00
{
2023-04-11 16:41:59 +00:00
var meta = GetOverridableInitializerMetadata ( i ) ;
2023-04-20 22:12:51 +00:00
var iname = GetOverridableInitializerName ( i , out IntPtr utf8 ) ;
_namesMemoryPtrs . Add ( utf8 ) ;
meta . ZeroTerminatedName = utf8 ;
2023-04-11 16:41:59 +00:00
_overridableInitializerMetadata [ iname ] = meta ;
2019-10-04 23:38:00 +00:00
}
2020-10-14 12:32:43 +00:00
// set profiling's start time
NativeApiStatus . VerifySuccess ( NativeMethods . OrtSessionGetProfilingStartTimeNs ( _nativeHandle ,
2023-04-11 16:41:59 +00:00
out UIntPtr startTime ) ) ;
2021-05-15 03:44:42 +00:00
_profilingStartTimeNs = ( ulong ) startTime ;
2019-08-14 19:02:02 +00:00
}
2023-04-11 16:41:59 +00:00
catch ( Exception )
2019-08-14 19:02:02 +00:00
{
2023-04-20 22:12:51 +00:00
DisposeImpl ( true ) ;
2021-10-27 10:07:07 +00:00
throw ;
2019-08-14 19:02:02 +00:00
}
2020-10-14 12:32:43 +00:00
_builtInRunOptions = new RunOptions ( ) ; // create a default built-in run option, and avoid creating a new one every run() call
2019-08-14 19:02:02 +00:00
}
2023-04-20 22:12:51 +00:00
private string GetOutputName ( ulong index , out IntPtr utf8 )
2018-11-20 00:48:22 +00:00
{
2020-08-10 20:33:49 +00:00
var allocator = OrtAllocator . DefaultInstance ;
NativeApiStatus . VerifySuccess ( NativeMethods . OrtSessionGetOutputName (
_nativeHandle ,
( UIntPtr ) index ,
allocator . Pointer ,
2023-04-11 16:41:59 +00:00
out IntPtr nameHandle ) ) ;
2018-11-20 00:48:22 +00:00
2023-04-20 22:12:51 +00:00
NativeOnnxValueHelper . StringAndUtf8FromNative ( allocator , nameHandle , out string str , out utf8 ) ;
2018-11-20 00:48:22 +00:00
return str ;
}
2023-04-20 22:12:51 +00:00
private string GetInputName ( ulong index , out IntPtr utf8 )
2018-11-20 00:48:22 +00:00
{
2020-08-10 20:33:49 +00:00
var allocator = OrtAllocator . DefaultInstance ;
NativeApiStatus . VerifySuccess ( NativeMethods . OrtSessionGetInputName (
_nativeHandle ,
( UIntPtr ) index ,
allocator . Pointer ,
2023-04-11 16:41:59 +00:00
out IntPtr nameHandle ) ) ;
2018-11-20 00:48:22 +00:00
2023-04-20 22:12:51 +00:00
NativeOnnxValueHelper . StringAndUtf8FromNative ( allocator , nameHandle , out string str , out utf8 ) ;
2018-11-20 00:48:22 +00:00
return str ;
}
2023-04-20 22:12:51 +00:00
private string GetOverridableInitializerName ( ulong index , out IntPtr utf8 )
2019-10-04 23:38:00 +00:00
{
2020-08-10 20:33:49 +00:00
var allocator = OrtAllocator . DefaultInstance ;
NativeApiStatus . VerifySuccess ( NativeMethods . OrtSessionGetOverridableInitializerName (
_nativeHandle ,
( UIntPtr ) index ,
allocator . Pointer ,
2023-04-11 16:41:59 +00:00
out IntPtr nameHandle ) ) ;
2023-04-20 22:12:51 +00:00
NativeOnnxValueHelper . StringAndUtf8FromNative ( allocator , nameHandle , out string str , out utf8 ) ;
2019-10-04 23:38:00 +00:00
return str ;
}
2018-11-20 00:48:22 +00:00
2018-11-23 04:56:43 +00:00
private NodeMetadata GetInputMetadata ( ulong index )
{
2023-04-11 16:41:59 +00:00
NativeApiStatus . VerifySuccess ( NativeMethods . OrtSessionGetInputTypeInfo ( _nativeHandle , ( UIntPtr ) index , out IntPtr typeInfo ) ) ;
2018-11-23 04:56:43 +00:00
try
{
return GetMetadataFromTypeInfo ( typeInfo ) ;
}
finally
{
2020-08-10 20:33:49 +00:00
NativeMethods . OrtReleaseTypeInfo ( typeInfo ) ;
2018-11-23 04:56:43 +00:00
}
}
2018-11-20 00:48:22 +00:00
2018-11-23 04:56:43 +00:00
private NodeMetadata GetOutputMetadata ( ulong index )
{
2023-04-11 16:41:59 +00:00
NativeApiStatus . VerifySuccess ( NativeMethods . OrtSessionGetOutputTypeInfo ( _nativeHandle , ( UIntPtr ) index , out IntPtr typeInfo ) ) ;
2018-11-23 04:56:43 +00:00
try
{
return GetMetadataFromTypeInfo ( typeInfo ) ;
}
finally
{
2020-08-10 20:33:49 +00:00
NativeMethods . OrtReleaseTypeInfo ( typeInfo ) ;
2019-10-04 23:38:00 +00:00
}
}
private NodeMetadata GetOverridableInitializerMetadata ( ulong index )
{
2023-04-11 16:41:59 +00:00
NativeApiStatus . VerifySuccess ( NativeMethods . OrtSessionGetOverridableInitializerTypeInfo ( _nativeHandle , ( UIntPtr ) index , out IntPtr typeInfo ) ) ;
2019-10-04 23:38:00 +00:00
try
{
return GetMetadataFromTypeInfo ( typeInfo ) ;
}
finally
{
2020-08-10 20:33:49 +00:00
NativeMethods . OrtReleaseTypeInfo ( typeInfo ) ;
2018-11-23 04:56:43 +00:00
}
}
2019-03-06 00:00:40 +00:00
internal static NodeMetadata GetMetadataFromTypeInfo ( IntPtr typeInfo )
2018-11-23 04:56:43 +00:00
{
2019-06-11 01:36:04 +00:00
OnnxValueType valueType ;
{
2023-04-11 16:41:59 +00:00
NativeApiStatus . VerifySuccess ( NativeMethods . OrtGetOnnxTypeFromTypeInfo ( typeInfo , out IntPtr valType ) ) ;
2020-08-10 20:33:49 +00:00
valueType = ( OnnxValueType ) valType ;
2019-06-11 01:36:04 +00:00
}
2023-04-11 16:41:59 +00:00
switch ( valueType )
2019-03-12 17:11:14 +00:00
{
2023-04-11 16:41:59 +00:00
case OnnxValueType . ONNX_TYPE_TENSOR :
case OnnxValueType . ONNX_TYPE_SPARSETENSOR :
return GetTensorNodeMetadata ( valueType , typeInfo ) ;
case OnnxValueType . ONNX_TYPE_SEQUENCE :
return GetSequenceMetadataFromTypeInfo ( typeInfo ) ;
case OnnxValueType . ONNX_TYPE_MAP :
return GetMapMetadataFromTypeInfo ( typeInfo ) ;
case OnnxValueType . ONNX_TYPE_OPTIONAL :
return GetOptionalMetadataFromTypeInfo ( typeInfo ) ;
2019-03-12 17:11:14 +00:00
}
2023-04-11 16:41:59 +00:00
throw new OnnxRuntimeException ( ErrorCode . NotImplemented , $"Value type: '{valueType}' not supported in this code" ) ;
}
2019-03-06 00:00:40 +00:00
2023-04-11 16:41:59 +00:00
internal static NodeMetadata GetSequenceMetadataFromTypeInfo ( IntPtr typeInfo )
{
NativeApiStatus . VerifySuccess ( NativeMethods . OrtCastTypeInfoToSequenceTypeInfo ( typeInfo , out IntPtr sequenceTypeInfo ) ) ;
// Casts API are broken. Always return success, but may return null for the result.
if ( sequenceTypeInfo = = IntPtr . Zero )
{
throw new OnnxRuntimeException ( ErrorCode . Fail , "TypeInfo cast to SequenceTypeInfo failed. The object does not represent a sequence" ) ;
}
2018-11-23 04:56:43 +00:00
2023-04-11 16:41:59 +00:00
NativeApiStatus . VerifySuccess ( NativeMethods . OrtGetSequenceElementType ( sequenceTypeInfo , out IntPtr elementType ) ) ;
try
2019-06-11 01:36:04 +00:00
{
2023-04-11 16:41:59 +00:00
var elementMeta = GetMetadataFromTypeInfo ( elementType ) ;
var seqMeta = new SequenceMetadata ( elementMeta ) ;
return new NodeMetadata ( seqMeta ) ;
}
finally
{
NativeMethods . OrtReleaseTypeInfo ( elementType ) ;
}
}
internal static NodeMetadata GetMapMetadataFromTypeInfo ( IntPtr typeInfo )
{
NativeApiStatus . VerifySuccess ( NativeMethods . OrtCastTypeInfoToMapTypeInfo ( typeInfo , out IntPtr mapTypeInfo ) ) ;
// Casts API are broken. Always return success, but may return null for the result.
if ( mapTypeInfo = = IntPtr . Zero )
{
throw new OnnxRuntimeException ( ErrorCode . Fail , "TypeInfo cast to MapTypeInfo failed. The object does not represent a map" ) ;
}
NativeApiStatus . VerifySuccess ( NativeMethods . OrtGetMapKeyType ( mapTypeInfo , out IntPtr keyType ) ) ;
NativeApiStatus . VerifySuccess ( NativeMethods . OrtGetMapValueType ( mapTypeInfo , out IntPtr valueTypeInfo ) ) ;
try
{
var valueMetadata = GetMetadataFromTypeInfo ( valueTypeInfo ) ;
var mapMeta = new MapMetadata ( ( TensorElementType ) keyType , valueMetadata ) ;
return new NodeMetadata ( mapMeta ) ;
}
finally
{
NativeMethods . OrtReleaseTypeInfo ( valueTypeInfo ) ;
}
}
internal static NodeMetadata GetOptionalMetadataFromTypeInfo ( IntPtr typeInfo )
{
// This should not be destroyed
NativeApiStatus . VerifySuccess ( NativeMethods . OrtCastTypeInfoToOptionalTypeInfo ( typeInfo , out IntPtr optTypeInfo ) ) ;
// Casts API are broken. Always return success, but may return null for the result.
if ( optTypeInfo = = IntPtr . Zero )
{
throw new OnnxRuntimeException ( ErrorCode . Fail , "TypeInfo cast to OptionalTypeInfo failed. The object does not represent a optional value" ) ;
}
NativeApiStatus . VerifySuccess ( NativeMethods . OrtGetOptionalContainedTypeInfo ( optTypeInfo , out IntPtr elementTypeInfo ) ) ;
try
{
var elementMetadata = GetMetadataFromTypeInfo ( elementTypeInfo ) ;
var optMetadata = new OptionalMetadata ( elementMetadata ) ;
return new NodeMetadata ( optMetadata ) ;
}
finally
{
NativeMethods . OrtReleaseTypeInfo ( elementTypeInfo ) ;
2019-06-11 01:36:04 +00:00
}
2023-04-11 16:41:59 +00:00
}
2022-02-02 02:09:24 +00:00
2023-04-11 16:41:59 +00:00
internal static NodeMetadata GetTensorNodeMetadata ( OnnxValueType valueType , IntPtr typeInfo )
{
// Fetch tensor type and shape from the TypeInfo
NativeApiStatus . VerifySuccess ( NativeMethods . OrtCastTypeInfoToTensorInfo ( typeInfo , out IntPtr tensorInfo ) ) ; //(IntPtr)(int)(uint)
2023-04-20 22:12:51 +00:00
// Casts API are broken. Always return success, but may return null for the result.
2023-04-11 16:41:59 +00:00
if ( tensorInfo = = IntPtr . Zero )
2022-02-02 02:09:24 +00:00
{
2023-04-11 16:41:59 +00:00
throw new OnnxRuntimeException ( ErrorCode . Fail , "TypeInfo cast to TensorTypeInfo failed. The object does not represent a tensor" ) ;
2022-02-02 02:09:24 +00:00
}
2023-04-11 16:41:59 +00:00
TensorElementType type ;
{
NativeApiStatus . VerifySuccess ( NativeMethods . OrtGetTensorElementType ( tensorInfo , out IntPtr el_type ) ) ;
type = ( TensorElementType ) el_type ;
}
NativeApiStatus . VerifySuccess ( NativeMethods . OrtGetDimensionsCount ( tensorInfo , out UIntPtr numDimensions ) ) ;
2019-10-12 22:46:28 +00:00
2018-11-23 04:56:43 +00:00
long [ ] dimensions = new long [ ( int ) numDimensions ] ;
2019-10-12 22:46:28 +00:00
NativeApiStatus . VerifySuccess ( NativeMethods . OrtGetDimensions ( tensorInfo , dimensions , numDimensions ) ) ;
2018-11-23 04:56:43 +00:00
int [ ] intDimensions = new int [ ( int ) numDimensions ] ;
2019-05-20 22:48:14 +00:00
for ( var i = 0 ; i < ( long ) numDimensions ; i + + )
2018-11-23 04:56:43 +00:00
{
intDimensions [ i ] = ( int ) dimensions [ i ] ;
}
2019-10-12 22:46:28 +00:00
IntPtr [ ] dimensionNamePtrs = new IntPtr [ ( int ) numDimensions ] ;
NativeApiStatus . VerifySuccess (
NativeMethods . OrtGetSymbolicDimensions ( tensorInfo , dimensionNamePtrs , numDimensions ) ) ;
string [ ] symbolicDimensions = new string [ ( int ) numDimensions ] ;
for ( var i = 0 ; i < ( int ) numDimensions ; i + + )
{
2020-08-10 20:33:49 +00:00
symbolicDimensions [ i ] = NativeOnnxValueHelper . StringFromNativeUtf8 ( dimensionNamePtrs [ i ] ) ;
2019-10-12 22:46:28 +00:00
}
2020-03-24 01:36:12 +00:00
2023-04-11 16:41:59 +00:00
var tensorTypeAndShape = new TensorTypeAndShape ( type , intDimensions , symbolicDimensions ) ;
return new NodeMetadata ( valueType , tensorTypeAndShape ) ;
2018-11-23 04:56:43 +00:00
}
2020-08-10 20:33:49 +00:00
/// <summary>
/// Other classes access
/// </summary>
internal IntPtr Handle
2018-11-20 00:48:22 +00:00
{
2020-08-10 20:33:49 +00:00
get
{
return _nativeHandle ;
}
2018-11-20 00:48:22 +00:00
}
2020-08-10 20:33:49 +00:00
#endregion
2020-08-27 16:17:42 +00:00
#region IDisposable
/// <summary>
/// Finalizer. to cleanup session in case it runs
/// and the user forgets to Dispose() of the session
/// </summary>
~ InferenceSession ( )
{
Dispose ( false ) ;
}
2020-08-10 20:33:49 +00:00
2020-11-20 22:03:55 +00:00
/// <summary>
/// IDisposable implementation
/// </summary>
2018-11-20 00:48:22 +00:00
public void Dispose ( )
{
Dispose ( true ) ;
2020-08-27 16:17:42 +00:00
GC . SuppressFinalize ( this ) ;
2018-11-20 00:48:22 +00:00
}
2020-11-20 22:03:55 +00:00
/// <summary>
/// IDisposable implementation
/// </summary>
/// <param name="disposing">true if invoked from Dispose() method</param>
2018-11-20 00:48:22 +00:00
protected virtual void Dispose ( bool disposing )
{
2021-05-15 03:44:42 +00:00
if ( _disposed )
2020-08-27 16:17:42 +00:00
{
return ;
}
2023-04-20 22:12:51 +00:00
DisposeImpl ( disposing ) ;
}
2020-08-27 16:17:42 +00:00
2023-04-20 22:12:51 +00:00
/// <summary>
/// This function is also used on failure in the constructor
/// </summary>
/// <param name="disposing"></param>
void DisposeImpl ( bool disposing )
{
2018-11-20 00:48:22 +00:00
if ( disposing )
{
2023-04-20 22:12:51 +00:00
if ( _namesMemoryPtrs ! = null )
{
foreach ( var ptr in _namesMemoryPtrs )
Marshal . FreeHGlobal ( ptr ) ;
_namesMemoryPtrs = null ;
}
2018-11-20 00:48:22 +00:00
// cleanup managed resources
2019-08-14 19:02:02 +00:00
if ( _builtInSessionOptions ! = null )
{
_builtInSessionOptions . Dispose ( ) ;
2020-08-27 16:17:42 +00:00
_builtInSessionOptions = null ;
2019-08-14 19:02:02 +00:00
}
if ( _builtInRunOptions ! = null )
{
_builtInRunOptions . Dispose ( ) ;
2020-08-27 16:17:42 +00:00
_builtInRunOptions = null ;
2019-08-14 19:02:02 +00:00
}
2018-11-20 00:48:22 +00:00
}
// cleanup unmanaged resources
if ( _nativeHandle ! = IntPtr . Zero )
{
2018-12-18 19:39:46 +00:00
NativeMethods . OrtReleaseSession ( _nativeHandle ) ;
2020-08-27 16:17:42 +00:00
_nativeHandle = IntPtr . Zero ;
2018-11-20 00:48:22 +00:00
}
2020-08-27 16:17:42 +00:00
_disposed = true ;
2018-11-20 00:48:22 +00:00
}
#endregion
}
2018-11-23 04:56:43 +00:00
/// <summary>
2023-04-11 16:41:59 +00:00
/// Represents tensor element type and its shapes
2018-11-23 04:56:43 +00:00
/// </summary>
2023-04-11 16:41:59 +00:00
public class TensorTypeAndShape
2018-11-20 00:48:22 +00:00
{
2023-04-11 16:41:59 +00:00
internal TensorTypeAndShape ( TensorElementType elementType , int [ ] dimensions , string [ ] symbolicDimensions )
2018-11-20 00:48:22 +00:00
{
2023-04-11 16:41:59 +00:00
ElementTypeInfo = TensorBase . GetElementTypeInfo ( elementType ) ;
if ( ElementTypeInfo = = null )
{
throw new OnnxRuntimeException ( ErrorCode . InvalidArgument , "Unregistered TensorElementType value of: " + elementType . ToString ( ) ) ;
}
ElementDataType = elementType ;
2020-11-20 22:03:55 +00:00
Dimensions = dimensions ;
SymbolicDimensions = symbolicDimensions ;
2018-11-23 04:56:43 +00:00
}
2020-11-20 22:03:55 +00:00
/// <summary>
2023-04-11 16:41:59 +00:00
/// Tensor Element type
2020-11-20 22:03:55 +00:00
/// </summary>
2023-04-11 16:41:59 +00:00
/// <value>TensorElementType enum</value>
public TensorElementType ElementDataType { get ; }
2019-03-12 17:11:14 +00:00
2020-11-20 22:03:55 +00:00
/// <summary>
/// Shape
/// </summary>
/// <value>Array of dimensions</value>
public int [ ] Dimensions { get ; }
2019-10-12 22:46:28 +00:00
2020-11-20 22:03:55 +00:00
/// <summary>
/// Symbolic dimensions
/// </summary>
/// <value>Array of symbolic dimensions if present.</value>
public string [ ] SymbolicDimensions { get ; }
2019-10-12 22:46:28 +00:00
2020-11-20 22:03:55 +00:00
/// <summary>
2023-04-11 16:41:59 +00:00
/// Tensor element metadata
/// </summary>
public TensorElementTypeInfo ElementTypeInfo { get ; }
}
/// <summary>
/// Represents sequnce metdata
/// </summary>
public class SequenceMetadata
{
/// <summary>
/// __ctor
/// </summary>
/// <param name="elementData"></param>
internal SequenceMetadata ( NodeMetadata elementData )
{
ElementMeta = elementData ;
}
/// <summary>
/// Element Metatada, recursive definition with a Tensor being a base case
/// may contain maps, tensors and other sequences
/// </summary>
public NodeMetadata ElementMeta { get ; }
}
/// <summary>
/// The class contains metadata for an optional input/output
/// </summary>
public class OptionalMetadata
{
/// <summary>
/// __ctor
/// </summary>
/// <param name="elementData"></param>
internal OptionalMetadata ( NodeMetadata elementData )
{
ElementMeta = elementData ;
}
/// <summary>
/// Element Metatada, recursive definition with a Tensor being a base case
/// may contain maps, tensors and sequences
/// </summary>
public NodeMetadata ElementMeta { get ; }
}
/// <summary>
/// Represents Map MetaData.
/// Key is always a tensor denoted by an element type
/// with value type being a recursive structure that may
/// contain other maps, sequences or tensors.
/// </summary>
public class MapMetadata
{
internal MapMetadata ( TensorElementType keyDataType , NodeMetadata valueMetadata )
{
KeyDataType = keyDataType ;
ValueMetadata = valueMetadata ;
}
/// <summary>
/// Key tensor data type
/// </summary>
/// <value>A value of TensorElementType enum</value>
public TensorElementType KeyDataType { get ; }
/// <summary>
/// Value metadata
/// </summary>
/// /// <value>Instance of Nodemetadata for the value of the map</value>
public NodeMetadata ValueMetadata { get ; }
}
/// <summary>
/// Resembles type and shape information of session-graph nodes, used for communicating the shape/type of input/output nodes
/// </summary>
public class NodeMetadata
{
private readonly Object _metadata ;
/// <summary>
/// Constructs NodeMetadata for tensor
/// </summary>
/// <param name="onnxValueType">either ONNX_TYPE_TENSOR or ONNX_TYPE_SPARSETENSOR</param>
/// <param name="typeAndShape">Tensor type and shape information</param>
internal NodeMetadata ( OnnxValueType onnxValueType , TensorTypeAndShape typeAndShape )
{
OnnxValueType = onnxValueType ;
CheckTensor ( ) ;
_metadata = typeAndShape ;
}
/// <summary>
/// __ctor for map metadata
/// </summary>
/// <param name="mapMetadata"></param>
internal NodeMetadata ( MapMetadata mapMetadata )
{
OnnxValueType = OnnxValueType . ONNX_TYPE_MAP ;
_metadata = mapMetadata ;
}
/// <summary>
/// __ctor for sequence metadata
/// </summary>
/// <param name="sequenceMetadata"></param>
internal NodeMetadata ( SequenceMetadata sequenceMetadata )
{
OnnxValueType = OnnxValueType . ONNX_TYPE_SEQUENCE ;
_metadata = sequenceMetadata ;
}
/// <summary>
/// __ctor
/// </summary>
/// <param name="optMetadata"></param>
internal NodeMetadata ( OptionalMetadata optMetadata )
{
OnnxValueType = OnnxValueType . ONNX_TYPE_OPTIONAL ;
_metadata = optMetadata ;
}
private void CheckTensor ( )
{
if ( ! IsTensor )
{
throw new OnnxRuntimeException ( ErrorCode . Fail , "OnnxValueType must either be a tensor or sparse tensor" ) ;
}
}
/// <summary>
/// Retrieves MapMetadata, valid only if this node represents a Map.
/// </summary>
/// <returns></returns>
/// <exception cref="InvalidOperationException">when the instance does not contain map metadata</exception>
public MapMetadata AsMapMetadata ( )
{
if ( OnnxValueType ! = OnnxValueType . ONNX_TYPE_MAP )
{
throw new OnnxRuntimeException ( ErrorCode . Fail , "Instance does not contain Map metadata" ) ;
}
return _metadata as MapMetadata ;
}
/// <summary>
/// Retrieves SequenceMetadata, valid only if this node represents a Sequence
/// </summary>
/// <returns></returns>
/// <exception cref="InvalidOperationException">when the instance does not contain sequence metadata</exception>
public SequenceMetadata AsSequenceMetadata ( )
{
if ( OnnxValueType ! = OnnxValueType . ONNX_TYPE_SEQUENCE )
{
throw new OnnxRuntimeException ( ErrorCode . Fail , "Instance does not contain Sequence metadata" ) ;
}
return _metadata as SequenceMetadata ;
}
/// <summary>
/// Retrieves Optional type metadata, valid if this node is optional
/// Optional metadata is nothing more than just a container for all the usual
/// element types.
/// </summary>
/// <returns></returns>
/// <exception cref="InvalidOperationException"></exception>
public OptionalMetadata AsOptionalMetadata ( )
{
if ( OnnxValueType ! = OnnxValueType . ONNX_TYPE_OPTIONAL )
{
throw new OnnxRuntimeException ( ErrorCode . Fail , "Instance does not contain Optional metadata" ) ;
}
return _metadata as OptionalMetadata ;
}
/// <summary>
/// Type value of the node
/// </summary>
/// <value>A value of OnnxValueType enum</value>
public OnnxValueType OnnxValueType { get ; }
/// <summary>
2023-04-20 22:12:51 +00:00
/// Node name in the natively allocated memory.
///
2023-04-11 16:41:59 +00:00
/// Present only on the top-level instance
/// metadata dictionary entries.
///
2023-04-20 22:12:51 +00:00
/// Avoids repeated conversion and pinning
///
/// This memory chunk is owned and freed by the InferenceSession
/// object.
2023-04-11 16:41:59 +00:00
/// </summary>
2023-04-20 22:12:51 +00:00
internal IntPtr ZeroTerminatedName { get ; set ; }
2023-04-11 16:41:59 +00:00
/// <summary>
/// Tensor shape valid only if this is a Tensor.
/// Preserved for API compatibility
/// </summary>
/// <value>Array of dimensions</value>
public int [ ] Dimensions
{
get
{
CheckTensor ( ) ;
return ( _metadata as TensorTypeAndShape ) . Dimensions ;
}
}
/// <summary>
/// Symbolic dimensions valid only if this is a Tensor.
/// Preserved for API compatibility
/// </summary>
/// <value>Array of symbolic dimensions if present.</value>
public string [ ] SymbolicDimensions
{
get
{
CheckTensor ( ) ;
return ( _metadata as TensorTypeAndShape ) . SymbolicDimensions ;
}
}
/// <summary>
/// .NET type that corresponds to the primitive Tensor data type.
/// Valid only if this is a Tensor.
2020-11-20 22:03:55 +00:00
/// </summary>
/// <value>System.Type</value>
2023-04-11 16:41:59 +00:00
public System . Type ElementType
{
get
{
CheckTensor ( ) ;
return ( _metadata as TensorTypeAndShape ) . ElementTypeInfo . TensorType ;
}
}
/// <summary>
/// Tensor Element Type. Valid if tensor
/// </summary>
public TensorElementType ElementDataType
{
get
{
CheckTensor ( ) ;
return ( _metadata as TensorTypeAndShape ) . ElementDataType ;
}
}
/// <summary>
/// Convinience method to check for string
/// </summary>
public bool IsString
{
get
{
CheckTensor ( ) ;
return ( _metadata as TensorTypeAndShape ) . ElementTypeInfo . IsString ;
}
}
2018-11-26 06:46:21 +00:00
2020-11-20 22:03:55 +00:00
/// <summary>
/// Whether it is a Tensor
/// </summary>
/// <value>currently always returns true</value>
2018-11-26 06:46:21 +00:00
public bool IsTensor
{
get
{
2023-04-11 16:41:59 +00:00
return ( OnnxValueType = = OnnxValueType . ONNX_TYPE_TENSOR ) | | ( OnnxValueType = = OnnxValueType . ONNX_TYPE_SPARSETENSOR ) ;
2018-11-26 06:46:21 +00:00
}
}
2018-11-20 00:48:22 +00:00
}
2020-11-20 22:03:55 +00:00
/// <summary>
/// A class that queries and caches model metadata and exposes
/// it as properties
/// </summary>
2020-08-25 18:13:49 +00:00
public class ModelMetadata
2018-11-20 00:48:22 +00:00
{
2020-08-25 18:13:49 +00:00
private string _producerName ;
private string _graphName ;
private string _domain ;
private string _description ;
2021-01-06 06:18:03 +00:00
private string _graphDescription ;
2020-08-25 18:13:49 +00:00
private long _version ;
private Dictionary < string , string > _customMetadataMap = new Dictionary < string , string > ( ) ;
internal ModelMetadata ( InferenceSession session )
{
var allocator = OrtAllocator . DefaultInstance ;
// Get the native ModelMetadata instance associated with the InferenceSession
2023-04-20 22:12:51 +00:00
NativeApiStatus . VerifySuccess ( NativeMethods . OrtSessionGetModelMetadata ( session . Handle , out IntPtr modelMetadataHandle ) ) ;
2020-08-25 18:13:49 +00:00
try
{
// Process producer name
2023-04-20 22:12:51 +00:00
NativeApiStatus . VerifySuccess ( NativeMethods . OrtModelMetadataGetProducerName ( modelMetadataHandle ,
allocator . Pointer , out IntPtr producerNameHandle ) ) ;
_producerName = NativeOnnxValueHelper . StringFromNativeUtf8 ( producerNameHandle , allocator ) ;
2020-08-25 18:13:49 +00:00
// Process graph name
2023-04-20 22:12:51 +00:00
NativeApiStatus . VerifySuccess ( NativeMethods . OrtModelMetadataGetGraphName ( modelMetadataHandle ,
allocator . Pointer , out IntPtr graphNameHandle ) ) ;
_graphName = NativeOnnxValueHelper . StringFromNativeUtf8 ( graphNameHandle , allocator ) ;
2020-08-25 18:13:49 +00:00
// Process domain
2023-04-20 22:12:51 +00:00
NativeApiStatus . VerifySuccess ( NativeMethods . OrtModelMetadataGetDomain ( modelMetadataHandle ,
allocator . Pointer , out IntPtr domainHandle ) ) ;
_domain = NativeOnnxValueHelper . StringFromNativeUtf8 ( domainHandle , allocator ) ;
2020-08-25 18:13:49 +00:00
// Process description
2023-04-20 22:12:51 +00:00
NativeApiStatus . VerifySuccess ( NativeMethods . OrtModelMetadataGetDescription ( modelMetadataHandle ,
allocator . Pointer , out IntPtr descriptionHandle ) ) ;
_description = NativeOnnxValueHelper . StringFromNativeUtf8 ( descriptionHandle , allocator ) ;
2020-08-25 18:13:49 +00:00
2021-01-06 06:18:03 +00:00
// Process graph description
2023-04-20 22:12:51 +00:00
NativeApiStatus . VerifySuccess ( NativeMethods . OrtModelMetadataGetGraphDescription ( modelMetadataHandle ,
allocator . Pointer , out IntPtr graphDescriptionHandle ) ) ;
_graphDescription = NativeOnnxValueHelper . StringFromNativeUtf8 ( graphDescriptionHandle , allocator ) ;
2021-01-06 06:18:03 +00:00
2020-08-25 18:13:49 +00:00
// Process version
NativeApiStatus . VerifySuccess ( NativeMethods . OrtModelMetadataGetVersion ( modelMetadataHandle , out _version ) ) ;
// Process CustomMetadata Map
2023-04-20 22:12:51 +00:00
NativeApiStatus . VerifySuccess ( NativeMethods . OrtModelMetadataGetCustomMetadataMapKeys ( modelMetadataHandle ,
allocator . Pointer , out IntPtr customMetadataMapKeysHandle , out long numKeys ) ) ;
2020-08-25 18:13:49 +00:00
// We have received an array of null terminated C strings which are the keys that we can use to lookup the custom metadata map
// The OrtAllocator will finally free the customMetadataMapKeysHandle
2023-04-20 22:12:51 +00:00
try
2020-08-25 18:13:49 +00:00
{
2023-06-29 15:59:23 +00:00
Span < IntPtr > keysHandles ;
unsafe
{
keysHandles = new Span < IntPtr > ( customMetadataMapKeysHandle . ToPointer ( ) , ( int ) numKeys ) ;
}
2023-04-20 22:12:51 +00:00
using ( var ortAllocationKeys = new DisposableList < OrtMemoryAllocation > ( ( int ) numKeys ) )
2020-08-25 18:13:49 +00:00
{
2023-04-20 22:12:51 +00:00
// Put all the handles to each key in the DisposableList to be disposed off in an exception-safe manner
2023-06-29 15:59:23 +00:00
foreach ( var keyHandle in keysHandles )
2023-04-20 22:12:51 +00:00
{
2023-06-29 15:59:23 +00:00
ortAllocationKeys . Add ( new OrtMemoryAllocation ( allocator , keyHandle , 0 ) ) ;
2023-04-20 22:12:51 +00:00
}
2020-08-25 18:13:49 +00:00
2023-04-20 22:12:51 +00:00
// Process each key via the stored key handles
2023-06-29 15:59:23 +00:00
foreach ( var keyHandle in keysHandles )
2020-08-25 18:13:49 +00:00
{
2023-04-20 22:12:51 +00:00
NativeApiStatus . VerifySuccess ( NativeMethods . OrtModelMetadataLookupCustomMetadataMap ( modelMetadataHandle ,
allocator . Pointer , keyHandle , out IntPtr valueHandle ) ) ;
2020-08-25 18:13:49 +00:00
2023-04-20 22:12:51 +00:00
var value = NativeOnnxValueHelper . StringFromNativeUtf8 ( valueHandle , allocator ) ;
var key = NativeOnnxValueHelper . StringFromNativeUtf8 ( keyHandle ) ;
2020-08-25 18:13:49 +00:00
// Put the key/value pair into the dictionary
_customMetadataMap [ key ] = value ;
}
}
}
2023-06-29 15:59:23 +00:00
finally
{
allocator . FreeMemory ( customMetadataMapKeysHandle ) ;
}
2020-08-25 18:13:49 +00:00
}
finally
{
// Free ModelMetadata handle
2021-05-15 03:44:42 +00:00
NativeMethods . OrtReleaseModelMetadata ( modelMetadataHandle ) ;
}
2020-08-25 18:13:49 +00:00
}
2020-11-20 22:03:55 +00:00
/// <summary>
/// Producer name string
/// </summary>
/// <value>producer name string</value>
2020-08-25 18:13:49 +00:00
public string ProducerName
{
get
{
return _producerName ;
}
}
2020-11-20 22:03:55 +00:00
/// <summary>
/// Graph name for this model
/// </summary>
/// <value>graph name string</value>
2020-08-25 18:13:49 +00:00
public string GraphName
{
get
{
return _graphName ;
}
}
2020-11-20 22:03:55 +00:00
/// <summary>
/// Domain for this model
/// </summary>
/// <value>domain name string</value>
2020-08-25 18:13:49 +00:00
public string Domain
{
get
{
return _domain ;
}
}
2020-11-20 22:03:55 +00:00
/// <summary>
/// Unstructured model description
/// </summary>
/// <value>description string</value>
2020-08-25 18:13:49 +00:00
public string Description
{
get
{
return _description ;
}
}
2021-01-06 06:18:03 +00:00
/// <summary>
/// Unstructured graph description
/// </summary>
/// <value>description string</value>
public string GraphDescription
{
get
{
return _graphDescription ;
}
}
2020-11-20 22:03:55 +00:00
/// <summary>
/// Version number
/// </summary>
/// <value>long version integer</value>
2020-08-25 18:13:49 +00:00
public long Version
{
get
{
return _version ;
}
}
2020-11-20 22:03:55 +00:00
/// <summary>
/// Custom metadata key/value pairs
/// </summary>
/// <value>An instance of a Dictionary<string,string></value>
2020-08-25 18:13:49 +00:00
public Dictionary < string , string > CustomMetadataMap
{
get
{
return _customMetadataMap ;
}
}
2018-11-20 00:48:22 +00:00
}
2018-11-23 04:56:43 +00:00
2018-11-20 00:48:22 +00:00
}