/// <summary>
 /// Creates new array
 /// </summary>
 /// <param name="shape">shape</param>
 public BurstTensorData(TensorShape shape) : base(shape)
 {
 }
Beispiel #2
0
        public virtual IEnumerator StartManualSchedule()
        {
            Profiler.BeginSample("Barracuda.Execute");

            ResetAllocatorIfRequested();
            m_Vars.PrepareStorage(m_Model, m_Ops, m_InputShapes);

            if (m_ModelCompiler != null)
            {
                m_ModelCompiler.PrepareModel(m_Model, m_InputShapes);
            }

            int idx = 0;

            foreach (var l in m_Model.layers)
            {
                idx++;

                m_Progress = idx / (float)m_Model.layers.Count;

                Profiler.BeginSample(l.name);
                var inputs = m_Vars.GatherInputs(l);

                Tensor X = inputs.Length > 0 ? inputs[0] : m_DummyInput;

                if (m_Verbose)
                {
                    D.Log("Layer: " + l.type + ((l.type == Layer.Type.Activation) ? ("." + l.activation) : "") + " " + l.name);
                }

                m_Vars.PrepareStorage(l);
                if (m_ModelCompiler != null)
                {
                    m_ModelCompiler.PreExecuteLayer(l, inputs);
                }

                // No operation, identity
                if (l.type == Layer.Type.Nop)
                {
                    Profiler.BeginSample("Barracuda.Nop");
                    X = m_Ops.Copy(X);
                }
                // Load const
                else if (l.type == Layer.Type.Load)
                {
                    Profiler.BeginSample("Barracuda.Load");
                }
                // GEMM
                else if (l.type == Layer.Type.Dense)
                {
                    Assert.AreEqual(inputs.Length, 3);
                    Profiler.BeginSample("Barracuda.Dense");
                    X = m_Ops.Dense(X, inputs[1], inputs[2], GetAndVerifyFusedActivation(l));
                }
                // 2D
                else if (l.type == Layer.Type.Conv2D)
                {
                    Assert.AreEqual(inputs.Length, 3);
                    Profiler.BeginSample("Barracuda.Conv2D");
                    var pad = X.AdjustPadToKernel(inputs[1], l.stride, l.pad);
                    X = m_Ops.Conv2D(X, inputs[1], inputs[2], l.stride, pad, GetAndVerifyFusedActivation(l));
                }
                else if (l.type == Layer.Type.DepthwiseConv2D)
                {
                    Assert.AreEqual(inputs.Length, 3);
                    Profiler.BeginSample("Barracuda.DepthwiseConv2D");
                    var pad = X.AdjustPadToKernel(inputs[1], l.stride, l.pad);
                    X = m_Ops.DepthwiseConv2D(X, inputs[1], inputs[2], l.stride, pad, GetAndVerifyFusedActivation(l));
                }
                else if (l.type == Layer.Type.Conv2DTrans)
                {
                    Assert.AreEqual(inputs.Length, 3);
                    Profiler.BeginSample("Barracuda.Conv2DTrans");
                    // pool size is treated as output_adjustment aka output_padding here
                    var outputAdjustment = l.pool;
                    var pad = X.AdjustPadToKernel(inputs[1], l.stride, l.pad);
                    X = m_Ops.Conv2DTrans(X, inputs[1], inputs[2], l.stride, pad, outputAdjustment, GetAndVerifyFusedActivation(l));
                }
                else if (l.type == Layer.Type.Upsample2D)
                {
                    Profiler.BeginSample("Barracuda.Upsample2D");
                    // pool size is treated as upsample scale coefficient here
                    var scale = l.pool;
                    // axis is treated as upsample point/bilinear flag
                    var bilinear = l.axis > 0;
                    if (scale.Length == 0 && inputs.Length > 1)
                    {
                        var scaleTensor = inputs[1];
                        Assert.AreEqual(scaleTensor.length, 4);
                        scale = new int[] { (int)scaleTensor[2], (int)scaleTensor[1] };
                    }
                    X = m_Ops.Upsample2D(X, scale, bilinear);
                }
                else if (l.type == Layer.Type.Resample2D)
                {
                    Profiler.BeginSample("Barracuda.Resample2D");
                    // pool size is treated as resample size here
                    var size = l.pool;
                    // axis is treated as upsample point/bilinear flag
                    var bilinear = l.axis > 0;
                    if (inputs.Length > 1)
                    {
                        var sizeTensor = inputs[1];
                        Assert.AreEqual(sizeTensor.length, 4);
                        size = new int[] { (int)sizeTensor[2], (int)sizeTensor[1] };
                    }
                    X = m_Ops.Resample2D(X, size, bilinear);
                }
                else if (l.type == Layer.Type.DepthToSpace)
                {
                    Profiler.BeginSample("Barracuda.DepthToSpace");
                    // pool size is treated as blocksize
                    var blocksize = l.pool;
                    // axis is treated as mode enum
                    var mode = (Layer.DepthToSpaceMode)l.axis;
                    X = m_Ops.DepthToSpace(X, blocksize, mode);
                }
                else if (l.type == Layer.Type.SpaceToDepth)
                {
                    Profiler.BeginSample("Barracuda.SpaceToDepth");
                    // pool size is treated as blocksize
                    var blocksize = l.pool;
                    X = m_Ops.SpaceToDepth(X, blocksize);
                }
                else if (l.type == Layer.Type.MaxPool2D)
                {
                    Profiler.BeginSample("Barracuda.MaxPool2D");
                    var pad = X.AdjustPadToPool(l.pool, l.stride, l.pad);
                    X = m_Ops.MaxPool2D(X, l.pool, l.stride, pad);
                }
                else if (l.type == Layer.Type.AvgPool2D)
                {
                    Profiler.BeginSample("Barracuda.AvgPool2D");
                    var pad = X.AdjustPadToPool(l.pool, l.stride, l.pad);
                    X = m_Ops.AvgPool2D(X, l.pool, l.stride, pad);
                }
                else if (l.type == Layer.Type.GlobalMaxPool2D)
                {
                    Profiler.BeginSample("Barracuda.GlobalMaxPool2D");
                    X = m_Ops.GlobalMaxPool2D(X);
                }
                else if (l.type == Layer.Type.GlobalAvgPool2D)
                {
                    Profiler.BeginSample("Barracuda.GlobalAvgPool2D");
                    X = m_Ops.GlobalAvgPool2D(X);
                }
                else if (l.type == Layer.Type.Border2D)
                {
                    Profiler.BeginSample("Barracuda.Border2D");

                    Assert.IsNotNull(l.pad);
                    // NOTE: beta is used to retrieve fillin value
                    // because beta is 0 by default (while alpha is 1 by default)
                    // 0 value is more inline with zero padding
                    float fillValue = l.beta;
                    X = m_Ops.Border2D(X, l.pad, fillValue);
                }
                else if (l.type == Layer.Type.Pad2DReflect)
                {
                    Profiler.BeginSample("Barracuda.Pad2DReflect");

                    Assert.IsNotNull(l.pad);
                    X = m_Ops.Pad2DReflect(X, l.pad);
                }
                else if (l.type == Layer.Type.Pad2DSymmetric)
                {
                    Profiler.BeginSample("Barracuda.Pad2DSymmetric");

                    Assert.IsNotNull(l.pad);
                    X = m_Ops.Pad2DSymmetric(X, l.pad);
                }
                else if (l.type == Layer.Type.Pad2DEdge)
                {
                    Profiler.BeginSample("Barracuda.Pad2DEdge");

                    Assert.IsNotNull(l.pad);
                    X = m_Ops.Pad2DEdge(X, l.pad);
                }
                // 3D
                else if (l.type == Layer.Type.Conv3D ||
                         l.type == Layer.Type.Conv3DTrans ||
                         l.type == Layer.Type.Upsample3D ||
                         l.type == Layer.Type.MaxPool3D ||
                         l.type == Layer.Type.AvgPool3D ||
                         l.type == Layer.Type.GlobalMaxPool3D ||
                         l.type == Layer.Type.GlobalAvgPool3D ||
                         l.type == Layer.Type.Border3D)
                {
                    throw new NotImplementedException("3D operations are not implemented yet!");
                }
                else if (l.type == Layer.Type.ScaleBias)
                {
                    Assert.AreEqual(inputs.Length, 3);
                    Profiler.BeginSample("Barracuda.ScaleBias");
                    X = m_Ops.ScaleBias(X, inputs[1], inputs[2]);
                }
                else if (l.type == Layer.Type.Normalization)
                {
                    Assert.AreEqual(inputs.Length, 3);
                    Profiler.BeginSample("Barracuda.Normalization");
                    // @TODO: support other types of Normalization at test time.
                    // Currently supported only pool=1 (InstanceNormalization)

                    // NOTE: beta is used to retrieve epsilon value
                    // because beta is 0 by default (while alpha is 1 by default)
                    // 0 value is more inline with very small epsilon
                    var epsilon = l.beta;
                    if (epsilon == 0)
                    {
                        epsilon = Mathf.Epsilon; // safety check to prevent division by zero
                    }
                    X = m_Ops.Normalization(X, inputs[1], inputs[2], 1, l.axis, epsilon, GetAndVerifyFusedActivation(l));
                }
                else if (l.type == Layer.Type.LRN)
                {
                    Profiler.BeginSample("Barracuda.LRN");

                    Assert.IsNotNull(l.pool);
                    Assert.AreEqual(l.pool.Length, 1);
                    int   count = l.pool[0];
                    float bias  = (l.weights.Length > 0) ? l.weights[0] : 1.0f;
                    X = m_Ops.LRN(X, l.alpha, l.beta, bias, count);
                }
                // Stochastic layers
                else if (l.type == Layer.Type.Dropout)
                {
                    Profiler.BeginSample("Barracuda.Dropout");

                    X = m_Ops.Dropout(X, l.alpha);
                }
                else if (l.type == Layer.Type.RandomNormal)
                {
                    Profiler.BeginSample("Barracuda.RandomNormal");

                    Assert.IsNotNull(l.pool);
                    // pool size is treated as shape constant, if not empty
                    // otherwise shape of the previous tensor is used
                    var shape = X.shape;
                    if (l.pool.Length > 0)
                    {
                        shape = new TensorShape(l.pool);
                    }

                    int   seed = (l.pad.Length > 0) ? l.pad[0] : 1337;
                    float scale = l.alpha, mean = l.beta;
                    X = m_Ops.RandomNormal(shape, mean, scale, seed);
                }
                else if (l.type == Layer.Type.RandomUniform)
                {
                    Profiler.BeginSample("Barracuda.RandomUniform");

                    Assert.IsNotNull(l.pool);
                    // pool size is treated as shape constant, if not empty
                    // otherwise shape of the previous tensor is used
                    var shape = X.shape;
                    if (l.pool.Length > 0)
                    {
                        shape = new TensorShape(l.pool);
                    }

                    int   seed = (l.pad.Length > 0) ? l.pad[0] : 1337;
                    float scale = l.alpha, mean = l.beta;
                    X = m_Ops.RandomUniform(shape, mean, scale, seed);
                }
                else if (l.type == Layer.Type.Multinomial)
                {
                    Profiler.BeginSample("Barracuda.Multinomial");

                    Assert.IsNotNull(l.pool);
                    Assert.AreEqual(l.pool.Length, 1);

                    int count = l.pool[0];
                    int seed  = (l.pad.Length > 0) ? l.pad[0] : 1337;
                    X = m_Ops.Multinomial(X, count, seed);
                }
                else if (l.type == Layer.Type.OneHot)
                {
                    Profiler.BeginSample("Barracuda.OneHot");

                    Assert.IsNotNull(l.pool);
                    Assert.AreEqual(l.pool.Length, 1);
                    int   depth = l.pool[0];
                    float on = l.alpha, off = l.beta;
                    X = m_Ops.OneHot(X, depth, on, off);
                }
                // Broadcast layers
                else if (l.type == Layer.Type.Add)
                {
                    Profiler.BeginSample("Barracuda.Add");

                    X = m_Ops.Add(inputs);
                }
                else if (l.type == Layer.Type.Sub)
                {
                    Profiler.BeginSample("Barracuda.Sub");

                    X = m_Ops.Sub(inputs);
                }
                else if (l.type == Layer.Type.Mul)
                {
                    Profiler.BeginSample("Barracuda.Mul");

                    X = m_Ops.Mul(inputs);
                }
                else if (l.type == Layer.Type.Div)
                {
                    Profiler.BeginSample("Barracuda.Div");

                    X = m_Ops.Div(inputs);
                }
                else if (l.type == Layer.Type.Pow)
                {
                    Profiler.BeginSample("Barracuda.Pow");

                    X = m_Ops.Pow(inputs);
                }
                else if (l.type == Layer.Type.Min)
                {
                    Profiler.BeginSample("Barracuda.Min");

                    X = m_Ops.Min(inputs);
                }
                else if (l.type == Layer.Type.Max)
                {
                    Profiler.BeginSample("Barracuda.Max");

                    X = m_Ops.Max(inputs);
                }
                else if (l.type == Layer.Type.Mean)
                {
                    Profiler.BeginSample("Barracuda.Mean");

                    X = m_Ops.Mean(inputs);
                }
                // Reduction layers
                else if (l.type == Layer.Type.ReduceMax)
                {
                    Profiler.BeginSample("Barracuda.ReduceMax");

                    X = m_Ops.ReduceMax(X, l.axis);
                }
                else if (l.type == Layer.Type.ReduceMean)
                {
                    Profiler.BeginSample("Barracuda.ReduceMean");

                    X = m_Ops.ReduceMean(X, l.axis);
                }
                else if (l.type == Layer.Type.ReduceMin)
                {
                    Profiler.BeginSample("Barracuda.ReduceMin");

                    X = m_Ops.ReduceMin(X, l.axis);
                }
                else if (l.type == Layer.Type.ReduceProd)
                {
                    Profiler.BeginSample("Barracuda.ReduceProd");

                    X = m_Ops.ReduceProd(X, l.axis);
                }
                else if (l.type == Layer.Type.ReduceSum)
                {
                    Profiler.BeginSample("Barracuda.ReduceSum");

                    X = m_Ops.ReduceSum(X, l.axis);
                }
                else if (
                    l.type == Layer.Type.ReduceL1 ||
                    l.type == Layer.Type.ReduceL2 ||
                    l.type == Layer.Type.ReduceLogSum ||
                    l.type == Layer.Type.ReduceLogSumExp ||
                    l.type == Layer.Type.ReduceSumSquare)
                {
                    throw new NotImplementedException("This reduction operation is not implemented yet!");
                }
                // Logical operators with broadcast
                else if (l.type == Layer.Type.Greater)
                {
                    Assert.AreEqual(inputs.Length, 2);
                    Profiler.BeginSample("Barracuda.Greater");
                    X = m_Ops.Greater(X, inputs[1]);
                }
                else if (l.type == Layer.Type.GreaterEqual)
                {
                    Assert.AreEqual(inputs.Length, 2);
                    Profiler.BeginSample("Barracuda.GreaterEqual");
                    X = m_Ops.GreaterEqual(X, inputs[1]);
                }
                else if (l.type == Layer.Type.Less)
                {
                    Assert.AreEqual(inputs.Length, 2);
                    Profiler.BeginSample("Barracuda.Less");
                    X = m_Ops.Less(X, inputs[1]);
                }
                else if (l.type == Layer.Type.LessEqual)
                {
                    Assert.AreEqual(inputs.Length, 2);
                    Profiler.BeginSample("Barracuda.LessEqual");
                    X = m_Ops.LessEqual(X, inputs[1]);
                }
                else if (l.type == Layer.Type.Equal)
                {
                    Assert.AreEqual(inputs.Length, 2);
                    Profiler.BeginSample("Barracuda.Equal");
                    X = m_Ops.Equal(X, inputs[1]);
                }
                else if (l.type == Layer.Type.LogicalOr)
                {
                    Assert.AreEqual(inputs.Length, 2);
                    Profiler.BeginSample("Barracuda.LogicalOr");
                    X = m_Ops.LogicalOr(X, inputs[1]);
                }
                else if (l.type == Layer.Type.LogicalAnd)
                {
                    Assert.AreEqual(inputs.Length, 2);
                    Profiler.BeginSample("Barracuda.LogicalAnd");
                    X = m_Ops.LogicalAnd(X, inputs[1]);
                }
                else if (l.type == Layer.Type.LogicalXor)
                {
                    Assert.AreEqual(inputs.Length, 2);
                    Profiler.BeginSample("Barracuda.LogicalXor");
                    X = m_Ops.LogicalXor(X, inputs[1]);
                }
                else if (l.type == Layer.Type.LogicalNot)
                {
                    Profiler.BeginSample("Barracuda.LogicalNot");
                    X = m_Ops.LogicalNot(X);
                }
                // Shape affecting layers
                else if (l.type == Layer.Type.Flatten)
                {
                    Profiler.BeginSample("Barracuda.Flatten");
                    X = m_Ops.Flatten(X);
                }
                else if (l.type == Layer.Type.Reshape)
                {
                    Profiler.BeginSample("Barracuda.Reshape");

                    // pool size is treated as reshape coefficient, if not empty
                    // otherwise shape of the 2nd input tensor is used
                    var size = l.pool;

                    Assert.IsNotNull(size);
                    if (size.Length == 0 && inputs.Length > 1)
                    {
                        size = inputs[1].shape.ToArray();
                    }

                    var newShape = X.shape.Reshape(size);
                    X = m_Ops.Reshape(X, newShape);
                }
                else if (l.type == Layer.Type.Expand)
                {
                    Profiler.BeginSample("Barracuda.Expand");

                    // pool size is treated as new shape
                    var newShape = l.pool;

                    Assert.IsNotNull(newShape);
                    Assert.AreEqual(newShape.Length, 4);

                    X = m_Ops.Expand(X, new TensorShape(newShape));
                }
                else if (l.type == Layer.Type.Transpose)
                {
                    Profiler.BeginSample("Barracuda.Transpose");
                    X = m_Ops.Transpose(X);
                }
                else if (l.type == Layer.Type.Gather)
                {
                    Profiler.BeginSample("Barracuda.Gather");
                    X = m_Ops.Gather(inputs, l.axis);
                }
                else if (l.type == Layer.Type.Squeeze ||
                         l.type == Layer.Type.Unsqueeze)
                {
                    throw new NotImplementedException();
                }
                else if (l.type == Layer.Type.Concat)
                {
                    Profiler.BeginSample("Barracuda.Concat");
                    X = m_Ops.Concat(inputs, l.axis);
                }
                else if (l.type == Layer.Type.StridedSlice)
                {
                    Profiler.BeginSample("Barracuda.StridedSlice");

                    Assert.IsNotNull(l.pad);
                    Assert.IsNotNull(l.pool);
                    Assert.IsNotNull(l.stride);
                    X = m_Ops.StridedSlice(X, l.pad, l.pool, l.stride);
                }
                else if (l.type == Layer.Type.Tile)
                {
                    throw new NotImplementedException();
                }
                // Activations
                else if (l.type == Layer.Type.Activation)
                {
                    Profiler.BeginSample("Barracuda.Activation");

                    if (l.activation == Layer.Activation.Relu)
                    {
                        X = m_Ops.Relu(X);
                    }
                    else if (l.activation == Layer.Activation.Softmax)
                    {
                        X = m_Ops.Softmax(X);
                    }
                    else if (l.activation == Layer.Activation.LogSoftmax)
                    {
                        X = m_Ops.LogSoftmax(X);
                    }
                    else if (l.activation == Layer.Activation.Tanh)
                    {
                        X = m_Ops.Tanh(X);
                    }
                    else if (l.activation == Layer.Activation.Sigmoid)
                    {
                        X = m_Ops.Sigmoid(X);
                    }
                    else if (l.activation == Layer.Activation.Relu6)
                    {
                        X = m_Ops.Relu6(X);
                    }
                    else if (l.activation == Layer.Activation.Elu)
                    {
                        X = m_Ops.Elu(X, l.alpha);
                    }
                    else if (l.activation == Layer.Activation.LeakyRelu)
                    {
                        X = m_Ops.LeakyRelu(X, l.alpha);
                    }
                    else if (l.activation == Layer.Activation.Selu)
                    {
                        X = m_Ops.Selu(X, l.alpha, l.beta);
                    }
                    else if (l.activation == Layer.Activation.Swish)
                    {
                        X = m_Ops.Swish(X);
                    }
                    else if (l.activation == Layer.Activation.PRelu)
                    {
                        Assert.AreEqual(inputs.Length, 2);
                        X = m_Ops.PRelu(X, inputs[1]);
                    }
                    else if (
                        l.activation == Layer.Activation.Softplus ||
                        l.activation == Layer.Activation.Softsign ||
                        l.activation == Layer.Activation.Hardmax ||
                        l.activation == Layer.Activation.HardSigmoid)
                    {
                        throw new NotImplementedException("This activation function is not implemented yet!");
                    }
                    else if (l.activation == Layer.Activation.Abs)
                    {
                        X = m_Ops.Abs(X);
                    }
                    else if (l.activation == Layer.Activation.Neg)
                    {
                        X = m_Ops.Neg(X);
                    }
                    else if (l.activation == Layer.Activation.Ceil)
                    {
                        X = m_Ops.Ceil(X);
                    }
                    else if (l.activation == Layer.Activation.Clip)
                    {
                        X = m_Ops.Clip(X, l.alpha, l.beta);
                    }
                    else if (l.activation == Layer.Activation.Floor)
                    {
                        X = m_Ops.Floor(X);
                    }
                    else if (l.activation == Layer.Activation.Reciprocal)
                    {
                        X = m_Ops.Reciprocal(X);
                    }
                    else if (l.activation == Layer.Activation.Pow)
                    {
                        X = m_Ops.Pow(X, l.alpha);
                    }
                    else if (l.activation == Layer.Activation.Exp)
                    {
                        X = m_Ops.Exp(X);
                    }
                    else if (l.activation == Layer.Activation.Log)
                    {
                        X = m_Ops.Log(X);
                    }
                    else if (l.activation == Layer.Activation.Sqrt)
                    {
                        X = m_Ops.Sqrt(X);
                    }
                    else if ((int)l.activation >= (int)Layer.Activation.Acos &&
                             (int)l.activation <= (int)Layer.Activation.Tan)
                    {
                        throw new NotImplementedException("Trig functions are not implemented yet!");
                    }
                    else
                    {
                        X = m_Ops.Copy(X);
                    }
                }
                else
                {
                    Profiler.BeginSample("Barracuda.Dummy");
                    Assert.AreEqual(l.activation, Layer.Activation.None);
                }

                m_Vars.Store(l, X);
                m_SyncTensor = X;

                // optype
                Profiler.EndSample();

                // layer.name
                Profiler.EndSample();

                yield return(null);
            }

            // request ResetAllocator before next Execute() starts
            m_RequestResetAllocator = true;
            Profiler.EndSample();

            if (m_Verbose)
            {
                D.Log(m_Vars.GetAllocator());
            }
        }
 /// <summary>
 /// Apply shape to the input tensor. Number of elements in the shape must match number of elements in input tensor.
 /// </summary>
 public Layer Reshape(string name, object input, TensorShape shape)
 {
     return(Reshape(name, input, shape.ToArray()));
 }
 /// <summary>
 /// Upload data to internal storage
 /// </summary>
 /// <param name="data">data</param>
 /// <param name="shape">shape</param>
 /// <param name="managedBufferStartIndex">`data` start index</param>
 public override void Upload(float[] data, TensorShape shape, int managedBufferStartIndex = 0)
 {
     CompleteAllPendingOperations();
     base.Upload(data, shape, managedBufferStartIndex);
 }
