File: TestDataLoader.cs

package info (click to toggle)
onnxruntime 1.21.0%2Bdfsg-1
  • links: PTS, VCS
  • area: main
  • in suites: trixie
  • size: 333,732 kB
  • sloc: cpp: 3,153,079; python: 179,219; ansic: 109,131; asm: 37,791; cs: 34,424; perl: 13,070; java: 11,047; javascript: 6,330; pascal: 4,126; sh: 3,277; xml: 598; objc: 281; makefile: 59
file content (621 lines) | stat: -rw-r--r-- 31,063 bytes parent folder | download | duplicates (2)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
using Microsoft.ML.OnnxRuntime.Tensors;
using System;
using System.Buffers;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using Xunit;

namespace Microsoft.ML.OnnxRuntime.Tests
{
    // Copy of the class that is internal in the main package
    public class DisposableListTest<T> : List<T>, IDisposableReadOnlyCollection<T>
        where T : IDisposable
    {
        public DisposableListTest()
        { }

        public DisposableListTest(IEnumerable<T> enumerable) : base(enumerable)
        { }

        public DisposableListTest(int count)
            : base(count)
        { }

        #region IDisposable Support
        private bool disposedValue = false; // To detect redundant calls

        protected virtual void Dispose(bool disposing)
        {
            if (!disposedValue)
            {
                if (disposing)
                {
                    // Dispose in the reverse order.
                    // Objects should typically be destroyed/disposed
                    // in the reverse order of its creation
                    // especially if the objects created later refer to the
                    // objects created earlier. For homogeneous collections of objects
                    // it would not matter.
                    for (int i = this.Count - 1; i >= 0; --i)
                    {
                        this[i]?.Dispose();
                    }
                    this.Clear();
                }

                disposedValue = true;
            }
        }

        // This code added to correctly implement the disposable pattern.
        public void Dispose()
        {
            // Do not change this code. Put cleanup code in Dispose(bool disposing) above.
            Dispose(true);
            GC.SuppressFinalize(this);
        }
        #endregion
    }

    internal struct DisposableTestPair<TValue> : IDisposable
        where TValue : IDisposable
    {
        public string Key;
        public TValue Value;
        public DisposableTestPair(string key, TValue value)
        {
            Key = key;
            Value = value;
        }
        public void Dispose()
        {
            Value?.Dispose();
        }
    }

    internal static class TestDataLoader
    {
        internal static byte[] LoadModelFromEmbeddedResource(string path)
        {
            var assembly = typeof(TestDataLoader).Assembly;
            byte[] model = null;

            var resourceName = assembly.GetManifestResourceNames().Single(p => p.EndsWith("." + path));
            using (Stream stream = assembly.GetManifestResourceStream(resourceName))
            {
                using (MemoryStream memoryStream = new MemoryStream())
                {
                    stream.CopyTo(memoryStream);
                    model = memoryStream.ToArray();
                }
            }

            return model;
        }

        internal static float[] LoadTensorFromEmbeddedResource(string path)
        {
            var tensorData = new List<float>();
            var assembly = typeof(TestDataLoader).Assembly;

            var resourceName = assembly.GetManifestResourceNames().Single(p => p.EndsWith("." + path));
            using (StreamReader inputFile = new StreamReader(assembly.GetManifestResourceStream(resourceName)))
            {
                inputFile.ReadLine(); // skip the input name
                string[] dataStr = inputFile.ReadLine().Split(new char[] { ',', '[', ']' }, StringSplitOptions.RemoveEmptyEntries);
                for (int i = 0; i < dataStr.Length; i++)
                {
                    tensorData.Add(Single.Parse(dataStr[i]));
                }
            }

            return tensorData.ToArray();
        }

