Exemplo n.º 1
0
        public static void BakeConstantWRBIntoLSTMLayer(Layer layer, Tensor W, Tensor R, Tensor B)
        {
            string name = layer.name;

            // Bake out constant tensors into layer
            void AddDataset(List <Layer.DataSet> datasets, BarracudaArray weights, string tensorName, Tensor t, ref int offset)
            {
                var dataset = new Layer.DataSet();

                dataset.name            = $"{name}/{tensorName}";
                dataset.shape           = t.shape;
                dataset.itemSizeInBytes = 4;
                dataset.length          = t.shape.length;
                dataset.offset          = offset;
                datasets.Add(dataset);

                t.ToReadOnlyArray().CopyToBarracudaArray(weights, offset);

                offset += t.shape.length;
            }

            var layerDatasets = new List <Layer.DataSet>();
            var layerWeights  = new BarracudaArray(W.shape.length + R.shape.length + B.shape.length);
            int dataOffset    = 0;

            var ops = new ReferenceCPUOps();

            using (var td = new TensorScope())
            {
                TensorScope.F _ = td._;

                Tensor[] w_iofj, r_iofj, wb_iofj, rb_iofj;
                SplitWRBForLSTM(ops, W, R, B, out w_iofj, out r_iofj, out wb_iofj, out rb_iofj);

                var indexName = new[] { "i", "o", "f", "j" };

                for (int i = 0; i < w_iofj.Length; i++)
                {
                    AddDataset(layerDatasets, layerWeights, $"w_{indexName[i]}", _(w_iofj[i]), ref dataOffset);
                }

                for (int i = 0; i < w_iofj.Length; i++)
                {
                    AddDataset(layerDatasets, layerWeights, $"r_{indexName[i]}", _(r_iofj[i]), ref dataOffset);
                }

                for (int i = 0; i < w_iofj.Length; i++)
                {
                    AddDataset(layerDatasets, layerWeights, $"wb_{indexName[i]}", _(wb_iofj[i]), ref dataOffset);
                }

                for (int i = 0; i < w_iofj.Length; i++)
                {
                    AddDataset(layerDatasets, layerWeights, $"rb_{indexName[i]}", _(rb_iofj[i]), ref dataOffset);
                }
            }

            layer.datasets = layerDatasets.ToArray();
            layer.weights  = layerWeights;
        }
        public void FuseInputsIntoLayer(ref Layer layer, Dictionary <string, Tensor> knownLayersValue, IDictionary <string, int?> ranksByName, List <Model.ImporterWarning> warnings)
        {
            switch (layer.type)
            {
            case Layer.Type.Border2D:
            case Layer.Type.Border3D:
            case Layer.Type.Pad2DEdge:
            case Layer.Type.Pad2DReflect:
            case Layer.Type.Pad2DSymmetric:
            {
                if (layer.inputs.Length <= 1 || !IsLayerKnown(layer.inputs[1], knownLayersValue))
                {
                    return;
                }

                float[] padsFloat = knownLayersValue[layer.inputs[1]].ToReadOnlyArray();
                layer.inputs = new[] { layer.inputs[0] };
                var pads = Array.ConvertAll(padsFloat, x => (int)x);

                var    starts         = pads.Take(pads.Length / 2).ToArray();
                var    ends           = pads.Skip(pads.Length / 2).ToArray();
                bool[] dimHavePadding = new bool[starts.Length];
                for (int i = 0; i < starts.Length; ++i)
                {
                    dimHavePadding[i] = starts[i] != 0 && ends[i] != 0;
                }

                if (dimHavePadding.SequenceEqual(new bool [] { false, true, true, false }))
                {
                    // Look like this padding operator is defined over NHWC layout
                    // We skip first and last dimension thus
                    starts     = starts.Skip(1).Take(2).ToArray();
                    ends       = ends.Skip(1).Take(2).ToArray();
                    layer.axes = new int[] { -1 };    // Mark the layer padding as being imported as NHWC layout
                }
                else
                {
                    // Skip non-spatial dimensions N, C (NCHW layout)
                    starts = starts.Skip(2).ToArray();
                    ends   = ends.Skip(2).ToArray();
                }

                switch (starts.Length)
                {
                case 1: layer.pad = new [] { starts[0], 0, ends[0], 0 }; break;                                    // 1D W => W_

                case 2: layer.pad = new [] { starts[1], starts[0], ends[1], ends[0] }; break;                      // 2D HW => WH

                default: layer.pad = new [] { starts[2], starts[1], starts[0], ends[2], ends[1], ends[0] }; break; // 3D DHW => WHD
                }

                float value = 0.0f;
                if (layer.inputs.Length >= 3 && IsLayerKnown(layer.inputs[2], knownLayersValue))
                {
                    value = knownLayersValue[layer.inputs[2]].ToReadOnlyArray()[0];
                }

                layer.beta = value;
                return;
            }

            case Layer.Type.Upsample2D:
            {
                if (layer.inputs.Length <= 1 || !IsLayerKnown(layer.inputs[1], knownLayersValue))
                {
                    return;
                }

                float[] scales = knownLayersValue[layer.inputs[1]].ToReadOnlyArray();

                if (scales[0] == 1 && scales[1] == 1 && scales[2] < 1.0f && scales[3] < 1.0f && layer.axis >= 0.0f)
                {
                    ValidationHelper.AppendWarning(scales.All(x => Mathf.Approximately(1f / x, Mathf.Round(1f / x))),
                                                   layer.name, $"Only inverse of scale values which produce integer are currently supported. Inverse of scale value will be rounded to closest integer.", ref warnings, MessageType.Warning);

                    scales     = new[] { scales[2], scales[3] };
                    layer.type = Layer.Type.AvgPool2D;
                    layer.pad  = new[] { 0, 0, 0, 0 };
                    var inverseScalesRoundedToInt = scales.Select(x => (int)Mathf.Round(1f / x)).ToArray();
                    layer.stride = inverseScalesRoundedToInt;
                    layer.pool   = inverseScalesRoundedToInt;
                }
                else
                {
                    ValidationHelper.AppendWarning(scales.All(x => Mathf.Approximately(x, Mathf.Round(x))),
                                                   layer.name, $"Only integer scale values are currently supported. Scale value will be rounded to closest integer value.", ref warnings, MessageType.Warning);

                    layer.inputs = new[] { layer.inputs[0] };
                    layer.pool   = Array.ConvertAll(scales, x => (int)x);
                }
                return;
            }

            case Layer.Type.Resample2D:
            {
                if (layer.inputs.Length <= 1 || !IsLayerKnown(layer.inputs[1], knownLayersValue))
                {
                    return;
                }

                int[] sizes = Array.ConvertAll(knownLayersValue[layer.inputs[1]].ToReadOnlyArray(), x => (int)x);

                layer.inputs = new[] { layer.inputs[0] };
                layer.pool   = sizes;
                return;
            }

            case Layer.Type.Expand:
            {
                if (layer.inputs.Length <= 1 || !IsLayerKnown(layer.inputs[1], knownLayersValue))
                {
                    return;
                }

                float[] shapeValue = knownLayersValue[layer.inputs[1]].ToReadOnlyArray();
                var     shape      = new int[shapeValue.Length];
                for (int i = 0; i < shapeValue.Length; i++)
                {
                    shape[i] = (int)shapeValue[i];
                }

                layer.pool   = shape;
                layer.inputs = new[] { layer.inputs[0] };
                return;
            }

            case Layer.Type.MatMul:
            {
                var input0 = layer.inputs[0]; var input1 = layer.inputs[1];
                if (!ranksByName.ContainsKey(input0) || !ranksByName[input0].HasValue)
                {
                    return;
                }
                if (!ranksByName.ContainsKey(input1) || !ranksByName[input1].HasValue)
                {
                    return;
                }
                int rank0 = ranksByName[input0].Value;
                int rank1 = ranksByName[input1].Value;

                if (rank0 > 2 || rank1 > 2)
                {
                    return;
                }

                if (!IsLayerKnown(input1, knownLayersValue))
                {
                    return;
                }

                layer.type = Layer.Type.Dense;

                var weight = knownLayersValue[input1];
                weight = weight.Reshape(new TensorShape(weight.batch, weight.height));
                var biasShape = new TensorShape(1, 1, 1, weight.shape.channels);

                layer.inputs                      = new [] { input0 };
                layer.datasets                    = new Layer.DataSet[2];
                layer.datasets[0].name            = $"{layer.name}/W";
                layer.datasets[0].shape           = weight.shape;
                layer.datasets[0].itemSizeInBytes = 4;
                layer.datasets[0].length          = weight.shape.length;
                layer.datasets[0].offset          = 0;
                layer.datasets[1].name            = $"{layer.name}/B";
                layer.datasets[1].shape           = biasShape;
                layer.datasets[1].itemSizeInBytes = 4;
                layer.datasets[1].length          = biasShape.length;
                layer.datasets[1].offset          = weight.shape.length;
                layer.weights                     = new BarracudaArray(weight.shape.length + biasShape.length);

                weight.ToReadOnlyArray().CopyToBarracudaArray(layer.weights, 0);
                var zeroBias = new float[biasShape.length];
                zeroBias.CopyToBarracudaArray(layer.weights, weight.shape.length);
                return;
            }

            case Layer.Type.Tile:
            {
                if (layer.inputs.Length <= 1 || !IsLayerKnown(layer.inputs[1], knownLayersValue))
                {
                    return;
                }

                var shape = Array.ConvertAll(knownLayersValue[layer.inputs[1]].ToReadOnlyArray(), x => (int)x);
                layer.pool = shape;

                layer.inputs = new[] { layer.inputs[0] };
                return;
            }

            case Layer.Type.Reshape:
            {
                if (layer.inputs.Length <= 1 || !IsLayerKnown(layer.inputs[1], knownLayersValue))
                {
                    return;
                }

                float[] shapeValue = knownLayersValue[layer.inputs[1]].ToReadOnlyArray();
                var     shape      = new int[shapeValue.Length];
                for (int i = 0; i < shapeValue.Length; i++)
                {
                    shape[i] = (int)shapeValue[i];
                }

                layer.pool   = shape;
                layer.inputs = new[] { layer.inputs[0] };
                return;
            }

            case Layer.Type.ConstantOfShape:
            {
                if (layer.inputs.Length < 1 || !IsLayerKnown(layer.inputs[0], knownLayersValue))
                {
                    return;
                }

                Tensor input       = knownLayersValue[layer.inputs[0]];
                var    shape       = Array.ConvertAll(input.ToReadOnlyArray(), x => (int)x);
                var    tensorShape = IRShapeInferenceHelper.ShapeInference.OnnxLayoutToTensorShape(shape);


                layer.type = Layer.Type.Load;


                layer.axis                        = shape.Length;
                layer.datasets                    = new Layer.DataSet[1];
                layer.datasets[0].name            = layer.name;
                layer.datasets[0].shape           = tensorShape;
                layer.datasets[0].itemSizeInBytes = 4;
                layer.datasets[0].length          = tensorShape.length;
                layer.datasets[0].offset          = 0;
                layer.weights                     = new BarracudaArray(tensorShape.length);

                var tensor = new Tensor(tensorShape);
                tensor.Fill(layer.alpha);
                tensor.ToReadOnlyArray().CopyToBarracudaArray(layer.weights, 0);

                layer.inputs = new string[0];
                return;
            }

            case Layer.Type.LSTM:
            {
                if (layer.inputs.Length <= 3 || !knownLayersValue.TryGetValue(layer.inputs[1], out Tensor W) ||
                    !knownLayersValue.TryGetValue(layer.inputs[2], out Tensor R) ||
                    !knownLayersValue.TryGetValue(layer.inputs[3], out Tensor B))
                {
                    return;
                }

                var ops = new ReferenceCPUOps();
                using (var td = new TensorScope())
                {
                    TensorScope.F _ = td._;

                    W = _(ops.Transpose(W, new[] { 2, 0, 3, 1 }));
                    R = _(ops.Transpose(R, new[] { 2, 0, 3, 1 }));
                    B = _(ops.Transpose(B, new[] { 0, 2, 3, 1 }));

                    OpsUtils.BakeConstantWRBIntoLSTMLayer(layer, W, R, B);
                }

                layer.inputs = new[] { layer.inputs[0], layer.inputs[4], layer.inputs[5] };

                return;
            }

            case Layer.Type.Activation:
            {
                if (layer.activation == Layer.Activation.None)
                {
                    if (layer.inputs.Length < 1 || !IsLayerKnown(layer.inputs[0], knownLayersValue))
                    {
                        return;
                    }

                    Tensor input       = knownLayersValue[layer.inputs[0]];
                    var    tensorShape = input.shape;

                    layer.type = Layer.Type.Load;

                    layer.axis                        = input.dimensions; // TODO real rank
                    layer.datasets                    = new Layer.DataSet[1];
                    layer.datasets[0].name            = layer.name;
                    layer.datasets[0].shape           = tensorShape;
                    layer.datasets[0].itemSizeInBytes = 4;
                    layer.datasets[0].length          = tensorShape.length;
                    layer.datasets[0].offset          = 0;
                    layer.weights                     = new BarracudaArray(tensorShape.length);

                    input.ToReadOnlyArray().CopyToBarracudaArray(layer.weights, 0);

                    layer.inputs = new string[0];
                }

                return;
            }

            case Layer.Type.Range:
            {
                if (layer.inputs.Length < 3 || !IsLayerKnown(layer.inputs[0], knownLayersValue) || !IsLayerKnown(layer.inputs[1], knownLayersValue) || !IsLayerKnown(layer.inputs[2], knownLayersValue))
                {
                    return;
                }

                Tensor input0 = knownLayersValue[layer.inputs[0]];
                Tensor input1 = knownLayersValue[layer.inputs[1]];
                Tensor input2 = knownLayersValue[layer.inputs[2]];

                var start = input0[0];
                var limit = input1[0];
                var delta = input2[0];

                int nbOfElements = Mathf.Max((int)Mathf.Ceil((limit - start) / delta), 0);

                layer.type = Layer.Type.Load;

                layer.axis                        = 1;
                layer.datasets                    = new Layer.DataSet[1];
                layer.datasets[0].name            = layer.name;
                layer.datasets[0].shape           = new TensorShape(nbOfElements, 1);
                layer.datasets[0].itemSizeInBytes = 4;
                layer.datasets[0].length          = nbOfElements;
                layer.datasets[0].offset          = 0;
                layer.weights                     = new BarracudaArray(nbOfElements);

                for (int i = 0; i < nbOfElements; ++i)
                {
                    layer.weights[i] = start + (i * delta);
                }

                layer.inputs = new string[0];
                return;
            }

            case Layer.Type.StridedSlice:
            {
                if (layer.inputs.Length <= 1 ||
                    !IsLayerKnown(layer.inputs[1], knownLayersValue) || !IsLayerKnown(layer.inputs[2], knownLayersValue) || !IsLayerKnown(layer.inputs[3], knownLayersValue) || !IsLayerKnown(layer.inputs[4], knownLayersValue))
                {
                    return;
                }

                var starts = Array.ConvertAll(knownLayersValue[layer.inputs[1]].ToReadOnlyArray(), x => x <= (float)int.MinValue ? int.MinValue : x >= (float)int.MaxValue ? int.MaxValue : (int)x);
                var ends   = Array.ConvertAll(knownLayersValue[layer.inputs[2]].ToReadOnlyArray(), x => x <= (float)int.MinValue ? int.MinValue : x >= (float)int.MaxValue ? int.MaxValue : (int)x);

                var strides = Enumerable.Repeat(1, starts.Length).Select(v => (int)v).ToArray();
                if (layer.inputs.Length >= 4)
                {
                    strides = Array.ConvertAll(knownLayersValue[layer.inputs[3]].ToReadOnlyArray(), x => (int)x);
                }
                var axes = Enumerable.Range(0, starts.Length).Select(v => (int)v).ToArray();
                if (layer.inputs.Length == 5)
                {
                    axes = Array.ConvertAll(knownLayersValue[layer.inputs[4]].ToReadOnlyArray(), x => (int)x);
                }

                layer.pad    = starts;
                layer.pool   = ends;
                layer.stride = strides;
                layer.axes   = axes;

                layer.inputs = new[] { layer.inputs[0] };

                return;
            }

            case Layer.Type.Squeeze:
            {
                if (layer.inputs.Length <= 1 || !IsLayerKnown(layer.inputs[1], knownLayersValue))
                {
                    return;
                }

                int[] axes = Array.ConvertAll(knownLayersValue[layer.inputs[1]].ToReadOnlyArray(), x => (int)x);

                layer.pool   = axes;
                layer.inputs = new[] { layer.inputs[0] };
                return;
            }

            case Layer.Type.Unsqueeze:
            {
                if (layer.inputs.Length <= 1 || !IsLayerKnown(layer.inputs[1], knownLayersValue))
                {
                    return;
                }

                int[] axes = Array.ConvertAll(knownLayersValue[layer.inputs[1]].ToReadOnlyArray(), x => (int)x);

                layer.pool   = axes;
                layer.inputs = new[] { layer.inputs[0] };
                return;
            }

            case Layer.Type.Pad:
            {
                if (layer.inputs.Length <= 1)
                {
                    return;
                }
                if (layer.inputs.Length == 2 && !IsLayerKnown(layer.inputs[1], knownLayersValue))
                {
                    return;
                }
                if (layer.inputs.Length == 3 && !IsLayerKnown(layer.inputs[1], knownLayersValue) && !IsLayerKnown(layer.inputs[2], knownLayersValue))
                {
                    return;
                }

                float value = (layer.inputs.Length == 2) ? layer.beta : knownLayersValue[layer.inputs[2]].ToReadOnlyArray()[0];
                int[] pads  = Array.ConvertAll(knownLayersValue[layer.inputs[1]].ToReadOnlyArray(), x => (int)x);

                layer.beta   = value;
                layer.pad    = pads;
                layer.inputs = (layer.inputs.Length == 2) ? new [] { layer.inputs[0] } : new [] { layer.inputs[0], layer.inputs[1] };
                return;
            }

            default:
                return;
            }
        }
        public void FuseInputsIntoLayer(ref Layer layer, Dictionary <string, Tensor> knownLayersValue, IDictionary <string, int?> ranksByName)
        {
            switch (layer.type)
            {
            case Layer.Type.Border2D:
            case Layer.Type.Border3D:
            case Layer.Type.Pad2DEdge:
            case Layer.Type.Pad2DReflect:
            case Layer.Type.Pad2DSymmetric:
            {
                if (layer.inputs.Length <= 1 || !IsLayerKnown(layer.inputs[1], knownLayersValue))
                {
                    return;
                }

                float[] padsFloat = knownLayersValue[layer.inputs[1]].ToReadOnlyArray();
                layer.inputs = new[] { layer.inputs[0] };
                var pads = Array.ConvertAll(padsFloat, x => (int)x);

                // Skip non-spatial dimensions N, C (NCHW layout)
                var starts = pads.Take(pads.Length / 2).ToArray();
                var ends   = pads.Skip(pads.Length / 2).ToArray();
                starts = starts.Skip(2).ToArray();
                ends   = ends.Skip(2).ToArray();
                switch (starts.Length)
                {
                case 1: layer.pad = new [] { starts[0], 0, ends[0], 0 }; break;                                    // 1D W => W_

                case 2: layer.pad = new [] { starts[1], starts[0], ends[1], ends[0] }; break;                      // 2D HW => WH

                default: layer.pad = new [] { starts[2], starts[1], starts[0], ends[2], ends[1], ends[0] }; break; // 3D DHW => WHD
                }

                float value = 0.0f;
                if (layer.inputs.Length >= 3 && IsLayerKnown(layer.inputs[2], knownLayersValue))
                {
                    value = knownLayersValue[layer.inputs[2]].ToReadOnlyArray()[0];
                }

                layer.beta = value;
                return;
            }

            case Layer.Type.Upsample2D:
            {
                if (layer.inputs.Length <= 1 || !IsLayerKnown(layer.inputs[1], knownLayersValue))
                {
                    return;
                }

                float[] scales = knownLayersValue[layer.inputs[1]].ToReadOnlyArray();

                if (scales[0] == 1 && scales[1] == 1 && scales[2] < 1.0f && scales[3] < 1.0f && layer.axis >= 0.0f)
                {
                    scales     = new[] { scales[2], scales[3] };
                    layer.type = Layer.Type.AvgPool2D;
                    layer.pad  = new[] { 0, 0, 0, 0 };
                    var inverseScalesRoundedToInt = scales.Select(x => (int)Mathf.Round(1f / x)).ToArray();
                    layer.stride = inverseScalesRoundedToInt;
                    layer.pool   = inverseScalesRoundedToInt;
                }
                else
                {
                    layer.inputs = new[] { layer.inputs[0] };
                    layer.pool   = Array.ConvertAll(scales, x => (int)x);
                }
                return;
            }

            case Layer.Type.Resample2D:
            {
                if (layer.inputs.Length <= 1 || !IsLayerKnown(layer.inputs[1], knownLayersValue))
                {
                    return;
                }

                int[] sizes = Array.ConvertAll(knownLayersValue[layer.inputs[1]].ToReadOnlyArray(), x => (int)x);

                layer.inputs = new[] { layer.inputs[0] };
                layer.pool   = sizes;
                return;
            }

            case Layer.Type.Expand:
            {
                if (layer.inputs.Length <= 1 || !IsLayerKnown(layer.inputs[1], knownLayersValue))
                {
                    return;
                }

                float[] shapeValue = knownLayersValue[layer.inputs[1]].ToReadOnlyArray();
                var     shape      = new int[shapeValue.Length];
                for (int i = 0; i < shapeValue.Length; i++)
                {
                    shape[i] = (int)shapeValue[i];
                }

                layer.pool   = shape;
                layer.inputs = new[] { layer.inputs[0] };
                return;
            }

            case Layer.Type.MatMul:
            {
                var input0 = layer.inputs[0]; var input1 = layer.inputs[1];
                if (!ranksByName.ContainsKey(input0) || !ranksByName[input0].HasValue)
                {
                    return;
                }
                if (!ranksByName.ContainsKey(input1) || !ranksByName[input1].HasValue)
                {
                    return;
                }
                int rank0 = ranksByName[input0].Value;
                int rank1 = ranksByName[input1].Value;

                if (rank0 > 2 || rank1 > 2)
                {
                    return;
                }

                if (!IsLayerKnown(input1, knownLayersValue))
                {
                    return;
                }

                layer.type = Layer.Type.Dense;

                var weight = knownLayersValue[input1];
                weight = weight.Reshape(new TensorShape(weight.batch, weight.height));
                var biasShape = new TensorShape(1, 1, 1, weight.shape.channels);

                layer.inputs                      = new [] { input0 };
                layer.datasets                    = new Layer.DataSet[2];
                layer.datasets[0].name            = $"{layer.name}/W";
                layer.datasets[0].shape           = weight.shape;
                layer.datasets[0].itemSizeInBytes = 4;
                layer.datasets[0].length          = weight.shape.length;
                layer.datasets[0].offset          = 0;
                layer.datasets[1].name            = $"{layer.name}/B";
                layer.datasets[1].shape           = biasShape;
                layer.datasets[1].itemSizeInBytes = 4;
                layer.datasets[1].length          = biasShape.length;
                layer.datasets[1].offset          = weight.shape.length;
                layer.weights                     = new float[weight.shape.length + biasShape.length];

                weight.ToReadOnlyArray().CopyTo(layer.weights, 0);
                return;
            }

            case Layer.Type.Tile:
            {
                if (layer.inputs.Length <= 1 || !IsLayerKnown(layer.inputs[1], knownLayersValue))
                {
                    return;
                }

                var shape = Array.ConvertAll(knownLayersValue[layer.inputs[1]].ToReadOnlyArray(), x => (int)x);
                layer.pool = shape;

                layer.inputs = new[] { layer.inputs[0] };
                return;
            }

            case Layer.Type.Reshape:
            {
                if (layer.inputs.Length <= 1 || !IsLayerKnown(layer.inputs[1], knownLayersValue))
                {
                    return;
                }

                float[] shapeValue = knownLayersValue[layer.inputs[1]].ToReadOnlyArray();
                var     shape      = new int[shapeValue.Length];
                for (int i = 0; i < shapeValue.Length; i++)
                {
                    shape[i] = (int)shapeValue[i];
                }

                layer.pool   = shape;
                layer.inputs = new[] { layer.inputs[0] };
                return;
            }

            case Layer.Type.ConstantOfShape:
            {
                if (layer.inputs.Length < 1 || !IsLayerKnown(layer.inputs[0], knownLayersValue))
                {
                    return;
                }

                Tensor input       = knownLayersValue[layer.inputs[0]];
                var    shape       = Array.ConvertAll(input.ToReadOnlyArray(), x => (int)x);
                var    tensorShape = IRShapeInferenceHelper.ShapeInference.OnnxLayoutToTensorShape(shape);


                layer.type = Layer.Type.Load;


                layer.axis                        = shape.Length;
                layer.datasets                    = new Layer.DataSet[1];
                layer.datasets[0].name            = layer.name;
                layer.datasets[0].shape           = tensorShape;
                layer.datasets[0].itemSizeInBytes = 4;
                layer.datasets[0].length          = tensorShape.length;
                layer.datasets[0].offset          = 0;
                layer.weights                     = new float[tensorShape.length];

                var tensor = new Tensor(tensorShape);
                tensor.Fill(layer.alpha);
                tensor.ToReadOnlyArray().CopyTo(layer.weights, 0);

                layer.inputs = new string[0];
                return;
            }

            case Layer.Type.LSTM:
            {
                if (layer.inputs.Length <= 3 || !knownLayersValue.TryGetValue(layer.inputs[1], out Tensor W) ||
                    !knownLayersValue.TryGetValue(layer.inputs[2], out Tensor R) ||
                    !knownLayersValue.TryGetValue(layer.inputs[3], out Tensor B))
                {
                    return;
                }

                var ops = new ReferenceCPUOps();
                using (var td = new TensorScope())
                {
                    TensorScope.F _ = td._;

                    W = _(ops.Transpose(W, new[] { 2, 0, 3, 1 }));
                    R = _(ops.Transpose(R, new[] { 2, 0, 3, 1 }));
                    B = _(ops.Transpose(B, new[] { 0, 2, 3, 1 }));

                    OpsUtils.BakeConstantWRBIntoLSTMLayer(layer, W, R, B);
                }

                layer.inputs = new[] { layer.inputs[0], layer.inputs[4], layer.inputs[5] };

                return;
            }

            case Layer.Type.Activation:
            {
                if (layer.activation == Layer.Activation.None)
                {
                    if (layer.inputs.Length < 1 || !IsLayerKnown(layer.inputs[0], knownLayersValue))
                    {
                        return;
                    }

                    Tensor input       = knownLayersValue[layer.inputs[0]];
                    var    tensorShape = input.shape;

                    layer.type = Layer.Type.Load;

                    layer.axis                        = input.dimensions; // TODO real rank
                    layer.datasets                    = new Layer.DataSet[1];
                    layer.datasets[0].name            = layer.name;
                    layer.datasets[0].shape           = tensorShape;
                    layer.datasets[0].itemSizeInBytes = 4;
                    layer.datasets[0].length          = tensorShape.length;
                    layer.datasets[0].offset          = 0;
                    layer.weights                     = new float[tensorShape.length];

                    input.ToReadOnlyArray().CopyTo(layer.weights, 0);

                    layer.inputs = new string[0];
                }

                return;
            }

            case Layer.Type.Range:
            {
                if (layer.inputs.Length < 3 || !IsLayerKnown(layer.inputs[0], knownLayersValue) || !IsLayerKnown(layer.inputs[1], knownLayersValue) || !IsLayerKnown(layer.inputs[2], knownLayersValue))
                {
                    return;
                }

                Tensor input0 = knownLayersValue[layer.inputs[0]];
                Tensor input1 = knownLayersValue[layer.inputs[1]];
                Tensor input2 = knownLayersValue[layer.inputs[2]];

                var start = input0[0];
                var limit = input1[0];
                var delta = input2[0];

                int nbOfElements = Mathf.Max((int)Mathf.Ceil((limit - start) / delta), 0);

                layer.type = Layer.Type.Load;

                layer.axis                        = 1;
                layer.datasets                    = new Layer.DataSet[1];
                layer.datasets[0].name            = layer.name;
                layer.datasets[0].shape           = new TensorShape(nbOfElements, 1);
                layer.datasets[0].itemSizeInBytes = 4;
                layer.datasets[0].length          = nbOfElements;
                layer.datasets[0].offset          = 0;
                layer.weights                     = new float[nbOfElements];

                for (int i = 0; i < nbOfElements; ++i)
                {
                    layer.weights[i] = start + (i * delta);
                }

                layer.inputs = new string[0];
                return;
            }

            case Layer.Type.StridedSlice:
            {
                if (layer.inputs.Length <= 1 ||
                    !IsLayerKnown(layer.inputs[1], knownLayersValue) || !IsLayerKnown(layer.inputs[2], knownLayersValue) || !IsLayerKnown(layer.inputs[3], knownLayersValue) || !IsLayerKnown(layer.inputs[4], knownLayersValue))
                {
                    return;
                }

                var starts = Array.ConvertAll(knownLayersValue[layer.inputs[1]].ToReadOnlyArray(), x => x <= (float)int.MinValue ? int.MinValue : x >= (float)int.MaxValue ? int.MaxValue : (int)x);
                var ends   = Array.ConvertAll(knownLayersValue[layer.inputs[2]].ToReadOnlyArray(), x => x <= (float)int.MinValue ? int.MinValue : x >= (float)int.MaxValue ? int.MaxValue : (int)x);

                var strides = Enumerable.Repeat(1, starts.Length).Select(v => (int)v).ToArray();
                if (layer.inputs.Length >= 4)
                {
                    strides = Array.ConvertAll(knownLayersValue[layer.inputs[3]].ToReadOnlyArray(), x => (int)x);
                }
                var axes = Enumerable.Range(0, starts.Length).Select(v => (int)v).ToArray();
                if (layer.inputs.Length == 5)
                {
                    axes = Array.ConvertAll(knownLayersValue[layer.inputs[4]].ToReadOnlyArray(), x => (int)x);
                }

                layer.pad    = starts;
                layer.pool   = ends;
                layer.stride = strides;
                layer.axes   = axes;

                layer.inputs = new[] { layer.inputs[0] };

                return;
            }

            default:
                return;
            }
        }