Beispiel #5
0
 static internal TensorShape ApplyPool(this TensorShape shape, int[] pool, int[] stride, int[] pad, bool ceilMode = false)
 {
     return(ApplyPool(shape, (pool[0], pool[1]), stride, pad, ceilMode));
 }
Beispiel #6
0
 static internal TensorShape ApplyKernel(this TensorShape shape, TensorShape kernel, int[] stride, int[] pad)
 {
     int[] shapeArray = ApplyPool(shape, (kernel.kernelWidth, kernel.kernelHeight), stride, pad).ToArray();
     shapeArray[7] = kernel.kernelCount;
     return(new TensorShape(shapeArray));
 }
Beispiel #7
0
 static internal int[] AdjustPadToKernel(this TensorShape shape, TensorShape kernel, int[] stride, int[] pad)
 {
     return(AdjustPadToPool(shape, (kernel.kernelWidth, kernel.kernelHeight), stride, pad));
 }
Beispiel #8
0
 static internal int[] AdjustPadToPool(this TensorShape shape, int[] pool, int[] stride, int[] pad)
 {
     return(AdjustPadToPool(shape, (pool[0], pool[1]), stride, pad));
 }
Beispiel #9
0
        /// <summary>
        /// Reshape TensorShape into new shape specified by `size`. At most one dimension of the new shape can be -1.
        /// See: https://github.com/onnx/onnx/blob/master/docs/Operators.md#Reshape
        /// </summary>
        /// <param name="shape">TensorShape</param>
        /// <param name="size">new shape</param>
        /// <returns>output shape</returns>
        /// <exception cref="ArgumentException">more than one dimension is unspecified</exception>
        static public TensorShape Reshape(this TensorShape shape, int[] size)
        {
            size = Get8DParametersFromNHWCParametersAndShape(shape, size, 1);
            var newShapeArray = shape.ToArray();

            // From: https://github.com/onnx/onnx/blob/master/docs/Operators.md#Reshape
            //
            // At most one dimension of the new shape can be -1.
            // In this case, the value is inferred from the size of the tensor and the remaining dimensions.
            //
            // A dimension could also be 0,
            // in which case the actual dimension value is unchanged (i.e. taken from the input tensor).

            var multipleOf   = 1;
            var unknownIndex = -1;

            for (int q = 0; q < size.Length; ++q)
            {
                if (size[q] > 0)
                {
                    multipleOf      *= size[q];
                    newShapeArray[q] = size[q];
                }
                else if (size[q] == 0)
                {
                    multipleOf *= newShapeArray[q];
                }
                else if (unknownIndex == -1)
                {
                    unknownIndex = q;
                }
                else
                {
                    throw new ArgumentException("Can only specify one unknown dimension");
                }
            }

            if (unknownIndex == -1)
            {
                // all dimensions are given
                var newShape = new TensorShape(newShapeArray);
                if (shape.length != newShape.length)
                {
                    throw new ArgumentException("Cannot reshape array of size " + shape.length +
                                                " into shape " + newShape);
                }
                return(newShape);
            }

            var  solveForIndex = shape.length / multipleOf;
            bool remainderLeft = shape.length % multipleOf != 0;

            if (remainderLeft)
            {
                throw new ArgumentException("Cannot reshape array of size " + shape.length +
                                            " into shape with multiple of " + multipleOf + " elements");
            }

            newShapeArray[unknownIndex] = solveForIndex;
            return(new TensorShape(newShapeArray));
        }