        static NamedOnnxValue LoadTensorPb(Onnx.TensorProto tensor, string nodeName, NodeMetadata nodeMeta)
        {
            if (nodeMeta.OnnxValueType != OnnxValueType.ONNX_TYPE_TENSOR)
            {
                throw new InvalidDataException($"Metadata for: '{nodeName}' has a type: '{nodeMeta.OnnxValueType}'" +
                                               $" but loading as tensor: '{tensor.Name}'");
            }

            var protoDt = (Tensors.TensorElementType)tensor.DataType;
            var metaElementType = nodeMeta.ElementDataType;
            if (!((protoDt == metaElementType) ||
                  (protoDt == TensorElementType.UInt16 &&
                   (metaElementType == TensorElementType.BFloat16 || metaElementType == TensorElementType.Float16))))
                throw new InvalidDataException($"For node: '{nodeName}' metadata expects: '{metaElementType}' but loaded loaded tensor type: '{protoDt}'");

            // Tensors within Sequences may have no dimensions as the standard allows
            // different dimensions for each tensor element of the sequence
            if (nodeMeta.Dimensions.Length > 0 && nodeMeta.Dimensions.Length != tensor.Dims.Count)
            {
                throw new InvalidDataException($"node: '{nodeName}' nodeMeta.Dim.Length: {nodeMeta.Dimensions.Length} " +
                                               $"is expected to be equal to tensor.Dims.Count {tensor.Dims.Count}");
            }

            var intDims = new int[tensor.Dims.Count];
            for (int i = 0; i < tensor.Dims.Count; i++)
            {
                intDims[i] = (int)tensor.Dims[i];
            }

            for (int i = 0; i < nodeMeta.Dimensions.Length; i++)
            {
                if ((nodeMeta.Dimensions[i] != -1) && (nodeMeta.Dimensions[i] != tensor.Dims[i]))
                    throw new InvalidDataException($"Node: '{nodeName}' dimension at idx {i} is {nodeMeta.Dimensions}[{i}] " +
                                                   $"is expected to either be -1 or {tensor.Dims[i]}");
            }

            // element type for Float16 and BFloat16 in the loaded tensor would always be uint16, so
            // we want to use element type from metadata
            if (protoDt == TensorElementType.String)
                return CreateNamedOnnxValueFromStringTensor(tensor.StringData, nodeName, intDims);

            return CreateNamedOnnxValueFromTensorRawData(nodeName, tensor.RawData.Span, metaElementType, intDims);
        }

        internal static NamedOnnxValue CreateNamedOnnxValueFromTensorRawData(string nodeName, ReadOnlySpan<byte> rawData,
                                                                             TensorElementType elementType, int[] intDims)
        {
            switch (elementType)
            {
                case TensorElementType.Float:
                    return CreateNamedOnnxValueFromRawData<float>(nodeName, rawData, intDims);
                case TensorElementType.Double:
                    return CreateNamedOnnxValueFromRawData<double>(nodeName, rawData, intDims);
                case TensorElementType.Int32:
                    return CreateNamedOnnxValueFromRawData<int>(nodeName, rawData, intDims);
                case TensorElementType.UInt32:
                    return CreateNamedOnnxValueFromRawData<uint>(nodeName, rawData, intDims);
                case TensorElementType.Int16:
                    return CreateNamedOnnxValueFromRawData<short>(nodeName, rawData, intDims);
                case TensorElementType.UInt16:
                    return CreateNamedOnnxValueFromRawData<ushort>(nodeName, rawData, intDims);
                case TensorElementType.Int64:
                    return CreateNamedOnnxValueFromRawData<long>(nodeName, rawData, intDims);
                case TensorElementType.UInt64:
                    return CreateNamedOnnxValueFromRawData<ulong>(nodeName, rawData, intDims);
                case TensorElementType.UInt8:
                    return CreateNamedOnnxValueFromRawData<byte>(nodeName, rawData, intDims);
                case TensorElementType.Int8:
                    return CreateNamedOnnxValueFromRawData<sbyte>(nodeName, rawData, intDims);
                case TensorElementType.Bool:
                    return CreateNamedOnnxValueFromRawData<bool>(nodeName, rawData, intDims);
                case TensorElementType.Float16:
                    return CreateNamedOnnxValueFromRawData<Float16>(nodeName, rawData, intDims);
                case TensorElementType.BFloat16:
                    return CreateNamedOnnxValueFromRawData<BFloat16>(nodeName, rawData, intDims);
                case TensorElementType.String:
                    throw new ArgumentException("For string tensors of type use: CreateNamedOnnxValueFromStringTensor.");
                default:
                    throw new NotImplementedException($"Tensors of type: {elementType} not currently supported by this function");
            }
        }

