55#include " core/providers/common.h"
66#include " core/providers/coreml/builders/helper.h"
77#include " core/providers/coreml/builders/impl/base_op_builder.h"
8+ #include " core/providers/coreml/builders/impl/builder_utils.h"
89#include " core/providers/coreml/builders/model_builder.h"
910#include " core/providers/coreml/builders/op_builder_factory.h"
1011#include " core/providers/shared/utils/utils.h"
1112
13+ #ifdef __APPLE__
14+ #include < TargetConditionals.h>
15+ #endif
16+
1217namespace onnxruntime {
1318namespace coreml {
1419
@@ -20,6 +25,7 @@ class ReductionOpBuilder : public BaseOpBuilder {
2025
2126 bool IsOpSupportedImpl (const Node& node, const OpBuilderInputParams& input_params,
2227 const logging::Logger& logger) const override ;
28+ bool SupportsMLProgram () const override { return true ; }
2329};
2430
2531namespace {
@@ -48,13 +54,12 @@ Status ReductionOpBuilder::AddToModelBuilderImpl(ModelBuilder& model_builder, co
4854 const logging::Logger& /* logger */ ) const {
4955 const auto & op_type (node.OpType ());
5056 const auto & input_defs (node.InputDefs ());
51- const auto & initializers (model_builder.GetInitializerTensors ());
5257
5358 std::vector<int64_t > axes;
5459
5560 NodeAttrHelper helper (node);
5661 if (input_defs.size () > 1 && input_defs[1 ]->Exists ()) {
57- auto & axes_tensor = *initializers. at (input_defs[1 ]->Name ());
62+ auto & axes_tensor = *model_builder. GetConstantInitializer (input_defs[1 ]->Name ());
5863 Initializer axes_initializer (axes_tensor);
5964 int64_t * data = axes_initializer.data <int64_t >();
6065 int64_t size = axes_initializer.size ();
@@ -66,28 +71,77 @@ Status ReductionOpBuilder::AddToModelBuilderImpl(ModelBuilder& model_builder, co
6671
6772 const bool keepdims = helper.Get (" keepdims" , 1 ) != 0 ;
6873 const bool noop_with_empty_axes = helper.Get (" noop_with_empty_axes" , 0 ) != 0 ;
74+ #if defined(COREML_ENABLE_MLPROGRAM)
75+ if (model_builder.CreateMLProgram ()) {
76+ using namespace CoreML ::Specification::MILSpec;
77+
78+ std::string_view coreml_op_type;
79+ if (noop_with_empty_axes && axes.size () == 0 ) {
80+ coreml_op_type = " identity" ;
81+ } else if (op_type == " ReduceSum" ) {
82+ coreml_op_type = " reduce_sum" ;
83+ } else if (op_type == " ReduceMean" ) {
84+ coreml_op_type = " reduce_mean" ;
85+ } else if (op_type == " ReduceMax" ) {
86+ coreml_op_type = " reduce_max" ;
87+ } else if (op_type == " ReduceMin" ) {
88+ coreml_op_type = " reduce_min" ;
89+ } else if (op_type == " ReduceProd" ) {
90+ coreml_op_type = " reduce_prod" ;
91+ } else {
92+ return ORT_MAKE_STATUS (ONNXRUNTIME, INVALID_ARGUMENT,
93+ " ReductionOpBuilder::AddToModelBuilderImpl, unexpected op: " , op_type);
94+ }
95+ std::unique_ptr<Operation> op = model_builder.CreateOperation (node, coreml_op_type);
96+ AddOperationInput (*op, " x" , input_defs[0 ]->Name ());
97+ if (coreml_op_type != " identity" ) {
98+ if (axes.size () > 0 ) {
99+ AddOperationInput (*op, " axes" , model_builder.AddConstant (op->type (), " axes" , axes));
100+ }
101+ AddOperationInput (*op, " keep_dims" , model_builder.AddScalarConstant (op->type (), " keep_dims" , keepdims));
102+ }
103+ AddOperationOutput (*op, *node.OutputDefs ()[0 ]);
104+
105+ model_builder.AddOperation (std::move (op));
106+ } else
107+ #endif // (COREML_ENABLE_MLPROGRAM)
108+ {
109+ std::unique_ptr<COREML_SPEC::NeuralNetworkLayer> layer = model_builder.CreateNNLayer (node);
110+
111+ if (op_type == " ReduceSum" ) {
112+ AddReductionParams (layer->mutable_reducesum (), axes, keepdims, noop_with_empty_axes);
113+ } else if (op_type == " ReduceMean" ) {
114+ AddReductionParams (layer->mutable_reducemean (), axes, keepdims, noop_with_empty_axes);
115+ } else {
116+ return ORT_MAKE_STATUS (ONNXRUNTIME, INVALID_ARGUMENT,
117+ " ReductionOpBuilder::AddToModelBuilderImpl, unknown op: " , op_type);
118+ }
69119
70- std::unique_ptr<COREML_SPEC::NeuralNetworkLayer> layer = model_builder.CreateNNLayer (node);
120+ *layer->mutable_input ()->Add () = node.InputDefs ()[0 ]->Name ();
121+ *layer->mutable_output ()->Add () = node.OutputDefs ()[0 ]->Name ();
71122
72- if (op_type == " ReduceSum" ) {
73- AddReductionParams (layer->mutable_reducesum (), axes, keepdims, noop_with_empty_axes);
74- } else if (op_type == " ReduceMean" ) {
75- AddReductionParams (layer->mutable_reducemean (), axes, keepdims, noop_with_empty_axes);
76- } else {
77- return ORT_MAKE_STATUS (ONNXRUNTIME, INVALID_ARGUMENT,
78- " ReductionOpBuilder::AddToModelBuilderImpl, unknown op: " , op_type);
123+ model_builder.AddLayer (std::move (layer));
79124 }
80-
81- *layer->mutable_input ()->Add () = node.InputDefs ()[0 ]->Name ();
82- *layer->mutable_output ()->Add () = node.OutputDefs ()[0 ]->Name ();
83-
84- model_builder.AddLayer (std::move (layer));
85125 return Status::OK ();
86126}
87127
88128bool ReductionOpBuilder::IsOpSupportedImpl (const Node& node, const OpBuilderInputParams& input_params,
89129 const logging::Logger& logger) const {
90130 const auto & input_defs = node.InputDefs ();
131+ if (!input_params.create_mlprogram &&
132+ (node.OpType () == " ReduceMax" || node.OpType () == " ReduceMin" || node.OpType () == " ReduceProd" )) {
133+ return false ;
134+ }
135+
136+ #if defined(TARGET_OS_IOS) && defined(TARGET_CPU_X86_64)
137+ // to pass https://dev.azure.com/onnxruntime/onnxruntime/_build/results?buildId=1563483&view=logs&j=f7cc61a9-cc70-56e7-b06c-4668ca17e426
138+ // ReductionOpTest.ReduceSum_half_bert
139+ int32_t input_type;
140+ GetType (*input_defs[0 ], input_type, logger);
141+ if (node.OpType () == " ReduceSum" && input_type == ONNX_NAMESPACE::TensorProto_DataType_FLOAT16) {
142+ return false ;
143+ }
144+ #endif
91145
92146 NodeAttrHelper helper (node);
93147
@@ -99,18 +153,16 @@ bool ReductionOpBuilder::IsOpSupportedImpl(const Node& node, const OpBuilderInpu
99153 if (input_defs.size () > 1 && input_defs[1 ]->Exists ()) {
100154 // 'axes' is optional input in new opsets
101155 const auto & axes_name = input_defs[1 ]->Name ();
102- const auto & initializers = input_params.graph_viewer .GetAllInitializedTensors ( );
103- if (!Contains (initializers, axes_name) ) {
156+ const auto * axes = input_params.graph_viewer .GetConstantInitializer (axes_name );
157+ if (!axes ) {
104158 LOGS (logger, VERBOSE) << " Axes of reduction must be a constant initializer" ;
105159 return false ;
106160 }
107161
108- empty_axes = initializers. at (axes_name) ->int64_data_size () == 0 ;
162+ empty_axes = axes ->int64_data_size () == 0 ;
109163 }
110-
111- if (empty_axes && noop_with_empty_axes) {
112- // TODO: When we add ML Program support we should enable this as it makes the node an Identity op
113- LOGS (logger, VERBOSE) << " CoreML doesn't support noop on empty axes for reduction layers" << std::endl;
164+ if (empty_axes && noop_with_empty_axes && !input_params.create_mlprogram ) {
165+ LOGS (logger, VERBOSE) << " NeuralNetwork doesn't support noop on empty axes for reduction layers" ;
114166 return false ;
115167 }
116168
0 commit comments