internal static Tensor Permute(Tensor inTensor, int[] permutations) // TODO: unify Permute() arguments { // See: https://stackoverflow.com/a/32034565 Profiler.BeginSample("ONNXTensor.Permute"); var outTensor = new Tensor(ONNXLayout.Permute(inTensor.shape.ToArray(), permutations)); Debug.Assert(outTensor.length == inTensor.length); // {0, 2, 3, 1} => {0, 3, 1, 2} // {2, 3, 1, 0} => {3, 2, 0, 1} // => {find_index(0), find_index(1), find_index(2), find_index(3)} var reversePermute = new int[permutations.Length]; for (var i = 0; i < permutations.Length; ++i) { reversePermute[i] = Array.IndexOf(permutations, i); } // outTensor strides var outStrideC = outTensor.channels; var outStrideWC = outStrideC * outTensor.width; var outStrideHWC = outStrideWC * outTensor.height; var outStride = new int[reversePermute.Length]; for (var i = 0; i < reversePermute.Length; ++i) { outStride[i] = new[] { 0, outStrideHWC, outStrideWC, outStrideC, 1 } }
public ONNXTensor Permute(int[] permutations) { // transpose both data & shape var transposedData = Permute(m_Data, permutations); var transposedShape = ONNXLayout.Permute(m_Shape, permutations); return(new ONNXTensor(transposedData, transposedShape)); }
// Complex attribute helpers private int[] GetPads() { var noPadding = new[] { 0, 0, 0, 0 }; if (SupportsAutoPad) { // known_paddings = { // 'VALID' : [0,0,0,0], // 'SAME_UPPER' : [-1], // 'SAME_LOWER' : [-2], // } var autoPad = GetOptionalString("auto_pad", "NOTSET"); if (autoPad == "VALID") { return(noPadding); } else if (autoPad == "SAME_UPPER") { return new[] { -1 } } ; else if (autoPad == "SAME_LOWER") { return new[] { -2 } } ; else { } // TODO: Assert NOTSET } // NOTE: ONNX has pad layout of [z, y, x ...] while Barracuda is opposite [x, y, z ...] var pads = GetOptionalIntArray("pads", noPadding); if (SupportsSpatialOnlyPads) { // See: https://github.com/onnx/onnx/blob/master/docs/Operators.md#AveragePool // Padding for the beginning and ending along each spatial axis, it can take any value greater than or equal to 0. // The value represent the number of pixels added to the beginning and end part of the corresponding axis. // `pads` format should be as follow [x1_begin, x2_begin...x1_end, x2_end,...], // where xi_begin the number of pixels added at the beginning of axis `i` and xi_end, // the number of pixels added at the end of axis `i`. switch (pads.Length) { case 2: return(new [] { pads[0], 0, pads[1], 0 }); // 1D WW => W_W_ case 4: return(new [] { pads[1], pads[0], pads[3], pads[2] }); // 2D HWHW => WHWH case 6: Warn("3D pads are not supported yet!"); return(new [] { pads[2], pads[1], pads[0], pads[3], pads[4], pads[5] }); // TODO: 3D DHWDHW => WHDWHD default: throw new OnnxLayerImportException( $"Attribute pads of unsupported length {pads.Length} in {Name} ot fype {OperatorType}."); } } // See: https://github.com/onnx/onnx/blob/master/docs/Operators.md#Pad // `pads` should be a 1D tensor of shape [2 * input_rank]. // `pads` format should be: [x1_begin, x2_begin,...,x1_end, x2_end,...], // where xi_begin is the number of pad values added at the beginning of axis `i` and xi_end, // the number of pad values added at the end of axis `i Debug.Assert(pads.Length % 2 == 0); long[] onnxStarts = new long[pads.Length / 2];//TODO make the semantic diff between Permute(int[] and Permute(long[] more clear. long[] onnxEnds = new long[pads.Length / 2]; for (int i = 0; i < onnxStarts.Length; ++i) { onnxStarts[i] = (long)pads[i]; onnxEnds[i] = (long)pads[i + onnxStarts.Length]; } var starts = ONNXLayout.Permute(onnxStarts, "NCHW"); var ends = ONNXLayout.Permute(onnxEnds, "NCHW"); if ((starts[0] != 0) || (starts[3] != 0) || (ends[0] != 0) || (ends[3] != 0)) { Warn($"Unsupported padding, only H and W padding are supported. Value will be ignored and defaulted to 0."); } return(new int[] { starts[2], starts[1], ends[2], ends[1] }); }