        internal static NamedOnnxValue LoadTensorFromEmbeddedResourcePb(string path, string nodeName, NodeMetadata nodeMeta)
        {
            Onnx.TensorProto tensor = null;

            var assembly = typeof(TestDataLoader).Assembly;

            using (Stream stream = assembly.GetManifestResourceStream($"{assembly.GetName().Name}.TestData.{path}"))
            {
                tensor = Onnx.TensorProto.Parser.ParseFrom(stream);
            }

            return LoadTensorPb(tensor, nodeName, nodeMeta);
        }

        internal static NamedOnnxValue LoadOnnxValueFromFilePb(string fullFilename, string nodeName, NodeMetadata nodeMeta)
        {
            // No sparse tensor support yet
            // Set buffer size to 4MB
            const int readBufferSize = 4194304;
            using (var file = new FileStream(fullFilename, FileMode.Open, FileAccess.Read, FileShare.Read, readBufferSize))
            {
                switch (nodeMeta.OnnxValueType)
                {
                    case OnnxValueType.ONNX_TYPE_TENSOR:
                        {
                            var tensor = Onnx.TensorProto.Parser.ParseFrom(file);
                            return LoadTensorPb(tensor, nodeName, nodeMeta);
                        }
                    case OnnxValueType.ONNX_TYPE_SEQUENCE:
                        {
                            var sequence = Onnx.SequenceProto.Parser.ParseFrom(file);
                            return CreateNamedOnnxValueFromSequence(sequence, nodeName, nodeMeta);
                        }
                    case OnnxValueType.ONNX_TYPE_MAP:
                        {
                            throw new NotImplementedException(
                                "Map test data format requires clarification: https://github.com/onnx/onnx/issues/5072");
                        }

                    case OnnxValueType.ONNX_TYPE_OPTIONAL:
                        {
                            var opt = Onnx.OptionalProto.Parser.ParseFrom(file);
                            return CreateNamedOnnxValueFromOptional(opt, nodeName, nodeMeta);
                        }
                    default:
                        throw new NotImplementedException($"Unable to load value type: {nodeMeta.OnnxValueType} not implemented");
                }
            }
        }

        internal static DisposableTestPair<OrtValue> LoadOrtValueFromFilePb(string fullFilename, string nodeName, NodeMetadata nodeMeta)
        {
            // No sparse tensor support yet
            // Set buffer size to 4MB
            const int readBufferSize = 4194304;
            using (var file = new FileStream(fullFilename, FileMode.Open, FileAccess.Read, FileShare.Read, readBufferSize))
            {
                switch (nodeMeta.OnnxValueType)
                {
                    case OnnxValueType.ONNX_TYPE_TENSOR:
                        {
                            var tensor = Onnx.TensorProto.Parser.ParseFrom(file);
                            return new DisposableTestPair<OrtValue>(nodeName, LoadOrValueTensorPb(tensor, nodeName, nodeMeta));
                        }
                    case OnnxValueType.ONNX_TYPE_SEQUENCE:
                        {
                            var sequence = Onnx.SequenceProto.Parser.ParseFrom(file);
                            return new DisposableTestPair<OrtValue>(nodeName, CreateOrtValueFromSequence(sequence, nodeName, nodeMeta));
                        }
                    case OnnxValueType.ONNX_TYPE_MAP:
                        {
                            throw new NotImplementedException(
                                "Map test data format requires clarification: https://github.com/onnx/onnx/issues/5072");
                        }

                    case OnnxValueType.ONNX_TYPE_OPTIONAL:
                        {
                            var opt = Onnx.OptionalProto.Parser.ParseFrom(file);
                            return new DisposableTestPair<OrtValue>(nodeName, CreateOrtValueFromOptional(opt, nodeName, nodeMeta));
                        }
                    default:
                        throw new NotImplementedException($"Unable to load value type: {nodeMeta.OnnxValueType} not implemented");
                }
            }
        }

