From 425541302c7e4b6fbeca7c0061286b131ee507c3 Mon Sep 17 00:00:00 2001 From: Alexander Bengtsson Date: Fri, 21 Feb 2025 13:27:20 +0100 Subject: [PATCH] MLBEDSW-10414: Port Vela supported-operator checks (1) Add the following supported operator checks: ConstraintMatchingQuantization Minimum and Maximum requires matching input/output quantization ConstraintPerAxisQuant Constrain which operations can handle per-axis quantization Constraint32bitOps (Ethos-U55 only): Constrain which opTypes can use 32-bit tensors Change-Id: I8a510063019c14be94e326f14944be44054ac61e Signed-off-by: Alexander Bengtsson --- .../test/test_tflite_supported_operators.cpp | 38 ++++++++++++++ .../tflite/tflite_supported_operators.cpp | 52 +++++++++++++++++++ .../tflite/tflite_supported_operators.hpp | 3 ++ .../tflite/tflite_supported_operators_u55.cpp | 37 +++++++++++++ .../tflite/tflite_supported_operators_u55.hpp | 1 + 5 files changed, 131 insertions(+) diff --git a/ethosu/regor/test/test_tflite_supported_operators.cpp b/ethosu/regor/test/test_tflite_supported_operators.cpp index 154c1be1..8edd6fae 100644 --- a/ethosu/regor/test/test_tflite_supported_operators.cpp +++ b/ethosu/regor/test/test_tflite_supported_operators.cpp @@ -133,6 +133,32 @@ TEST_CASE("Supported operators Common") REQUIRE(supportedOps->Check(op.get()) == false); op->Disconnect(); } + + SECTION("ConstraintPerAxisQuant") + { + auto op = CreateOperation(OpType::Add, Shape(1, 1, 1, 3), DataType::Int8, Shape(1, 1, 1, 3), DataType::Int8, + Shape(1, 1, 1, 3), DataType::Int8); + REQUIRE(supportedOps->Check(op.get()) == true); + Quantization q = Quantization::Unit(); + q.scales.push_back({8, 2}); + q.zeroPoints.push_back(2); + op->Input(TensorUsage::IFM)->Set(q); + REQUIRE(supportedOps->Check(op.get()) == false); + op->Disconnect(); + } + + SECTION("ConstraintMatchingQuantization") + { + auto op = CreateOperation(OpType::Minimum, Shape(1, 1, 1, 1), DataType::Int8, Shape(1, 1, 1, 1), DataType::Int8, + Shape(1, 1, 1, 1), DataType::Int8); + REQUIRE(supportedOps->Check(op.get()) == true); + Quantization q; + q.scales.push_back(8); + q.zeroPoints.push_back(2); + op->Input(TensorUsage::IFM)->Set(q); + REQUIRE(supportedOps->Check(op.get()) == false); + op->Disconnect(); + } } TEST_CASE("Supported operators EthosU55") @@ -235,6 +261,18 @@ TEST_CASE("Supported operators EthosU55") REQUIRE(supportedOps->Check(op.get()) == false); op->Disconnect(); } + + + SECTION("Constraint32BitOps") + { + auto op = CreateOperation(OpType::Add, Shape(1, 1, 1, 1), DataType::Int32, Shape(1, 1, 1, 1), DataType::Int32, + Shape(1, 1, 1, 1), DataType::Int32); + REQUIRE(supportedOps->Check(op.get()) == true); + auto op2 = CreateOperation(OpType::MaxPool, Shape(1, 1, 1, 1), DataType::Int32, Shape(1, 1, 1, 1), DataType::Int32); + REQUIRE(supportedOps->Check(op2.get()) == false); + op->Disconnect(); + op2->Disconnect(); + } } TEST_CASE("Supported operators EthosU85") diff --git a/ethosu/regor/tflite/tflite_supported_operators.cpp b/ethosu/regor/tflite/tflite_supported_operators.cpp index 7fdd1337..ac0e8844 100644 --- a/ethosu/regor/tflite/tflite_supported_operators.cpp +++ b/ethosu/regor/tflite/tflite_supported_operators.cpp @@ -203,6 +203,56 @@ bool TfLiteSupportedOperators::ConstraintFCWeightShape(const Operation *op) return true; } +bool TfLiteSupportedOperators::ConstraintPerAxisQuant(const Operation *op) +{ + OpType opType = op->Type(); + if ( IsConvolution(opType) || opType == OpType::FullyConnected ) + { + return true; + } + + for ( const auto *list : {&op->Inputs(), &op->Outputs()} ) + { + for ( const auto &[usage, conn] : list->pairs() ) + { + if ( conn.quantization.scales.size() > 1 || conn.quantization.zeroPoints.size() > 1 ) + { + Failure(op, "Operation does not support per-axis quantization", ""); + return false; + } + } + } + return true; +} + +bool TfLiteSupportedOperators::ConstraintMatchingQuantization(const Operation *op) +{ + const char *constraint = "Both Input quantization parameters must match OFM quantization parameters"; + + OpType opType = op->Type(); + + if ( opType != OpType::Minimum && opType != OpType::Maximum ) + { + return true; + } + + const auto ofmConn = op->Output(TensorUsage::OFM); + const auto ifmConn = op->Input(TensorUsage::IFM); + const auto ifm2Conn = op->Input(TensorUsage::IFM1); + assert(ofmConn); + assert(ifmConn); + assert(ifm2Conn); + const auto &ofmQuant = ofmConn->quantization; + const auto &ifmQuant = ifmConn->quantization; + const auto &ifm2Quant = ifm2Conn->quantization; + if ( ifmQuant != ofmQuant || ifm2Quant != ofmQuant ) + { + Failure(op, "Operation has mismatching quantization parameters.", constraint); + return false; + } + return true; +} + void TfLiteSupportedOperators::Failure(const Operation *op, const std::string &message, const std::string &constraint) { assert(op); @@ -237,6 +287,8 @@ TfLiteSupportedOperators::TfLiteSupportedOperators(IArchitectureConstraints *con &TfLiteSupportedOperators::ConstraintTensMustHaveShape, &TfLiteSupportedOperators::ConstraintFCWeightShape, &TfLiteSupportedOperators::ConstraintTensQuantized, + &TfLiteSupportedOperators::ConstraintPerAxisQuant, + &TfLiteSupportedOperators::ConstraintMatchingQuantization, }; } diff --git a/ethosu/regor/tflite/tflite_supported_operators.hpp b/ethosu/regor/tflite/tflite_supported_operators.hpp index 8fc995f1..4d062f97 100644 --- a/ethosu/regor/tflite/tflite_supported_operators.hpp +++ b/ethosu/regor/tflite/tflite_supported_operators.hpp @@ -54,5 +54,8 @@ private: bool ConstraintTensMustHaveShape(const Operation *op); bool ConstraintTensQuantized(const Operation *op); bool ConstraintFCWeightShape(const Operation *op); + bool ConstraintPerAxisQuant(const Operation *op); + bool ConstraintMatchingQuantization(const Operation *op); + bool ConstraintDepthMultiplier(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 c3dc1582..f3dc242c 100644 --- a/ethosu/regor/tflite/tflite_supported_operators_u55.cpp +++ b/ethosu/regor/tflite/tflite_supported_operators_u55.cpp @@ -90,6 +90,7 @@ TfLiteSupportedOperatorsU55::TfLiteSupportedOperatorsU55(IArchitectureConstraint _checks = { &TfLiteSupportedOperatorsU55::ConstraintBroadcastShapes, &TfLiteSupportedOperatorsU55::ConstraintReverse, + &TfLiteSupportedOperatorsU55::Constraint32bitOps, }; } @@ -155,4 +156,40 @@ bool TfLiteSupportedOperatorsU55::ConstraintReverse(const Operation *op) return true; } +bool TfLiteSupportedOperatorsU55::Constraint32bitOps(const Operation *op) +{ + static const std::unordered_set supported = { + OpType::ReduceSum, + OpType::Shape, + OpType::ArgMax, + OpType::Transpose, + OpType::MirrorPad, + OpType::Add, + OpType::Mul, + OpType::Sub, + OpType::BatchMatMul, + OpType::FullyConnected, + }; + + OpType opType = op->Type(); + + if ( supported.count(opType) > 0 ) + { + return true; + } + + for ( const auto *list : {&op->Inputs(), &op->Outputs()} ) + { + for ( const auto &[usage, conn] : list->pairs() ) + { + auto type = conn.tensor->Type(); + if ( type == DataType::Int32 && (IsIFM(usage) || IsOFM(usage)) ) + { + Failure(op, "Operation does not support Int32 inputs/outputs", ""); + return false; + } + } + } + return true; +} } // namespace regor diff --git a/ethosu/regor/tflite/tflite_supported_operators_u55.hpp b/ethosu/regor/tflite/tflite_supported_operators_u55.hpp index f3e483d0..74cfe59a 100644 --- a/ethosu/regor/tflite/tflite_supported_operators_u55.hpp +++ b/ethosu/regor/tflite/tflite_supported_operators_u55.hpp @@ -42,5 +42,6 @@ public: private: bool ConstraintBroadcastShapes(const Operation *op); bool ConstraintReverse(const Operation *op); + bool Constraint32bitOps(const Operation *op); }; } // namespace regor -- GitLab