Beispiel #10
0
        public static TensorShape?[] ListTemporaryTensorShapes(Model model, IDictionary <string, TensorShape> inputShapes,
                                                               out IDictionary <string, TensorShape?> shapesByName)
        {
            Profiler.BeginSample("Barracuda.ListTemporaryTensorShapes");
            var shapes = new List <TensorShape?>();

            shapesByName = new Dictionary <string, TensorShape?>();
            foreach (var entry in inputShapes)
            {
                shapesByName.Add(entry.Key, entry.Value);
            }

            TensorShape?Xn;

            shapesByName.TryGetValue(GetDefaultInputName(model), out Xn); // default input
            TensorShape?O = Xn;

            foreach (var l in model.layers)
            {
                if (l.inputs.Length > 0 && shapesByName.TryGetValue(l.inputs[0], out TensorShape? xShape))
                {
                    Xn = xShape;
                }
                else
                {
                    Xn = O; // previous output is used, if-and-only-if layer has no explicit inputs
                }
                if (Xn == null)
                {
                    shapes.Add(Xn);
                    shapesByName.Add(l.name, Xn);
                    continue;
                }

                TensorShape X = Xn.Value;

                if (l.type == Layer.Type.Dense)
                {
                    Assert.IsNotNull(l.datasets);
                    var W = l.datasets[0].shape;
                    O = new TensorShape(X.flatHeight, W.flatWidth);
                }
                else if (l.type == Layer.Type.MatMul)
                {
                    if (!shapesByName.ContainsKey(l.inputs[1]) || shapesByName[l.inputs[1]] == null)
                    {
                        O = null;
                        break;
                    }

                    var Y = shapesByName[l.inputs[1]].Value;

                    // MatMul on 2D input: N,1,1,C
                    // MatMul on ND inputs: N,H,W,C with N*C = batches
                    var isStackOfMatrices = (X.dimensions != 2) || (Y.dimensions != 2);
                    if (isStackOfMatrices)
                    {
                        // X is multidim so N,H,W,c with H,W matmul dims
                        var batches = X.batch * X.channels;
                        Assert.AreEqual(X.batch * X.channels, Y.batch * Y.channels);

                        O = new TensorShape(X.batch, X.height, Y.width, X.channels);
                    }
                    else
                    {
                        O = new TensorShape(X.flatHeight, Y.flatWidth);
                    }
                }
                else if (
                    l.type == Layer.Type.Conv2D ||
                    l.type == Layer.Type.DepthwiseConv2D)
                {
                    var K = l.datasets[0].shape;

                    Assert.IsNotNull(l.stride);
                    Assert.IsNotNull(l.pad);
                    var pad = X.AdjustPadToKernel(K, l.stride, l.pad);

                    O = X.ApplyKernel(K, l.stride, pad);
                }
                else if (
                    l.type == Layer.Type.Conv2DTrans)
                {
                    var K = l.datasets[0].shape;
                    Assert.IsNotNull(l.stride);
                    Assert.IsNotNull(l.pad);
                    // pool size is treated as output_adjustment aka output_padding here
                    var outputAdjustment = l.pool;
                    var pad = X.AdjustPadToKernel(K, l.stride, l.pad);
                    O = X.ApplyKernelInverse(K, l.stride, pad, outputAdjustment);
                }
                else if (
                    l.type == Layer.Type.Upsample2D)
                {
                    if (inputShapes.Count > 1)
                    {
                        O = null;
                    }
                    else
                    {
                        // pool size is treated as upsample coefficient here
                        Assert.IsNotNull(l.pool);
                        Assert.AreEqual(l.pool.Length, 2);
                        O = new TensorShape(X.batch, X.height * l.pool[1], X.width * l.pool[0], X.channels);
                    }
                }
                else if (
                    l.type == Layer.Type.Resample2D)
                {
                    if (inputShapes.Count > 1)
                    {
                        O = null;
                    }
                    else
                    {
                        // pool is treated as resample size here
                        var size = l.pool;
                        Assert.IsNotNull(size);
                        Assert.AreEqual(size.Length, 2);
                        O = new TensorShape(X.batch, size[1], size[0], X.channels);
                    }
                }
                else if (
                    l.type == Layer.Type.DepthToSpace)
                {
                    // pool size is treated as blocksize here
                    Assert.IsNotNull(l.pool);
                    Assert.AreEqual(l.pool.Length, 2);
                    Assert.AreEqual(X.channels % (l.pool[0] * l.pool[1]), 0);
                    O = new TensorShape(X.batch, X.height * l.pool[1], X.width * l.pool[0], X.channels / (l.pool[0] * l.pool[1]));
                }
                else if (
                    l.type == Layer.Type.SpaceToDepth)
                {
                    // pool size is treated as blocksize here
                    Assert.IsNotNull(l.pool);
                    Assert.AreEqual(l.pool.Length, 2);
                    O = new TensorShape(X.batch, X.height / l.pool[1], X.width / l.pool[0], X.channels * (l.pool[0] * l.pool[1]));
                }
                else if (
                    l.type == Layer.Type.MaxPool2D ||
                    l.type == Layer.Type.AvgPool2D)
                {
                    Assert.IsNotNull(l.pool);
                    Assert.IsNotNull(l.stride);
                    Assert.IsNotNull(l.pad);
                    var pad = X.AdjustPadToPool(l.pool, l.stride, l.pad);
                    O = X.ApplyPool(l.pool, l.stride, pad);
                }
                else if (
                    l.type == Layer.Type.GlobalMaxPool2D ||
                    l.type == Layer.Type.GlobalAvgPool2D)
                {
                    O = new TensorShape(X.batch, 1, 1, X.channels);
                }
                else if (
                    l.type == Layer.Type.Border2D ||
                    l.type == Layer.Type.Pad2DReflect ||
                    l.type == Layer.Type.Pad2DSymmetric ||
                    l.type == Layer.Type.Pad2DEdge)
                {
                    Assert.IsNotNull(l.pad);
                    O = X.ApplyBorder(l.pad);
                }
                else if (
                    l.type == Layer.Type.Conv3D ||
                    l.type == Layer.Type.Conv3DTrans ||
                    l.type == Layer.Type.Upsample3D ||
                    l.type == Layer.Type.MaxPool3D ||
                    l.type == Layer.Type.AvgPool3D ||
                    l.type == Layer.Type.GlobalMaxPool3D ||
                    l.type == Layer.Type.GlobalAvgPool3D ||
                    l.type == Layer.Type.Border3D)
                {
                    throw new NotImplementedException();
                }
                else if (
                    l.type == Layer.Type.RandomNormal ||
                    l.type == Layer.Type.RandomUniform)
                {
                    Assert.IsNotNull(l.pool);
                    // pool size is treated as shape constant, if not empty
                    // otherwise shape of the previous tensor is used
                    if (l.pool.Length > 0)
                    {
                        O = new TensorShape(l.pool);
                    }
                    else
                    {
                        O = X;
                    }
                }
                else if (
                    l.type == Layer.Type.Multinomial)
                {
                    Assert.IsNotNull(l.pool);
                    Assert.AreEqual(l.pool.Length, 1);
                    O = new TensorShape(X.batch, l.pool[0]);
                }
                else if (
                    l.type == Layer.Type.OneHot)
                {
                    Assert.IsNotNull(l.pool);
                    Assert.AreEqual(l.pool.Length, 1);
                    int features = X.flatWidth;
                    int depth    = l.pool[0];
                    O = new TensorShape(X.batch, 1, features, depth);
                }
                else if (
                    l.type == Layer.Type.Add ||
                    l.type == Layer.Type.Sub ||
                    l.type == Layer.Type.Mul ||
                    l.type == Layer.Type.Div ||
                    l.type == Layer.Type.Pow ||
                    l.type == Layer.Type.Min ||
                    l.type == Layer.Type.Max ||
                    l.type == Layer.Type.Mean ||
                    l.type == Layer.Type.Greater ||
                    l.type == Layer.Type.GreaterEqual ||
                    l.type == Layer.Type.Less ||
                    l.type == Layer.Type.LessEqual ||
                    l.type == Layer.Type.Equal ||
                    l.type == Layer.Type.LogicalOr ||
                    l.type == Layer.Type.LogicalAnd ||
                    l.type == Layer.Type.LogicalXor)
                {
                    // gather shapes by names
                    var  list           = new List <TensorShape>(l.inputs.Length);
                    bool allShapesKnown = true;
                    foreach (var i in l.inputs)
                    {
                        if (shapesByName.TryGetValue(i, out TensorShape? shape) && shape != null)
                        {
                            list.Add(shape.Value);
                        }
                        else
                        {
                            allShapesKnown = false;
                        }
                    }

                    O = allShapesKnown ? TensorExtensions.Max(list.ToArray()) : default(TensorShape?);
                }
                else if (
                    l.type == Layer.Type.ReduceL1 ||
                    l.type == Layer.Type.ReduceL2 ||
                    l.type == Layer.Type.ReduceLogSum ||
                    l.type == Layer.Type.ReduceLogSumExp ||
                    l.type == Layer.Type.ReduceMax ||
                    l.type == Layer.Type.ReduceMean ||
                    l.type == Layer.Type.ReduceMin ||
                    l.type == Layer.Type.ReduceProd ||
                    l.type == Layer.Type.ReduceSum ||
                    l.type == Layer.Type.ReduceSumSquare)
                {
                    O = X.Reduce(l.axis);
                }
                else if (
                    l.type == Layer.Type.Flatten)
                {
                    O = X.Flatten();
                }
                else if (
                    l.type == Layer.Type.Reshape)
                {
                    // pool size is treated as the shape, if not empty
                    var size = l.pool;

                    Assert.IsNotNull(size);

                    if (size.Length == 0 && l.inputs.Length > 1)
                    {
                        switch (l.axis)
                        {
                        // Legacy - use the shape of the input tensor as the shape
                        case -1:
                            if (shapesByName.TryGetValue(l.inputs[1], out TensorShape? shape))
                            {
                                size = shape.Value.ToArray();
                            }
                            break;

                        // Use the tensor values as the shape; Calculated at runtime
                        case 1:
                            O = null;
                            break;
                        }

                        if (O == null)
                        {
                            break;
                        }
                    }

                    Assert.IsTrue((size.Length == 4) || (size.Length == 8));
                    O = X.Reshape(size);
                }
                else if (
                    l.type == Layer.Type.Expand)
                {
                    // pool size is treated as new shape
                    var newShape = l.pool;

                    Assert.IsNotNull(newShape);
                    Assert.IsTrue(newShape.Length == 8 || newShape.Length == 4);

                    O = new TensorShape(newShape);
                }
                else if (
                    l.type == Layer.Type.Transpose)
                {
                    var permutations = l.pool;
                    if (permutations == null)
                    {
                        O = new TensorShape(X.flatWidth, X.flatHeight);
                    }
                    else
                    {
                        Assert.IsTrue(permutations.Length == 8 || permutations.Length == 4);
                        O = X.Permute(permutations);
                    }
                }
                else if (
                    l.type == Layer.Type.Gather)
                {
                    if (!shapesByName.TryGetValue(l.inputs[0], out TensorShape? input0Shape) || input0Shape == null ||
                        !shapesByName.TryGetValue(l.inputs[1], out TensorShape? input1Shape) || input1Shape == null)
                    {
                        O = null;
                        break;
                    }

                    int[] shape = input0Shape.Value.ToArray();
                    shape[l.axis] = input1Shape.Value.length;

                    O = new TensorShape(shape);
                }
                else if (
                    l.type == Layer.Type.Squeeze ||
                    l.type == Layer.Type.Unsqueeze)
                {
                    throw new NotImplementedException();
                }
                else if (
                    l.type == Layer.Type.Concat)
                {
                    // gather shapes by names
                    var  list           = new List <TensorShape>(l.inputs.Length);
                    bool allShapesKnown = true;
                    foreach (var i in l.inputs)
                    {
                        if (!shapesByName.TryGetValue(i, out var shape) || shape == null)
                        {
                            allShapesKnown = false;
                            continue;
                        }
                        list.Add(shape.Value);
                    }

                    O = allShapesKnown ? TensorExtensions.Concat(list.ToArray(), l.axis) : default(TensorShape?);
                }
                else if (
                    l.type == Layer.Type.StridedSlice)
                {
                    Assert.IsNotNull(l.pad);
                    Assert.IsNotNull(l.pool);
                    Assert.IsNotNull(l.stride);
                    O = X.ApplyStridedSlice(l.pad, l.pool, l.stride);
                }
                else if (
                    l.type == Layer.Type.Tile)
                {
                    // pool size is treated as tiling coefficient here
                    Assert.IsNotNull(l.pool);
                    Assert.AreEqual(l.pool.Length, 4);
                    var scale = l.pool;
                    O = X.Scale(scale);
                }
                else if (
                    l.type == Layer.Type.Load)
                {
                    O = l.datasets[0].shape;
                }
                else if (// elementwise operations
                    l.type == Layer.Type.Nop ||
                    l.type == Layer.Type.Activation ||
                    l.type == Layer.Type.ScaleBias ||
                    l.type == Layer.Type.Normalization ||
                    l.type == Layer.Type.LRN ||
                    l.type == Layer.Type.Dropout ||
                    l.type == Layer.Type.LogicalNot ||
                    l.activation == Layer.Activation.PRelu)
                {
                    // works in place, keeps the same shape size
                    O = X;
                }
                else if (
                    l.type == Layer.Type.TopKIndices ||
                    l.type == Layer.Type.TopKValues ||
                    l.type == Layer.Type.NonMaxSuppression ||
                    l.type == Layer.Type.NonZero)
                {
                    // Calculated at runtime
                    O = null;
                }
                else if (l.type == Layer.Type.Shape)
                {
                    int shapeRank = l.axis > 0 ? 1 : X.length;
                    O = new TensorShape(shapeRank, 1, 1, 1);
                }
                else if (l.type == Layer.Type.Where)
                {
                    O = X;
                }
                else if (
                    l.type == Layer.Type.Conv3D ||
                    l.type == Layer.Type.Conv3DTrans ||
                    l.type == Layer.Type.Upsample3D ||
                    l.type == Layer.Type.MaxPool3D ||
                    l.type == Layer.Type.AvgPool3D ||
                    l.type == Layer.Type.GlobalMaxPool3D ||
                    l.type == Layer.Type.GlobalAvgPool3D ||
                    l.type == Layer.Type.Border3D)
                {
                    throw new NotImplementedException("3D operations are not implemented yet!");
                }
                else
                {
                    throw new NotImplementedException($"Layer type {l.type} needs to be explicitly handled");
                }

                shapes.Add(O);
                shapesByName.Add(l.name, O);
            }

            Profiler.EndSample();
            return(shapes.ToArray());
        }
