From 90c44d707d6d57b8d33b85679130e8c8f4c44146 Mon Sep 17 00:00:00 2001 From: Alexander Bengtsson Date: Mon, 3 Mar 2025 17:47:59 +0100 Subject: [PATCH] MLBEDSW-10414: Port Vela supported-operator checks (4) Add supported operator checks for TransposeConv Change-Id: I58f0fe08e948123f2c677f361b37bcc7855a8bc3 Signed-off-by: Alexander Bengtsson --- .../tflite/tflite_supported_operators.cpp | 97 ++++++++++++++++++- .../tflite/tflite_supported_operators.hpp | 2 + .../tflite/tflite_supported_operators_u55.cpp | 2 +- .../tflite/tflite_supported_operators_u85.cpp | 4 +- 4 files changed, 100 insertions(+), 5 deletions(-) diff --git a/ethosu/regor/tflite/tflite_supported_operators.cpp b/ethosu/regor/tflite/tflite_supported_operators.cpp index 54047fe3..1485da80 100644 --- a/ethosu/regor/tflite/tflite_supported_operators.cpp +++ b/ethosu/regor/tflite/tflite_supported_operators.cpp @@ -272,7 +272,7 @@ bool TfLiteSupportedOperators::ConstraintWeightsPrecision(const Operation *op) bool TfLiteSupportedOperators::ConstraintWeightSum(const Operation *op) { - std::string constraint = fmt::format( + static const std::string constraint = fmt::format( "The sum of absolute weights cannot exceed:\n" "\t{} for 8-bit IFM\n" "\t{} for 16-bit IFM", @@ -382,7 +382,7 @@ bool TfLiteSupportedOperators::ConstraintBias(const Operation *op) { if ( bias > _maxBias ) { - std::string constraint = fmt::format("Int64 bias must be smaller than {}", _maxBias); + static const std::string constraint = fmt::format("Int64 bias must be smaller than {}", _maxBias); Failure(op, fmt::format("Bias is out of range: {} > {}", bias, _maxBias), constraint); return false; } @@ -460,6 +460,97 @@ bool TfLiteSupportedOperators::ConstraintMaxPool(const Operation *op) return true; } +bool TfLiteSupportedOperators::ConstraintTCStrides(const Operation *op) +{ + static const std::string constraint = + "Stride values WxH must be:\n" + "\t1x1 OR 2x2\n" + "\tOR 2x1 if ifm height and kernel height = 1\n" + "\tOR 1x2 if ifm width and kernel width = 1"; + OpType opType = op->Type(); + if ( opType != OpType::TransposeConv2D ) + { + return true; + } + auto ifmConn = op->Input(TensorUsage::IFM); + auto kernel = op->Kernel(); + assert(ifmConn); + assert(kernel); + const auto &ifmShape = ifmConn->shape; + auto [kw, kh] = kernel->Size(); + auto stride = kernel->Stride(); + + if ( stride.x < 1 || stride.x > 2 || stride.y < 1 || stride.y > 2 ) + { + Failure(op, fmt::format("stride out of range: ({},{})", stride.x, stride.y), constraint); + return false; + } + if ( stride == Point2i(1, 2) && !(ifmShape.Height() == 1 && kh == 1) ) + { + Failure(op, fmt::format("unsupported stride combination: ({},{})", stride.x, stride.y), constraint); + return false; + } + if ( stride == Point2i(2, 1) && !(ifmShape.Width() == 1 && kw == 1) ) + { + Failure(op, fmt::format("unsupported stride combination: ({},{})", stride.x, stride.y), constraint); + return false; + } + return true; +} + +bool TfLiteSupportedOperators::ConstraintTCShapes(const Operation *op) +{ + static const std::string constraint = + "if PADDING=SAME\n" + "\tOFM must equal IFM * stride\n" + "if PADDING=VALID\n" + "\tOFM must equal IFM * stride + (kernel - stride)"; + OpType opType = op->Type(); + if ( opType != OpType::TransposeConv2D ) + { + return true; + } + auto ifmConn = op->Input(TensorUsage::IFM); + auto ofmConn = op->Output(TensorUsage::OFM); + auto kernel = op->Kernel(); + assert(ifmConn); + assert(ofmConn); + assert(kernel); + const auto &ifmShape = ifmConn->shape; + const auto &ofmShape = ofmConn->shape; + auto stride = kernel->Stride(); + assert(op->Passthrough()); + const tflite::Operator *passthrough = static_cast(op->Passthrough()); + const auto *opt = passthrough->builtin_options_as(); + assert(opt); + Point2i ifmWH(ifmShape.Width(), ifmShape.Height()); + Point2i ofmWH(ofmShape.Width(), ofmShape.Height()); + if ( opt->padding() == tflite::Padding::SAME ) + { + if ( ifmWH * stride != ofmWH ) + { + Failure(op, + fmt::format("(Padding::SAME) Unsupported IFM/OFM shapes. ifm:({},{}), ofm:({},{}), stride:({},{})", + ifmWH.x, ifmWH.y, ofmWH.x, ofmWH.y, stride.x, stride.y), + constraint); + return false; + } + } + else + { + Point2i diff = (kernel->Size() - stride); + if ( (ifmWH * stride + diff) != ofmWH ) + { + Failure(op, + fmt::format("(Padding::VALID) Unsupported IFM/OFM shapes. ifm:({},{}) ofm:({},{}), stride:({},{}), kernel:({},{})", + ifmWH.x, ifmWH.y, ofmWH.x, ofmWH.y, stride.x, stride.y, kernel->Size().x, kernel->Size().y), + constraint); + return false; + } + } + return true; +} + void TfLiteSupportedOperators::Failure(const Operation *op, const std::string &message, const std::string &constraint) { assert(op); @@ -504,6 +595,8 @@ TfLiteSupportedOperators::TfLiteSupportedOperators(IArchitectureConstraints *con &TfLiteSupportedOperators::ConstraintBias, &TfLiteSupportedOperators::ConstraintAvgPool, &TfLiteSupportedOperators::ConstraintMaxPool, + &TfLiteSupportedOperators::ConstraintTCStrides, + &TfLiteSupportedOperators::ConstraintTCShapes, }; } diff --git a/ethosu/regor/tflite/tflite_supported_operators.hpp b/ethosu/regor/tflite/tflite_supported_operators.hpp index 943203e6..cd31817b 100644 --- a/ethosu/regor/tflite/tflite_supported_operators.hpp +++ b/ethosu/regor/tflite/tflite_supported_operators.hpp @@ -65,5 +65,7 @@ private: bool ConstraintBias(const Operation *op); bool ConstraintAvgPool(const Operation *op); bool ConstraintMaxPool(const Operation *op); + bool ConstraintTCStrides(const Operation *op); + bool ConstraintTCShapes(const Operation *op); }; } // namespace regor diff --git a/ethosu/regor/tflite/tflite_supported_operators_u55.cpp b/ethosu/regor/tflite/tflite_supported_operators_u55.cpp index 33ba97fa..5d49a09c 100644 --- a/ethosu/regor/tflite/tflite_supported_operators_u55.cpp +++ b/ethosu/regor/tflite/tflite_supported_operators_u55.cpp @@ -112,7 +112,7 @@ bool TfLiteSupportedOperatorsU55::Check(const Operation *op) bool TfLiteSupportedOperatorsU55::ConstraintBroadcastShapes(const Operation *op) { - const char *constraint = "One input-tensor must match the shape of the output-tensor."; + static const char *constraint = "One input-tensor must match the shape of the output-tensor."; if ( !IsElementwise(op->Type()) ) { // only applied to elementwise ops diff --git a/ethosu/regor/tflite/tflite_supported_operators_u85.cpp b/ethosu/regor/tflite/tflite_supported_operators_u85.cpp index f0575625..5cb7a0aa 100644 --- a/ethosu/regor/tflite/tflite_supported_operators_u85.cpp +++ b/ethosu/regor/tflite/tflite_supported_operators_u85.cpp @@ -139,7 +139,7 @@ bool TfLiteSupportedOperatorsU85::Check(const Operation *op) bool TfLiteSupportedOperatorsU85::ConstraintResizeCommon(const Operation *op) { - const char *constraint = + static const char *constraint = "ALIGN_CORNERS and HALF_PIXEL_CENTERS are mutually exclusive.\n" "if ALIGN_CORNERS:\n" "\tScale-factor can be maximum 2048\n" @@ -230,7 +230,7 @@ bool TfLiteSupportedOperatorsU85::ConstraintResizeCommon(const Operation *op) bool TfLiteSupportedOperatorsU85::ConstraintResizeBilinear(const Operation *op) { - const char *constraint = + static const char *constraint = "if IFM HxW > 1x1\n" "\tand ALIGN_CORNERS:\n" "\t\tOFM W-1 and H-1 must be a power-of-two integer-multiple of IFM W-1 and H-1\n" -- GitLab