        private static void SequenceCheckMatchOnnxType(string nodeName, SequenceMetadata meta,
                                                       OnnxValueType onnxType)
        {
            if (meta.ElementMeta.OnnxValueType == onnxType)
                return;

            throw new InvalidDataException($"Sequence node: '{nodeName}' " +
                                           $"has element type: '{onnxType}'" +
                                           $" expected: '{meta.ElementMeta.OnnxValueType}'");
        }

        private static string MakeSequenceElementName(string nodeName, string seqName, int seqNum)
        {
            if (seqName.Length > 0)
                return $"seq.{nodeName}.data.{seqName}.{seqNum}";
            else
                return $"seq.{nodeName}.data._.{seqNum}";
        }

        internal static NamedOnnxValue CreateNamedOnnxValueFromSequence(Onnx.SequenceProto sequence, string nodeName, NodeMetadata nodeMeta)
        {
            var sequenceMeta = nodeMeta.AsSequenceMetadata();
            var elemMeta = sequenceMeta.ElementMeta;

            int seqNum = 0;
            var seqElemType = (Onnx.SequenceProto.Types.DataType)sequence.ElemType;
            switch (seqElemType)
            {
                case Onnx.SequenceProto.Types.DataType.Tensor:
                    {
                        SequenceCheckMatchOnnxType(nodeName, sequenceMeta, OnnxValueType.ONNX_TYPE_TENSOR);
                        var sequenceOfTensors = new List<NamedOnnxValue>(sequence.TensorValues.Count);
                        foreach (var tensor in sequence.TensorValues)
                        {
                            var elemName = MakeSequenceElementName(nodeName, sequence.Name, seqNum++);
                            var namedOnnxValue = LoadTensorPb(tensor, elemName, elemMeta);
                            sequenceOfTensors.Add(namedOnnxValue);
                        }
                        return NamedOnnxValue.CreateFromSequence(nodeName, sequenceOfTensors);
                    }
                case Onnx.SequenceProto.Types.DataType.Sequence:
                    {
                        SequenceCheckMatchOnnxType(nodeName, sequenceMeta, OnnxValueType.ONNX_TYPE_SEQUENCE);
                        var seqOfSequences = new List<NamedOnnxValue>(sequence.SequenceValues.Count);
                        foreach (var s in sequence.SequenceValues)
                        {
                            var elemName = MakeSequenceElementName(nodeName, sequence.Name, seqNum++);
                            seqOfSequences.Add(CreateNamedOnnxValueFromSequence(s, elemName, elemMeta));
                        }
                        return NamedOnnxValue.CreateFromSequence(nodeName, seqOfSequences);
                    }
                case Onnx.SequenceProto.Types.DataType.Map:
                    {
                        SequenceCheckMatchOnnxType(nodeName, sequenceMeta, OnnxValueType.ONNX_TYPE_MAP);
                        var seqOfMaps = new List<NamedOnnxValue>(sequence.MapValues.Count);
                        foreach (var m in sequence.MapValues)
                        {
                            var elemName = MakeSequenceElementName(nodeName, sequence.Name, seqNum++);
                            seqOfMaps.Add(CreateNamedOnnxValueFromMap(m, elemName, elemMeta));
                        }
                        return NamedOnnxValue.CreateFromSequence(nodeName, seqOfMaps);
                    }
                case Onnx.SequenceProto.Types.DataType.Optional:
                    {
                        SequenceCheckMatchOnnxType(nodeName, sequenceMeta, OnnxValueType.ONNX_TYPE_OPTIONAL);
                        var seqOfOpts = new List<NamedOnnxValue>(sequence.OptionalValues.Count);
                        foreach (var opt in sequence.OptionalValues)
                        {
                            var elemName = MakeSequenceElementName(nodeName, sequence.Name, seqNum++);
                            seqOfOpts.Add(CreateNamedOnnxValueFromOptional(opt, elemName, elemMeta));
                        }
                        return NamedOnnxValue.CreateFromSequence(nodeName, seqOfOpts);
                    }
                default:
                    throw new NotImplementedException($"Sequence test data loading does not support element type: " +
                                                      $"'{seqElemType}'");
            }
        }
        internal static NamedOnnxValue CreateNamedOnnxValueFromMap(Onnx.MapProto map, string nodeName, NodeMetadata nodeMetadata)
        {
            // See GH issue https://github.com/onnx/onnx/issues/5072
            throw new NotImplementedException($"Loading map node: '{nodeName}' not implemented yet");
        }