Beispiel #11
0
        public static bool TryGetOutputTensorShape(Model model, IDictionary <string, TensorShape> inputShapes, string output, out TensorShape shape)
        {
            shape = new TensorShape();
            IDictionary <string, TensorShape?> shapesByName;

            ListTemporaryTensorShapes(model, inputShapes, out shapesByName);

            TensorShape?dynamicShape;
            bool        found = shapesByName.TryGetValue(output, out dynamicShape);

            if (found && (dynamicShape != null))
            {
                shape = dynamicShape.Value;
            }
            return(found);
        }
Beispiel #12
0
        public static TensorShape?[] ListTemporaryTensorShapes(Model model, IDictionary <string, TensorShape> inputShapes,
                                                               out IDictionary <string, TensorShape?> shapesByName)
        {
            Profiler.BeginSample("Barracuda.ListTemporaryTensorShapes");
            var shapes = new List <TensorShape?>();

            shapesByName = new Dictionary <string, TensorShape?>();
            foreach (var entry in inputShapes)
            {
                shapesByName.Add(entry.Key, entry.Value);
            }

            TensorShape?Xn;

            shapesByName.TryGetValue(GetDefaultInputName(model), out Xn); // default input
            TensorShape?O = Xn;

            foreach (var l in model.layers)
            {
                if (l.inputs.Length > 0 && shapesByName.TryGetValue(l.inputs[0], out TensorShape? xShape))
                {
                    Xn = xShape;
                }
                else
                {
                    Xn = O; // previous output is used, if-and-only-if layer has no explicit inputs
                }
                if (Xn == null)
                {
                    shapes.Add(Xn);
                    shapesByName.Add(l.name, Xn);
                    continue;
                }

                TensorShape X = Xn.Value;

                if (l.type == Layer.Type.Dense)
                {
                    Assert.IsNotNull(l.datasets);
                    var W = l.datasets[0].shape;
                    O = new TensorShape(X.flatHeight, W.flatWidth);
                }
                else if (l.type == Layer.Type.Dense3)
                {
                    Assert.IsNotNull(l.datasets);
                    var W = l.datasets[0].shape;
                    O = new TensorShape(X.batch, 1, W.channels, X.channels);
                }
                else if (l.type == Layer.Type.MatMul)
                {
                    if (!shapesByName.ContainsKey(l.inputs[1]) || shapesByName[l.inputs[1]] == null)
                    {
                        O = null;
                        break;
                    }

                    var Y = shapesByName[l.inputs[1]].Value;

                    int        rankX;
                    int        rankY;
                    List <int> onnxXshape;
                    List <int> onnxYshape;

                    if (l.pool == null || l.pool.Length == 0)
                    {
                        LegacyGetXYRanks(X, Y, out rankX, out rankY);
                    }
                    else
                    {
                        rankX = l.pool[0];
                        rankY = l.pool[1];
                    }

                    onnxXshape = Compiler.IRShapeInferenceHelper.ShapeInference.BarracudaShapeToOnnxLayout(X, rankX);
                    onnxYshape = Compiler.IRShapeInferenceHelper.ShapeInference.BarracudaShapeToOnnxLayout(Y, rankY);

                    int rankO = Math.Max(rankX, rankY);

                    // pad 1 on front of shape to both be rankO shape
                    for (int i = 0; i < (rankX - rankY); i++)
                    {
                        onnxYshape.Insert(0, 1);
                    }

                    for (int i = 0; i < (rankY - rankX); i++)
                    {
                        onnxXshape.Insert(0, 1);
                    }

                    if (rankO == 2)
                    {
                        O = new TensorShape(onnxXshape[0], 1, 1, onnxYshape[1]);
                    }
                    else if (rankO == 3)
                    {
                        O = new TensorShape(Math.Max(onnxXshape[0], onnxYshape[0]), 1, onnxYshape[2], onnxXshape[1]);
                    }
                    else
                    {
                        O = new TensorShape(Math.Max(onnxXshape[0], onnxYshape[0]), onnxXshape[2], onnxYshape[3], Math.Max(onnxXshape[1], onnxYshape[1]));
                    }
                }
                else if (
                    l.type == Layer.Type.Conv2D ||
                    l.type == Layer.Type.Conv3D ||
                    l.type == Layer.Type.DepthwiseConv2D)
                {
                    var K = l.datasets[0].shape;

                    Assert.IsNotNull(l.stride);
                    Assert.IsNotNull(l.pad);
                    var pad = X.AdjustPadToKernel(K, l.stride, l.pad);

                    O = X.ApplyKernel(K, l.stride, pad);
                }
                else if (
                    l.type == Layer.Type.Conv2DTrans)
                {
                    var K = l.datasets[0].shape;
                    Assert.IsNotNull(l.stride);
                    Assert.IsNotNull(l.pad);
                    // pool size is treated as output_adjustment aka output_padding here
                    var outputAdjustment = l.pool;
                    var pad = X.AdjustPadToKernel(K, l.stride, l.pad);
                    O = X.ApplyKernelInverse(K, l.stride, pad, outputAdjustment);
                }
                else if (
                    l.type == Layer.Type.Upsample2D)
                {
                    if (inputShapes.Count > 1)
                    {
                        O = null;
                    }
                    else
                    {
                        // pool size is treated as upsample coefficient here
                        Assert.IsNotNull(l.pool);
                        Assert.AreEqual(l.pool.Length, 2);
                        O = new TensorShape(X.batch, X.height * l.pool[1], X.width * l.pool[0], X.channels);
                    }
                }
                else if (
                    l.type == Layer.Type.Upsample3D)
                {
                    if (inputShapes.Count > 1)
                    {
                        O = null;
                    }
                    else
                    {
                        // pool size is treated as upsample coefficient here
                        Assert.IsNotNull(l.pool);
                        Assert.AreEqual(l.pool.Length, 3);
                        O = new TensorShape(1, 1, X.batch, 1, X.depth * l.pool[2], X.height * l.pool[1], X.width * l.pool[0], X.channels);
                    }
                }
                else if (
                    l.type == Layer.Type.Resample2D)
                {
                    if (inputShapes.Count > 1)
                    {
                        O = null;
                    }
                    else
                    {
                        // pool is treated as resample size here
                        var size = l.pool;
                        Assert.IsNotNull(size);
                        Assert.AreEqual(size.Length, 2);
                        O = new TensorShape(X.batch, size[1], size[0], X.channels);
                    }
                }
                else if (
                    l.type == Layer.Type.DepthToSpace)
                {
                    // pool size is treated as blocksize here
                    Assert.IsNotNull(l.pool);
                    Assert.AreEqual(l.pool.Length, 2);
                    Assert.AreEqual(X.channels % (l.pool[0] * l.pool[1]), 0);
                    O = new TensorShape(X.batch, X.height * l.pool[1], X.width * l.pool[0], X.channels / (l.pool[0] * l.pool[1]));
                }
                else if (
                    l.type == Layer.Type.SpaceToDepth)
                {
                    // pool size is treated as blocksize here
                    Assert.IsNotNull(l.pool);
                    Assert.AreEqual(l.pool.Length, 2);
                    O = new TensorShape(X.batch, X.height / l.pool[1], X.width / l.pool[0], X.channels * (l.pool[0] * l.pool[1]));
                }
                else if (
                    l.type == Layer.Type.MaxPool2D ||
                    l.type == Layer.Type.AvgPool2D)
                {
                    Assert.IsNotNull(l.pool);
                    Assert.IsNotNull(l.stride);
                    Assert.IsNotNull(l.pad);
                    var pad = X.AdjustPadToPool(l.pool, l.stride, l.pad);
                    O = X.ApplyPool(l.pool, l.stride, pad);
                }
                else if (
                    l.type == Layer.Type.GlobalMaxPool2D ||
                    l.type == Layer.Type.GlobalAvgPool2D)
                {
                    O = new TensorShape(X.batch, 1, 1, X.channels);
                }
                else if (l.type == Layer.Type.Border3D)
                {
                    Assert.IsNotNull(l.pad);
                    // legacy support
                    if (l.pad.Length == 6)
                    {
                        X = X.ApplyBorder(new[] { l.pad[0], l.pad[1], l.pad[2], 0, l.pad[3], l.pad[4], l.pad[5], 0 });
                    }
                    else
                    {
                        O = X.ApplyBorder(l.pad);
                    }
                }
                else if (
                    l.type == Layer.Type.Border2D ||
                    l.type == Layer.Type.Pad2DReflect ||
                    l.type == Layer.Type.Pad2DSymmetric ||
                    l.type == Layer.Type.Pad2DEdge)
                {
                    Assert.IsNotNull(l.pad);
                    // legacy support
                    if (l.pad.Length == 4)
                    {
                        X = X.ApplyBorder(new[] { l.pad[0], l.pad[1], 0, l.pad[2], l.pad[3], 0 });
                    }
                    else
                    {
                        O = X.ApplyBorder(l.pad);
                    }
                }
                else if (
                    l.type == Layer.Type.Conv3D ||
                    l.type == Layer.Type.Conv3DTrans ||
                    l.type == Layer.Type.Upsample3D ||
                    l.type == Layer.Type.MaxPool3D ||
                    l.type == Layer.Type.AvgPool3D ||
                    l.type == Layer.Type.GlobalMaxPool3D ||
                    l.type == Layer.Type.GlobalAvgPool3D ||
                    l.type == Layer.Type.Border3D)
                {
                    throw new NotImplementedException();
                }
                else if (
                    l.type == Layer.Type.RandomNormal ||
                    l.type == Layer.Type.RandomUniform)
                {
                    Assert.IsNotNull(l.pool);
                    // pool size is treated as shape constant, if not empty
                    // otherwise shape of the previous tensor is used
                    if (l.pool.Length > 0)
                    {
                        O = new TensorShape(l.pool);
                    }
                    else
                    {
                        O = X;
                    }
                }
                else if (l.type == Layer.Type.ConstantOfShape)
                {
                    if (l.axis != 1)
                    {
                        O = null;
                    }
                    else
                    {
                        O = X;
                    }
                }
                else if (
                    l.type == Layer.Type.Multinomial)
                {
                    Assert.IsNotNull(l.pool);
                    Assert.AreEqual(l.pool.Length, 1);
                    O = new TensorShape(X.batch, l.pool[0]);
                }
                else if (
                    l.type == Layer.Type.OneHot)
                {
                    Assert.IsNotNull(l.pool);
                    Assert.AreEqual(l.pool.Length, 1);
                    int features = X.flatWidth;
                    int depth    = l.pool[0];

                    if (X.flatWidth == 1) // 1D input
                    {
                        O = new TensorShape(X.batch, depth);
                    }
                    else
                    {
                        O = new TensorShape(X.batch, 1, depth, features);
                    }
                }
                else if (l.type == Layer.Type.RoiAlign)
                {
                    Assert.IsNotNull(l.pool);
                    Assert.AreEqual(l.pool.Length, 2);

                    if (shapesByName.TryGetValue(l.inputs[1], out TensorShape? shape) && shape != null)
                    {
                        int batches = shape.Value.flatHeight;
                        O = new TensorShape(batches, l.pool[0], l.pool[1], X.channels);
                    }
                    else
                    {
                        O = null;
                    }
                }
                else if (
                    l.type == Layer.Type.Add ||
                    l.type == Layer.Type.Sub ||
                    l.type == Layer.Type.Mul ||
                    l.type == Layer.Type.Div ||
                    l.type == Layer.Type.Pow ||
                    l.type == Layer.Type.Min ||
                    l.type == Layer.Type.Max ||
                    l.type == Layer.Type.Mean ||
                    l.type == Layer.Type.Greater ||
                    l.type == Layer.Type.GreaterEqual ||
                    l.type == Layer.Type.Less ||
                    l.type == Layer.Type.LessEqual ||
                    l.type == Layer.Type.Equal ||
                    l.type == Layer.Type.LogicalOr ||
                    l.type == Layer.Type.LogicalAnd ||
                    l.type == Layer.Type.LogicalXor ||
                    l.type == Layer.Type.Where)
                {
                    // gather shapes by names
                    var  list           = new List <TensorShape>(l.inputs.Length);
                    bool allShapesKnown = true;
                    foreach (var i in l.inputs)
                    {
                        if (shapesByName.TryGetValue(i, out TensorShape? shape) && shape != null)
                        {
                            list.Add(shape.Value);
                        }
                        else
                        {
                            allShapesKnown = false;
                        }
                    }

                    O = allShapesKnown ? TensorExtensions.Max(list.ToArray()) : default(TensorShape?);
                }
                else if (
                    l.type == Layer.Type.ReduceL1 ||
                    l.type == Layer.Type.ReduceL2 ||
                    l.type == Layer.Type.ReduceLogSum ||
                    l.type == Layer.Type.ReduceLogSumExp ||
                    l.type == Layer.Type.ReduceMax ||
                    l.type == Layer.Type.ReduceMean ||
                    l.type == Layer.Type.ReduceMin ||
                    l.type == Layer.Type.ReduceProd ||
                    l.type == Layer.Type.ReduceSum ||
                    l.type == Layer.Type.ReduceSumSquare ||
                    l.type == Layer.Type.ArgMax ||
                    l.type == Layer.Type.ArgMin)
                {
                    O = X.Reduce(l.axis);
                }
                else if (
                    l.type == Layer.Type.Flatten)
                {
                    O = X.Flatten();
                }
                else if (
                    l.type == Layer.Type.Reshape)
                {
                    // pool size is treated as the shape, if not empty
                    var size = l.pool;

                    Assert.IsNotNull(size);

                    if (size.Length == 0 && l.inputs.Length > 1)
                    {
                        switch (l.axis)
                        {
                        // Legacy - use the shape of the input tensor as the shape
                        case -1:
                            if (shapesByName.TryGetValue(l.inputs[1], out TensorShape? shape))
                            {
                                size = shape.Value.ToArray();
                            }
                            break;

                        // Use the tensor values as the shape; Calculated at runtime
                        case 1:
                            O = null;
                            break;
                        }

                        if (O == null)
                        {
                            break;
                        }
                    }

                    Assert.IsTrue((size.Length == 4) || (size.Length == 8));
                    O = X.Reshape(size);
                }
                else if (
                    l.type == Layer.Type.Expand)
                {
                    // pool size is treated as new shape
                    var newShape = l.pool;

                    Assert.IsNotNull(newShape);
                    Assert.IsTrue(newShape.Length == 8 || newShape.Length == 4);

                    O = new TensorShape(newShape);
                }
                else if (
                    l.type == Layer.Type.Transpose)
                {
                    var permutations = l.pool;
                    if (permutations == null)
                    {
                        O = new TensorShape(X.flatWidth, X.flatHeight);
                    }
                    else
                    {
                        Assert.IsTrue(permutations.Length == 8 || permutations.Length == 4);
                        O = X.Permute(permutations);
                    }
                }
                else if (
                    l.type == Layer.Type.Gather)
                {
                    if (!shapesByName.TryGetValue(l.inputs[0], out TensorShape? input0Shape) || input0Shape == null ||
                        !shapesByName.TryGetValue(l.inputs[1], out TensorShape? input1Shape) || input1Shape == null)
                    {
                        O = null;
                        break;
                    }

                    int[] shape = input0Shape.Value.ToArray();
                    shape[l.axis] = input1Shape.Value.length;

                    O = new TensorShape(shape);

                    if (l.pool != null && l.pool.Length == 2 && l.pool[1] > 1)
                    {
                        int xRank        = l.pool[0];
                        int indicesRank  = l.pool[1];
                        var oShape       = Compiler.IRShapeInferenceHelper.ShapeInference.BarracudaShapeToList(O.Value, xRank);
                        var indicesShape = Compiler.IRShapeInferenceHelper.ShapeInference.BarracudaShapeToList(input1Shape.Value, indicesRank);

                        int axis = Compiler.IRShapeInferenceHelper.ShapeInference.BarracudaAxisToTensor(l.axis, xRank);
                        oShape.InsertRange(axis, indicesShape);
                        oShape.RemoveAt(axis + indicesShape.Count);

                        O = (O.Value).Reshape(Compiler.IRShapeInferenceHelper.ShapeInference.BarracudaLayoutToTensorShapeLayout(oShape.ToArray()));

                        // rank 2 -> 3
                        if (xRank == 2 && oShape.Count == 3)
                        {
                            O = (O.Value).Permute(new int[] { 0, 1, 3, 2 });
                        }
                    }
                }
                else if (l.type == Layer.Type.ScatterND)
                {
                    O = X;
                }
                else if (
                    l.type == Layer.Type.Squeeze ||
                    l.type == Layer.Type.Unsqueeze)
                {
                    O = X;
                }
                else if (
                    l.type == Layer.Type.Concat)
                {
                    // gather shapes by names
                    var  list           = new List <TensorShape>(l.inputs.Length);
                    bool allShapesKnown = true;
                    foreach (var i in l.inputs)
                    {
                        if (!shapesByName.TryGetValue(i, out var shape) || shape == null)
                        {
                            allShapesKnown = false;
                            continue;
                        }
                        list.Add(shape.Value);
                    }

                    O = allShapesKnown ? TensorExtensions.Concat(list.ToArray(), l.axis) : default(TensorShape?);
                }
                else if (
                    l.type == Layer.Type.StridedSlice)
                {
                    Assert.IsNotNull(l.pad);
                    Assert.IsNotNull(l.pool);
                    Assert.IsNotNull(l.stride);
                    O = X.ApplyStridedSlice(l.pad, l.pool, l.stride);
                }
                else if (
                    l.type == Layer.Type.Tile)
                {
                    // pool size is treated as tiling coefficient here
                    Assert.IsNotNull(l.pool);
                    var scale = l.pool;
                    O = X.Scale(scale);
                }
                else if (
                    l.type == Layer.Type.Load)
                {
                    O = l.datasets[0].shape;
                }
                else if (// elementwise operations
                    l.type == Layer.Type.Nop ||
                    l.type == Layer.Type.Activation ||
                    l.type == Layer.Type.ScaleBias ||
                    l.type == Layer.Type.Normalization ||
                    l.type == Layer.Type.LRN ||
                    l.type == Layer.Type.Dropout ||
                    l.type == Layer.Type.LogicalNot ||
                    l.type == Layer.Type.Sign)
                {
                    // works in place, keeps the same shape size
                    O = X;
                }
                else if (
                    l.type == Layer.Type.TopKIndices ||
                    l.type == Layer.Type.TopKValues ||
                    l.type == Layer.Type.NonMaxSuppression ||
                    l.type == Layer.Type.LSTM ||
                    l.type == Layer.Type.NonZero)
                {
                    // Calculated at runtime
                    O = null;
                }
                else if (l.type == Layer.Type.Shape)
                {
                    int shapeRank = l.axis > 0 ? 1 : X.length;
                    O = new TensorShape(shapeRank, 1, 1, 1);
                }
                else if (
                    l.type == Layer.Type.Conv3D ||
                    l.type == Layer.Type.Conv3DTrans ||
                    l.type == Layer.Type.Upsample3D ||
                    l.type == Layer.Type.MaxPool3D ||
                    l.type == Layer.Type.AvgPool3D ||
                    l.type == Layer.Type.GlobalMaxPool3D ||
                    l.type == Layer.Type.GlobalAvgPool3D ||
                    l.type == Layer.Type.Border3D)
                {
                    throw new NotImplementedException("3D operations are not implemented yet!");
                }
                else
                {
                    throw new NotImplementedException($"Layer type {l.type} needs to be explicitly handled");
                }

                shapes.Add(O);
                shapesByName.Add(l.name, O);
            }

            Profiler.EndSample();
            return(shapes.ToArray());
        }
 Tensor IOps.Reshape(Tensor X, TensorShape shape)
 {
     return(m_Ops.Reshape(X, shape));
 }