public ONNXTensor Permute(int[] permutations)
        {
            // transpose both data & shape
            var transposedData  = Permute(m_Data, permutations);
            var transposedShape = ONNXLayout.Permute(m_Shape, permutations);

            return(new ONNXTensor(transposedData, transposedShape));
        }
        public ONNXTensor Reshape(long[] onnxShape)
        {
            var symbolicShape = ONNXLayout.ConvertSymbolicShapeToBarracuda(onnxShape, "?");
            var reshapedData  = m_Data.Reshape(symbolicShape);

            for (var i = 0; i < onnxShape.Length; ++i)
            {
                if (onnxShape[i] < 0)
                {
                    onnxShape[i] = reshapedData.shape[i];
                }
                Debug.Assert(onnxShape[i] == reshapedData.shape[i]);
            }
            return(new ONNXTensor(reshapedData, onnxShape));
        }
        public Tensor ToBarracuda(string onnxLayout)
        {
            Profiler.BeginSample("ONNXTensor.ToBarracuda");
            if (onnxLayout == "?")
            {
                throw new OnnxLayerImportException("Unknown ONNX layout in not supported when converting constant tensor to Barracuda");
            }

            Debug.Assert(m_Shape.All(v => v > 0));
            var permutations = ONNXLayout.AxisPermutationsForMappingONNXLayoutToBarracuda(rank, onnxLayout);

            Debug.Assert(rank <= permutations.Length);

            var outTensor = Permute(m_Data, permutations);

            Profiler.EndSample();
            return(outTensor);
        }
        internal static Tensor Permute(Tensor inTensor, int[] permutations) // TODO: unify Permute() arguments
        {
            var padPermutationsToBarracudaRank = 4 - permutations.Length;

            if (padPermutationsToBarracudaRank > 0)
            {
                permutations = permutations.Concat(Enumerable.Range(permutations.Length, padPermutationsToBarracudaRank)).ToArray();
            }
            Debug.Assert(permutations.Length == 4);

            // See: https://stackoverflow.com/a/32034565
            Profiler.BeginSample("ONNXTensor.Permute");
            var outTensor = new Tensor(ONNXLayout.Permute(inTensor.shape.ToArray(), permutations));

            Debug.Assert(outTensor.length == inTensor.length);

            // {0, 2, 3, 1} => {0, 3, 1, 2}
            // {2, 3, 1, 0} => {3, 2, 0, 1}
            //              => {find_index(0), find_index(1), find_index(2), find_index(3)}
            var reversePermute = new int[permutations.Length];

            for (var i = 0; i < permutations.Length; ++i)
            {
                reversePermute[i] = Array.IndexOf(permutations, i);
            }

            // outTensor strides
            var outStrideC   = outTensor.channels;
            var outStrideWC  = outStrideC * outTensor.width;
            var outStrideHWC = outStrideWC * outTensor.height;

            var outStride = new int[reversePermute.Length];

            for (var i = 0; i < reversePermute.Length; ++i)
            {
                outStride[i] = new[] { 0, outStrideHWC, outStrideWC, outStrideC, 1 }
            }
        public ONNXTensor(TensorProto onnxTensor)
        {
            // read shape
            var onnxShape = onnxTensor.Dims.ToArray();


            if (onnxShape.Any(s => s == 0))
            {
                // empty tensor, not data
                m_Shape = onnxShape;
                m_Data  = null;
            }
            else
            {
                // read data
                var     shape = ONNXLayout.ConvertShapeToBarracuda(onnxShape, onnxLayout: "?");
                float[] data;
                if ((onnxTensor.RawData != null) && (!onnxTensor.RawData.IsEmpty))
                {
                    var byteArray = new byte[onnxTensor.RawData.Length];
                    onnxTensor.RawData.CopyTo(byteArray, 0);

                    // Double
                    if (onnxTensor.DataType == (int)TensorProto.Types.DataType.Double)
                    {
                        var typedData = new double[shape.length];
                        Debug.Assert((sizeof(double) * shape.length) == onnxTensor.RawData.Length);
                        Buffer.BlockCopy(byteArray, 0, typedData, 0, byteArray.Length);
                        data = typedData.Select(x => (float)x).ToArray();
                    }
                    // Float32
                    else if (onnxTensor.DataType == (int)TensorProto.Types.DataType.Float)
                    {
                        data = new float[shape.length];
                        Debug.Assert((sizeof(float) * shape.length) == onnxTensor.RawData.Length);
                        Buffer.BlockCopy(byteArray, 0, data, 0, byteArray.Length);
                    }
                    // Float16
                    else if (onnxTensor.DataType == (int)TensorProto.Types.DataType.Float16)
                    {
                        var typedData = new UInt16[shape.length];
                        Debug.Assert((sizeof(UInt16) * shape.length) == onnxTensor.RawData.Length);
                        Buffer.BlockCopy(byteArray, 0, typedData, 0, byteArray.Length);
                        data = typedData.Select(x => HalfHelper.HalfToSingle(x)).ToArray();
                    }
                    // Int8
                    else if (onnxTensor.DataType == (int)TensorProto.Types.DataType.Int8)
                    {
                        var typedData = new sbyte[shape.length];
                        Debug.Assert((sizeof(sbyte) * shape.length) == onnxTensor.RawData.Length);
                        Buffer.BlockCopy(byteArray, 0, typedData, 0, byteArray.Length);
                        data = typedData.Select(x => (float)x).ToArray();
                    }
                    // Int16
                    else if (onnxTensor.DataType == (int)TensorProto.Types.DataType.Int16)
                    {
                        var typedData = new short[shape.length];
                        Debug.Assert((sizeof(short) * shape.length) == onnxTensor.RawData.Length);
                        Buffer.BlockCopy(byteArray, 0, typedData, 0, byteArray.Length);
                        data = typedData.Select(x => (float)x).ToArray();
                    }
                    // Int32
                    else if (onnxTensor.DataType == (int)TensorProto.Types.DataType.Int32)
                    {
                        var typedData = new int[shape.length];
                        Debug.Assert((sizeof(int) * shape.length) == onnxTensor.RawData.Length);
                        Buffer.BlockCopy(byteArray, 0, typedData, 0, byteArray.Length);
                        data = typedData.Select(x => (float)x).ToArray();
                    }
                    // Int64
                    else if (onnxTensor.DataType == (int)TensorProto.Types.DataType.Int64)
                    {
                        var typedData = new long[shape.length];
                        Debug.Assert((sizeof(long) * shape.length) == onnxTensor.RawData.Length);
                        Buffer.BlockCopy(byteArray, 0, typedData, 0, byteArray.Length);
                        data = typedData.Select(x => (float)x).ToArray();
                    }
                    // UInt8
                    else if (onnxTensor.DataType == (int)TensorProto.Types.DataType.Uint8)
                    {
                        var typedData = new byte[shape.length];
                        Debug.Assert((sizeof(byte) * shape.length) == onnxTensor.RawData.Length);
                        Buffer.BlockCopy(byteArray, 0, typedData, 0, byteArray.Length);
                        data = typedData.Select(x => (float)x).ToArray();
                    }
                    // UInt16
                    else if (onnxTensor.DataType == (int)TensorProto.Types.DataType.Uint16)
                    {
                        var typedData = new ushort[shape.length];
                        Debug.Assert((sizeof(ushort) * shape.length) == onnxTensor.RawData.Length);
                        Buffer.BlockCopy(byteArray, 0, typedData, 0, byteArray.Length);
                        data = typedData.Select(x => (float)x).ToArray();
                    }
                    // UInt32
                    else if (onnxTensor.DataType == (int)TensorProto.Types.DataType.Uint32)
                    {
                        var typedData = new uint[shape.length];
                        Debug.Assert((sizeof(uint) * shape.length) == onnxTensor.RawData.Length);
                        Buffer.BlockCopy(byteArray, 0, typedData, 0, byteArray.Length);
                        data = typedData.Select(x => (float)x).ToArray();
                    }
                    // UInt64
                    else if (onnxTensor.DataType == (int)TensorProto.Types.DataType.Uint64)
                    {
                        var typedData = new ulong[shape.length];
                        Debug.Assert((sizeof(ulong) * shape.length) == onnxTensor.RawData.Length);
                        Buffer.BlockCopy(byteArray, 0, typedData, 0, byteArray.Length);
                        data = typedData.Select(x => (float)x).ToArray();
                    }
                    // Bool
                    else if (onnxTensor.DataType == (int)TensorProto.Types.DataType.Bool)
                    {
                        var typedData = new bool[shape.length];
                        Debug.Assert((sizeof(bool) * shape.length) == onnxTensor.RawData.Length);
                        Buffer.BlockCopy(byteArray, 0, typedData, 0, byteArray.Length);
                        data = typedData.Select(x => x ? 1.0f : 0.0f).ToArray();
                    }
                    else
                    {
                        throw new OnnxLayerImportException($"Tensor data type {(TensorProto.Types.DataType)onnxTensor.DataType} is not supported.");
                    }
                }
                // Float32
                else if ((onnxTensor.FloatData != null) && (onnxTensor.FloatData.Count != 0))
                {
                    Debug.Assert(shape.length == onnxTensor.FloatData.Count);
                    data = new float[shape.length];
                    onnxTensor.FloatData.CopyTo(data, 0);
                }
                // Int32
                else if ((onnxTensor.Int32Data != null) && (onnxTensor.Int32Data.Count != 0))
                {
                    Debug.Assert(shape.length == onnxTensor.Int32Data.Count);
                    data = onnxTensor.Int32Data.Select(x => (float)x).ToArray();
                }
                // Int64
                else if ((onnxTensor.Int64Data != null) && (onnxTensor.Int64Data.Count != 0))
                {
                    Debug.Assert(shape.length == onnxTensor.Int64Data.Count);
                    data = onnxTensor.Int64Data.Select(x => (float)x).ToArray();
                }
                else
                {
                    throw new OnnxLayerImportException("Could not read tensor data for constant tensor.");
                }

                m_Data  = new Tensor(shape, new SharedArrayTensorData(data));
                m_Shape = onnxShape;
            }
        }