        internal static NamedOnnxValue CreateNamedOnnxValueFromOptional(Onnx.OptionalProto optional, string nodeName, NodeMetadata nodeMetadata)
        {
            var meta = nodeMetadata.AsOptionalMetadata().ElementMeta;
            switch ((Onnx.OptionalProto.Types.DataType)optional.ElemType)
            {
                case Onnx.OptionalProto.Types.DataType.Tensor:
                    {
                        var tensor = optional.TensorValue;
                        return LoadTensorPb(tensor, nodeName, meta);
                    }
                case Onnx.OptionalProto.Types.DataType.Sequence:
                    {
                        var sequence = optional.SequenceValue;
                        return CreateNamedOnnxValueFromSequence(sequence, nodeName, meta);
                    }
                case Onnx.OptionalProto.Types.DataType.Map:
                    {
                        var map = optional.MapValue;
                        return CreateNamedOnnxValueFromMap(map, nodeName, meta);
                    }
                case Onnx.OptionalProto.Types.DataType.Optional:
                    throw new NotImplementedException($"Unable to load '{nodeName}' optional contained within optional");
                default:
                    // Test data contains OptionalProto with the contained element type undefined.
                    // the premise is, if the element is not fed as an input, we should not care
                    // what Onnx type it is. However, we do not need to support AFAIK such inputs
                    // since the value for them could never be supplied.
                    throw new NotImplementedException($"Unable to load '{nodeName}' optional element type of: {(Onnx.OptionalProto.Types.DataType)optional.ElemType} type");
            }
        }

        internal static NamedOnnxValue CreateNamedOnnxValueFromRawData<T>(string name, ReadOnlySpan<byte> rawData,
                                                                          int[] dimensions)
            where T : struct
        {
            var typedSrcSpan = MemoryMarshal.Cast<byte, T>(rawData);
            var dt = new DenseTensor<T>(typedSrcSpan.ToArray(), dimensions);
            return NamedOnnxValue.CreateFromTensor<T>(name, dt);
        }

        static OrtValue LoadOrValueTensorPb(Onnx.TensorProto tensor, string nodeName, NodeMetadata nodeMeta)
        {
            if (nodeMeta.OnnxValueType != OnnxValueType.ONNX_TYPE_TENSOR)
            {
                throw new InvalidDataException($"Metadata for: '{nodeName}' has a type: '{nodeMeta.OnnxValueType}'" +
                                               $" but loading as tensor: {tensor.Name}");
            }

            var protoDt = (Tensors.TensorElementType)tensor.DataType;
            var metaElementType = nodeMeta.ElementDataType;
            if (!((protoDt == metaElementType) ||
                  (protoDt == TensorElementType.UInt16 &&
                   (metaElementType == TensorElementType.BFloat16 || metaElementType == TensorElementType.Float16))))
                throw new InvalidDataException($"For node: '{nodeName}' metadata expects: '{metaElementType}' but loaded loaded tensor type: '{protoDt}'");

            // Tensors within Sequences may have no dimensions as the standard allows
            // different dimensions for each tensor element of the sequence
            if (nodeMeta.Dimensions.Length > 0 && nodeMeta.Dimensions.Length != tensor.Dims.Count)
            {
                throw new InvalidDataException($"node: '{nodeName}' nodeMeta.Dim.Length: {nodeMeta.Dimensions.Length} " +
                                               $"is expected to be equal to tensor.Dims.Count {tensor.Dims.Count}");
            }

            var shape = tensor.Dims.ToArray();

            for (int i = 0; i < nodeMeta.Dimensions.Length; i++)
            {
                if ((nodeMeta.Dimensions[i] != -1) && (nodeMeta.Dimensions[i] != shape[i]))
                    throw new InvalidDataException($"Node: '{nodeName}' dimension at idx {i} is {nodeMeta.Dimensions}[{i}] " +
                                                   $"is expected to either be -1 or {shape[i]}");
            }

            // element type for Float16 and BFloat16 in the loaded tensor would always be uint16, so
            // we want to use element type from metadata
            if (protoDt == TensorElementType.String)
                return CreateOrtValueFromStringTensor(tensor.StringData, shape);

            return CreateOrtValueFromRawData(OrtAllocator.DefaultInstance, tensor.RawData.Span, metaElementType, shape);
        }

