diff --git a/include/onnxruntime/core/graph/graph.h b/include/onnxruntime/core/graph/graph.h index 22827d43b200f..b9b8a25286b7b 100644 --- a/include/onnxruntime/core/graph/graph.h +++ b/include/onnxruntime/core/graph/graph.h @@ -753,7 +753,6 @@ class Graph { // NOLINT(clang-analyzer-optin.performance.Padding): preserve exi cannot be overridden at runtime. If the initializer is not found or is not constant, a nullptr is returned. @param check_outer_scope If true and the graph is a subgraph, check ancestor graph/s for 'name' if not found in 'graph'. - @remarks check_outer_scope of true is not supported in a minimal build */ const ONNX_NAMESPACE::TensorProto* GetConstantInitializer(const std::string& name, bool check_outer_scope) const; diff --git a/include/onnxruntime/core/graph/graph_viewer.h b/include/onnxruntime/core/graph/graph_viewer.h index 3cdbb07099cab..1023d50310181 100644 --- a/include/onnxruntime/core/graph/graph_viewer.h +++ b/include/onnxruntime/core/graph/graph_viewer.h @@ -165,7 +165,8 @@ class GraphViewer { if a const initializer is part of the underlying Graph but not part of this GraphViewer, it will still be returned instead of nullptr */ - const ONNX_NAMESPACE::TensorProto* GetConstantInitializer(const std::string& name, bool check_outer_scope) const; + const ONNX_NAMESPACE::TensorProto* GetConstantInitializer(const std::string& name, + bool check_outer_scope = true) const; /** Get the Node containing this Graph if IsSubgraph is true. Returns nullptr otherwise. */ const Node* ParentNode() const noexcept { return graph_->ParentNode(); } diff --git a/onnxruntime/core/graph/graph_viewer.cc b/onnxruntime/core/graph/graph_viewer.cc index cf78040ea5ac6..acf7b3a16541f 100644 --- a/onnxruntime/core/graph/graph_viewer.cc +++ b/onnxruntime/core/graph/graph_viewer.cc @@ -212,6 +212,8 @@ const std::string& GraphViewer::Description() const noexcept { bool GraphViewer::GetInitializedTensor(const std::string& tensor_name, const ONNX_NAMESPACE::TensorProto*& value) const { + value = nullptr; + // if we are using filtered subgraph, the initializer has to be part of the subgraph if (filter_info_ != nullptr && filtered_initializers_.find(tensor_name) == filtered_initializers_.cend()) return false; diff --git a/onnxruntime/core/providers/coreml/builders/impl/clip_op_builder.cc b/onnxruntime/core/providers/coreml/builders/impl/clip_op_builder.cc index 3a3f89d24c7d8..a298a8d12c741 100644 --- a/onnxruntime/core/providers/coreml/builders/impl/clip_op_builder.cc +++ b/onnxruntime/core/providers/coreml/builders/impl/clip_op_builder.cc @@ -50,7 +50,7 @@ Status ClipOpBuilder::AddToModelBuilderImpl(ModelBuilder& model_builder, const auto& input_name = node.InputDefs()[0]->Name(); const auto& output_name = node.OutputDefs()[0]->Name(); float min, max; - ORT_RETURN_IF_NOT(GetClipMinMax(model_builder.GetInitializerTensors(), node, min, max, logger), "GetClipMinMax failed"); + ORT_RETURN_IF_NOT(GetClipMinMax(model_builder.GetGraphViewer(), node, min, max, logger), "GetClipMinMax failed"); bool has_min = min != std::numeric_limits::lowest(); bool has_max = max != std::numeric_limits::max(); @@ -132,8 +132,7 @@ Status ClipOpBuilder::AddToModelBuilderImpl(ModelBuilder& model_builder, bool ClipOpBuilder::IsOpSupportedImpl(const Node& node, const OpBuilderInputParams& input_params, const logging::Logger& logger) const { float min, max; - const auto& initializers = input_params.graph_viewer.GetAllInitializedTensors(); - return GetClipMinMax(initializers, node, min, max, logger); + return GetClipMinMax(input_params.graph_viewer, node, min, max, logger); } void CreateClipOpBuilder(const std::string& op_type, OpBuilderRegistrations& op_registrations) { diff --git a/onnxruntime/core/providers/nnapi/nnapi_builtin/builders/helper.cc b/onnxruntime/core/providers/nnapi/nnapi_builtin/builders/helper.cc index 3209ad734fa20..0b32508a5bb38 100644 --- a/onnxruntime/core/providers/nnapi/nnapi_builtin/builders/helper.cc +++ b/onnxruntime/core/providers/nnapi/nnapi_builtin/builders/helper.cc @@ -184,9 +184,8 @@ bool HasValidBinaryOpQuantizedInputTypes(const NodeUnit& node_unit) { return true; } -common::Status GetQuantizationScaleAndZeroPoint( - const InitializedTensorSet& initializers, const NodeUnitIODef& io_def, const Path& model_path, - float& scale, int32_t& zero_point) { +common::Status GetQuantizationScaleAndZeroPoint(const GraphViewer& graph_viewer, const NodeUnitIODef& io_def, + const Path& model_path, float& scale, int32_t& zero_point) { scale = 0.0f; zero_point = 0; @@ -198,14 +197,24 @@ common::Status GetQuantizationScaleAndZeroPoint( const auto& quant_param = *io_def.quant_param; { // get the scale const auto& name = quant_param.scale.Name(); - Initializer unpacked_tensor(*initializers.at(name), model_path); + const auto* s = graph_viewer.GetConstantInitializer(name); + if (!s) { + return ORT_MAKE_STATUS(ONNXRUNTIME, INVALID_ARGUMENT, name, " is not a constant initializer"); + }; + + Initializer unpacked_tensor(*s, model_path); // The scale should be one or more floats scale = unpacked_tensor.DataAsSpan()[0]; } if (quant_param.zero_point) { // get the zero point if it's there const auto& name = quant_param.zero_point->Name(); - Initializer unpacked_tensor(*initializers.at(name), model_path); + const auto* zp = graph_viewer.GetConstantInitializer(name); + if (!zp) { + return ORT_MAKE_STATUS(ONNXRUNTIME, INVALID_ARGUMENT, name, " is not a constant initializer"); + }; + + Initializer unpacked_tensor(*zp, model_path); // Onnx quantization uses uint8 [int8 not yet supported], need to cast to int32_t used by NNAPI zero_point = static_cast(unpacked_tensor.DataAsByteSpan()[0]); } @@ -213,13 +222,13 @@ common::Status GetQuantizationScaleAndZeroPoint( return Status::OK(); } -common::Status GetQuantizationScaleAndZeroPoint( - const InitializedTensorSet& initializers, const NodeUnit& node_unit, const std::string& name, - float& scale, int32_t& zero_point, ArgType arg_type) { +common::Status GetQuantizationScaleAndZeroPoint(const GraphViewer& graph_viewer, const NodeUnit& node_unit, + const std::string& name, float& scale, int32_t& zero_point, + ArgType arg_type) { const auto& io_defs = arg_type == ArgType::kInput ? node_unit.Inputs() : node_unit.Outputs(); for (const auto& io_def : io_defs) { if (io_def.node_arg.Name() == name) - return GetQuantizationScaleAndZeroPoint(initializers, io_def, node_unit.ModelPath(), + return GetQuantizationScaleAndZeroPoint(graph_viewer, io_def, node_unit.ModelPath(), scale, zero_point); } @@ -348,7 +357,7 @@ bool IsNodeSupported(const NodeUnit& node_unit, const GraphViewer& graph_viewer, } const auto* op_builder = op_builder_it->second; - return op_builder->IsOpSupported(graph_viewer.GetAllInitializedTensors(), node_unit, params); + return op_builder->IsOpSupported(graph_viewer, node_unit, params); } bool IsNodeSupportedInGroup(const NodeUnit& node_unit, const GraphViewer& graph_viewer, @@ -381,11 +390,11 @@ uint32_t ShapeSize(const Shape& shape, size_t begin_idx, size_t end_idx) { SafeInt{1}, std::multiplies>{}); } -bool CheckIsInitializer(const InitializedTensorSet& initializers, const NodeUnit& node_unit, - const std::string& input_name, const char* input_description) { - if (!Contains(initializers, input_name)) { +bool CheckIsConstantInitializer(const GraphViewer& graph_viewer, const NodeUnit& node_unit, + const std::string& input_name, const char* input_description) { + if (!graph_viewer.GetConstantInitializer(input_name)) { LOGS_DEFAULT(VERBOSE) << input_description << " of " << node_unit.Name() << "of type [" - << node_unit.OpType() << "] must be an initializer tensor"; + << node_unit.OpType() << "] must be a constant initializer"; return false; } diff --git a/onnxruntime/core/providers/nnapi/nnapi_builtin/builders/helper.h b/onnxruntime/core/providers/nnapi/nnapi_builtin/builders/helper.h index 766034b3decea..a606b8aceb63d 100644 --- a/onnxruntime/core/providers/nnapi/nnapi_builtin/builders/helper.h +++ b/onnxruntime/core/providers/nnapi/nnapi_builtin/builders/helper.h @@ -132,11 +132,11 @@ bool IsQuantizedBinaryOp(QuantizedOpType quant_op_type); bool HasValidBinaryOpQuantizedInputTypes(const NodeUnit& node_unit); common::Status GetQuantizationScaleAndZeroPoint( - const InitializedTensorSet& initializers, const NodeUnitIODef& io_def, const Path& model_path, + const GraphViewer& graph_viewer, const NodeUnitIODef& io_def, const Path& model_path, float& scale, int32_t& zero_point); common::Status GetQuantizationScaleAndZeroPoint( - const InitializedTensorSet& initializers, const NodeUnit& node_unit, const std::string& name, + const GraphViewer& graph_viewer, const NodeUnit& node_unit, const std::string& name, float& scale, int32_t& zero_point, ArgType arg_type = ArgType::kInput); // Get Shape/Type of a NodeArg @@ -167,11 +167,11 @@ inline uint32_t ShapeSize(const Shape& shape) { return ShapeSize(shape, 0, shape.size()); } -// Check the given input is an initializer tensor +// Check the given input is a constant initializer // input_name is the name of the initializer // input_description is the string describing the input in the output message (if any) -bool CheckIsInitializer(const InitializedTensorSet& initializers, const NodeUnit& node_unit, - const std::string& input_name, const char* input_description); +bool CheckIsConstantInitializer(const GraphViewer& graph_viewer, const NodeUnit& node_unit, + const std::string& input_name, const char* input_description); // Convert ONNX int64 input to NNAPI int32 type input and optionally handle negative axis if needed // Mostly used in handling `axes` input for now diff --git a/onnxruntime/core/providers/nnapi/nnapi_builtin/builders/impl/LRN_op_builder.cc b/onnxruntime/core/providers/nnapi/nnapi_builtin/builders/impl/LRN_op_builder.cc index 00bca4001326c..91cad034d8854 100644 --- a/onnxruntime/core/providers/nnapi/nnapi_builtin/builders/impl/LRN_op_builder.cc +++ b/onnxruntime/core/providers/nnapi/nnapi_builtin/builders/impl/LRN_op_builder.cc @@ -29,7 +29,7 @@ class LRNOpBuilder : public BaseOpBuilder { // Operator support related private: - bool IsOpSupportedImpl(const InitializedTensorSet& initializers, const NodeUnit& node_unit, + bool IsOpSupportedImpl(const GraphViewer& graph_viewer, const NodeUnit& node_unit, const OpSupportCheckParams& params) const override; int32_t GetMinSupportedNNAPIFeatureLevel(const NodeUnit& /* node_unit */, @@ -91,7 +91,7 @@ Status LRNOpBuilder::AddToModelBuilderImpl(ModelBuilder& model_builder, const No // Operator support related -bool LRNOpBuilder::IsOpSupportedImpl(const InitializedTensorSet& /* initializers */, const NodeUnit& node_unit, +bool LRNOpBuilder::IsOpSupportedImpl(const GraphViewer& /* graph_viewer */, const NodeUnit& node_unit, const OpSupportCheckParams& /* params */) const { Shape input_shape; if (!GetShape(node_unit.Inputs()[0].node_arg, input_shape)) diff --git a/onnxruntime/core/providers/nnapi/nnapi_builtin/builders/impl/base_op_builder.cc b/onnxruntime/core/providers/nnapi/nnapi_builtin/builders/impl/base_op_builder.cc index 7797e0a47caaf..adc79576272ab 100644 --- a/onnxruntime/core/providers/nnapi/nnapi_builtin/builders/impl/base_op_builder.cc +++ b/onnxruntime/core/providers/nnapi/nnapi_builtin/builders/impl/base_op_builder.cc @@ -1,6 +1,7 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT License. +#include "core/graph/graph_viewer.h" #include "core/providers/nnapi/nnapi_builtin/builders/impl/base_op_builder.h" namespace onnxruntime { @@ -11,10 +12,11 @@ bool HasExternalInitializer(const InitializedTensorSet& initializers, const Node const auto is_ext_initializer = [&](const NodeArg& node_arg) { const auto& input_name(node_arg.Name()); - if (!Contains(initializers, input_name)) + const auto initializer = initializers.find(input_name); + if (initializer == initializers.end()) return false; - const auto& tensor = *initializers.at(input_name); + const auto& tensor = *initializer->second; if (tensor.has_data_location() && tensor.data_location() == ONNX_NAMESPACE::TensorProto_DataLocation_EXTERNAL) { LOGS_DEFAULT(VERBOSE) << "Initializer [" << input_name @@ -51,8 +53,12 @@ Status BaseOpBuilder::AddToModelBuilder(ModelBuilder& model_builder, const NodeU model_builder.GetEffectiveFeatureLevel(), model_builder.UseNCHW(), }; - ORT_RETURN_IF_NOT(IsOpSupported(model_builder.GetInitializerTensors(), node_unit, params), - "Unsupported operator ", node_unit.OpType()); + + // We checked supported in IExecutionProvider::GetCapability. + // Checking again in AddToModelBuilder which is called in IExecutionProvider::Compile is redundant. + // ORT_RETURN_IF_NOT(IsOpSupported(model_builder.GetGraphViewer(), node_unit, params), + // "Unsupported operator ", node_unit.OpType()); + #ifndef NDEBUG model_builder.SetDebugCurrentOnnxNodeIndex(node_unit.Index()); #endif @@ -64,7 +70,7 @@ Status BaseOpBuilder::AddToModelBuilder(ModelBuilder& model_builder, const NodeU // Operator support related -bool BaseOpBuilder::IsOpSupported(const InitializedTensorSet& initializers, const NodeUnit& node_unit, +bool BaseOpBuilder::IsOpSupported(const GraphViewer& graph_viewer, const NodeUnit& node_unit, const OpSupportCheckParams& params) const { int32_t required_feature_level = GetMinSupportedNNAPIFeatureLevel(node_unit, params); if (required_feature_level > params.android_feature_level) { @@ -77,20 +83,20 @@ bool BaseOpBuilder::IsOpSupported(const InitializedTensorSet& initializers, cons if (!IsNodeUnitTypeSupported(node_unit)) return false; - if (!HasSupportedInputOutputs(initializers, node_unit, params)) + if (!HasSupportedInputOutputs(graph_viewer, node_unit, params)) return false; // We do not support external initializers for now - if (HasExternalInitializer(initializers, node_unit)) + if (HasExternalInitializer(graph_viewer.GetAllInitializedTensors(), node_unit)) return false; if (!HasSupportedOpSet(node_unit)) return false; - return IsOpSupportedImpl(initializers, node_unit, params); + return IsOpSupportedImpl(graph_viewer, node_unit, params); } -bool BaseOpBuilder::HasSupportedInputOutputs(const InitializedTensorSet& initializers, const NodeUnit& node_unit, +bool BaseOpBuilder::HasSupportedInputOutputs(const GraphViewer& graph_viewer, const NodeUnit& node_unit, const OpSupportCheckParams& params) const { // We do not support unknown(null) input shape auto has_supported_shape = [](const NodeArg& node_arg, const std::string& name, const std::string& op_type) { @@ -128,12 +134,12 @@ bool BaseOpBuilder::HasSupportedInputOutputs(const InitializedTensorSet& initial return false; } } - return HasSupportedInputOutputsImpl(initializers, node_unit, params); + + return HasSupportedInputOutputsImpl(graph_viewer, node_unit, params); } -bool BaseOpBuilder::HasSupportedInputOutputsImpl( - const InitializedTensorSet& /* initializers */, const NodeUnit& node_unit, - const OpSupportCheckParams& /* params */) const { +bool BaseOpBuilder::HasSupportedInputOutputsImpl(const GraphViewer& /* graph_viewer */, const NodeUnit& node_unit, + const OpSupportCheckParams& /* params */) const { // We only check the type of input 0 by default // specific op builder can override this const auto& input = node_unit.Inputs()[0].node_arg; diff --git a/onnxruntime/core/providers/nnapi/nnapi_builtin/builders/impl/base_op_builder.h b/onnxruntime/core/providers/nnapi/nnapi_builtin/builders/impl/base_op_builder.h index 339ccd67f33e3..6a54bf7bdb938 100644 --- a/onnxruntime/core/providers/nnapi/nnapi_builtin/builders/impl/base_op_builder.h +++ b/onnxruntime/core/providers/nnapi/nnapi_builtin/builders/impl/base_op_builder.h @@ -52,11 +52,11 @@ class BaseOpBuilder : public IOpBuilder { // Operator support related public: - bool IsOpSupported(const InitializedTensorSet& initializers, const NodeUnit& node_unit, + bool IsOpSupported(const GraphViewer& graph_viewer, const NodeUnit& node_unit, const OpSupportCheckParams& params) const override; protected: - virtual bool IsOpSupportedImpl(const InitializedTensorSet& /* initializers */, const NodeUnit& /* node_unit */, + virtual bool IsOpSupportedImpl(const GraphViewer& /* graph_viewer */, const NodeUnit& /* node_unit */, const OpSupportCheckParams& /* params */) const { return true; } @@ -68,9 +68,8 @@ class BaseOpBuilder : public IOpBuilder { return ANEURALNETWORKS_FEATURE_LEVEL_1; } - virtual bool HasSupportedInputOutputsImpl( - const InitializedTensorSet& initializers, const NodeUnit& node_unit, - const OpSupportCheckParams& params) const; + virtual bool HasSupportedInputOutputsImpl(const GraphViewer& graph_viewer, const NodeUnit& node_unit, + const OpSupportCheckParams& params) const; virtual int GetMinSupportedOpSet(const NodeUnit& /* node_unit */) const { return 1; } virtual int GetMaxSupportedOpSet(const NodeUnit& /* node_unit */) const { return 19; } @@ -82,7 +81,7 @@ class BaseOpBuilder : public IOpBuilder { private: bool HasSupportedOpSet(const NodeUnit& node_unit) const; - bool HasSupportedInputOutputs(const InitializedTensorSet& initializers, const NodeUnit& node_unit, + bool HasSupportedInputOutputs(const GraphViewer& graph_viewer, const NodeUnit& node_unit, const OpSupportCheckParams& params) const; }; diff --git a/onnxruntime/core/providers/nnapi/nnapi_builtin/builders/impl/batchnorm_op_builder.cc b/onnxruntime/core/providers/nnapi/nnapi_builtin/builders/impl/batchnorm_op_builder.cc index 3add0ac26c0d4..75a66d3a14643 100644 --- a/onnxruntime/core/providers/nnapi/nnapi_builtin/builders/impl/batchnorm_op_builder.cc +++ b/onnxruntime/core/providers/nnapi/nnapi_builtin/builders/impl/batchnorm_op_builder.cc @@ -33,7 +33,7 @@ class BatchNormalizationOpBuilder : public BaseOpBuilder { // Operator support related private: - bool IsOpSupportedImpl(const InitializedTensorSet& initializers, const NodeUnit& node_unit, + bool IsOpSupportedImpl(const GraphViewer& graph_viewer, const NodeUnit& node_unit, const OpSupportCheckParams& params) const override; // BatchNormalization opset 6- has unsupported attributes @@ -127,7 +127,7 @@ Status BatchNormalizationOpBuilder::AddToModelBuilderImpl(ModelBuilder& model_bu // Operator support related -bool BatchNormalizationOpBuilder::IsOpSupportedImpl(const InitializedTensorSet& initializers, const NodeUnit& node_unit, +bool BatchNormalizationOpBuilder::IsOpSupportedImpl(const GraphViewer& graph_viewer, const NodeUnit& node_unit, const OpSupportCheckParams& /* params */) const { if (node_unit.Outputs().size() != 1) { LOGS_DEFAULT(VERBOSE) << "Your onnx model may be in training mode, please export " @@ -158,20 +158,20 @@ bool BatchNormalizationOpBuilder::IsOpSupportedImpl(const InitializedTensorSet& const auto& b_name = inputs[2].node_arg.Name(); const auto& mean_name = inputs[3].node_arg.Name(); const auto& var_name = inputs[4].node_arg.Name(); - if (!Contains(initializers, scale_name)) { - LOGS_DEFAULT(VERBOSE) << "Scale of BN must be known"; + if (!graph_viewer.GetConstantInitializer(scale_name)) { + LOGS_DEFAULT(VERBOSE) << "Scale of BN must be a constant initializer"; return false; } - if (!Contains(initializers, b_name)) { - LOGS_DEFAULT(VERBOSE) << "B of BN must be known"; + if (!graph_viewer.GetConstantInitializer(b_name)) { + LOGS_DEFAULT(VERBOSE) << "B of BN must be a constant initializer"; return false; } - if (!Contains(initializers, mean_name)) { - LOGS_DEFAULT(VERBOSE) << "Mean of BN must be known"; + if (!graph_viewer.GetConstantInitializer(mean_name)) { + LOGS_DEFAULT(VERBOSE) << "Mean of BN must be a constant initializer"; return false; } - if (!Contains(initializers, var_name)) { - LOGS_DEFAULT(VERBOSE) << "Var of BN must be known"; + if (!graph_viewer.GetConstantInitializer(var_name)) { + LOGS_DEFAULT(VERBOSE) << "Var of BN must be a constant initializer"; return false; } diff --git a/onnxruntime/core/providers/nnapi/nnapi_builtin/builders/impl/binary_op_builder.cc b/onnxruntime/core/providers/nnapi/nnapi_builtin/builders/impl/binary_op_builder.cc index dce1a7c8659bf..5599fbdc69bdd 100644 --- a/onnxruntime/core/providers/nnapi/nnapi_builtin/builders/impl/binary_op_builder.cc +++ b/onnxruntime/core/providers/nnapi/nnapi_builtin/builders/impl/binary_op_builder.cc @@ -34,10 +34,10 @@ class BinaryOpBuilder : public BaseOpBuilder { private: int32_t GetMinSupportedNNAPIFeatureLevel(const NodeUnit& node_unit, const OpSupportCheckParams& params) const override; - bool IsOpSupportedImpl(const InitializedTensorSet& initializers, const NodeUnit& node_unit, + bool IsOpSupportedImpl(const GraphViewer& graph_viewer, const NodeUnit& node_unit, const OpSupportCheckParams& params) const override; bool HasSupportedInputOutputsImpl( - const InitializedTensorSet& initializers, const NodeUnit& node_unit, + const GraphViewer& graph_viewer, const NodeUnit& node_unit, const OpSupportCheckParams& params) const override; int GetMinSupportedOpSet(const NodeUnit& node_unit) const override; @@ -95,7 +95,7 @@ Status BinaryOpBuilder::AddToModelBuilderImpl(ModelBuilder& model_builder, const if (is_quant_op) { ORT_RETURN_IF_ERROR(GetBinaryOpQuantizationScaleAndZeroPoint( - model_builder.GetInitializerTensors(), node_unit, + model_builder.GetGraphViewer(), node_unit, a_scale, b_scale, y_scale, a_zero_point, b_zero_point, y_zero_point)); } @@ -163,22 +163,22 @@ int BinaryOpBuilder::GetMinSupportedOpSet(const NodeUnit& node_unit) const { } bool BinaryOpBuilder::HasSupportedInputOutputsImpl( - const InitializedTensorSet& initializers, const NodeUnit& node_unit, + const GraphViewer& graph_viewer, const NodeUnit& node_unit, const OpSupportCheckParams& params) const { bool is_quantized_op = IsQuantizedOp(node_unit); bool is_pow = node_unit.OpType() == "Pow"; if (!is_quantized_op && !is_pow) - return BaseOpBuilder::HasSupportedInputOutputsImpl(initializers, node_unit, params); + return BaseOpBuilder::HasSupportedInputOutputsImpl(graph_viewer, node_unit, params); if (is_quantized_op) { // QLinearAdd/QDQAdd/QLinearMul/QDQMul if (!HasValidBinaryOpQuantizedInputTypes(node_unit)) return false; - if (!IsQuantizedIOSupported(initializers, node_unit, {0, 1}, params, ArgType::kInput)) + if (!IsQuantizedIOSupported(graph_viewer, node_unit, {0, 1}, params, ArgType::kInput)) return false; - if (!IsQuantizedIOSupported(initializers, node_unit, {0}, params, ArgType::kOutput)) + if (!IsQuantizedIOSupported(graph_viewer, node_unit, {0}, params, ArgType::kOutput)) return false; } @@ -203,7 +203,7 @@ bool BinaryOpBuilder::HasSupportedInputOutputsImpl( return true; } -bool BinaryOpBuilder::IsOpSupportedImpl(const InitializedTensorSet& /* initializers */, const NodeUnit& node_unit, +bool BinaryOpBuilder::IsOpSupportedImpl(const GraphViewer& /* graph_viewer */, const NodeUnit& node_unit, const OpSupportCheckParams& /* params */) const { const auto& op_type(node_unit.OpType()); const auto& inputs = node_unit.Inputs(); diff --git a/onnxruntime/core/providers/nnapi/nnapi_builtin/builders/impl/cast_op_builder.cc b/onnxruntime/core/providers/nnapi/nnapi_builtin/builders/impl/cast_op_builder.cc index b31ee484dc5a2..9059de817e210 100644 --- a/onnxruntime/core/providers/nnapi/nnapi_builtin/builders/impl/cast_op_builder.cc +++ b/onnxruntime/core/providers/nnapi/nnapi_builtin/builders/impl/cast_op_builder.cc @@ -29,7 +29,7 @@ class CastOpBuilder : public BaseOpBuilder { // Operator support related private: - bool IsOpSupportedImpl(const InitializedTensorSet& initializers, const NodeUnit& node_unit, + bool IsOpSupportedImpl(const GraphViewer& graph_viewer, const NodeUnit& node_unit, const OpSupportCheckParams& params) const override; int32_t GetMinSupportedNNAPIFeatureLevel(const NodeUnit& /* node_unit */, @@ -70,7 +70,7 @@ Status CastOpBuilder::AddToModelBuilderImpl(ModelBuilder& model_builder, const N return Status::OK(); } -bool CastOpBuilder::IsOpSupportedImpl(const InitializedTensorSet& /* initializers */, const NodeUnit& node_unit, +bool CastOpBuilder::IsOpSupportedImpl(const GraphViewer& /* graph_viewer */, const NodeUnit& node_unit, const OpSupportCheckParams& /* params */) const { NodeAttrHelper helper(node_unit); const auto to = helper.Get("to", 0); diff --git a/onnxruntime/core/providers/nnapi/nnapi_builtin/builders/impl/clip_op_builder.cc b/onnxruntime/core/providers/nnapi/nnapi_builtin/builders/impl/clip_op_builder.cc index b3e294d2f0845..9821d9267c71f 100644 --- a/onnxruntime/core/providers/nnapi/nnapi_builtin/builders/impl/clip_op_builder.cc +++ b/onnxruntime/core/providers/nnapi/nnapi_builtin/builders/impl/clip_op_builder.cc @@ -32,7 +32,7 @@ class ClipOpBuilder : public BaseOpBuilder { // Operator support related private: - bool IsOpSupportedImpl(const InitializedTensorSet& initializers, const NodeUnit& node_unit, + bool IsOpSupportedImpl(const GraphViewer& graph_viewer, const NodeUnit& node_unit, const OpSupportCheckParams& params) const override; }; @@ -64,7 +64,7 @@ Status ClipOpBuilder::AddToModelBuilderImpl(ModelBuilder& model_builder, const N } float min, max; - GetClipMinMax(model_builder.GetInitializerTensors(), node_unit.GetNode(), min, max, + GetClipMinMax(model_builder.GetGraphViewer(), node_unit.GetNode(), min, max, logging::LoggingManager::DefaultLogger()); int32_t op_code; @@ -85,10 +85,10 @@ Status ClipOpBuilder::AddToModelBuilderImpl(ModelBuilder& model_builder, const N // Operator support related -bool ClipOpBuilder::IsOpSupportedImpl(const InitializedTensorSet& initializers, const NodeUnit& node_unit, +bool ClipOpBuilder::IsOpSupportedImpl(const GraphViewer& graph_viewer, const NodeUnit& node_unit, const OpSupportCheckParams& /* params */) const { float min, max; - if (!GetClipMinMax(initializers, node_unit.GetNode(), min, max, logging::LoggingManager::DefaultLogger())) + if (!GetClipMinMax(graph_viewer, node_unit.GetNode(), min, max, logging::LoggingManager::DefaultLogger())) return false; // We only supoort relu6 or relu1 diff --git a/onnxruntime/core/providers/nnapi/nnapi_builtin/builders/impl/concat_op_builder.cc b/onnxruntime/core/providers/nnapi/nnapi_builtin/builders/impl/concat_op_builder.cc index 2bf8f07e26fd4..a8394faec51be 100644 --- a/onnxruntime/core/providers/nnapi/nnapi_builtin/builders/impl/concat_op_builder.cc +++ b/onnxruntime/core/providers/nnapi/nnapi_builtin/builders/impl/concat_op_builder.cc @@ -32,11 +32,11 @@ class ConcatOpBuilder : public BaseOpBuilder { // Operator support related private: - bool IsOpSupportedImpl(const InitializedTensorSet& initializers, const NodeUnit& node_unit, + bool IsOpSupportedImpl(const GraphViewer& graph_viewer, const NodeUnit& node_unit, const OpSupportCheckParams& params) const override; bool HasSupportedInputOutputsImpl( - const InitializedTensorSet& initializers, const NodeUnit& node_unit, + const GraphViewer& graph_viewer, const NodeUnit& node_unit, const OpSupportCheckParams& params) const override; bool IsNodeUnitTypeSupported(const NodeUnit& /* node_unit */) const override { return true; } @@ -113,7 +113,7 @@ Status ConcatOpBuilder::AddToModelBuilderImpl(ModelBuilder& model_builder, const float scale = 0.0f; int32_t zero_point = 0; ORT_RETURN_IF_ERROR(GetQuantizationScaleAndZeroPoint( - model_builder.GetInitializerTensors(), node_unit.Inputs()[i], node_unit.ModelPath(), + model_builder.GetGraphViewer(), node_unit.Inputs()[i], node_unit.ModelPath(), scale, zero_point)); ORT_RETURN_IF_ERROR(IsValidInputQuantizedType(model_builder, input, scale, zero_point)); @@ -128,7 +128,7 @@ Status ConcatOpBuilder::AddToModelBuilderImpl(ModelBuilder& model_builder, const int32_t y_zero_point = operand_types.at(input0).operandType.zeroPoint; if (is_quant_op) { ORT_RETURN_IF_ERROR(GetQuantizationScaleAndZeroPoint( - model_builder.GetInitializerTensors(), node_unit.Outputs()[0], node_unit.ModelPath(), + model_builder.GetGraphViewer(), node_unit.Outputs()[0], node_unit.ModelPath(), y_scale, y_zero_point)); } @@ -151,7 +151,7 @@ bool ConcatOpBuilder::IsQuantizedOp(const NodeUnit& node_unit) const { return GetQuantizedOpType(node_unit) == QuantizedOpType::QDQConcat; } -bool ConcatOpBuilder::IsOpSupportedImpl(const InitializedTensorSet& /* initializers */, const NodeUnit& node_unit, +bool ConcatOpBuilder::IsOpSupportedImpl(const GraphViewer& /* graph_viewer */, const NodeUnit& node_unit, const OpSupportCheckParams& /* params */) const { Shape input_shape; if (!GetShape(node_unit.Inputs()[0].node_arg, input_shape)) @@ -168,7 +168,7 @@ bool ConcatOpBuilder::IsOpSupportedImpl(const InitializedTensorSet& /* initializ } bool ConcatOpBuilder::HasSupportedInputOutputsImpl( - const InitializedTensorSet& initializers, const NodeUnit& node_unit, + const GraphViewer& graph_viewer, const NodeUnit& node_unit, const OpSupportCheckParams& params) const { const auto& op_type = node_unit.OpType(); const auto& op_name = node_unit.Name(); @@ -188,11 +188,11 @@ bool ConcatOpBuilder::HasSupportedInputOutputsImpl( if (IsQuantizedOp(node_unit)) { std::vector input_indices(input_size); std::iota(input_indices.begin(), input_indices.end(), 0); - if (!IsQuantizedIOSupported(initializers, node_unit, input_indices, params, ArgType::kInput)) { + if (!IsQuantizedIOSupported(graph_viewer, node_unit, input_indices, params, ArgType::kInput)) { return false; } - if (!IsQuantizedIOSupported(initializers, node_unit, {0}, params, ArgType::kOutput)) { + if (!IsQuantizedIOSupported(graph_viewer, node_unit, {0}, params, ArgType::kOutput)) { return false; } @@ -203,7 +203,7 @@ bool ConcatOpBuilder::HasSupportedInputOutputsImpl( size_t input_idx = 0; auto status = GetQuantizationScaleAndZeroPoint( - initializers, node_unit.Inputs()[input_idx], node_unit.ModelPath(), + graph_viewer, node_unit.Inputs()[input_idx], node_unit.ModelPath(), input_scales[input_idx], input_zps[input_idx]); if (!status.IsOK()) { @@ -214,7 +214,7 @@ bool ConcatOpBuilder::HasSupportedInputOutputsImpl( } for (++input_idx; input_idx < input_size; ++input_idx) { - if (!HasRequiredScaleAndZeroPoint(initializers, + if (!HasRequiredScaleAndZeroPoint(graph_viewer, MakeString("Op [", op_type, "] name [", op_name, "] input ", input_idx), node_unit.Inputs()[input_idx], node_unit.ModelPath(), @@ -225,7 +225,7 @@ bool ConcatOpBuilder::HasSupportedInputOutputsImpl( } // NNAPI (28-) requires the output scale and zp be the same as the input 0 - if (!HasRequiredScaleAndZeroPoint(initializers, + if (!HasRequiredScaleAndZeroPoint(graph_viewer, MakeString("Op [", op_type, "] name [", op_name, "]'s output 0"), node_unit.Outputs()[0], node_unit.ModelPath(), input_scales[0] /* required_scale */, diff --git a/onnxruntime/core/providers/nnapi/nnapi_builtin/builders/impl/conv_op_builder.cc b/onnxruntime/core/providers/nnapi/nnapi_builtin/builders/impl/conv_op_builder.cc index 5b8bbd338a13d..5477cd16f9c01 100644 --- a/onnxruntime/core/providers/nnapi/nnapi_builtin/builders/impl/conv_op_builder.cc +++ b/onnxruntime/core/providers/nnapi/nnapi_builtin/builders/impl/conv_op_builder.cc @@ -33,7 +33,7 @@ class ConvOpBuilder : public BaseOpBuilder { // Operator support related private: - bool IsOpSupportedImpl(const InitializedTensorSet& initializers, const NodeUnit& node_unit, + bool IsOpSupportedImpl(const GraphViewer& graph_viewer, const NodeUnit& node_unit, const OpSupportCheckParams& params) const override; int32_t GetMinSupportedNNAPIFeatureLevel(const NodeUnit& /* node_unit */, @@ -41,9 +41,8 @@ class ConvOpBuilder : public BaseOpBuilder { return params.use_nchw ? ANEURALNETWORKS_FEATURE_LEVEL_3 : ANEURALNETWORKS_FEATURE_LEVEL_2; } - bool HasSupportedInputOutputsImpl( - const InitializedTensorSet& /* initializers */, const NodeUnit& node_unit, - const OpSupportCheckParams& /* params */) const override; + bool HasSupportedInputOutputsImpl(const GraphViewer& graph_viewer, const NodeUnit& node_unit, + const OpSupportCheckParams& params) const override; bool IsNodeUnitTypeSupported(const NodeUnit& /* node_unit */) const override { return true; } bool IsQuantizedOp(const NodeUnit& node_unit) const override; }; @@ -279,19 +278,19 @@ bool ConvOpBuilder::IsQuantizedOp(const NodeUnit& node_unit) const { } bool ConvOpBuilder::HasSupportedInputOutputsImpl( - const InitializedTensorSet& initializers, const NodeUnit& node_unit, + const GraphViewer& graph_viewer, const NodeUnit& node_unit, const OpSupportCheckParams& params) const { if (!IsQuantizedOp(node_unit)) - return BaseOpBuilder::HasSupportedInputOutputsImpl(initializers, node_unit, params); + return BaseOpBuilder::HasSupportedInputOutputsImpl(graph_viewer, node_unit, params); // QLinearConv only supports input of uint8 for now if (!HasValidBinaryOpQuantizedInputTypes(node_unit)) return false; - if (!IsQuantizedIOSupported(initializers, node_unit, {0, 1}, params, ArgType::kInput)) + if (!IsQuantizedIOSupported(graph_viewer, node_unit, {0, 1}, params, ArgType::kInput)) return false; - if (!IsQuantizedIOSupported(initializers, node_unit, {0}, params, ArgType::kOutput)) + if (!IsQuantizedIOSupported(graph_viewer, node_unit, {0}, params, ArgType::kOutput)) return false; return true; @@ -299,7 +298,7 @@ bool ConvOpBuilder::HasSupportedInputOutputsImpl( // Operator support related -bool ConvOpBuilder::IsOpSupportedImpl(const InitializedTensorSet& initializers, const NodeUnit& node_unit, +bool ConvOpBuilder::IsOpSupportedImpl(const GraphViewer& graph_viewer, const NodeUnit& node_unit, const OpSupportCheckParams& params) const { const auto& op_type = node_unit.OpType(); bool is_quant_conv = IsQuantizedOp(node_unit); @@ -314,8 +313,9 @@ bool ConvOpBuilder::IsOpSupportedImpl(const InitializedTensorSet& initializers, NodeAttrHelper helper(node_unit); const auto group = helper.Get("group", 1); const auto weight_name = inputs[1].node_arg.Name(); - if (Contains(initializers, weight_name)) { - const auto& tensor = *initializers.at(weight_name); + const auto* weight = graph_viewer.GetConstantInitializer(weight_name); + if (weight) { + const auto& tensor = *weight; if (tensor.dims().size() != 4) { LOGS_DEFAULT(VERBOSE) << "Only conv 2d is supported."; return false; @@ -335,13 +335,13 @@ bool ConvOpBuilder::IsOpSupportedImpl(const InitializedTensorSet& initializers, } } } else { - LOGS_DEFAULT(VERBOSE) << "The weight of convolution must be known"; + LOGS_DEFAULT(VERBOSE) << "The weight of convolution must be a constant initializer"; return false; } if (is_quant_conv) { - if (inputs.size() > 2 && !Contains(initializers, inputs[2].node_arg.Name())) { - LOGS_DEFAULT(VERBOSE) << "Bias of QLinearConv must be known"; + if (inputs.size() > 2 && !graph_viewer.GetConstantInitializer(inputs[2].node_arg.Name())) { + LOGS_DEFAULT(VERBOSE) << "Bias of QLinearConv must be a constant initializer"; return false; } } diff --git a/onnxruntime/core/providers/nnapi/nnapi_builtin/builders/impl/depthtospace_op_builder.cc b/onnxruntime/core/providers/nnapi/nnapi_builtin/builders/impl/depthtospace_op_builder.cc index 649f1e1cff2b7..ef8709641e2d0 100644 --- a/onnxruntime/core/providers/nnapi/nnapi_builtin/builders/impl/depthtospace_op_builder.cc +++ b/onnxruntime/core/providers/nnapi/nnapi_builtin/builders/impl/depthtospace_op_builder.cc @@ -29,7 +29,7 @@ class DepthToSpaceOpBuilder : public BaseOpBuilder { // Operator support related private: - bool IsOpSupportedImpl(const InitializedTensorSet& initializers, const NodeUnit& node_unit, + bool IsOpSupportedImpl(const GraphViewer& graph_viewer, const NodeUnit& node_unit, const OpSupportCheckParams& params) const override; }; @@ -66,7 +66,7 @@ Status DepthToSpaceOpBuilder::AddToModelBuilderImpl(ModelBuilder& model_builder, // Operator support related -bool DepthToSpaceOpBuilder::IsOpSupportedImpl(const InitializedTensorSet& /* initializers */, const NodeUnit& node_unit, +bool DepthToSpaceOpBuilder::IsOpSupportedImpl(const GraphViewer& /* graph_viewer */, const NodeUnit& node_unit, const OpSupportCheckParams& params) const { NodeAttrHelper helper(node_unit); diff --git a/onnxruntime/core/providers/nnapi/nnapi_builtin/builders/impl/dequantizelinear_op_builder.cc b/onnxruntime/core/providers/nnapi/nnapi_builtin/builders/impl/dequantizelinear_op_builder.cc index b2d89ffecdca4..7d0e04fbd7b0e 100644 --- a/onnxruntime/core/providers/nnapi/nnapi_builtin/builders/impl/dequantizelinear_op_builder.cc +++ b/onnxruntime/core/providers/nnapi/nnapi_builtin/builders/impl/dequantizelinear_op_builder.cc @@ -38,9 +38,9 @@ class DequantizeLinearOpBuilder : public BaseOpBuilder { } bool HasSupportedInputOutputsImpl( - const InitializedTensorSet& initializers, const NodeUnit& node_unit, + const GraphViewer& graph_viewer, const NodeUnit& node_unit, const OpSupportCheckParams& params) const override { - return IsQuantizedIOSupported(initializers, node_unit, {0}, params, ArgType::kInput); + return IsQuantizedIOSupported(graph_viewer, node_unit, {0}, params, ArgType::kInput); } }; @@ -61,7 +61,7 @@ Status DequantizeLinearOpBuilder::AddToModelBuilderImpl(ModelBuilder& model_buil float scale = 0.0; int32_t zero_point = 0; ORT_RETURN_IF_ERROR(GetQuantizationScaleAndZeroPoint( - model_builder.GetInitializerTensors(), node_unit.Inputs()[0], node_unit.ModelPath(), scale, zero_point)); + model_builder.GetGraphViewer(), node_unit.Inputs()[0], node_unit.ModelPath(), scale, zero_point)); ORT_RETURN_IF_ERROR(IsValidInputQuantizedType(model_builder, input, scale, zero_point)); diff --git a/onnxruntime/core/providers/nnapi/nnapi_builtin/builders/impl/flatten_op_builder.cc b/onnxruntime/core/providers/nnapi/nnapi_builtin/builders/impl/flatten_op_builder.cc index 065b9638bdf64..b5e9c011990ce 100644 --- a/onnxruntime/core/providers/nnapi/nnapi_builtin/builders/impl/flatten_op_builder.cc +++ b/onnxruntime/core/providers/nnapi/nnapi_builtin/builders/impl/flatten_op_builder.cc @@ -44,7 +44,7 @@ class FlattenOpBuilder : public BaseOpBuilder { // Operator support related private: - bool IsOpSupportedImpl(const InitializedTensorSet& initializers, const NodeUnit& node_unit, + bool IsOpSupportedImpl(const GraphViewer& graph_viewer, const NodeUnit& node_unit, const OpSupportCheckParams& params) const override; }; @@ -70,7 +70,7 @@ Status FlattenOpBuilder::AddToModelBuilderImpl(ModelBuilder& model_builder, cons // Operator support related -bool FlattenOpBuilder::IsOpSupportedImpl(const InitializedTensorSet& /* initializers */, const NodeUnit& node_unit, +bool FlattenOpBuilder::IsOpSupportedImpl(const GraphViewer& /* graph_viewer */, const NodeUnit& node_unit, const OpSupportCheckParams& /* params */) const { Shape input_shape; if (!GetShape(node_unit.Inputs()[0].node_arg, input_shape)) diff --git a/onnxruntime/core/providers/nnapi/nnapi_builtin/builders/impl/gather_op_builder.cc b/onnxruntime/core/providers/nnapi/nnapi_builtin/builders/impl/gather_op_builder.cc index ac8970f19df06..d6da9181b5a3d 100644 --- a/onnxruntime/core/providers/nnapi/nnapi_builtin/builders/impl/gather_op_builder.cc +++ b/onnxruntime/core/providers/nnapi/nnapi_builtin/builders/impl/gather_op_builder.cc @@ -36,7 +36,7 @@ class GatherOpBuilder : public BaseOpBuilder { return ANEURALNETWORKS_FEATURE_LEVEL_3; } - bool IsOpSupportedImpl(const InitializedTensorSet& initializers, const NodeUnit& node_unit, + bool IsOpSupportedImpl(const GraphViewer& graph_viewer, const NodeUnit& node_unit, const OpSupportCheckParams& params) const override; }; @@ -133,7 +133,7 @@ Status GatherOpBuilder::AddToModelBuilderImpl(ModelBuilder& model_builder, const // Operator support related -bool GatherOpBuilder::IsOpSupportedImpl(const InitializedTensorSet& initializers, const NodeUnit& node_unit, +bool GatherOpBuilder::IsOpSupportedImpl(const GraphViewer& graph_viewer, const NodeUnit& node_unit, const OpSupportCheckParams& /* params */) const { const auto& inputs = node_unit.Inputs(); Shape input_shape; @@ -166,8 +166,8 @@ bool GatherOpBuilder::IsOpSupportedImpl(const InitializedTensorSet& initializers return false; if (indices_type != ONNX_NAMESPACE::TensorProto_DataType_INT32) { - if (!Contains(initializers, indices_name)) { - LOGS_DEFAULT(VERBOSE) << "Indices of Gather must be known."; + if (!graph_viewer.GetConstantInitializer(indices_name)) { + LOGS_DEFAULT(VERBOSE) << "Indices of Gather must be a constant initializer."; return false; } } diff --git a/onnxruntime/core/providers/nnapi/nnapi_builtin/builders/impl/gemm_op_builder.cc b/onnxruntime/core/providers/nnapi/nnapi_builtin/builders/impl/gemm_op_builder.cc index 9b3003d472b02..8488f7cc74a6e 100644 --- a/onnxruntime/core/providers/nnapi/nnapi_builtin/builders/impl/gemm_op_builder.cc +++ b/onnxruntime/core/providers/nnapi/nnapi_builtin/builders/impl/gemm_op_builder.cc @@ -69,11 +69,10 @@ class GemmOpBuilder : public BaseOpBuilder { // Operator support related private: - bool IsOpSupportedImpl(const InitializedTensorSet& initializers, const NodeUnit& node_unit, + bool IsOpSupportedImpl(const GraphViewer& graph_viewer, const NodeUnit& node_unit, const OpSupportCheckParams& params) const override; - bool HasSupportedInputOutputsImpl( - const InitializedTensorSet& /* initializers */, const NodeUnit& node_unit, - const OpSupportCheckParams& /* params */) const override; + bool HasSupportedInputOutputsImpl(const GraphViewer& graph_viewer, const NodeUnit& node_unit, + const OpSupportCheckParams& params) const override; int GetMinSupportedOpSet(const NodeUnit& node_unit) const override; bool IsNodeUnitTypeSupported(const NodeUnit& /* node_unit */) const override { return true; } @@ -261,21 +260,20 @@ Status GemmOpBuilder::AddToModelBuilderImpl(ModelBuilder& model_builder, const N // Operator support related -bool GemmOpBuilder::HasSupportedInputOutputsImpl( - const InitializedTensorSet& initializers, const NodeUnit& node_unit, - const OpSupportCheckParams& params) const { +bool GemmOpBuilder::HasSupportedInputOutputsImpl(const GraphViewer& graph_viewer, const NodeUnit& node_unit, + const OpSupportCheckParams& params) const { if (!IsQuantizedOp(node_unit)) { - return BaseOpBuilder::HasSupportedInputOutputsImpl(initializers, node_unit, params); + return BaseOpBuilder::HasSupportedInputOutputsImpl(graph_viewer, node_unit, params); } // QLinearMatMul/QDQGemm/QDQMatMul if (!HasValidBinaryOpQuantizedInputTypes(node_unit)) return false; - if (!IsQuantizedIOSupported(initializers, node_unit, {0, 1}, params, ArgType::kInput)) + if (!IsQuantizedIOSupported(graph_viewer, node_unit, {0, 1}, params, ArgType::kInput)) return false; - if (!IsQuantizedIOSupported(initializers, node_unit, {0}, params, ArgType::kOutput)) + if (!IsQuantizedIOSupported(graph_viewer, node_unit, {0}, params, ArgType::kOutput)) return false; return true; @@ -295,7 +293,7 @@ bool GemmOpBuilder::IsQuantizedOp(const NodeUnit& node_unit) const { return IsQuantizedGemm(GetQuantizedOpType(node_unit)); } -bool GemmOpBuilder::IsOpSupportedImpl(const InitializedTensorSet& initializers, const NodeUnit& node_unit, +bool GemmOpBuilder::IsOpSupportedImpl(const GraphViewer& graph_viewer, const NodeUnit& node_unit, const OpSupportCheckParams& params) const { // check batch matmul first, then fall back to checking single gemm/matmul { @@ -355,8 +353,8 @@ bool GemmOpBuilder::IsOpSupportedImpl(const InitializedTensorSet& initializers, return false; } - if (transB == 0 && !Contains(initializers, inputs[1].node_arg.Name())) { - LOGS_DEFAULT(VERBOSE) << "B of Gemm must be known if transB != 1"; + if (transB == 0 && !graph_viewer.GetConstantInitializer(inputs[1].node_arg.Name())) { + LOGS_DEFAULT(VERBOSE) << "B of Gemm must be a constant initializer if transB != 1"; return false; } @@ -380,8 +378,8 @@ bool GemmOpBuilder::IsOpSupportedImpl(const InitializedTensorSet& initializers, } } else if (op_type == "MatMul" || is_qlinear_matmul) { // Only support A*B B is an initializer - if (!Contains(initializers, inputs[1].node_arg.Name())) { - LOGS_DEFAULT(VERBOSE) << "B of MatMul must be known"; + if (!graph_viewer.GetConstantInitializer(inputs[1].node_arg.Name())) { + LOGS_DEFAULT(VERBOSE) << "B of MatMul must be a constant initializer"; return false; } } else { @@ -389,8 +387,8 @@ bool GemmOpBuilder::IsOpSupportedImpl(const InitializedTensorSet& initializers, } if (is_quant_gemm) { - if (inputs.size() > 2 && !Contains(initializers, inputs[2].node_arg.Name())) { - LOGS_DEFAULT(VERBOSE) << "Bias of QDQ Gemm must be known"; + if (inputs.size() > 2 && !graph_viewer.GetConstantInitializer(inputs[2].node_arg.Name())) { + LOGS_DEFAULT(VERBOSE) << "Bias of QDQ Gemm must be a constant initializer"; return false; } } diff --git a/onnxruntime/core/providers/nnapi/nnapi_builtin/builders/impl/leakyrelu_op_builder.cc b/onnxruntime/core/providers/nnapi/nnapi_builtin/builders/impl/leakyrelu_op_builder.cc index 3db63a756ab1a..6a633c443c9e5 100644 --- a/onnxruntime/core/providers/nnapi/nnapi_builtin/builders/impl/leakyrelu_op_builder.cc +++ b/onnxruntime/core/providers/nnapi/nnapi_builtin/builders/impl/leakyrelu_op_builder.cc @@ -27,7 +27,7 @@ class LeakyReluOpBuilder : public BaseOpBuilder { // Operator support related private: - bool IsOpSupportedImpl(const InitializedTensorSet& initializers, const NodeUnit& node_unit, + bool IsOpSupportedImpl(const GraphViewer& graph_viewer, const NodeUnit& node_unit, const OpSupportCheckParams& params) const override; // LeakyRelu opset 6- has unsupported attributes @@ -111,7 +111,7 @@ Status LeakyReluOpBuilder::AddToModelBuilderImpl(ModelBuilder& model_builder, // Operator support related -bool LeakyReluOpBuilder::IsOpSupportedImpl(const InitializedTensorSet& /*initializers*/, const NodeUnit& node_unit, +bool LeakyReluOpBuilder::IsOpSupportedImpl(const GraphViewer& /*graph_viewer*/, const NodeUnit& node_unit, const OpSupportCheckParams& /* params */) const { Shape input_shape; if (!GetShape(node_unit.Inputs()[0].node_arg, input_shape)) diff --git a/onnxruntime/core/providers/nnapi/nnapi_builtin/builders/impl/minmax_op_builder.cc b/onnxruntime/core/providers/nnapi/nnapi_builtin/builders/impl/minmax_op_builder.cc index 522f389ae62a0..aeadbd17053cf 100644 --- a/onnxruntime/core/providers/nnapi/nnapi_builtin/builders/impl/minmax_op_builder.cc +++ b/onnxruntime/core/providers/nnapi/nnapi_builtin/builders/impl/minmax_op_builder.cc @@ -37,7 +37,7 @@ class MinMaxOpBuilder : public BaseOpBuilder { // Min/Max opset 5- uses consumed_inputs attribute which is not supported for now int GetMinSupportedOpSet(const NodeUnit& /* node_unit */) const override { return 6; } - bool IsOpSupportedImpl(const InitializedTensorSet& initializers, const NodeUnit& node_unit, + bool IsOpSupportedImpl(const GraphViewer& graph_viewer, const NodeUnit& node_unit, const OpSupportCheckParams& params) const override; }; @@ -53,7 +53,7 @@ Status MinMaxOpBuilder::AddToModelBuilderImpl(ModelBuilder& model_builder, const // Operator support related -bool MinMaxOpBuilder::IsOpSupportedImpl(const InitializedTensorSet& /* initializers */, const NodeUnit& node_unit, +bool MinMaxOpBuilder::IsOpSupportedImpl(const GraphViewer& /* graph_viewer */, const NodeUnit& node_unit, const OpSupportCheckParams& /* params */) const { // TODO: support 2+ inputs for Min/Max op if (node_unit.Inputs().size() != 2) { diff --git a/onnxruntime/core/providers/nnapi/nnapi_builtin/builders/impl/pad_op_builder.cc b/onnxruntime/core/providers/nnapi/nnapi_builtin/builders/impl/pad_op_builder.cc index 11d37f9036b11..b0404ebec0583 100644 --- a/onnxruntime/core/providers/nnapi/nnapi_builtin/builders/impl/pad_op_builder.cc +++ b/onnxruntime/core/providers/nnapi/nnapi_builtin/builders/impl/pad_op_builder.cc @@ -45,7 +45,7 @@ class PadOpBuilder : public BaseOpBuilder { return 11; } - bool IsOpSupportedImpl(const InitializedTensorSet& initializers, const NodeUnit& node_unit, + bool IsOpSupportedImpl(const GraphViewer& graph_viewer, const NodeUnit& node_unit, const OpSupportCheckParams& params) const override; }; @@ -115,7 +115,7 @@ Status PadOpBuilder::AddToModelBuilderImpl(ModelBuilder& model_builder, const No return model_builder.AddOperation(op_code, input_indices, {output}, {output_operand_type}); } -bool PadOpBuilder::IsOpSupportedImpl(const InitializedTensorSet& initializers, const NodeUnit& node_unit, +bool PadOpBuilder::IsOpSupportedImpl(const GraphViewer& graph_viewer, const NodeUnit& node_unit, const OpSupportCheckParams& /* params */) const { const auto& inputs = node_unit.Inputs(); @@ -152,14 +152,13 @@ bool PadOpBuilder::IsOpSupportedImpl(const InitializedTensorSet& initializers, c // only support if `pads` input is known and does not contain negative values { - const auto pads_initializer_it = initializers.find(inputs[1].node_arg.Name()); - if (pads_initializer_it == initializers.end()) { - LOGS_DEFAULT(VERBOSE) << "pads must be known"; + const auto* pads_initializer = graph_viewer.GetConstantInitializer(inputs[1].node_arg.Name()); + if (!pads_initializer) { + LOGS_DEFAULT(VERBOSE) << "pads must be a constant initializer"; return false; } - const ONNX_NAMESPACE::TensorProto& pads_initializer = *pads_initializer_it->second; - Initializer unpacked_tensor(pads_initializer); + Initializer unpacked_tensor(*pads_initializer); auto tensor_data = unpacked_tensor.DataAsSpan(); for (size_t i = 0; i < unpacked_tensor.size(); i++) { if (tensor_data[i] < 0) { @@ -173,8 +172,8 @@ bool PadOpBuilder::IsOpSupportedImpl(const InitializedTensorSet& initializers, c // only support if `constant_value` input is known // Note: Could add support for non-constant initializer later. Then we need to ensure it is a scalar (with shape []). if (inputs.size() > 2) { - if (!Contains(initializers, inputs[2].node_arg.Name())) { - LOGS_DEFAULT(VERBOSE) << "constant_value must be known"; + if (!graph_viewer.GetConstantInitializer(inputs[2].node_arg.Name())) { + LOGS_DEFAULT(VERBOSE) << "constant_value must be a constant initializer"; return false; } } diff --git a/onnxruntime/core/providers/nnapi/nnapi_builtin/builders/impl/pool_op_builder.cc b/onnxruntime/core/providers/nnapi/nnapi_builtin/builders/impl/pool_op_builder.cc index c14568aaccfa3..a2a4786b72ec7 100644 --- a/onnxruntime/core/providers/nnapi/nnapi_builtin/builders/impl/pool_op_builder.cc +++ b/onnxruntime/core/providers/nnapi/nnapi_builtin/builders/impl/pool_op_builder.cc @@ -32,7 +32,7 @@ class PoolOpBuilder : public BaseOpBuilder { // Operator support related private: - bool IsOpSupportedImpl(const InitializedTensorSet& initializers, const NodeUnit& node_unit, + bool IsOpSupportedImpl(const GraphViewer& graph_viewer, const NodeUnit& node_unit, const OpSupportCheckParams& params) const override; int32_t GetMinSupportedNNAPIFeatureLevel(const NodeUnit& /* node_unit */, @@ -40,10 +40,9 @@ class PoolOpBuilder : public BaseOpBuilder { return params.use_nchw ? ANEURALNETWORKS_FEATURE_LEVEL_3 : ANEURALNETWORKS_FEATURE_LEVEL_2; } - bool HasSupportedInputOutputsImpl( - const InitializedTensorSet& initializers, const NodeUnit& node_unit, - const OpSupportCheckParams& params) const override; - bool IsNodeUnitTypeSupported(const NodeUnit& /* node_unit */) const override; + bool HasSupportedInputOutputsImpl(const GraphViewer& graph_viewer, const NodeUnit& node_unit, + const OpSupportCheckParams& params) const override; + bool IsNodeUnitTypeSupported(const NodeUnit& node_unit) const override; bool IsQuantizedOp(const NodeUnit& node_unit) const override; }; @@ -116,16 +115,16 @@ Status PoolOpBuilder::AddToModelBuilderImpl(ModelBuilder& model_builder, const N float y_scale = input_operand_type.operandType.scale; int32_t y_zero_point = input_operand_type.operandType.zeroPoint; if (is_quant_pool) { - const auto& initializers = model_builder.GetInitializerTensors(); + const auto& graph_viewer = model_builder.GetGraphViewer(); float x_scale = 0.0f; int32_t x_zero_point = 0; ORT_RETURN_IF_ERROR(GetQuantizationScaleAndZeroPoint( - initializers, node_unit.Inputs()[0], node_unit.ModelPath(), x_scale, x_zero_point)); + graph_viewer, node_unit.Inputs()[0], node_unit.ModelPath(), x_scale, x_zero_point)); // Verify if the scale and zero point values from onnx input and nnapi input match ORT_RETURN_IF_ERROR(IsValidInputQuantizedType(model_builder, input, x_scale, x_zero_point)); ORT_RETURN_IF_ERROR(GetQuantizationScaleAndZeroPoint( - initializers, node_unit.Outputs()[0], node_unit.ModelPath(), y_scale, y_zero_point)); + graph_viewer, node_unit.Outputs()[0], node_unit.ModelPath(), y_scale, y_zero_point)); } InlinedVector input_indices; @@ -171,7 +170,7 @@ bool PoolOpBuilder::IsQuantizedOp(const NodeUnit& node_unit) const { return IsQuantizedPool(GetQuantizedOpType(node_unit)); } -bool PoolOpBuilder::IsOpSupportedImpl(const InitializedTensorSet& initializers, const NodeUnit& node_unit, +bool PoolOpBuilder::IsOpSupportedImpl(const GraphViewer& graph_viewer, const NodeUnit& node_unit, const OpSupportCheckParams& /* params */) const { const auto& op_name = node_unit.Name(); const auto& op_type = node_unit.OpType(); @@ -236,7 +235,7 @@ bool PoolOpBuilder::IsOpSupportedImpl(const InitializedTensorSet& initializers, float input_scale = 0.0f; int32_t input_zp = 0; auto status = GetQuantizationScaleAndZeroPoint( - initializers, node_unit.Inputs()[0], node_unit.ModelPath(), input_scale, input_zp); + graph_viewer, node_unit.Inputs()[0], node_unit.ModelPath(), input_scale, input_zp); if (!status.IsOK()) { LOGS_DEFAULT(ERROR) << "Op [" << op_type << "] name [" << op_name << "] GetQuantizationScaleAndZeroPoint for input_scale/zp failed, message: " @@ -247,7 +246,7 @@ bool PoolOpBuilder::IsOpSupportedImpl(const InitializedTensorSet& initializers, float output_scale = 0.0f; int32_t output_zp = 0; status = GetQuantizationScaleAndZeroPoint( - initializers, node_unit.Outputs()[0], node_unit.ModelPath(), output_scale, output_zp); + graph_viewer, node_unit.Outputs()[0], node_unit.ModelPath(), output_scale, output_zp); if (!status.IsOK()) { LOGS_DEFAULT(ERROR) << "Op [" << op_type << "] name [" << op_name << "] GetQuantizationScaleAndZeroPoint for output_scale/zp failed, message: " @@ -274,7 +273,7 @@ bool PoolOpBuilder::IsOpSupportedImpl(const InitializedTensorSet& initializers, } bool PoolOpBuilder::HasSupportedInputOutputsImpl( - const InitializedTensorSet& initializers, const NodeUnit& node_unit, + const GraphViewer& graph_viewer, const NodeUnit& node_unit, const OpSupportCheckParams& params) const { const auto& op_type = node_unit.OpType(); bool is_quant_pool = IsQuantizedOp(node_unit); @@ -282,13 +281,13 @@ bool PoolOpBuilder::HasSupportedInputOutputsImpl( bool is_average_pool = op_type == "AveragePool" || op_type == "QLinearAveragePool"; bool is_quant_average_pool = is_quant_pool && is_average_pool; if (!is_max_pool && !is_quant_average_pool) - return BaseOpBuilder::HasSupportedInputOutputsImpl(initializers, node_unit, params); + return BaseOpBuilder::HasSupportedInputOutputsImpl(graph_viewer, node_unit, params); if (is_quant_average_pool) { - if (!IsQuantizedIOSupported(initializers, node_unit, {0}, params, ArgType::kInput)) + if (!IsQuantizedIOSupported(graph_viewer, node_unit, {0}, params, ArgType::kInput)) return false; - if (!IsQuantizedIOSupported(initializers, node_unit, {0}, params, ArgType::kOutput)) + if (!IsQuantizedIOSupported(graph_viewer, node_unit, {0}, params, ArgType::kOutput)) return false; } diff --git a/onnxruntime/core/providers/nnapi/nnapi_builtin/builders/impl/quantizelinear_op_builder.cc b/onnxruntime/core/providers/nnapi/nnapi_builtin/builders/impl/quantizelinear_op_builder.cc index 49ff01d27219a..d13b81c2a14b8 100644 --- a/onnxruntime/core/providers/nnapi/nnapi_builtin/builders/impl/quantizelinear_op_builder.cc +++ b/onnxruntime/core/providers/nnapi/nnapi_builtin/builders/impl/quantizelinear_op_builder.cc @@ -38,9 +38,9 @@ class QuantizeLinearOpBuilder : public BaseOpBuilder { } bool HasSupportedInputOutputsImpl( - const InitializedTensorSet& initializers, const NodeUnit& node_unit, + const GraphViewer& graph_viewer, const NodeUnit& node_unit, const OpSupportCheckParams& params) const override { - return IsQuantizedIOSupported(initializers, node_unit, {0}, params, ArgType::kOutput); + return IsQuantizedIOSupported(graph_viewer, node_unit, {0}, params, ArgType::kOutput); } }; @@ -60,7 +60,7 @@ Status QuantizeLinearOpBuilder::AddToModelBuilderImpl(ModelBuilder& model_builde float scale = 0.0f; int32_t zero_point = 0; ORT_RETURN_IF_ERROR(GetQuantizationScaleAndZeroPoint( - model_builder.GetInitializerTensors(), node_unit.Outputs()[0], node_unit.ModelPath(), scale, zero_point)); + model_builder.GetGraphViewer(), node_unit.Outputs()[0], node_unit.ModelPath(), scale, zero_point)); Type output_type = Type::TENSOR_QUANT8_ASYMM; const OperandType output_operand_type(output_type, shaper[output], scale, zero_point); diff --git a/onnxruntime/core/providers/nnapi/nnapi_builtin/builders/impl/reduction_op_builder.cc b/onnxruntime/core/providers/nnapi/nnapi_builtin/builders/impl/reduction_op_builder.cc index 8d0347673ba56..a6da290753b74 100644 --- a/onnxruntime/core/providers/nnapi/nnapi_builtin/builders/impl/reduction_op_builder.cc +++ b/onnxruntime/core/providers/nnapi/nnapi_builtin/builders/impl/reduction_op_builder.cc @@ -35,7 +35,7 @@ class ReductionOpBuilder : public BaseOpBuilder { private: int32_t GetMinSupportedNNAPIFeatureLevel(const NodeUnit& node_unit, const OpSupportCheckParams& params) const override; - bool IsOpSupportedImpl(const InitializedTensorSet& initializers, const NodeUnit& node_unit, + bool IsOpSupportedImpl(const GraphViewer& graph_viewer, const NodeUnit& node_unit, const OpSupportCheckParams& params) const override; }; @@ -169,7 +169,7 @@ int32_t ReductionOpBuilder::GetMinSupportedNNAPIFeatureLevel( return ANEURALNETWORKS_FEATURE_LEVEL_3; } -bool ReductionOpBuilder::IsOpSupportedImpl(const InitializedTensorSet& initializers, const NodeUnit& node_unit, +bool ReductionOpBuilder::IsOpSupportedImpl(const GraphViewer& graph_viewer, const NodeUnit& node_unit, const OpSupportCheckParams& /* params */) const { const auto& inputs = node_unit.Inputs(); const auto& op(node_unit.OpType()); @@ -190,7 +190,7 @@ bool ReductionOpBuilder::IsOpSupportedImpl(const InitializedTensorSet& initializ const bool noop_with_empty_axes = helper.Get("noop_with_empty_axes", 0) != 0; if (inputs.size() > 1 && inputs[1].node_arg.Exists()) { const auto& axes_name = inputs[1].node_arg.Name(); - if (!Contains(initializers, axes_name)) { + if (!graph_viewer.GetConstantInitializer(axes_name)) { LOGS_DEFAULT(VERBOSE) << "Axes of ReduceMean must be a constant initializer."; return false; } diff --git a/onnxruntime/core/providers/nnapi/nnapi_builtin/builders/impl/reshape_op_builder.cc b/onnxruntime/core/providers/nnapi/nnapi_builtin/builders/impl/reshape_op_builder.cc index 869883b98b22e..f2f9165d2f3cc 100644 --- a/onnxruntime/core/providers/nnapi/nnapi_builtin/builders/impl/reshape_op_builder.cc +++ b/onnxruntime/core/providers/nnapi/nnapi_builtin/builders/impl/reshape_op_builder.cc @@ -35,14 +35,13 @@ class ReshapeOpBuilder : public BaseOpBuilder { // Operator support related private: - bool IsOpSupportedImpl(const InitializedTensorSet& initializers, const NodeUnit& node_unit, + bool IsOpSupportedImpl(const GraphViewer& graph_viewer, const NodeUnit& node_unit, const OpSupportCheckParams& params) const override; // Reshape opset 4- uses attributes for new shape which we do not support for now int GetMinSupportedOpSet(const NodeUnit& /* node_unit */) const override { return 5; } - bool HasSupportedInputOutputsImpl( - const InitializedTensorSet& /* initializers */, const NodeUnit& node_unit, - const OpSupportCheckParams& /* params */) const override; + bool HasSupportedInputOutputsImpl(const GraphViewer& graph_viewer, const NodeUnit& node_unit, + const OpSupportCheckParams& params) const override; bool IsNodeUnitTypeSupported(const NodeUnit& /* node_unit */) const override { return true; } bool IsQuantizedOp(const NodeUnit& node_unit) const override; }; @@ -59,10 +58,10 @@ void ReshapeOpBuilder::AddInitializersToSkip(ModelBuilder& model_builder, const Status ReshapeOpBuilder::AddToModelBuilderImpl(ModelBuilder& model_builder, const NodeUnit& node_unit) const { auto& shaper(model_builder.GetShaper()); - const auto& initializers(model_builder.GetInitializerTensors()); + const auto& graph_viewer(model_builder.GetGraphViewer()); auto input = node_unit.Inputs()[0].node_arg.Name(); - const auto& shape_tensor = *initializers.at(node_unit.Inputs()[1].node_arg.Name()); + const auto& shape_tensor = *graph_viewer.GetConstantInitializer(node_unit.Inputs()[1].node_arg.Name()); Initializer unpacked_tensor(shape_tensor); auto raw_shape = unpacked_tensor.DataAsSpan(); const auto size = SafeInt(shape_tensor.dims()[0]); @@ -80,7 +79,7 @@ Status ReshapeOpBuilder::AddToModelBuilderImpl(ModelBuilder& model_builder, cons int32_t x_zero_point = 0; if (IsQuantizedOp(node_unit)) { ORT_RETURN_IF_ERROR(GetQuantizationScaleAndZeroPoint( - initializers, node_unit.Inputs()[0], node_unit.ModelPath(), x_scale, x_zero_point)); + graph_viewer, node_unit.Inputs()[0], node_unit.ModelPath(), x_scale, x_zero_point)); ORT_RETURN_IF_ERROR(IsValidInputQuantizedType(model_builder, input, x_scale, x_zero_point)); } @@ -93,12 +92,13 @@ bool ReshapeOpBuilder::IsQuantizedOp(const NodeUnit& node_unit) const { return GetQuantizedOpType(node_unit) == QuantizedOpType::QDQReshape; } -bool ReshapeOpBuilder::IsOpSupportedImpl(const InitializedTensorSet& initializers, const NodeUnit& node_unit, +bool ReshapeOpBuilder::IsOpSupportedImpl(const GraphViewer& graph_viewer, const NodeUnit& node_unit, const OpSupportCheckParams& /* params */) const { const auto& inputs = node_unit.Inputs(); const auto& perm_name = inputs[1].node_arg.Name(); - if (!Contains(initializers, perm_name)) { - LOGS_DEFAULT(VERBOSE) << "New shape of reshape must be known"; + const auto* perm = graph_viewer.GetConstantInitializer(perm_name); + if (!perm) { + LOGS_DEFAULT(VERBOSE) << "New shape of reshape must be a constant initializer"; return false; } @@ -112,7 +112,7 @@ bool ReshapeOpBuilder::IsOpSupportedImpl(const InitializedTensorSet& initializer return false; } - const auto& perm_tensor = *initializers.at(perm_name); + const auto& perm_tensor = *perm; Initializer unpacked_tensor(perm_tensor); auto raw_perm = unpacked_tensor.DataAsSpan(); const auto perm_size = SafeInt(perm_tensor.dims()[0]); @@ -138,17 +138,17 @@ bool ReshapeOpBuilder::IsOpSupportedImpl(const InitializedTensorSet& initializer } bool ReshapeOpBuilder::HasSupportedInputOutputsImpl( - const InitializedTensorSet& initializers, const NodeUnit& node_unit, + const GraphViewer& graph_viewer, const NodeUnit& node_unit, const OpSupportCheckParams& params) const { if (!IsQuantizedOp(node_unit)) { - return BaseOpBuilder::HasSupportedInputOutputsImpl(initializers, node_unit, params); + return BaseOpBuilder::HasSupportedInputOutputsImpl(graph_viewer, node_unit, params); } - if (!IsQuantizedIOSupported(initializers, node_unit, {0}, params, ArgType::kInput)) { + if (!IsQuantizedIOSupported(graph_viewer, node_unit, {0}, params, ArgType::kInput)) { return false; } - if (!IsQuantizedIOSupported(initializers, node_unit, {0}, params, ArgType::kOutput)) { + if (!IsQuantizedIOSupported(graph_viewer, node_unit, {0}, params, ArgType::kOutput)) { return false; } diff --git a/onnxruntime/core/providers/nnapi/nnapi_builtin/builders/impl/resize_op_builder.cc b/onnxruntime/core/providers/nnapi/nnapi_builtin/builders/impl/resize_op_builder.cc index cdaa1c8fac76c..d75b9cc72ff4b 100644 --- a/onnxruntime/core/providers/nnapi/nnapi_builtin/builders/impl/resize_op_builder.cc +++ b/onnxruntime/core/providers/nnapi/nnapi_builtin/builders/impl/resize_op_builder.cc @@ -33,19 +33,18 @@ class ResizeOpBuilder : public BaseOpBuilder { // Operator support related private: - bool IsOpSupportedImpl(const InitializedTensorSet& initializers, const NodeUnit& node_unit, + bool IsOpSupportedImpl(const GraphViewer& graph_viewer, const NodeUnit& node_unit, const OpSupportCheckParams& params) const override; - int32_t GetMinSupportedNNAPIFeatureLevel(const NodeUnit& /* node_unit */, - const OpSupportCheckParams& /* params */) const override; + int32_t GetMinSupportedNNAPIFeatureLevel(const NodeUnit& node_unit, + const OpSupportCheckParams& params) const override; // Resize opset 10- is very different than Resize opset 11+, with many key attributes missing // We only support Resize opset 11+ here int GetMinSupportedOpSet(const NodeUnit& /* node_unit */) const override { return 11; } - bool HasSupportedInputOutputsImpl( - const InitializedTensorSet& /* initializers */, const NodeUnit& node_unit, - const OpSupportCheckParams& /* params */) const override; + bool HasSupportedInputOutputsImpl(const GraphViewer& graph_viewer, const NodeUnit& node_unit, + const OpSupportCheckParams& params) const override; bool IsNodeUnitTypeSupported(const NodeUnit& /* node_unit */) const override { return true; } bool IsQuantizedOp(const NodeUnit& node_unit) const override; }; @@ -74,7 +73,6 @@ Status ResizeOpBuilder::AddToModelBuilderImpl(ModelBuilder& model_builder, const auto& shaper(model_builder.GetShaper()); const auto& operand_indices(model_builder.GetOperandIndices()); const auto& operand_types(model_builder.GetOperandTypes()); - const auto& initializers(model_builder.GetInitializerTensors()); NodeAttrHelper helper(node_unit); const auto& inputs = node_unit.Inputs(); const auto android_feature_level = model_builder.GetEffectiveFeatureLevel(); @@ -92,7 +90,7 @@ Status ResizeOpBuilder::AddToModelBuilderImpl(ModelBuilder& model_builder, const float x_scale = 0.0f; int32_t x_zero_point = 0; ORT_RETURN_IF_ERROR(GetQuantizationScaleAndZeroPoint( - initializers, node_unit.Inputs()[0], node_unit.ModelPath(), x_scale, x_zero_point)); + model_builder.GetGraphViewer(), node_unit.Inputs()[0], node_unit.ModelPath(), x_scale, x_zero_point)); ORT_RETURN_IF_ERROR(IsValidInputQuantizedType(model_builder, input, x_scale, x_zero_point)); } @@ -147,7 +145,7 @@ bool ResizeOpBuilder::IsQuantizedOp(const NodeUnit& node_unit) const { return GetQuantizedOpType(node_unit) == QuantizedOpType::QDQResize; } -bool ResizeOpBuilder::IsOpSupportedImpl(const InitializedTensorSet& initializers, const NodeUnit& node_unit, +bool ResizeOpBuilder::IsOpSupportedImpl(const GraphViewer& graph_viewer, const NodeUnit& node_unit, const OpSupportCheckParams& params) const { Shape input_shape; if (!GetShape(node_unit.Inputs()[0].node_arg, input_shape)) @@ -228,32 +226,29 @@ bool ResizeOpBuilder::IsOpSupportedImpl(const InitializedTensorSet& initializers } } - { // scales and sizes (if present) must be initializers + // scales or sizes must be constant initializers + { + // scales is input 3, sizes input 4, one must exist. only one is used. const auto inputs = node_unit.Inputs(); - if (inputs.size() < 3) { + bool using_scales = inputs.size() > 2 && inputs[2].node_arg.Exists(); + bool using_sizes = !using_scales && inputs.size() > 3 && inputs[3].node_arg.Exists(); + if (!using_scales && !using_sizes) { LOGS_DEFAULT(VERBOSE) << "Input scales or sizes of Resize must be known"; return false; } - // scales - bool using_scales = (inputs.size() > 2 && inputs[2].node_arg.Exists()); - if (using_scales && !Contains(initializers, inputs[2].node_arg.Name())) { - LOGS_DEFAULT(VERBOSE) << "Input scales of Resize must be known"; - return false; - } - - // sizes - bool using_sizes = inputs.size() > 3 && inputs[3].node_arg.Exists(); - if (using_sizes && !Contains(initializers, inputs[3].node_arg.Name())) { - LOGS_DEFAULT(VERBOSE) << "Input sizes of Resize must be known"; - return false; - } - bool input_is_nchw = false; // haven't a good solution to check layout when scale is 1.0F // We want to check if the scales or sizes are not trying to resize on N/C channels here - if (using_scales) { // we are using scales - const auto& scales_tensor = *initializers.at(inputs[2].node_arg.Name()); - Initializer const unpacked_tensor(scales_tensor); + bool input_is_nchw = false; + + if (using_scales) { + const auto* scales = graph_viewer.GetConstantInitializer(inputs[2].node_arg.Name()); + if (!scales) { + LOGS_DEFAULT(VERBOSE) << "Input scales of Resize must be a constant initializer"; + return false; + } + + const Initializer unpacked_tensor(*scales); auto scales_data = unpacked_tensor.DataAsSpan(); input_is_nchw = scales_data[1] == 1.0F; float const scale_n = scales_data[0]; @@ -265,10 +260,13 @@ bool ResizeOpBuilder::IsOpSupportedImpl(const InitializedTensorSet& initializers return false; } } else { - // we are using sizes - const auto& sizes_name = inputs[3].node_arg.Name(); - const auto& sizes_tensor = *initializers.at(sizes_name); - Initializer unpacked_tensor(sizes_tensor); + const auto* sizes = graph_viewer.GetConstantInitializer(inputs[3].node_arg.Name()); + if (!sizes) { + LOGS_DEFAULT(VERBOSE) << "Input sizes of Resize must be a constant initializer"; + return false; + } + + Initializer unpacked_tensor(*sizes); auto sizes_data = unpacked_tensor.DataAsSpan(); input_is_nchw = sizes_data[1] == input_shape[1]; @@ -308,7 +306,7 @@ int32_t ResizeOpBuilder::GetMinSupportedNNAPIFeatureLevel(const NodeUnit& node_u } bool ResizeOpBuilder::HasSupportedInputOutputsImpl( - const InitializedTensorSet& initializers, const NodeUnit& node_unit, + const GraphViewer& graph_viewer, const NodeUnit& node_unit, const OpSupportCheckParams& params) const { int32_t input_type; if (!GetType(node_unit.Inputs()[0].node_arg, input_type)) @@ -323,10 +321,10 @@ bool ResizeOpBuilder::HasSupportedInputOutputsImpl( } if (IsQuantizedOp(node_unit)) { - if (!IsQuantizedIOSupported(initializers, node_unit, {0}, params, ArgType::kInput)) + if (!IsQuantizedIOSupported(graph_viewer, node_unit, {0}, params, ArgType::kInput)) return false; - if (!IsQuantizedIOSupported(initializers, node_unit, {0}, params, ArgType::kOutput)) + if (!IsQuantizedIOSupported(graph_viewer, node_unit, {0}, params, ArgType::kOutput)) return false; } diff --git a/onnxruntime/core/providers/nnapi/nnapi_builtin/builders/impl/slice_op_builder.cc b/onnxruntime/core/providers/nnapi/nnapi_builtin/builders/impl/slice_op_builder.cc index 903469d34e67c..facdc7132dc00 100644 --- a/onnxruntime/core/providers/nnapi/nnapi_builtin/builders/impl/slice_op_builder.cc +++ b/onnxruntime/core/providers/nnapi/nnapi_builtin/builders/impl/slice_op_builder.cc @@ -40,7 +40,7 @@ class SliceOpBuilder : public BaseOpBuilder { // We only support slice from opset 10 int GetMinSupportedOpSet(const NodeUnit& /* node_unit */) const override { return 10; } - bool IsOpSupportedImpl(const InitializedTensorSet& initializers, const NodeUnit& node_unit, + bool IsOpSupportedImpl(const GraphViewer& graph_viewer, const NodeUnit& node_unit, const OpSupportCheckParams& params) const override; }; @@ -201,7 +201,7 @@ Status SliceOpBuilder::AddToModelBuilderImpl(ModelBuilder& model_builder, const // Operator support related -bool SliceOpBuilder::IsOpSupportedImpl(const InitializedTensorSet& initializers, const NodeUnit& node_unit, +bool SliceOpBuilder::IsOpSupportedImpl(const GraphViewer& graph_viewer, const NodeUnit& node_unit, const OpSupportCheckParams& /* params */) const { Shape input_shape; if (!GetShape(node_unit.Inputs()[0].node_arg, input_shape)) @@ -219,19 +219,19 @@ bool SliceOpBuilder::IsOpSupportedImpl(const InitializedTensorSet& initializers, return false; } - if (!CheckIsInitializer(initializers, node_unit, node_unit.Inputs()[1].node_arg.Name(), "starts")) { + if (!CheckIsConstantInitializer(graph_viewer, node_unit, node_unit.Inputs()[1].node_arg.Name(), "starts")) { return false; } - if (!CheckIsInitializer(initializers, node_unit, node_unit.Inputs()[2].node_arg.Name(), "ends")) { + if (!CheckIsConstantInitializer(graph_viewer, node_unit, node_unit.Inputs()[2].node_arg.Name(), "ends")) { return false; } const auto& inputs = node_unit.Inputs(); if (inputs.size() > 3) { - if (!CheckIsInitializer(initializers, node_unit, node_unit.Inputs()[3].node_arg.Name(), "axes")) { + if (!CheckIsConstantInitializer(graph_viewer, node_unit, node_unit.Inputs()[3].node_arg.Name(), "axes")) { return false; } if (inputs.size() > 4) { - if (!CheckIsInitializer(initializers, node_unit, node_unit.Inputs()[4].node_arg.Name(), "steps")) { + if (!CheckIsConstantInitializer(graph_viewer, node_unit, node_unit.Inputs()[4].node_arg.Name(), "steps")) { return false; } } diff --git a/onnxruntime/core/providers/nnapi/nnapi_builtin/builders/impl/softmax_op_builder.cc b/onnxruntime/core/providers/nnapi/nnapi_builtin/builders/impl/softmax_op_builder.cc index 1e420fec80827..a2a8b4512b028 100644 --- a/onnxruntime/core/providers/nnapi/nnapi_builtin/builders/impl/softmax_op_builder.cc +++ b/onnxruntime/core/providers/nnapi/nnapi_builtin/builders/impl/softmax_op_builder.cc @@ -33,7 +33,7 @@ class SoftMaxOpBuilder : public BaseOpBuilder { // Operator support related private: - bool IsOpSupportedImpl(const InitializedTensorSet& initializers, const NodeUnit& node_unit, + bool IsOpSupportedImpl(const GraphViewer& graph_viewer, const NodeUnit& node_unit, const OpSupportCheckParams& params) const override; int32_t GetMinSupportedNNAPIFeatureLevel(const NodeUnit& /* node_unit */, @@ -41,7 +41,7 @@ class SoftMaxOpBuilder : public BaseOpBuilder { return ANEURALNETWORKS_FEATURE_LEVEL_2; } bool HasSupportedInputOutputsImpl( - const InitializedTensorSet& initializers, const NodeUnit& node_unit, + const GraphViewer& graph_viewer, const NodeUnit& node_unit, const OpSupportCheckParams& params) const override; bool IsNodeUnitTypeSupported(const NodeUnit& /* node_unit */) const override { return true; } @@ -77,8 +77,7 @@ Status SoftMaxOpBuilder::AddToModelBuilderImpl(ModelBuilder& model_builder, cons int32_t y_zero_point = 0; if (IsQuantizedOp(node_unit)) { ORT_RETURN_IF_ERROR(GetQuantizationScaleAndZeroPoint( - model_builder.GetInitializerTensors(), node_unit.Inputs()[0], node_unit.ModelPath(), - x_scale, x_zero_point)); + model_builder.GetGraphViewer(), node_unit.Inputs()[0], node_unit.ModelPath(), x_scale, x_zero_point)); ORT_RETURN_IF_ERROR(IsValidInputQuantizedType(model_builder, input, x_scale, x_zero_point)); @@ -156,7 +155,7 @@ bool SoftMaxOpBuilder::IsQuantizedOp(const NodeUnit& node_unit) const { return GetQuantizedOpType(node_unit) == QuantizedOpType::QDQSoftmax; } -bool SoftMaxOpBuilder::IsOpSupportedImpl(const InitializedTensorSet& /* initializers */, const NodeUnit& node_unit, +bool SoftMaxOpBuilder::IsOpSupportedImpl(const GraphViewer& /* graph_viewer */, const NodeUnit& node_unit, const OpSupportCheckParams& params) const { Shape input_shape; if (!GetShape(node_unit.Inputs()[0].node_arg, input_shape)) @@ -197,24 +196,23 @@ bool SoftMaxOpBuilder::IsOpSupportedImpl(const InitializedTensorSet& /* initiali return true; } -bool SoftMaxOpBuilder::HasSupportedInputOutputsImpl( - const InitializedTensorSet& initializers, const NodeUnit& node_unit, - const OpSupportCheckParams& params) const { +bool SoftMaxOpBuilder::HasSupportedInputOutputsImpl(const GraphViewer& graph_viewer, const NodeUnit& node_unit, + const OpSupportCheckParams& params) const { if (!IsQuantizedOp(node_unit)) { - return BaseOpBuilder::HasSupportedInputOutputsImpl(initializers, node_unit, params); + return BaseOpBuilder::HasSupportedInputOutputsImpl(graph_viewer, node_unit, params); } - if (!IsQuantizedIOSupported(initializers, node_unit, {0}, params, ArgType::kInput)) { + if (!IsQuantizedIOSupported(graph_viewer, node_unit, {0}, params, ArgType::kInput)) { return false; } - if (!IsQuantizedIOSupported(initializers, node_unit, {0}, params, ArgType::kOutput)) { + if (!IsQuantizedIOSupported(graph_viewer, node_unit, {0}, params, ArgType::kOutput)) { return false; } // NNAPI requires the scale be 1.f/256 and zero point to be 0 if (!HasRequiredScaleAndZeroPoint( - initializers, + graph_viewer, MakeString("Op [", node_unit.OpType(), "] name [", node_unit.Name(), "]'s output 0 "), node_unit.Outputs()[0], node_unit.ModelPath(), 1.f / 256 /* required_scale */, 0 /* required_zp */)) { diff --git a/onnxruntime/core/providers/nnapi/nnapi_builtin/builders/impl/split_op_builder.cc b/onnxruntime/core/providers/nnapi/nnapi_builtin/builders/impl/split_op_builder.cc index 68b63badb8f7e..b2225643b788e 100644 --- a/onnxruntime/core/providers/nnapi/nnapi_builtin/builders/impl/split_op_builder.cc +++ b/onnxruntime/core/providers/nnapi/nnapi_builtin/builders/impl/split_op_builder.cc @@ -35,7 +35,7 @@ class SplitOpBuilder : public BaseOpBuilder { // Operator support related private: - bool IsOpSupportedImpl(const InitializedTensorSet& initializers, const NodeUnit& node_unit, + bool IsOpSupportedImpl(const GraphViewer& graph_viewer, const NodeUnit& node_unit, const OpSupportCheckParams& params) const override; // Split opset 13- uses "split" as attribute. Currently it's not supported. @@ -85,7 +85,7 @@ Status SplitOpBuilder::AddToModelBuilderImpl(ModelBuilder& model_builder, const // Operator support related -bool SplitOpBuilder::IsOpSupportedImpl(const InitializedTensorSet& initializers, const NodeUnit& node_unit, +bool SplitOpBuilder::IsOpSupportedImpl(const GraphViewer& graph_viewer, const NodeUnit& node_unit, const OpSupportCheckParams& /* params */) const { Shape input_shape; if (!GetShape(node_unit.Inputs()[0].node_arg, input_shape)) @@ -98,13 +98,13 @@ bool SplitOpBuilder::IsOpSupportedImpl(const InitializedTensorSet& initializers, const auto split_dims_at_axis = input_shape[SafeInt(HandleNegativeAxis(axis, input_shape.size()))]; if (input_defs.size() > 1 && input_defs[1].node_arg.Exists()) { // if optional input `split` is provided - auto split_initializer_it = initializers.find(input_defs[1].node_arg.Name()); - if (split_initializer_it == initializers.end()) { - LOGS_DEFAULT(VERBOSE) << "Optional input 'split' must be initializer if provided."; + const auto* splits = graph_viewer.GetConstantInitializer(input_defs[1].node_arg.Name()); + if (!splits) { + LOGS_DEFAULT(VERBOSE) << "Optional input 'split' must be a constant initializer if provided."; return false; } - const auto& splits_tensor = *split_initializer_it->second; - Initializer unpacked_tensor(splits_tensor); + + Initializer unpacked_tensor(*splits); auto splits_span = unpacked_tensor.DataAsSpan(); uint32_t sum_of_splits = std::accumulate(splits_span.begin(), splits_span.end(), SafeInt(0)); if (sum_of_splits != split_dims_at_axis) { @@ -119,6 +119,7 @@ bool SplitOpBuilder::IsOpSupportedImpl(const InitializedTensorSet& initializers, auto it = std::adjacent_find(splits_span.begin(), splits_span.end(), [](const auto& a, const auto& b) { return a != b; }); + if (it != splits_span.end()) { LOGS_DEFAULT(VERBOSE) << "NNAPI only supports the case that number of splits evenly divides split axis size"; return false; diff --git a/onnxruntime/core/providers/nnapi/nnapi_builtin/builders/impl/squeeze_op_builder.cc b/onnxruntime/core/providers/nnapi/nnapi_builtin/builders/impl/squeeze_op_builder.cc index a0fe744eaacc8..fb3ca5e6175fa 100644 --- a/onnxruntime/core/providers/nnapi/nnapi_builtin/builders/impl/squeeze_op_builder.cc +++ b/onnxruntime/core/providers/nnapi/nnapi_builtin/builders/impl/squeeze_op_builder.cc @@ -32,7 +32,7 @@ class SqueezeOpBuilder : public BaseOpBuilder { // Operator support related private: - bool IsOpSupportedImpl(const InitializedTensorSet& initializers, const NodeUnit& node_unit, + bool IsOpSupportedImpl(const GraphViewer& graph_viewer, const NodeUnit& node_unit, const OpSupportCheckParams& params) const override; int32_t GetMinSupportedNNAPIFeatureLevel(const NodeUnit& /* node_unit */, @@ -59,7 +59,7 @@ Status SqueezeOpBuilder::AddToModelBuilderImpl(ModelBuilder& model_builder, cons // Operator support related -bool SqueezeOpBuilder::IsOpSupportedImpl(const InitializedTensorSet& initializers, const NodeUnit& node_unit, +bool SqueezeOpBuilder::IsOpSupportedImpl(const GraphViewer& graph_viewer, const NodeUnit& node_unit, const OpSupportCheckParams& /* params */) const { const auto& inputs = node_unit.Inputs(); Shape input_shape; @@ -76,8 +76,8 @@ bool SqueezeOpBuilder::IsOpSupportedImpl(const InitializedTensorSet& initializer // Squeeze opset 13 use input 1 as axes, if we have input 1 then it need to be an initializer if (node_unit.SinceVersion() > 12 && inputs.size() > 1) { const auto& axes_name = inputs[1].node_arg.Name(); - if (!Contains(initializers, axes_name)) { - LOGS_DEFAULT(VERBOSE) << "Input axes of Squeeze must be known"; + if (!graph_viewer.GetConstantInitializer(axes_name)) { + LOGS_DEFAULT(VERBOSE) << "Input axes of Squeeze must be a constant initializer"; return false; } } diff --git a/onnxruntime/core/providers/nnapi/nnapi_builtin/builders/impl/transpose_op_builder.cc b/onnxruntime/core/providers/nnapi/nnapi_builtin/builders/impl/transpose_op_builder.cc index 4d243c730bf05..6fe5ca32fe044 100644 --- a/onnxruntime/core/providers/nnapi/nnapi_builtin/builders/impl/transpose_op_builder.cc +++ b/onnxruntime/core/providers/nnapi/nnapi_builtin/builders/impl/transpose_op_builder.cc @@ -32,7 +32,7 @@ class TransposeOpBuilder : public BaseOpBuilder { // Operator support related private: - bool IsOpSupportedImpl(const InitializedTensorSet& initializers, const NodeUnit& node_unit, + bool IsOpSupportedImpl(const GraphViewer& graph_viewer, const NodeUnit& node_unit, const OpSupportCheckParams& params) const override; int32_t GetMinSupportedNNAPIFeatureLevel(const NodeUnit& /* node_unit */, @@ -41,7 +41,7 @@ class TransposeOpBuilder : public BaseOpBuilder { } bool HasSupportedInputOutputsImpl( - const InitializedTensorSet& initializers, const NodeUnit& node_unit, + const GraphViewer& graph_viewer, const NodeUnit& node_unit, const OpSupportCheckParams& params) const override; bool IsNodeUnitTypeSupported(const NodeUnit& /* node_unit */) const override { return true; } bool IsQuantizedOp(const NodeUnit& node_unit) const override; @@ -59,7 +59,6 @@ void TransposeOpBuilder::AddInitializersToSkip(ModelBuilder& model_builder, cons Status TransposeOpBuilder::AddToModelBuilderImpl(ModelBuilder& model_builder, const NodeUnit& node_unit) const { auto& shaper(model_builder.GetShaper()); - const auto& initializers(model_builder.GetInitializerTensors()); const auto& input = node_unit.Inputs()[0].node_arg.Name(); const auto& output = node_unit.Outputs()[0].node_arg.Name(); @@ -78,7 +77,7 @@ Status TransposeOpBuilder::AddToModelBuilderImpl(ModelBuilder& model_builder, co float x_scale = 0.0f; int32_t x_zero_point = 0; ORT_RETURN_IF_ERROR(GetQuantizationScaleAndZeroPoint( - initializers, node_unit.Inputs()[0], node_unit.ModelPath(), x_scale, x_zero_point)); + model_builder.GetGraphViewer(), node_unit.Inputs()[0], node_unit.ModelPath(), x_scale, x_zero_point)); ORT_RETURN_IF_ERROR(IsValidInputQuantizedType(model_builder, input, x_scale, x_zero_point)); } @@ -95,7 +94,7 @@ bool TransposeOpBuilder::IsQuantizedOp(const NodeUnit& node_unit) const { return GetQuantizedOpType(node_unit) == QuantizedOpType::QDQTranspose; } -bool TransposeOpBuilder::IsOpSupportedImpl(const InitializedTensorSet& /* initializers */, const NodeUnit& node_unit, +bool TransposeOpBuilder::IsOpSupportedImpl(const GraphViewer& /* graph_viewer */, const NodeUnit& node_unit, const OpSupportCheckParams& /* params */) const { Shape input_shape; if (!GetShape(node_unit.Inputs()[0].node_arg, input_shape)) @@ -112,7 +111,7 @@ bool TransposeOpBuilder::IsOpSupportedImpl(const InitializedTensorSet& /* initia } bool TransposeOpBuilder::HasSupportedInputOutputsImpl( - const InitializedTensorSet& initializers, const NodeUnit& node_unit, + const GraphViewer& graph_viewer, const NodeUnit& node_unit, const OpSupportCheckParams& params) const { int32_t input_type; if (!GetType(node_unit.Inputs()[0].node_arg, input_type)) @@ -127,10 +126,10 @@ bool TransposeOpBuilder::HasSupportedInputOutputsImpl( } if (IsQuantizedOp(node_unit)) { - if (!IsQuantizedIOSupported(initializers, node_unit, {0}, params, ArgType::kInput)) + if (!IsQuantizedIOSupported(graph_viewer, node_unit, {0}, params, ArgType::kInput)) return false; - if (!IsQuantizedIOSupported(initializers, node_unit, {0}, params, ArgType::kOutput)) + if (!IsQuantizedIOSupported(graph_viewer, node_unit, {0}, params, ArgType::kOutput)) return false; } diff --git a/onnxruntime/core/providers/nnapi/nnapi_builtin/builders/impl/unary_op_builder.cc b/onnxruntime/core/providers/nnapi/nnapi_builtin/builders/impl/unary_op_builder.cc index 796fd207fe428..dbd960ee5536c 100644 --- a/onnxruntime/core/providers/nnapi/nnapi_builtin/builders/impl/unary_op_builder.cc +++ b/onnxruntime/core/providers/nnapi/nnapi_builtin/builders/impl/unary_op_builder.cc @@ -32,19 +32,18 @@ class UnaryOpBuilder : public BaseOpBuilder { // Operator support related private: - bool IsOpSupportedImpl(const InitializedTensorSet& initializers, const NodeUnit& node_unit, + bool IsOpSupportedImpl(const GraphViewer& graph_viewer, const NodeUnit& node_unit, const OpSupportCheckParams& params) const override; - int32_t GetMinSupportedNNAPIFeatureLevel(const NodeUnit& /* node_unit */, + int32_t GetMinSupportedNNAPIFeatureLevel(const NodeUnit& node_unit, const OpSupportCheckParams& params) const override; - bool HasSupportedInputOutputsImpl( - const InitializedTensorSet& /* initializers */, const NodeUnit& node_unit, - const OpSupportCheckParams& /* params */) const override; + bool HasSupportedInputOutputsImpl(const GraphViewer& graph_viewer, const NodeUnit& node_unit, + const OpSupportCheckParams& params) const override; int GetMinSupportedOpSet(const NodeUnit& node_unit) const override; - static bool IsQuantizedOpSupported(const InitializedTensorSet& initializers, const NodeUnit& node_unit, + static bool IsQuantizedOpSupported(const GraphViewer& graph_viewer, const NodeUnit& node_unit, const OpSupportCheckParams& params); }; @@ -117,11 +116,10 @@ Status UnaryOpBuilder::AddToModelBuilderImpl(ModelBuilder& model_builder, const float y_scale = 0.0f; int32_t y_zero_point = 0; if (is_qlinear_sigmoid) { - const auto& initializers = model_builder.GetInitializerTensors(); float x_scale = 0.0f; int32_t x_zero_point = 0; ORT_RETURN_IF_ERROR(GetQuantizationScaleAndZeroPoint( - initializers, node_unit.Inputs()[0], node_unit.ModelPath(), x_scale, x_zero_point)); + model_builder.GetGraphViewer(), node_unit.Inputs()[0], node_unit.ModelPath(), x_scale, x_zero_point)); // Verify if the scale and zero point values from onnx input and nnapi input match ORT_RETURN_IF_ERROR(IsValidInputQuantizedType(model_builder, input, x_scale, x_zero_point)); @@ -141,10 +139,10 @@ Status UnaryOpBuilder::AddToModelBuilderImpl(ModelBuilder& model_builder, const // Operator support related -bool UnaryOpBuilder::IsOpSupportedImpl(const InitializedTensorSet& initializers, const NodeUnit& node_unit, +bool UnaryOpBuilder::IsOpSupportedImpl(const GraphViewer& graph_viewer, const NodeUnit& node_unit, const OpSupportCheckParams& params) const { if (node_unit.OpType() == "QLinearSigmoid") { - return IsQuantizedOpSupported(initializers, node_unit, params); + return IsQuantizedOpSupported(graph_viewer, node_unit, params); } else if (node_unit.OpType() == "Sigmoid") { Shape input_shape; if (!GetShape(node_unit.Inputs()[0].node_arg, input_shape)) @@ -178,16 +176,16 @@ int32_t UnaryOpBuilder::GetMinSupportedNNAPIFeatureLevel(const NodeUnit& node_un } bool UnaryOpBuilder::HasSupportedInputOutputsImpl( - const InitializedTensorSet& initializers, const NodeUnit& node_unit, + const GraphViewer& graph_viewer, const NodeUnit& node_unit, const OpSupportCheckParams& params) const { // We only need to override input check for QLinearSigmoid if (node_unit.OpType() != "QLinearSigmoid") - return BaseOpBuilder::HasSupportedInputOutputsImpl(initializers, node_unit, params); + return BaseOpBuilder::HasSupportedInputOutputsImpl(graph_viewer, node_unit, params); - if (!IsQuantizedIOSupported(initializers, node_unit, {0}, params, ArgType::kInput)) + if (!IsQuantizedIOSupported(graph_viewer, node_unit, {0}, params, ArgType::kInput)) return false; - if (!IsQuantizedIOSupported(initializers, node_unit, {0}, params, ArgType::kOutput)) + if (!IsQuantizedIOSupported(graph_viewer, node_unit, {0}, params, ArgType::kOutput)) return false; return true; @@ -204,13 +202,13 @@ int UnaryOpBuilder::GetMinSupportedOpSet(const NodeUnit& node_unit) const { } /* static */ bool UnaryOpBuilder::IsQuantizedOpSupported( - const InitializedTensorSet& initializers, const NodeUnit& node_unit, const OpSupportCheckParams& /* params */) { + const GraphViewer& graph_viewer, const NodeUnit& node_unit, const OpSupportCheckParams& /* params */) { const auto& op_type = node_unit.OpType(); ORT_ENFORCE(op_type == "QLinearSigmoid"); // NNAPI requires the scale be 1.f/256 and zero point to be 0 // See https://android.googlesource.com/platform/frameworks/ml/+/refs/heads/android10-c2f2-release/nn/common/operations/Activation.cpp#180 - if (!HasRequiredScaleAndZeroPoint(initializers, + if (!HasRequiredScaleAndZeroPoint(graph_viewer, MakeString("Op [", op_type, "] name [", node_unit.Name(), "]'s output 0 "), node_unit.Outputs()[0], node_unit.ModelPath(), 1.f / 256 /* required_scale */, 0 /* required_zp */)) { diff --git a/onnxruntime/core/providers/nnapi/nnapi_builtin/builders/impl/unsqueeze_op_builder.cc b/onnxruntime/core/providers/nnapi/nnapi_builtin/builders/impl/unsqueeze_op_builder.cc index a9bece7d42364..95cd813800c9a 100644 --- a/onnxruntime/core/providers/nnapi/nnapi_builtin/builders/impl/unsqueeze_op_builder.cc +++ b/onnxruntime/core/providers/nnapi/nnapi_builtin/builders/impl/unsqueeze_op_builder.cc @@ -32,7 +32,7 @@ class UnsqueezeOpBuilder : public BaseOpBuilder { // Operator support related private: - bool IsOpSupportedImpl(const InitializedTensorSet& initializers, const NodeUnit& node_unit, + bool IsOpSupportedImpl(const GraphViewer& graph_viewer, const NodeUnit& node_unit, const OpSupportCheckParams& params) const override; }; @@ -74,7 +74,7 @@ Status UnsqueezeOpBuilder::AddToModelBuilderImpl(ModelBuilder& model_builder, co // Operator support related -bool UnsqueezeOpBuilder::IsOpSupportedImpl(const InitializedTensorSet& initializers, const NodeUnit& node_unit, +bool UnsqueezeOpBuilder::IsOpSupportedImpl(const GraphViewer& graph_viewer, const NodeUnit& node_unit, const OpSupportCheckParams& /* params */) const { const auto& inputs = node_unit.Inputs(); Shape input_shape; @@ -93,8 +93,8 @@ bool UnsqueezeOpBuilder::IsOpSupportedImpl(const InitializedTensorSet& initializ // Unsqueeze opset 13 uses input 1 as axes, if we have input 1 then it needs to be an initializer if (node_unit.SinceVersion() > 12 && inputs.size() > 1) { const auto& axes_name = inputs[1].node_arg.Name(); - if (!Contains(initializers, axes_name)) { - LOGS_DEFAULT(VERBOSE) << "Input axes of Unsqueeze must be known"; + if (!graph_viewer.GetConstantInitializer(axes_name)) { + LOGS_DEFAULT(VERBOSE) << "Input axes of Unsqueeze must be a constant initializer"; return false; } } diff --git a/onnxruntime/core/providers/nnapi/nnapi_builtin/builders/model_builder.cc b/onnxruntime/core/providers/nnapi/nnapi_builtin/builders/model_builder.cc index b75e78cbfe7cc..6962a7be94bb6 100644 --- a/onnxruntime/core/providers/nnapi/nnapi_builtin/builders/model_builder.cc +++ b/onnxruntime/core/providers/nnapi/nnapi_builtin/builders/model_builder.cc @@ -100,7 +100,7 @@ void ModelBuilder::PreprocessActivations() { activation_node_units_.emplace(node_unit.get(), ANEURALNETWORKS_FUSED_RELU); } else if (op_type == "Clip") { // Relu1 or Relu6 float min, max; - if (!GetClipMinMax(GetInitializerTensors(), node, min, max, logging::LoggingManager::DefaultLogger())) + if (!GetClipMinMax(graph_viewer_, node, min, max, logging::LoggingManager::DefaultLogger())) continue; if (min == -1.0f && max == 1.0f) { @@ -151,7 +151,7 @@ void ModelBuilder::GetAllQuantizedOpInputs() { } static Status GetInputDataType( - const InitializedTensorSet& initializers, + const GraphViewer& graph_viewer, const std::unordered_map>& all_quantized_op_inputs, const std::string& name, int32_t data_type, const Shape& shape, OperandType& operand_type) { @@ -177,7 +177,7 @@ static Status GetInputDataType( // TODO, verify the scale and zero point match if there are multiple op using same input const auto* node_unit = all_quantized_op_inputs.at(name)[0]; ORT_RETURN_IF_ERROR(GetQuantizationScaleAndZeroPoint( - initializers, *node_unit, name, scale, zero_point, ArgType::kInput)); + graph_viewer, *node_unit, name, scale, zero_point, ArgType::kInput)); break; } case ONNX_NAMESPACE::TensorProto_DataType_INT32: @@ -226,9 +226,8 @@ Status ModelBuilder::RegisterInitializers() { } OperandType operand_type(Type::TENSOR_FLOAT32, shape); - ORT_RETURN_IF_ERROR( - GetInputDataType(GetInitializerTensors(), all_quantized_op_inputs_, - name, tensor.data_type(), shape, operand_type)); + ORT_RETURN_IF_ERROR(GetInputDataType(graph_viewer_, all_quantized_op_inputs_, name, tensor.data_type(), shape, + operand_type)); shaper_.AddShape(name, operand_type.dimensions); uint32_t index = 0; @@ -304,7 +303,7 @@ Status ModelBuilder::RegisterModelInputs() { "The input of graph doesn't have elem_type: ", input_name); } else { ORT_RETURN_IF_ERROR( - GetInputDataType(GetInitializerTensors(), all_quantized_op_inputs_, + GetInputDataType(graph_viewer_, all_quantized_op_inputs_, input_name, type_proto->tensor_type().elem_type(), shape, operand_type)); } diff --git a/onnxruntime/core/providers/nnapi/nnapi_builtin/builders/op_builder.h b/onnxruntime/core/providers/nnapi/nnapi_builtin/builders/op_builder.h index c565af491ff90..f6db4022fb8f4 100644 --- a/onnxruntime/core/providers/nnapi/nnapi_builtin/builders/op_builder.h +++ b/onnxruntime/core/providers/nnapi/nnapi_builtin/builders/op_builder.h @@ -56,7 +56,7 @@ class IOpBuilder { // Operator support check related // Check if an operator is supported - virtual bool IsOpSupported(const InitializedTensorSet& initializers, const NodeUnit& node_unit, + virtual bool IsOpSupported(const GraphViewer& graph_viewer, const NodeUnit& node_unit, const OpSupportCheckParams& params) const = 0; }; diff --git a/onnxruntime/core/providers/nnapi/nnapi_builtin/builders/op_builder_helpers.cc b/onnxruntime/core/providers/nnapi/nnapi_builtin/builders/op_builder_helpers.cc index 26db7c8e7afea..a066c64dac67d 100644 --- a/onnxruntime/core/providers/nnapi/nnapi_builtin/builders/op_builder_helpers.cc +++ b/onnxruntime/core/providers/nnapi/nnapi_builtin/builders/op_builder_helpers.cc @@ -679,16 +679,15 @@ Status HandleAutoPad(const Shape& input_shape, return Status::OK(); } -Status GetBinaryOpQuantizationScaleAndZeroPoint( - const InitializedTensorSet& initializers, const NodeUnit& node_unit, - float& a_scale, float& b_scale, float& y_scale, - int32_t& a_zero_point, int32_t& b_zero_point, int32_t& y_zero_point) { +Status GetBinaryOpQuantizationScaleAndZeroPoint(const GraphViewer& graph_viewer, const NodeUnit& node_unit, + float& a_scale, float& b_scale, float& y_scale, + int32_t& a_zero_point, int32_t& b_zero_point, int32_t& y_zero_point) { ORT_RETURN_IF_ERROR(GetQuantizationScaleAndZeroPoint( - initializers, node_unit.Inputs()[0], node_unit.ModelPath(), a_scale, a_zero_point)); + graph_viewer, node_unit.Inputs()[0], node_unit.ModelPath(), a_scale, a_zero_point)); ORT_RETURN_IF_ERROR(GetQuantizationScaleAndZeroPoint( - initializers, node_unit.Inputs()[1], node_unit.ModelPath(), b_scale, b_zero_point)); + graph_viewer, node_unit.Inputs()[1], node_unit.ModelPath(), b_scale, b_zero_point)); ORT_RETURN_IF_ERROR(GetQuantizationScaleAndZeroPoint( - initializers, node_unit.Outputs()[0], node_unit.ModelPath(), y_scale, y_zero_point)); + graph_viewer, node_unit.Outputs()[0], node_unit.ModelPath(), y_scale, y_zero_point)); return Status::OK(); } @@ -699,16 +698,18 @@ Status GetConvMatMulOpQuantizationScaleAndZeroPoint( int32_t& a_zero_point, int32_t& w_zero_point, int32_t& y_zero_point, std::optional>& w_scales, bool& is_per_tensor_u8s8) { is_per_tensor_u8s8 = false; - const auto& initializers(model_builder.GetInitializerTensors()); + const auto& graph_viewer(model_builder.GetGraphViewer()); + // Get scale and zero points // We will handle per-channel weight scale and zero point later ORT_RETURN_IF_ERROR( - GetBinaryOpQuantizationScaleAndZeroPoint(initializers, node_unit, + GetBinaryOpQuantizationScaleAndZeroPoint(graph_viewer, node_unit, a_scale, w_scale, y_scale, a_zero_point, w_zero_point, y_zero_point)); const auto& inputs = node_unit.Inputs(); - const auto& weight_tensor = *initializers.at(inputs[1].node_arg.Name()); + // all these were checked to be constant in GemmOpBuilder::IsOpSupportedImpl + const auto& weight_tensor = *graph_viewer.GetConstantInitializer(inputs[1].node_arg.Name()); // We are done here if this is u8u8 QLinearConv if (weight_tensor.data_type() == ONNX_NAMESPACE::TensorProto_DataType_UINT8) @@ -719,7 +720,7 @@ Status GetConvMatMulOpQuantizationScaleAndZeroPoint( // For this case we will need to convert the int8 weight tensor to uint8 // And have same scale and 128 as zero point // The conversion of the weight tensor itself will be done in the OpBuilder - const auto& scale_tensor = *initializers.at(inputs[1].quant_param->scale.Name()); + const auto& scale_tensor = *graph_viewer.GetConstantInitializer(inputs[1].quant_param->scale.Name()); int64_t scale_dim = scale_tensor.dims().empty() ? 1 : scale_tensor.dims()[0]; if (scale_dim == 1) { w_zero_point = 128; @@ -1072,20 +1073,20 @@ Status AddReshapeOperator(ModelBuilder& model_builder, return Status::OK(); } -bool IsQuantizationScaleSupported(const InitializedTensorSet& initializers, +bool IsQuantizationScaleSupported(const GraphViewer& graph_viewer, const NodeUnitIODef& io_def, const OpSupportCheckParams& params, const std::string& op_type, bool is_quant_matmul, bool is_conv_matmul_u8s8_weight) { const auto scale_name = io_def.quant_param->scale.Name(); - auto it = initializers.find(scale_name); - if (it == initializers.cend()) { - LOGS_DEFAULT(VERBOSE) << "The scale of " << op_type << " must be an initializer tensor"; + const auto* scale = graph_viewer.GetConstantInitializer(scale_name); + if (!scale) { + LOGS_DEFAULT(VERBOSE) << "The scale of " << op_type << " must be a constant initializer"; return false; } - const auto& scale_tensor = *it->second; + const auto& scale_tensor = *scale; int64_t scales_dim = scale_tensor.dims().empty() ? 1 : scale_tensor.dims()[0]; if (!is_conv_matmul_u8s8_weight) { if (scales_dim != 1) { @@ -1123,7 +1124,7 @@ bool IsQuantizationScaleSupported(const InitializedTensorSet& initializers, return true; } -bool IsQuantizationZeroPointSupported(const InitializedTensorSet& initializers, +bool IsQuantizationZeroPointSupported(const GraphViewer& graph_viewer, const NodeUnitIODef& io_def, const std::string& op_type, const Path& model_path, @@ -1134,12 +1135,13 @@ bool IsQuantizationZeroPointSupported(const InitializedTensorSet& initializers, return true; const auto& zero_point_name = io_def.quant_param->zero_point->Name(); - if (!Contains(initializers, zero_point_name)) { - LOGS_DEFAULT(VERBOSE) << "The zero point of " << op_type << " must be an initializer tensor"; + const auto* zero_point = graph_viewer.GetConstantInitializer(zero_point_name); + if (!zero_point) { + LOGS_DEFAULT(VERBOSE) << "The zero point of " << op_type << " must be a constant initializer"; return false; } - const auto& zero_tensor = *initializers.at(zero_point_name); + const auto& zero_tensor = *zero_point; int64_t zero_dim = zero_tensor.dims().empty() ? 1 : zero_tensor.dims()[0]; if (!is_conv_matmul_u8s8_weight) { @@ -1194,8 +1196,9 @@ bool IsQuantizationZeroPointSupported(const InitializedTensorSet& initializers, return true; } -bool IsQuantizedIOSupported(const InitializedTensorSet& initializers, const NodeUnit& node_unit, - const std::vector& indices, const OpSupportCheckParams& params, ArgType arg_type) { +bool IsQuantizedIOSupported(const GraphViewer& graph_viewer, const NodeUnit& node_unit, + const std::vector& indices, const OpSupportCheckParams& params, + ArgType arg_type) { const auto& op_type = node_unit.OpType(); auto quant_op_type = GetQuantizedOpType(node_unit); @@ -1247,12 +1250,12 @@ bool IsQuantizedIOSupported(const InitializedTensorSet& initializers, const Node } // Check scale and zero point - if (!IsQuantizationScaleSupported(initializers, io_def, params, op_type, + if (!IsQuantizationScaleSupported(graph_viewer, io_def, params, op_type, is_quant_matmul, is_conv_matmul_u8s8_weight)) { return false; } - if (!IsQuantizationZeroPointSupported(initializers, io_def, op_type, node_unit.ModelPath(), + if (!IsQuantizationZeroPointSupported(graph_viewer, io_def, op_type, node_unit.ModelPath(), is_quant_matmul, is_conv_matmul_u8s8_weight)) { return false; } @@ -1261,33 +1264,27 @@ bool IsQuantizedIOSupported(const InitializedTensorSet& initializers, const Node return true; } -bool HasRequiredScaleAndZeroPoint(const InitializedTensorSet& initializers, +bool HasRequiredScaleAndZeroPoint(const GraphViewer& graph_viewer, const std::string& op_desc, const NodeUnitIODef& io_def, const Path& path, float required_scale, int32_t required_zp) { float scale = 0.0f; int32_t zp = 0; - auto status = GetQuantizationScaleAndZeroPoint(initializers, io_def, path, - scale, zp); + auto status = GetQuantizationScaleAndZeroPoint(graph_viewer, io_def, path, scale, zp); if (!status.IsOK()) { - LOGS_DEFAULT(ERROR) << op_desc - << " GetQuantizationScaleAndZeroPoint failed, message: " - << status.ErrorMessage(); + LOGS_DEFAULT(ERROR) << op_desc << " GetQuantizationScaleAndZeroPoint failed, message: " << status.ErrorMessage(); return false; } if (scale != required_scale) { - LOGS_DEFAULT(VERBOSE) << op_desc - << " scale can only be [" << required_scale - << "], actual scale: " << scale; + LOGS_DEFAULT(VERBOSE) << op_desc << " scale can only be [" << required_scale << "], actual scale: " << scale; return false; } if (zp != required_zp) { - LOGS_DEFAULT(VERBOSE) << op_desc - << "] zero point can only be [" << required_zp - << "], actual zero point: " << scale; + LOGS_DEFAULT(VERBOSE) << op_desc << "] zero point can only be [" << required_zp << "], actual zero point: " + << zp; return false; } diff --git a/onnxruntime/core/providers/nnapi/nnapi_builtin/builders/op_builder_helpers.h b/onnxruntime/core/providers/nnapi/nnapi_builtin/builders/op_builder_helpers.h index 0cc442890ab6e..7ccf4c1ef7555 100644 --- a/onnxruntime/core/providers/nnapi/nnapi_builtin/builders/op_builder_helpers.h +++ b/onnxruntime/core/providers/nnapi/nnapi_builtin/builders/op_builder_helpers.h @@ -118,7 +118,7 @@ Status HandleAutoPad(const Shape& input_shape, // Get scales and zero points for the qlinear binary ops (which has 2 input and 1 output) // QLinearConv, QLinearMatmul, QLinearAdd, QLinearMul // a, b are inputs, and y is output -Status GetBinaryOpQuantizationScaleAndZeroPoint(const InitializedTensorSet& initializers, const NodeUnit& node_unit, +Status GetBinaryOpQuantizationScaleAndZeroPoint(const GraphViewer& graph_viewer, const NodeUnit& node_unit, float& a_scale, float& b_scale, float& y_scale, int32_t& a_zero_point, int32_t& b_zero_point, int32_t& y_zero_point); @@ -193,14 +193,14 @@ inline bool IsNodeLayoutNHWC(const NodeUnit& node_unit) { return node_unit.Domain() == kMSInternalNHWCDomain; } -bool IsQuantizationScaleSupported(const InitializedTensorSet& initializers, +bool IsQuantizationScaleSupported(const GraphViewer& graph_viewer, const NodeUnitIODef& io_def, const OpSupportCheckParams& params, const std::string& op_type, bool is_quant_matmul, bool is_conv_matmul_u8s8_weight); -bool IsQuantizationZeroPointSupported(const InitializedTensorSet& initializers, +bool IsQuantizationZeroPointSupported(const GraphViewer& graph_viewer, const NodeUnitIODef& io_def, const std::string& op_type, const Path& model_path, @@ -208,13 +208,13 @@ bool IsQuantizationZeroPointSupported(const InitializedTensorSet& initializers, bool is_conv_matmul_u8s8_weight); // Check if the given quantized input(s) or output(s) is supported -bool IsQuantizedIOSupported(const InitializedTensorSet& initializers, const NodeUnit& node_unit, +bool IsQuantizedIOSupported(const GraphViewer& graph_viewer, const NodeUnit& node_unit, const std::vector& indices, const OpSupportCheckParams& params, ArgType arg_type); // Some Quantized NNAPI operations have required output scale and zero point // e.g. Softmax (uint8) requires output scale be 1.f/256 and zp be 0 // This helper function checks if the given io_def has required scale and zp -bool HasRequiredScaleAndZeroPoint(const InitializedTensorSet& initializers, +bool HasRequiredScaleAndZeroPoint(const GraphViewer& graph_viewer, const std::string& op_desc, const NodeUnitIODef& io_def, const Path& path, diff --git a/onnxruntime/core/providers/shared/utils/utils.cc b/onnxruntime/core/providers/shared/utils/utils.cc index 39ea4dd8412bb..37ad14ac2e9b1 100644 --- a/onnxruntime/core/providers/shared/utils/utils.cc +++ b/onnxruntime/core/providers/shared/utils/utils.cc @@ -25,12 +25,14 @@ bool GetType(const NodeArg& node_arg, int32_t& type, const logging::Logger& logg return true; } -bool GetClipMinMax(const InitializedTensorSet& initializers, const Node& node, - float& min, float& max, const logging::Logger& logger) { +namespace { +bool GetClipMinMaxImpl(std::function get_const_initializer, + const Node& node, float& min, float& max, const logging::Logger& logger) { const auto& node_name = node.Name(); int32_t input_type; - if (!GetType(*node.InputDefs()[0], input_type, logger)) + if (!GetType(*node.InputDefs()[0], input_type, logger)) { return false; + } min = std::numeric_limits::lowest(); max = std::numeric_limits::max(); @@ -41,49 +43,73 @@ bool GetClipMinMax(const InitializedTensorSet& initializers, const Node& node, min = helper.Get("min", std::numeric_limits::lowest()); max = helper.Get("max", std::numeric_limits::max()); } else { - if (node.InputDefs().size() > 1) { - // we have input min - const auto& min_name = node.InputDefs()[1]->Name(); - if (!Contains(initializers, min_name)) { - LOGS(logger, VERBOSE) << "Input min of Clip must be known"; + auto get_value = + [&](const ONNX_NAMESPACE::TensorProto* initializer, std::string_view type, float& value) -> bool { + if (!initializer) { + LOGS(logger, VERBOSE) << type << " input of Clip must be a constant initializer"; return false; } - Initializer unpacked_tensor_min(*initializers.at(min_name)); + + Initializer unpacked_tensor_min(*initializer); switch (input_type) { case ONNX_NAMESPACE::TensorProto_DataType_FLOAT: - min = unpacked_tensor_min.DataAsSpan()[0]; + value = unpacked_tensor_min.DataAsSpan()[0]; break; case ONNX_NAMESPACE::TensorProto_DataType_FLOAT16: - min = (unpacked_tensor_min.DataAsSpan()[0]).ToFloat(); + value = unpacked_tensor_min.DataAsSpan()[0].ToFloat(); break; default: - LOGS(logger, VERBOSE) << "GetClipMinMax() only support Clip node with float inputs for now. " - << "The node [" << node_name << "] has input 0 type: " << input_type; + LOGS(logger, VERBOSE) << "GetClipMinMax() only supports float and float16 as min and max inputs for now." + << " The node [" << node_name << "] has input type: " << input_type; return false; } - if (node.InputDefs().size() > 2) { - // we have input max - const auto& max_name = node.InputDefs()[2]->Name(); - if (!Contains(initializers, max_name)) { - LOGS(logger, VERBOSE) << "Input max of Clip must be known"; - return false; - } - Initializer unpacked_tensor_max(*initializers.at(max_name)); - switch (input_type) { - case ONNX_NAMESPACE::TensorProto_DataType_FLOAT: - max = unpacked_tensor_max.DataAsSpan()[0]; - break; - case ONNX_NAMESPACE::TensorProto_DataType_FLOAT16: - max = (unpacked_tensor_max.DataAsSpan()[0]).ToFloat(); - break; - } + return true; + }; + + // min and max are both optional. could have neither, one or both. + if (node.InputDefs().size() > 1 && node.InputDefs()[1]->Exists()) { + // we have input min + const auto& min_name = node.InputDefs()[1]->Name(); + const auto* min_value = get_const_initializer(min_name); + if (!get_value(min_value, "Min", min)) { + return false; + } + } + + if (node.InputDefs().size() > 2 && node.InputDefs()[2]->Exists()) { + // we have input max + const auto& max_name = node.InputDefs()[2]->Name(); + const auto* max_value = get_const_initializer(max_name); + if (!get_value(max_value, "Max", max)) { + return false; } } } return true; } +} // namespace + +bool GetClipMinMax(const GraphViewer& graph_viewer, const Node& node, float& min, float& max, + const logging::Logger& logger) { + return GetClipMinMaxImpl( + [&graph_viewer](const std::string& name) -> const ONNX_NAMESPACE::TensorProto* { + return graph_viewer.GetConstantInitializer(name); + }, + node, min, max, logger); +} + +// deprecated version that is not able to check if the initializer is constant +bool GetClipMinMax(const InitializedTensorSet& initializers, const Node& node, float& min, float& max, + const logging::Logger& logger) { + return GetClipMinMaxImpl( + [&initializers](const std::string& name) -> const ONNX_NAMESPACE::TensorProto* { + auto entry = initializers.find(name); + return entry == initializers.end() ? nullptr : entry->second; + }, + node, min, max, logger); +} NodeAttrHelper::NodeAttrHelper(const onnxruntime::Node& node) : node_attributes_(node.GetAttributes()) {} diff --git a/onnxruntime/core/providers/shared/utils/utils.h b/onnxruntime/core/providers/shared/utils/utils.h index 1e93f040711df..31b1aba2e1a63 100644 --- a/onnxruntime/core/providers/shared/utils/utils.h +++ b/onnxruntime/core/providers/shared/utils/utils.h @@ -16,14 +16,20 @@ namespace logging { class Logger; } +class GraphViewer; class Node; class NodeArg; class NodeUnit; -// Get the min/max of a Clip operator. -// If min/max are not known initializer tensors, will return false -// For now we only support getting float min/max, -// since in most cases, Clip(0,6)[Relu6] will be fused by quantization tool +// Get the min/max of a Clip operator. Reads values from attributes for opset < 11 and inputs after that. +// For opset 11+, if min/max are not constant initializers, will return false. +// For now we only support getting float min/max. +bool GetClipMinMax(const GraphViewer& graph_viewer, const Node& node, + float& min, float& max, const logging::Logger& logger); + +/// GraphViewer GetConstantInitializer/IsConstantInitializer should be used to ensure the initializer is +/// constant. Low risk for Clip min/max but in general the infrastructure to check if an operator is supported needs +/// to be updated to not use InitializedTensorSet which may contain non-constant initializers. bool GetClipMinMax(const InitializedTensorSet& initializers, const Node& node, float& min, float& max, const logging::Logger& logger); diff --git a/onnxruntime/core/providers/webnn/builders/impl/clip_op_builder.cc b/onnxruntime/core/providers/webnn/builders/impl/clip_op_builder.cc index 9de5b889808fc..0d6001bcba89f 100644 --- a/onnxruntime/core/providers/webnn/builders/impl/clip_op_builder.cc +++ b/onnxruntime/core/providers/webnn/builders/impl/clip_op_builder.cc @@ -47,7 +47,7 @@ Status ClipOpBuilder::AddToModelBuilderImpl(ModelBuilder& model_builder, const auto& output_name = node.OutputDefs()[0]->Name(); emscripten::val options = emscripten::val::object(); float minValue, maxValue; - ORT_RETURN_IF_NOT(GetClipMinMax(model_builder.GetInitializerTensors(), node, minValue, maxValue, logger), + ORT_RETURN_IF_NOT(GetClipMinMax(model_builder.GetGraphViewer(), node, minValue, maxValue, logger), "GetClipMinMax failed"); options.set("minValue", minValue); options.set("maxValue", maxValue); @@ -70,6 +70,9 @@ bool ClipOpBuilder::IsOpSupportedImpl(const InitializedTensorSet& initializers, const Node& node, const WebnnDeviceType /* device_type */, const logging::Logger& logger) const { + // TODO: Update IsOpSupportedImpl to pass GraphViewer instead of InitializedTensorSet so the implementations + // can ensure initializers are constant. See #19401 for details of how this update was made to the NNAPI EP. + // GetClipMinMax(graph_viewer, node, minValue, maxValue, logger) float min, max; return GetClipMinMax(initializers, node, min, max, logger); }