From ff0e25fdc93c0efff0a5935bd9de355905f208a1 Mon Sep 17 00:00:00 2001 From: Tian Jin Date: Tue, 21 Jan 2020 19:41:22 -0700 Subject: [PATCH 01/10] Re-enable Reciprocal tests. --- test/backend/test.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/backend/test.py b/test/backend/test.py index 60ca4a8..1b257c8 100644 --- a/test/backend/test.py +++ b/test/backend/test.py @@ -144,8 +144,8 @@ test_to_enable = [ #"test_sum_two_inputs_cpu", <- error # Reciprocal Op: - #"test_reciprocal_cpu", <- error on shape inference. - #"test_reciprocal_example_cpu", <- error on shape inference. + "test_reciprocal_cpu", + "test_reciprocal_example_cpu", ] # Extract name of all test cases. From ab8e2f9a1bba1b7081e4b6755b89881029d8ad5a Mon Sep 17 00:00:00 2001 From: Doru Bercea Date: Mon, 20 Jan 2020 11:16:27 -0500 Subject: [PATCH 02/10] Add verifier to check for required attributes. --- src/dialect/onnx/onnx.td | 4 +- src/dialect/onnx/onnx_ops.cpp | 88 +++++++++++++++++++++++++++++++ src/dialect/onnx/onnxop.inc | 9 ++++ src/pass/shape_inference_pass.cpp | 3 +- 4 files changed, 102 insertions(+), 2 deletions(-) diff --git a/src/dialect/onnx/onnx.td b/src/dialect/onnx/onnx.td index 29733f7..710f3af 100644 --- a/src/dialect/onnx/onnx.td +++ b/src/dialect/onnx/onnx.td @@ -104,7 +104,7 @@ def ONNXGemmNoBiasOp: ONNX_Op<"GemmNoBias", } def ONNXConvNoBiasOp:ONNX_Op<"ConvNoBias", - [NoSideEffect]> { + [NoSideEffect, DeclareOpInterfaceMethods]> { let summary = "ONNX Conv operation with no Bias operand."; let description = [{ "The convolution operator consumes an input tensor and a filter, and" @@ -112,6 +112,8 @@ def ONNXConvNoBiasOp:ONNX_Op<"ConvNoBias", }]; let arguments = (ins AnyTypeOf<[AnyMemRef, AnyTensor]>:$X, AnyTypeOf<[AnyMemRef, AnyTensor]>:$W); let results = (outs AnyTypeOf<[AnyMemRef, AnyTensor]>); + + let verifier = [{ return ::verify(*this); }]; } def ONNXMaxPoolSingleOutOp: ONNX_Op<"MaxPoolSingleOut", diff --git a/src/dialect/onnx/onnx_ops.cpp b/src/dialect/onnx/onnx_ops.cpp index 53e463d..44332b5 100644 --- a/src/dialect/onnx/onnx_ops.cpp +++ b/src/dialect/onnx/onnx_ops.cpp @@ -448,6 +448,94 @@ LogicalResult verify(ONNXTransposeOp op) { return success(); } +//===----------------------------------------------------------------------===// + +// Conv + +// For this operation, we define the attributes once in the original Conv +// operation class. There is no need to redefine the attribute names for the +// other classes based on Conv. +void ONNXConvNoBiasOp::inferShapes() { + // Generic shape for data input X and weight tensor W: + // X: (N x C x D1 x D2 ... x Dn) + // W: (M x C/group x k1 x k2 x ... x kn) + + // Cannot infer shape if no shape exists. + if (!getOperand(0).getType().isa() || + !getOperand(1).getType().isa()) + return; + auto dataTy = getOperand(0)->getType().cast(); + auto weightTy = getOperand(1)->getType().cast(); + auto dataShape = dataTy.getShape(); + auto weightShape = weightTy.getShape(); + + if (dataShape.size() != weightShape.size()) + emitError("ConvNoBias: weight size not compatible with data size."); + + // Group is a required attribute and should have default value of 1. + int64_t group = getAttrOfType( + ONNXConvOp::getGroupAttrName()).getInt(); + if (!group) + emitError("ConvNoBias: group attribute missing."); + + // Check that the X.shape[1] == (W.shape[1] * group) == C condition holds. + if (dataShape[1] != (weightShape[1] * group)) + emitError("ConvNoBias: channel dimension mismatch."); + + // Required attributes. + auto auto_pad = getAttrOfType( + ONNXConvOp::getAutoPadAttrName()); + auto pads = getAttrOfType( + ONNXConvOp::getPadsAttrName()); + + SmallVector dims; + // Insert batch size. + dims.emplace_back(dataShape[0]); + // Insert number of filters being applied (number of output channels). + dims.emplace_back(weightShape[0]); + + // // Compute the spatial dimensions. + // SmallVector spatialDims; + // // Number of spatial dimensions. + // int32_t nDims = dataTy.size() - 2; + // // Initialize dimenions based on the input and weight spatial dimensions. + // for (int i = 2; i < dataTy.size(); ++i) + // spatialDims.emplace_back(dataTy[i] - weightTy[i]); + // // Add padding information. + // if () { + // for (int i = 0; i < nDims; ++i) { + // // Padding for beginning of axis. + // int32_t p = (pads.getValue()[i]).cast().getInt(); + // spatialDims[i] += p; + // // Padding for end of axis. + // p = (pads.getValue()[i + nDims]).cast().getInt(); + // spatialDims[i] += p; + // } + // } else if () { + // // Attribute pads has not been provided. + // } + + getResult().setType(RankedTensorType::get(dims, dataTy.getElementType())); +} + +LogicalResult verify(ONNXConvNoBiasOp op) { + auto module = op.getParentOfType(); + if (!module) + op.emitError("expected to belong to a module"); + + auto autoPadAttr = op.getAttrOfType( + ONNXConvOp::getAutoPadAttrName()); + if (!autoPadAttr) + op.emitError("ONNXConvNoBiasOp: auto_pad attribute not specified."); + + auto groupAttr = + op.getAttrOfType(ONNXConvOp::getGroupAttrName()); + if (!groupAttr) + op.emitError("ONNXConvNoBiasOp: group attribute not specified."); + + return success(); +} + //===----------------------------------------------------------------------===// // TableGen'd op method definitions //===----------------------------------------------------------------------===// diff --git a/src/dialect/onnx/onnxop.inc b/src/dialect/onnx/onnxop.inc index e87a01a..3a54fa0 100644 --- a/src/dialect/onnx/onnxop.inc +++ b/src/dialect/onnx/onnxop.inc @@ -324,6 +324,15 @@ def ONNXConvOp:ONNX_Op<"Conv", }]; let arguments = (ins AnyTypeOf<[AnyMemRef, AnyTensor]>:$X, AnyTypeOf<[AnyMemRef, AnyTensor]>:$W, AnyTypeOf<[AnyMemRef, AnyTensor]>:$B); let results = (outs AnyTypeOf<[AnyMemRef, AnyTensor]>); + + let extraClassDeclaration = [{ + static StringRef getAutoPadAttrName() { return "auto_pad"; } + static StringRef getDilationsAttrName() { return "dilations"; } + static StringRef getGroupAttrName() { return "group"; } + static StringRef getKernelShapeAttrName() { return "kernel_shape"; } + static StringRef getPadsAttrName() { return "pads"; } + static StringRef getStridesAttrName() { return "strides"; } + }]; } def ONNXConvIntegerOp:ONNX_Op<"ConvInteger", diff --git a/src/pass/shape_inference_pass.cpp b/src/pass/shape_inference_pass.cpp index 3226f16..5239904 100644 --- a/src/pass/shape_inference_pass.cpp +++ b/src/pass/shape_inference_pass.cpp @@ -117,7 +117,8 @@ public: op->getName().getStringRef() != "onnx.GemmNoBias" && op->getName().getStringRef() != "onnx.Reshape" && op->getName().getStringRef() != "onnx.Transpose" && - op->getName().getStringRef() != "onnx.Softmax") + op->getName().getStringRef() != "onnx.Softmax" && + op->getName().getStringRef() != "onnx.ConvNoBias") return false; return llvm::any_of(op->getResultTypes(), [](Type result_type) { return !result_type.isa(); From 3fe0f2e735785606ef6bad5644f9102a82c97721 Mon Sep 17 00:00:00 2001 From: Doru Bercea Date: Mon, 20 Jan 2020 15:46:15 -0500 Subject: [PATCH 03/10] Fix operand type access. --- src/dialect/onnx/onnx_ops.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/dialect/onnx/onnx_ops.cpp b/src/dialect/onnx/onnx_ops.cpp index 44332b5..cb9407f 100644 --- a/src/dialect/onnx/onnx_ops.cpp +++ b/src/dialect/onnx/onnx_ops.cpp @@ -464,8 +464,8 @@ void ONNXConvNoBiasOp::inferShapes() { if (!getOperand(0).getType().isa() || !getOperand(1).getType().isa()) return; - auto dataTy = getOperand(0)->getType().cast(); - auto weightTy = getOperand(1)->getType().cast(); + auto dataTy = getOperand(0).getType().cast(); + auto weightTy = getOperand(1).getType().cast(); auto dataShape = dataTy.getShape(); auto weightShape = weightTy.getShape(); From ec9e023f04a5c350a943e1850597f04789c9e494 Mon Sep 17 00:00:00 2001 From: Doru Bercea Date: Mon, 20 Jan 2020 18:50:21 -0500 Subject: [PATCH 04/10] Add shape inference method. --- src/dialect/onnx/onnx_ops.cpp | 120 ++++++++++++++++++++++++---------- 1 file changed, 87 insertions(+), 33 deletions(-) diff --git a/src/dialect/onnx/onnx_ops.cpp b/src/dialect/onnx/onnx_ops.cpp index cb9407f..2b65c15 100644 --- a/src/dialect/onnx/onnx_ops.cpp +++ b/src/dialect/onnx/onnx_ops.cpp @@ -469,52 +469,102 @@ void ONNXConvNoBiasOp::inferShapes() { auto dataShape = dataTy.getShape(); auto weightShape = weightTy.getShape(); + // Check that shape of weight and data have same length. if (dataShape.size() != weightShape.size()) - emitError("ConvNoBias: weight size not compatible with data size."); + emitError("Weight size not compatible with data size."); + // Required attribute auto_pad defaults to NOTSET. + auto autoPad = getAttrOfType( + ONNXConvOp::getAutoPadAttrName()).getValue(); // Group is a required attribute and should have default value of 1. int64_t group = getAttrOfType( ONNXConvOp::getGroupAttrName()).getInt(); - if (!group) - emitError("ConvNoBias: group attribute missing."); - // Check that the X.shape[1] == (W.shape[1] * group) == C condition holds. if (dataShape[1] != (weightShape[1] * group)) - emitError("ConvNoBias: channel dimension mismatch."); - - // Required attributes. - auto auto_pad = getAttrOfType( - ONNXConvOp::getAutoPadAttrName()); - auto pads = getAttrOfType( - ONNXConvOp::getPadsAttrName()); + emitError("Channel dimension mismatch."); + // First two output dimensions consist of the number of batches and the + // number of kernels being applied. + // SmallVector dims; // Insert batch size. dims.emplace_back(dataShape[0]); // Insert number of filters being applied (number of output channels). dims.emplace_back(weightShape[0]); - // // Compute the spatial dimensions. - // SmallVector spatialDims; - // // Number of spatial dimensions. - // int32_t nDims = dataTy.size() - 2; - // // Initialize dimenions based on the input and weight spatial dimensions. - // for (int i = 2; i < dataTy.size(); ++i) - // spatialDims.emplace_back(dataTy[i] - weightTy[i]); - // // Add padding information. - // if () { - // for (int i = 0; i < nDims; ++i) { - // // Padding for beginning of axis. - // int32_t p = (pads.getValue()[i]).cast().getInt(); - // spatialDims[i] += p; - // // Padding for end of axis. - // p = (pads.getValue()[i + nDims]).cast().getInt(); - // spatialDims[i] += p; - // } - // } else if () { - // // Attribute pads has not been provided. - // } + // Spatial dimensions are computed using the formula: + // + // dim = (inputDim - kernelDim + startPadding + endPadding) / stride + 1 + // + SmallVector spatialDims; + // Number of spatial dimensions. + int32_t nDims = dataShape.size() - 2; + // Initialize dimenions based on the input spatial dimensions. + for (int i = 2; i < dataShape.size(); ++i) + spatialDims.emplace_back(dataShape[i]); + + // Use kernel_shape attribute if present otherwise use size from weight + // argument. + if (auto kernel_shape = getAttrOfType( + ONNXConvOp::getKernelShapeAttrName())) { + if (kernel_shape.getValue().size() != nDims) + emitError("kernel_shape length incompatible with spatial dimensions."); + for (int i = 0; i < nDims; ++i) { + int64_t kernelDim = + (kernel_shape.getValue()[i]).cast().getInt(); + spatialDims[i] -= kernelDim; + } + } else { + for (int i = 0; i < nDims; ++i) + spatialDims[i] -= weightShape[i + 2]; + } + + // Add padding information. + if (autoPad == "NOTSET") { + // Use pads to to determine the padding. If attribute is not + // present then pads is considered to be all zeros (no padding). + if (auto pads = getAttrOfType( + ONNXConvOp::getPadsAttrName())) { + // pads consists of two entries for each spatial axis. + if (pads.getValue().size() != 2 * nDims) + emitError("pads size is not twice the spatial size."); + + for (int i = 0; i < nDims; ++i) { + // Padding for beginning of axis. + int32_t p = (pads.getValue()[i]).cast().getInt(); + spatialDims[i] += p; + // Padding for end of axis. + p = (pads.getValue()[i + nDims]).cast().getInt(); + spatialDims[i] += p; + } + } + } else if (autoPad == "VALID") { + // TODO + } else if (autoPad == "SAME_UPPER") { + // TODO + } else if (autoPad == "SAME_LOWER") { + // TODO + } else { + emitError("Unexpected attribute value for auto_pad."); + } + + // Strides + if (auto strides = getAttrOfType( + ONNXConvOp::getStridesAttrName())) { + if (strides.getValue().size() != nDims) + emitError("strides length incompatible with spatial dimensions."); + for (int i = 0; i < nDims; ++i) { + int64_t stride = + (strides.getValue()[i]).cast().getInt(); + spatialDims[i] = floor(spatialDims[i] / stride); + } + } + + for (int i = 0; i < nDims; ++i) + spatialDims[i] += 1; + + dims.append(spatialDims.begin(), spatialDims.end()); getResult().setType(RankedTensorType::get(dims, dataTy.getElementType())); } @@ -526,12 +576,16 @@ LogicalResult verify(ONNXConvNoBiasOp op) { auto autoPadAttr = op.getAttrOfType( ONNXConvOp::getAutoPadAttrName()); if (!autoPadAttr) - op.emitError("ONNXConvNoBiasOp: auto_pad attribute not specified."); + op.emitError("auto_pad attribute not specified."); + if (autoPadAttr.getValue() != "NOTSET") + if (auto pads = op.getAttrOfType( + ONNXConvOp::getPadsAttrName())) + op.emitError("auto_pad and pads are both set."); auto groupAttr = op.getAttrOfType(ONNXConvOp::getGroupAttrName()); if (!groupAttr) - op.emitError("ONNXConvNoBiasOp: group attribute not specified."); + op.emitError("group attribute not specified."); return success(); } From 169236a8fc50b64e224c806430fc5db6b9c1bf33 Mon Sep 17 00:00:00 2001 From: Doru Bercea Date: Tue, 21 Jan 2020 20:39:11 -0500 Subject: [PATCH 05/10] Handle SAME_LOWER and SAME_UPPER. --- src/dialect/onnx/onnx_ops.cpp | 53 ++++++++++++++++++++--------------- 1 file changed, 31 insertions(+), 22 deletions(-) diff --git a/src/dialect/onnx/onnx_ops.cpp b/src/dialect/onnx/onnx_ops.cpp index 2b65c15..e597c9a 100644 --- a/src/dialect/onnx/onnx_ops.cpp +++ b/src/dialect/onnx/onnx_ops.cpp @@ -412,7 +412,7 @@ void ONNXReshapeOp::inferShapes() { void ONNXTransposeOp::inferShapes() { // Cannot infer shape if no shape exists. if (!getOperand().getType().isa()) - emitError("Shape tensor not ranked."); + return; // Naive transposition which handles the default case of // reversing the shape of the tensor (similar to numpy.transpose). @@ -464,6 +464,7 @@ void ONNXConvNoBiasOp::inferShapes() { if (!getOperand(0).getType().isa() || !getOperand(1).getType().isa()) return; + auto dataTy = getOperand(0).getType().cast(); auto weightTy = getOperand(1).getType().cast(); auto dataShape = dataTy.getShape(); @@ -492,34 +493,37 @@ void ONNXConvNoBiasOp::inferShapes() { // Insert number of filters being applied (number of output channels). dims.emplace_back(weightShape[0]); - // Spatial dimensions are computed using the formula: + // Spatial dimensions of the output are computed using the formula: // // dim = (inputDim - kernelDim + startPadding + endPadding) / stride + 1 // - SmallVector spatialDims; + SmallVector outSpatialDims; // Number of spatial dimensions. int32_t nDims = dataShape.size() - 2; // Initialize dimenions based on the input spatial dimensions. for (int i = 2; i < dataShape.size(); ++i) - spatialDims.emplace_back(dataShape[i]); + outSpatialDims.emplace_back(dataShape[i]); // Use kernel_shape attribute if present otherwise use size from weight // argument. - if (auto kernel_shape = getAttrOfType( + SmallVector kernelDims; + if (auto kernelShape = getAttrOfType( ONNXConvOp::getKernelShapeAttrName())) { - if (kernel_shape.getValue().size() != nDims) + if (kernelShape.getValue().size() != nDims) emitError("kernel_shape length incompatible with spatial dimensions."); - for (int i = 0; i < nDims; ++i) { - int64_t kernelDim = - (kernel_shape.getValue()[i]).cast().getInt(); - spatialDims[i] -= kernelDim; - } + for (int i = 0; i < nDims; ++i) + kernelDims[i] = + (kernelShape.getValue()[i]).cast().getInt(); } else { for (int i = 0; i < nDims; ++i) - spatialDims[i] -= weightShape[i + 2]; + kernelDims[i] = weightShape[i + 2]; } + // Subtract kernel dimensions from input data dimensions. + for (int i = 0; i < nDims; ++i) + outSpatialDims[i] -= kernelDims[i]; + // Add padding information. if (autoPad == "NOTSET") { // Use pads to to determine the padding. If attribute is not @@ -533,18 +537,23 @@ void ONNXConvNoBiasOp::inferShapes() { for (int i = 0; i < nDims; ++i) { // Padding for beginning of axis. int32_t p = (pads.getValue()[i]).cast().getInt(); - spatialDims[i] += p; + outSpatialDims[i] += p; // Padding for end of axis. p = (pads.getValue()[i + nDims]).cast().getInt(); - spatialDims[i] += p; + outSpatialDims[i] += p; } } + } else if (autoPad == "SAME_UPPER" || autoPad == "SAME_LOWER") { + // Pad input so that output size matches input size. + // Each spatial dimension needs to be padded by: + // + // ( K - 1 ) / 2 + // + // where K is a kernel spatial dimension. + for (int i = 0; i < nDims; ++i) + outSpatialDims[i] += floor((kernelDims[i] - 1) / 2); } else if (autoPad == "VALID") { - // TODO - } else if (autoPad == "SAME_UPPER") { - // TODO - } else if (autoPad == "SAME_LOWER") { - // TODO + // No padding } else { emitError("Unexpected attribute value for auto_pad."); } @@ -557,14 +566,14 @@ void ONNXConvNoBiasOp::inferShapes() { for (int i = 0; i < nDims; ++i) { int64_t stride = (strides.getValue()[i]).cast().getInt(); - spatialDims[i] = floor(spatialDims[i] / stride); + outSpatialDims[i] = floor(outSpatialDims[i] / stride); } } for (int i = 0; i < nDims; ++i) - spatialDims[i] += 1; + outSpatialDims[i] += 1; - dims.append(spatialDims.begin(), spatialDims.end()); + dims.append(outSpatialDims.begin(), outSpatialDims.end()); getResult().setType(RankedTensorType::get(dims, dataTy.getElementType())); } From de77758faf9cbc64be302cbed74e4870c67772f1 Mon Sep 17 00:00:00 2001 From: Doru Bercea Date: Wed, 22 Jan 2020 10:10:06 -0500 Subject: [PATCH 06/10] Fix kernel dimensions. --- src/dialect/onnx/onnx_ops.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/dialect/onnx/onnx_ops.cpp b/src/dialect/onnx/onnx_ops.cpp index e597c9a..687a431 100644 --- a/src/dialect/onnx/onnx_ops.cpp +++ b/src/dialect/onnx/onnx_ops.cpp @@ -509,15 +509,15 @@ void ONNXConvNoBiasOp::inferShapes() { // argument. SmallVector kernelDims; if (auto kernelShape = getAttrOfType( - ONNXConvOp::getKernelShapeAttrName())) { + ONNXConvOp::getKernelShapeAttrName())) { if (kernelShape.getValue().size() != nDims) emitError("kernel_shape length incompatible with spatial dimensions."); for (int i = 0; i < nDims; ++i) - kernelDims[i] = - (kernelShape.getValue()[i]).cast().getInt(); + kernelDims.emplace_back( + (kernelShape.getValue()[i]).cast().getInt()); } else { for (int i = 0; i < nDims; ++i) - kernelDims[i] = weightShape[i + 2]; + kernelDims.emplace_back(weightShape[i + 2]); } // Subtract kernel dimensions from input data dimensions. From ea45cbcca9a353ae6beaeff952db2fdaaae63ebf Mon Sep 17 00:00:00 2001 From: Doru Bercea Date: Wed, 22 Jan 2020 14:40:10 -0500 Subject: [PATCH 07/10] Add support for dilations attribute and add tests. --- src/dialect/onnx/onnx_ops.cpp | 27 ++++- test/mlir/onnx/onnx_shape_inference.mlir | 122 ++++++++++++++++++++++- 2 files changed, 144 insertions(+), 5 deletions(-) diff --git a/src/dialect/onnx/onnx_ops.cpp b/src/dialect/onnx/onnx_ops.cpp index 687a431..265842b 100644 --- a/src/dialect/onnx/onnx_ops.cpp +++ b/src/dialect/onnx/onnx_ops.cpp @@ -520,6 +520,26 @@ void ONNXConvNoBiasOp::inferShapes() { kernelDims.emplace_back(weightShape[i + 2]); } + // Check if dilations attribute is present. + // If it is then compute new kernel size that includes the receptive field. + // In this calculation we assume that the receptive field pixels must all be + // within the bounds of the image. In this case the new kernel size is given + // by: + // + // ( K + 1 ) * d - 1 + // where K is a kernel dimension and d is the dilation along that axis. + // + // From a dimensionality perspective the kernel size becomes the dilated + // kernel size. + if (auto dilations = getAttrOfType( + ONNXConvOp::getDilationsAttrName())) { + if (dilations.getValue().size() != nDims) + emitError("dilations length incompatible with spatial dimensions."); + for (int i = 0; i < nDims; ++i) + kernelDims[i] = (kernelDims[i] + 1) * + (dilations.getValue()[i]).cast().getInt() + 1; + } + // Subtract kernel dimensions from input data dimensions. for (int i = 0; i < nDims; ++i) outSpatialDims[i] -= kernelDims[i]; @@ -545,13 +565,14 @@ void ONNXConvNoBiasOp::inferShapes() { } } else if (autoPad == "SAME_UPPER" || autoPad == "SAME_LOWER") { // Pad input so that output size matches input size. - // Each spatial dimension needs to be padded by: + // Each spatial dimension needs to be padded by a total of: // - // ( K - 1 ) / 2 + // K - 1 // // where K is a kernel spatial dimension. + // Pad as if stride is 1. for (int i = 0; i < nDims; ++i) - outSpatialDims[i] += floor((kernelDims[i] - 1) / 2); + outSpatialDims[i] += kernelDims[i] - 1; } else if (autoPad == "VALID") { // No padding } else { diff --git a/test/mlir/onnx/onnx_shape_inference.mlir b/test/mlir/onnx/onnx_shape_inference.mlir index aaa08a7..5b7530f 100644 --- a/test/mlir/onnx/onnx_shape_inference.mlir +++ b/test/mlir/onnx/onnx_shape_inference.mlir @@ -1,7 +1,10 @@ // RUN: onnf-opt --shape-inference %s -split-input-file | FileCheck %s +//===----------------------------------------------------------------------===// /// Test the default behavior of transpose when no information for the -/// permutation of the axes is provided. +/// permutation of the axes is provided and when a permutation is provided. +//===----------------------------------------------------------------------===// + func @test_default_transpose(%arg0 : tensor<5x5x1x32xf32>) -> tensor<*xf32> { %0 = "onnx.Transpose"(%arg0) : (tensor<5x5x1x32xf32>) -> tensor<*xf32> "std.return"(%0) : (tensor<*xf32>) -> () @@ -12,6 +15,7 @@ func @test_default_transpose(%arg0 : tensor<5x5x1x32xf32>) -> tensor<*xf32> { // CHECK: return [[RES]] : tensor<32x1x5x5xf32> /// Test shape inference for transposition when perm attribute is specified. + func @test_transpose(%arg0 : tensor<5x5x1x32xf32>) -> tensor<*xf32> { %0 = "onnx.Transpose"(%arg0) {perm = [2, 0, 3, 1]} : (tensor<5x5x1x32xf32>) -> tensor<*xf32> "std.return"(%0) : (tensor<*xf32>) -> () @@ -19,4 +23,118 @@ func @test_transpose(%arg0 : tensor<5x5x1x32xf32>) -> tensor<*xf32> { // CHECK-LABEL: test_transpose // CHECK: [[RES_ATTR:%.+]] = "onnx.Transpose"(%arg0) {perm = [2, 0, 3, 1]} : (tensor<5x5x1x32xf32>) -> tensor<1x5x32x5xf32> -// CHECK: return [[RES_ATTR]] : tensor<1x5x32x5xf32> \ No newline at end of file +// CHECK: return [[RES_ATTR]] : tensor<1x5x32x5xf32> + +//===----------------------------------------------------------------------===// +/// Test shape inference for ConvNoBias operation and all its attributes. +//===----------------------------------------------------------------------===// + +/// Default and required attributes. + +func @test_conv_no_bias_1(%arg0 : tensor<1x2x32x64xf32>, %arg1 : tensor<5x2x6x7xf32>) -> tensor<*xf32> { + %0 = "onnx.ConvNoBias"(%arg0, %arg1) {auto_pad = "NOTSET", group = 1 : i32} : (tensor<1x2x32x64xf32>, tensor<5x2x6x7xf32>) -> tensor<*xf32> + "std.return"(%0) : (tensor<*xf32>) -> () +} + +// CHECK-LABEL: test_conv_no_bias_1 +// CHECK: [[RES_ATTR:%.+]] = "onnx.ConvNoBias"(%arg0, %arg1) {auto_pad = "NOTSET", group = 1 : i32} : (tensor<1x2x32x64xf32>, tensor<5x2x6x7xf32>) -> tensor<1x5x27x58xf32> +// CHECK: return [[RES_ATTR]] : tensor<1x5x27x58xf32> + +/// kernel_shape attribute. + +func @test_conv_no_bias_2(%arg0 : tensor<1x2x32x64xf32>, %arg1 : tensor<5x2x6x7xf32>) -> tensor<*xf32> { + %0 = "onnx.ConvNoBias"(%arg0, %arg1) {auto_pad = "NOTSET", group = 1 : i32, kernel_shape = [8, 9]} : (tensor<1x2x32x64xf32>, tensor<5x2x6x7xf32>) -> tensor<*xf32> + "std.return"(%0) : (tensor<*xf32>) -> () +} + +// CHECK-LABEL: test_conv_no_bias_2 +// CHECK: [[RES_ATTR:%.+]] = "onnx.ConvNoBias"(%arg0, %arg1) {auto_pad = "NOTSET", group = 1 : i32, kernel_shape = [8, 9]} : (tensor<1x2x32x64xf32>, tensor<5x2x6x7xf32>) -> tensor<1x5x25x56xf32> +// CHECK: return [[RES_ATTR]] : tensor<1x5x25x56xf32> + +/// pads attribute. +/// Use pads to make output size equal to input size by adding K - 1 to the result. + +func @test_conv_no_bias_3(%arg0 : tensor<1x2x32x64xf32>, %arg1 : tensor<5x2x6x10xf32>) -> tensor<*xf32> { + %0 = "onnx.ConvNoBias"(%arg0, %arg1) {auto_pad = "NOTSET", group = 1 : i32, pads = [2, 4, 3, 5]} : (tensor<1x2x32x64xf32>, tensor<5x2x6x10xf32>) -> tensor<*xf32> + "std.return"(%0) : (tensor<*xf32>) -> () +} + +// CHECK-LABEL: test_conv_no_bias_3 +// CHECK: [[RES_ATTR:%.+]] = "onnx.ConvNoBias"(%arg0, %arg1) {auto_pad = "NOTSET", group = 1 : i32, pads = [2, 4, 3, 5]} : (tensor<1x2x32x64xf32>, tensor<5x2x6x10xf32>) -> tensor<1x5x32x64xf32> +// CHECK: return [[RES_ATTR]] : tensor<1x5x32x64xf32> + +/// auto_pad set to SAME_UPPER and SAME_LOWER. + +func @test_conv_no_bias_4(%arg0 : tensor<1x2x32x64xf32>, %arg1 : tensor<5x2x6x10xf32>) -> tensor<*xf32> { + %0 = "onnx.ConvNoBias"(%arg0, %arg1) {auto_pad = "SAME_UPPER", group = 1 : i32} : (tensor<1x2x32x64xf32>, tensor<5x2x6x10xf32>) -> tensor<*xf32> + "std.return"(%0) : (tensor<*xf32>) -> () +} + +// CHECK-LABEL: test_conv_no_bias_4 +// CHECK: [[RES_ATTR:%.+]] = "onnx.ConvNoBias"(%arg0, %arg1) {auto_pad = "SAME_UPPER", group = 1 : i32} : (tensor<1x2x32x64xf32>, tensor<5x2x6x10xf32>) -> tensor<1x5x32x64xf32> +// CHECK: return [[RES_ATTR]] : tensor<1x5x32x64xf32> + +func @test_conv_no_bias_5(%arg0 : tensor<1x2x32x64xf32>, %arg1 : tensor<5x2x6x10xf32>) -> tensor<*xf32> { + %0 = "onnx.ConvNoBias"(%arg0, %arg1) {auto_pad = "SAME_LOWER", group = 1 : i32} : (tensor<1x2x32x64xf32>, tensor<5x2x6x10xf32>) -> tensor<*xf32> + "std.return"(%0) : (tensor<*xf32>) -> () +} + +// CHECK-LABEL: test_conv_no_bias_5 +// CHECK: [[RES_ATTR:%.+]] = "onnx.ConvNoBias"(%arg0, %arg1) {auto_pad = "SAME_LOWER", group = 1 : i32} : (tensor<1x2x32x64xf32>, tensor<5x2x6x10xf32>) -> tensor<1x5x32x64xf32> +// CHECK: return [[RES_ATTR]] : tensor<1x5x32x64xf32> + +/// auto_pad set to VALID. + +func @test_conv_no_bias_6(%arg0 : tensor<1x2x32x64xf32>, %arg1 : tensor<5x2x6x10xf32>) -> tensor<*xf32> { + %0 = "onnx.ConvNoBias"(%arg0, %arg1) {auto_pad = "VALID", group = 1 : i32} : (tensor<1x2x32x64xf32>, tensor<5x2x6x10xf32>) -> tensor<*xf32> + "std.return"(%0) : (tensor<*xf32>) -> () +} + +// CHECK-LABEL: test_conv_no_bias_6 +// CHECK: [[RES_ATTR:%.+]] = "onnx.ConvNoBias"(%arg0, %arg1) {auto_pad = "VALID", group = 1 : i32} : (tensor<1x2x32x64xf32>, tensor<5x2x6x10xf32>) -> tensor<1x5x27x55xf32> +// CHECK: return [[RES_ATTR]] : tensor<1x5x27x55xf32> + +/// With strides attribute. + +func @test_conv_no_bias_7(%arg0 : tensor<1x2x32x64xf32>, %arg1 : tensor<5x2x6x7xf32>) -> tensor<*xf32> { + %0 = "onnx.ConvNoBias"(%arg0, %arg1) {auto_pad = "NOTSET", group = 1 : i32, strides = [2, 3]} : (tensor<1x2x32x64xf32>, tensor<5x2x6x7xf32>) -> tensor<*xf32> + "std.return"(%0) : (tensor<*xf32>) -> () +} + +// CHECK-LABEL: test_conv_no_bias_7 +// CHECK: [[RES_ATTR:%.+]] = "onnx.ConvNoBias"(%arg0, %arg1) {auto_pad = "NOTSET", group = 1 : i32, strides = [2, 3]} : (tensor<1x2x32x64xf32>, tensor<5x2x6x7xf32>) -> tensor<1x5x14x20xf32> +// CHECK: return [[RES_ATTR]] : tensor<1x5x14x20xf32> + +/// auto_pad set to SAME_UPPER with strides attribute. +/// The auto_pad will pas as if stride is equal to 1. + +func @test_conv_no_bias_8(%arg0 : tensor<1x2x32x64xf32>, %arg1 : tensor<5x2x6x7xf32>) -> tensor<*xf32> { + %0 = "onnx.ConvNoBias"(%arg0, %arg1) {auto_pad = "SAME_UPPER", group = 1 : i32, strides = [2, 3]} : (tensor<1x2x32x64xf32>, tensor<5x2x6x7xf32>) -> tensor<*xf32> + "std.return"(%0) : (tensor<*xf32>) -> () +} + +// CHECK-LABEL: test_conv_no_bias_8 +// CHECK: [[RES_ATTR:%.+]] = "onnx.ConvNoBias"(%arg0, %arg1) {auto_pad = "SAME_UPPER", group = 1 : i32, strides = [2, 3]} : (tensor<1x2x32x64xf32>, tensor<5x2x6x7xf32>) -> tensor<1x5x16x22xf32> +// CHECK: return [[RES_ATTR]] : tensor<1x5x16x22xf32> + +/// dilations attribute. + +func @test_conv_no_bias_9(%arg0 : tensor<1x2x32x64xf32>, %arg1 : tensor<5x2x6x7xf32>) -> tensor<*xf32> { + %0 = "onnx.ConvNoBias"(%arg0, %arg1) {auto_pad = "NOTSET", group = 1 : i32, dilations = [2, 3]} : (tensor<1x2x32x64xf32>, tensor<5x2x6x7xf32>) -> tensor<*xf32> + "std.return"(%0) : (tensor<*xf32>) -> () +} + +// CHECK-LABEL: test_conv_no_bias_9 +// CHECK: [[RES_ATTR:%.+]] = "onnx.ConvNoBias"(%arg0, %arg1) {auto_pad = "NOTSET", dilations = [2, 3], group = 1 : i32} : (tensor<1x2x32x64xf32>, tensor<5x2x6x7xf32>) -> tensor<1x5x18x40xf32> +// CHECK: return [[RES_ATTR]] : tensor<1x5x18x40xf32> + +/// dilations attribute with stride. + +func @test_conv_no_bias_10(%arg0 : tensor<1x2x32x64xf32>, %arg1 : tensor<5x2x6x7xf32>) -> tensor<*xf32> { + %0 = "onnx.ConvNoBias"(%arg0, %arg1) {auto_pad = "NOTSET", group = 1 : i32, dilations = [2, 3], strides = [2, 2]} : (tensor<1x2x32x64xf32>, tensor<5x2x6x7xf32>) -> tensor<*xf32> + "std.return"(%0) : (tensor<*xf32>) -> () +} + +// CHECK-LABEL: test_conv_no_bias_10 +// CHECK: [[RES_ATTR:%.+]] = "onnx.ConvNoBias"(%arg0, %arg1) {auto_pad = "NOTSET", dilations = [2, 3], group = 1 : i32, strides = [2, 2]} : (tensor<1x2x32x64xf32>, tensor<5x2x6x7xf32>) -> tensor<1x5x9x20xf32> +// CHECK: return [[RES_ATTR]] : tensor<1x5x9x20xf32> From 94391a3cdef0095d68bcb8606caa00161f0d7840 Mon Sep 17 00:00:00 2001 From: Doru Bercea Date: Wed, 22 Jan 2020 15:05:56 -0500 Subject: [PATCH 08/10] Add comment. --- src/dialect/onnx/onnx_ops.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/dialect/onnx/onnx_ops.cpp b/src/dialect/onnx/onnx_ops.cpp index 265842b..83059cd 100644 --- a/src/dialect/onnx/onnx_ops.cpp +++ b/src/dialect/onnx/onnx_ops.cpp @@ -484,6 +484,9 @@ void ONNXConvNoBiasOp::inferShapes() { if (dataShape[1] != (weightShape[1] * group)) emitError("Channel dimension mismatch."); + // Note: the value of the group attribut only impacts the way the + // computation is carried out and not the actual output size. + // First two output dimensions consist of the number of batches and the // number of kernels being applied. // From 68efd2106482166e8439695708d0e8ed3b1c230c Mon Sep 17 00:00:00 2001 From: Doru Bercea Date: Wed, 22 Jan 2020 16:34:59 -0500 Subject: [PATCH 09/10] Fix dilation formula in the code. --- src/dialect/onnx/onnx_ops.cpp | 2 +- test/mlir/onnx/onnx_shape_inference.mlir | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/dialect/onnx/onnx_ops.cpp b/src/dialect/onnx/onnx_ops.cpp index 83059cd..6fcb241 100644 --- a/src/dialect/onnx/onnx_ops.cpp +++ b/src/dialect/onnx/onnx_ops.cpp @@ -540,7 +540,7 @@ void ONNXConvNoBiasOp::inferShapes() { emitError("dilations length incompatible with spatial dimensions."); for (int i = 0; i < nDims; ++i) kernelDims[i] = (kernelDims[i] + 1) * - (dilations.getValue()[i]).cast().getInt() + 1; + (dilations.getValue()[i]).cast().getInt() - 1; } // Subtract kernel dimensions from input data dimensions. diff --git a/test/mlir/onnx/onnx_shape_inference.mlir b/test/mlir/onnx/onnx_shape_inference.mlir index 5b7530f..9022b05 100644 --- a/test/mlir/onnx/onnx_shape_inference.mlir +++ b/test/mlir/onnx/onnx_shape_inference.mlir @@ -125,8 +125,8 @@ func @test_conv_no_bias_9(%arg0 : tensor<1x2x32x64xf32>, %arg1 : tensor<5x2x6x7x } // CHECK-LABEL: test_conv_no_bias_9 -// CHECK: [[RES_ATTR:%.+]] = "onnx.ConvNoBias"(%arg0, %arg1) {auto_pad = "NOTSET", dilations = [2, 3], group = 1 : i32} : (tensor<1x2x32x64xf32>, tensor<5x2x6x7xf32>) -> tensor<1x5x18x40xf32> -// CHECK: return [[RES_ATTR]] : tensor<1x5x18x40xf32> +// CHECK: [[RES_ATTR:%.+]] = "onnx.ConvNoBias"(%arg0, %arg1) {auto_pad = "NOTSET", dilations = [2, 3], group = 1 : i32} : (tensor<1x2x32x64xf32>, tensor<5x2x6x7xf32>) -> tensor<1x5x20x42xf32> +// CHECK: return [[RES_ATTR]] : tensor<1x5x20x42xf32> /// dilations attribute with stride. @@ -136,5 +136,5 @@ func @test_conv_no_bias_10(%arg0 : tensor<1x2x32x64xf32>, %arg1 : tensor<5x2x6x7 } // CHECK-LABEL: test_conv_no_bias_10 -// CHECK: [[RES_ATTR:%.+]] = "onnx.ConvNoBias"(%arg0, %arg1) {auto_pad = "NOTSET", dilations = [2, 3], group = 1 : i32, strides = [2, 2]} : (tensor<1x2x32x64xf32>, tensor<5x2x6x7xf32>) -> tensor<1x5x9x20xf32> -// CHECK: return [[RES_ATTR]] : tensor<1x5x9x20xf32> +// CHECK: [[RES_ATTR:%.+]] = "onnx.ConvNoBias"(%arg0, %arg1) {auto_pad = "NOTSET", dilations = [2, 3], group = 1 : i32, strides = [2, 2]} : (tensor<1x2x32x64xf32>, tensor<5x2x6x7xf32>) -> tensor<1x5x10x21xf32> +// CHECK: return [[RES_ATTR]] : tensor<1x5x10x21xf32> From 7dda698e7e0023d22bd8a16acefe43ad482e79cd Mon Sep 17 00:00:00 2001 From: Doru Bercea Date: Wed, 22 Jan 2020 16:40:33 -0500 Subject: [PATCH 10/10] Add test with dilations and auto padding set to SAME_UPPER. --- test/mlir/onnx/onnx_shape_inference.mlir | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/test/mlir/onnx/onnx_shape_inference.mlir b/test/mlir/onnx/onnx_shape_inference.mlir index 9022b05..656866e 100644 --- a/test/mlir/onnx/onnx_shape_inference.mlir +++ b/test/mlir/onnx/onnx_shape_inference.mlir @@ -138,3 +138,14 @@ func @test_conv_no_bias_10(%arg0 : tensor<1x2x32x64xf32>, %arg1 : tensor<5x2x6x7 // CHECK-LABEL: test_conv_no_bias_10 // CHECK: [[RES_ATTR:%.+]] = "onnx.ConvNoBias"(%arg0, %arg1) {auto_pad = "NOTSET", dilations = [2, 3], group = 1 : i32, strides = [2, 2]} : (tensor<1x2x32x64xf32>, tensor<5x2x6x7xf32>) -> tensor<1x5x10x21xf32> // CHECK: return [[RES_ATTR]] : tensor<1x5x10x21xf32> + +/// dilations attribute with auto_pad set to SAME_UPPER. + +func @test_conv_no_bias_11(%arg0 : tensor<1x2x32x64xf32>, %arg1 : tensor<5x2x6x7xf32>) -> tensor<*xf32> { + %0 = "onnx.ConvNoBias"(%arg0, %arg1) {auto_pad = "SAME_UPPER", group = 1 : i32, dilations = [2, 3]} : (tensor<1x2x32x64xf32>, tensor<5x2x6x7xf32>) -> tensor<*xf32> + "std.return"(%0) : (tensor<*xf32>) -> () +} + +// CHECK-LABEL: test_conv_no_bias_11 +// CHECK: [[RES_ATTR:%.+]] = "onnx.ConvNoBias"(%arg0, %arg1) {auto_pad = "SAME_UPPER", dilations = [2, 3], group = 1 : i32} : (tensor<1x2x32x64xf32>, tensor<5x2x6x7xf32>) -> tensor<1x5x32x64xf32> +// CHECK: return [[RES_ATTR]] : tensor<1x5x32x64xf32>