        internal static OrtValue CreateOrtValueFromSequence(Onnx.SequenceProto sequence, string nodeName, NodeMetadata nodeMeta)
        {
            var sequenceMeta = nodeMeta.AsSequenceMetadata();
            var elemMeta = sequenceMeta.ElementMeta;

            int seqNum = 0;
            var seqElemType = (Onnx.SequenceProto.Types.DataType)sequence.ElemType;
            switch (seqElemType)
            {
                case Onnx.SequenceProto.Types.DataType.Tensor:
                    {
                        SequenceCheckMatchOnnxType(nodeName, sequenceMeta, OnnxValueType.ONNX_TYPE_TENSOR);
                        using DisposableListTest<OrtValue> sequenceOfTensors = new(sequence.TensorValues.Count);
                        foreach (var tensor in sequence.TensorValues)
                        {
                            var element = LoadOrValueTensorPb(tensor, sequence.Name, elemMeta);
                            sequenceOfTensors.Add(element);
                        }
                        // Will take possession of ortValues in the sequence and will clear this container
                        return OrtValue.CreateSequence(sequenceOfTensors);
                    }
                case Onnx.SequenceProto.Types.DataType.Sequence: // Sequence of sequences
                    {
                        SequenceCheckMatchOnnxType(nodeName, sequenceMeta, OnnxValueType.ONNX_TYPE_SEQUENCE);
                        using DisposableListTest<OrtValue> seqOfSequences = new(sequence.TensorValues.Count);
                        foreach (var s in sequence.SequenceValues)
                        {
                            var elemName = MakeSequenceElementName(nodeName, sequence.Name, seqNum++);
                            var ortValue = CreateOrtValueFromSequence(s, elemName, elemMeta);
                            seqOfSequences.Add(ortValue);
                        }
                        return OrtValue.CreateSequence(seqOfSequences);
                    }
                case Onnx.SequenceProto.Types.DataType.Map:
                    {
                        throw new NotImplementedException(
                            "Test data format for maps is under investigation");
                    }
                case Onnx.SequenceProto.Types.DataType.Optional:
                    {
                        SequenceCheckMatchOnnxType(nodeName, sequenceMeta, OnnxValueType.ONNX_TYPE_OPTIONAL);
                        using DisposableListTest<OrtValue> seqOfSequences = new(sequence.TensorValues.Count);
                        foreach (var opt in sequence.OptionalValues)
                        {
                            var elemName = MakeSequenceElementName(nodeName, sequence.Name, seqNum++);
                            var ortValue = CreateOrtValueFromOptional(opt, elemName, elemMeta);
                            seqOfSequences.Add(ortValue);
                        }
                        return OrtValue.CreateSequence(seqOfSequences);
                    }
                default:
                    throw new NotImplementedException($"Sequence test data loading does not support element type: " +
                                                      $"'{seqElemType}'");
            }
        }

        internal static OrtValue CreateOrtValueFromOptional(Onnx.OptionalProto optional, string nodeName, NodeMetadata nodeMetadata)
        {
            var meta = nodeMetadata.AsOptionalMetadata().ElementMeta;
            switch ((Onnx.OptionalProto.Types.DataType)optional.ElemType)
            {
                case Onnx.OptionalProto.Types.DataType.Tensor:
                    {
                        var tensor = optional.TensorValue;
                        return LoadOrValueTensorPb(tensor, nodeName, meta);
                    }
                case Onnx.OptionalProto.Types.DataType.Sequence:
                    {
                        var sequence = optional.SequenceValue;
                        return CreateOrtValueFromSequence(sequence, nodeName, meta);
                    }
                case Onnx.OptionalProto.Types.DataType.Map:
                    {
                        throw new NotImplementedException(
                            "Test data format for maps is under investigation");
                    }
                case Onnx.OptionalProto.Types.DataType.Optional:
                    throw new NotImplementedException($"Unable to load '{nodeName}' optional contained within optional");
                default:
                    // Test data contains OptionalProto with the contained element type undefined.
                    // the premise is, if the element is not fed as an input, we should not care
                    // what Onnx type it is. However, we do not need to support AFAIK such inputs
                    // since the value for them could never be supplied.
                    throw new NotImplementedException($"Unable to load '{nodeName}' optional element type of: {(Onnx.OptionalProto.Types.DataType)optional.ElemType} type");
            }
        }

        internal static OrtValue CreateOrtValueFromRawData(OrtAllocator allocator, ReadOnlySpan<byte> rawData, TensorElementType elementType, long[] shape)
        {
            Debug.Assert(elementType != TensorElementType.String, "Does not support strings");
            var typeInfo = TensorBase.GetElementTypeInfo(elementType);
            Assert.NotNull(typeInfo);

            // ArrayUtilities not accessible in all builds
            var shapeSize = ShapeUtils.GetSizeForShape(shape);
            var inferredSize = rawData.Length / typeInfo.TypeSize;
            Assert.Equal(shapeSize, inferredSize);
            Assert.Equal(0, rawData.Length % typeInfo.TypeSize);

            var ortValue = OrtValue.CreateAllocatedTensorValue(allocator, elementType, shape);
            try
            {
                // The endianess data in protobuf is little endian.
                // We simply copy raw memory into the tensor raw data.
                var span = ortValue.GetTensorMutableRawData();
                Assert.Equal(rawData.Length, span.Length);
                rawData.CopyTo(span);
                return ortValue;
            }
            catch (Exception)
            {
                ortValue.Dispose();
                throw;
            }
        }

        internal static NamedOnnxValue CreateNamedOnnxValueFromStringTensor(IList<Google.Protobuf.ByteString> strings,
                                                                            string nodeName, int[] dimensions)
        {
            string[] strArray = new string[strings.Count];
            for (int i = 0; i < strings.Count; ++i)
            {
#if NET6_0_OR_GREATER
                strArray[i] = Encoding.UTF8.GetString(strings[i].Span);
#else
                strArray[i] = Encoding.UTF8.GetString(strings[i].ToByteArray());
#endif
            }

            var dt = new DenseTensor<string>(strArray, dimensions);
            return NamedOnnxValue.CreateFromTensor<string>(nodeName, dt);
        }
        internal static OrtValue CreateOrtValueFromStringTensor(IList<Google.Protobuf.ByteString> strings,
                                                                long[] shape)
        {
            var ortValue = OrtValue.CreateTensorWithEmptyStrings(OrtAllocator.DefaultInstance, shape);
            try
            {
                for (int i = 0; i < strings.Count; ++i)
                {
                    ortValue.StringTensorSetElementAt(strings[i].Span, i);
                }
                return ortValue;
            }
            catch (Exception)
            {
                ortValue.Dispose();
                throw;
            }
        }

        internal static float[] LoadTensorFromFile(string filename, bool skipheader = true)
        {
            var tensorData = new List<float>();

            // read data from file
            using (var inputFile = new System.IO.StreamReader(filename))
            {
                if (skipheader)
                    inputFile.ReadLine(); // skip the input name
                string[] dataStr = inputFile.ReadLine().Split(new char[] { ',', '[', ']', ' ' }, StringSplitOptions.RemoveEmptyEntries);
                for (int i = 0; i < dataStr.Length; i++)
                {
                    tensorData.Add(Single.Parse(dataStr[i]));
                }
            }

            return tensorData.ToArray();
        }
    }
}