xref: /aosp_15_r20/external/tensorflow/tensorflow/lite/delegates/coreml/builders/op_builder.cc (revision b6fb3261f9314811a0f4371741dbb8839866f948)
1 /* Copyright 2019 The TensorFlow Authors. All Rights Reserved.
2 
3 Licensed under the Apache License, Version 2.0 (the "License");
4 you may not use this file except in compliance with the License.
5 You may obtain a copy of the License at
6 
7     http://www.apache.org/licenses/LICENSE-2.0
8 
9 Unless required by applicable law or agreed to in writing, software
10 distributed under the License is distributed on an "AS IS" BASIS,
11 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 See the License for the specific language governing permissions and
13 limitations under the License.
14 ==============================================================================*/
15 #include "tensorflow/lite/delegates/coreml/builders/op_builder.h"
16 
17 #include <functional>
18 #include <memory>
19 #include <string>
20 
21 #include "tensorflow/lite/builtin_ops.h"
22 #include "tensorflow/lite/c/builtin_op_data.h"
23 #include "tensorflow/lite/delegates/coreml/builders/op_factory.h"
24 #include "tensorflow/lite/kernels/kernel_util.h"
25 
26 namespace tflite {
27 namespace delegates {
28 namespace coreml {
29 
ToString() const30 std::string TensorID::ToString() const {
31   return std::to_string(node_) + "_" + std::to_string(output_id_);
32 }
33 
NodeID() const34 int TensorID::NodeID() const { return node_; }
35 
OutputID() const36 int TensorID::OutputID() const { return output_id_; }
37 
AddBuilder(int builtin_code,const TfLiteNode * node)38 OpBuilder* GraphBuilder::AddBuilder(int builtin_code, const TfLiteNode* node) {
39   switch (builtin_code) {
40     case kTfLiteBuiltinAdd:
41       return AddBuilder(CreateAddOpBuilder, node);
42     case kTfLiteBuiltinAveragePool2d:
43       return AddBuilder(CreateAveragePool2dOpBuilder, node);
44     case kTfLiteBuiltinConcatenation:
45       return AddBuilder(CreateConcatenationOpBuilder, node);
46     case kTfLiteBuiltinConv2d:
47       return AddBuilder(CreateConvolutionOpBuilder, node);
48     case kTfLiteBuiltinDepthwiseConv2d:
49       return AddBuilder(CreateDepthwiseConvolutionOpBuilder, node);
50     // TODO(b/141490853): Add proper dequantize OpBuilder for int8/uint8 inputs.
51     case kTfLiteBuiltinDequantize:
52       // FP16 dequantize is claimed by the delegate to prevent them from running
53       // on CPU, but don't need to be excuted on the Core ML delegate either.
54       return AddBuilder(CreateDummyOpBuilder, node);
55     case kTfLiteBuiltinFullyConnected:
56       return AddBuilder(CreateFullyConnectedOpBuilder, node);
57     case kTfLiteBuiltinLogistic:
58       return AddBuilder(CreateLogisticOpBuilder, node);
59     case kTfLiteBuiltinMaxPool2d:
60       return AddBuilder(CreateMaxPool2dOpBuilder, node);
61     case kTfLiteBuiltinMean:
62       return AddBuilder(CreateMeanOpBuilder, node);
63     case kTfLiteBuiltinMirrorPad:
64       return AddBuilder(CreateMirrorPadOpBuilder, node);
65     case kTfLiteBuiltinMul:
66       return AddBuilder(CreateMulOpBuilder, node);
67     case kTfLiteBuiltinPad:
68     case kTfLiteBuiltinPadv2:
69       return AddBuilder(CreatePadOpBuilder, node);
70     case kTfLiteBuiltinRelu:
71       return AddBuilder(CreateReluOpBuilder, node);
72     case kTfLiteBuiltinReluN1To1:
73       return AddBuilder(CreateReluN1To1OpBuilder, node);
74     case kTfLiteBuiltinRelu6:
75       return AddBuilder(CreateRelu6OpBuilder, node);
76     case kTfLiteBuiltinReshape:
77       return AddBuilder(CreateReshapeOpBuilder, node);
78     case kTfLiteBuiltinResizeBilinear:
79       return AddBuilder(CreateResizeBilinearOpBuilder, node);
80     case kTfLiteBuiltinSoftmax:
81       return AddBuilder(CreateSoftmaxOpBuilder, node);
82     case kTfLiteBuiltinTanh:
83       return AddBuilder(CreateTanhOpBuilder, node);
84     case kTfLiteBuiltinTransposeConv:
85       return AddBuilder(CreateTransposeConvolutionOpBuilder, node);
86     case kTfLiteBuiltinHardSwish:
87       return AddBuilder(CreateHardSwishOpBuilder, node);
88     default:
89       return nullptr;
90   }
91 }
92 
AddBuilder(const std::function<OpBuilder * (GraphBuilder *)> & builder,const TfLiteNode * node)93 OpBuilder* GraphBuilder::AddBuilder(
94     const std::function<OpBuilder*(GraphBuilder*)>& builder,
95     const TfLiteNode* node) {
96   if (builder == nullptr) {
97     fprintf(stderr, "builder should be set.\n");
98     return nullptr;
99   }
100   OpBuilder* op = builder(this);
101 
102   builders_.emplace_back(op);
103   op->SetNodeID(builders_.size());
104   if (node != nullptr) {
105     op->SetBuiltinData(node->builtin_data);
106     op->SetTfLiteNode(node);
107   }
108   return builders_.back().get();
109 }
110 
BuildModel()111 CoreML::Specification::Model* GraphBuilder::BuildModel() {
112   CoreML::Specification::Model* model = new CoreML::Specification::Model();
113   if (coreml_version_ == 2) {  // Core ML 2, iOS >= 12.0
114     model->set_specificationversion(3);
115   } else if (coreml_version_ == 3) {  // Core ML 3, iOS >= 13.0
116     model->set_specificationversion(4);
117     model->mutable_neuralnetwork()->set_arrayinputshapemapping(
118         CoreML::Specification::EXACT_ARRAY_MAPPING);
119   } else {
120     fprintf(stderr, "Unsupported Core ML version: %d\n", coreml_version_);
121     delete model;
122     return nullptr;
123   }
124   auto* neural_network = model->mutable_neuralnetwork();
125   for (auto& builder : builders_) {
126     CoreML::Specification::NeuralNetworkLayer* layer = builder->Build();
127     if (layer == nullptr) {
128       fprintf(stderr, "Null layer returned from builder: %s\n",
129               builder->DebugName().c_str());
130       continue;
131     }
132     neural_network->mutable_layers()->AddAllocated(layer);
133   }
134   return model;
135 }
136 
AddTensorWithID(int tf_tensor_id,const TensorID & tensor_id)137 void GraphBuilder::AddTensorWithID(int tf_tensor_id,
138                                    const TensorID& tensor_id) {
139   if (tensors_.size() <= tf_tensor_id) {
140     tensors_.resize(tf_tensor_id + 1);
141     used_tensor_.resize(tf_tensor_id + 1);
142   }
143   tensors_[tf_tensor_id] = tensor_id;
144 }
145 
GetTensorName(int tensor_id)146 std::string GraphBuilder::GetTensorName(int tensor_id) {
147   return GetTensorID(tensor_id).ToString();
148 }
149 
GetTensorID(int tensor_id)150 const TensorID GraphBuilder::GetTensorID(int tensor_id) {
151   if (!HasTensor(tensor_id)) {
152     // TODO(karimnosseir): Double check if this happened, if we are
153     // adding in execution order it shouldn't happen.
154     fprintf(stderr, "index out of range...!!! Requested index %d , size %d\n",
155             tensor_id, static_cast<int>(tensors_.size()));
156     // Return invalid ID.
157     return TensorID(-1, -1);
158   }
159   used_tensor_[tensor_id] = true;
160   return tensors_[tensor_id];
161 }
162 
HasTensor(int tflite_tensor_index)163 bool GraphBuilder::HasTensor(int tflite_tensor_index) {
164   if (tensors_.size() <= tflite_tensor_index) {
165     return false;
166   }
167   return tensors_[tflite_tensor_index].NodeID() != -1;
168 }
169 
IsTensorUsed(int tflite_tensor_index)170 bool GraphBuilder::IsTensorUsed(int tflite_tensor_index) {
171   if (!HasTensor(tflite_tensor_index)) return false;
172   return used_tensor_[tflite_tensor_index];
173 }
174 
Build()175 CoreML::Specification::NeuralNetworkLayer* OpBuilder::Build() {
176   layer_->set_name(DebugName());
177   return layer_.release();
178 }
179 
PopulateSubgraph(TfLiteContext * context)180 TfLiteStatus OpBuilder::PopulateSubgraph(TfLiteContext* context) {
181   builder_output_ = AddOutput();
182   return kTfLiteOk;
183 }
184 
SetBuiltinData(void * builtin_data)185 void OpBuilder::SetBuiltinData(void* builtin_data) {
186   builtin_data_ = builtin_data;
187 }
188 
SetNodeID(int id)189 void OpBuilder::SetNodeID(int id) { node_id_ = id; }
190 
SetTfLiteNode(const TfLiteNode * node)191 void OpBuilder::SetTfLiteNode(const TfLiteNode* node) { tflite_node_ = node; }
192 
GetID() const193 int OpBuilder::GetID() const { return node_id_; }
194 
GetOutput(TfLiteContext * context)195 TensorID OpBuilder::GetOutput(TfLiteContext* context) {
196   if (builder_output_.NodeID() != -1) {
197     return builder_output_;
198   }
199   // builder_output_ is not set when PopulateSubgraph is not called.
200   builder_output_ = AddOutput();
201   return builder_output_;
202 }
203 
AddInput(const std::string & input_name)204 void OpBuilder::AddInput(const std::string& input_name) {
205   if (layer_ == nullptr) {
206     layer_ = std::make_unique<CoreML::Specification::NeuralNetworkLayer>();
207   }
208   *layer_->mutable_input()->Add() = input_name;
209 }
210 
AddInput(const TensorID & input_id)211 void OpBuilder::AddInput(const TensorID& input_id) {
212   AddInput(input_id.ToString());
213 }
214 
AddInput(int tf_input_id)215 void OpBuilder::AddInput(int tf_input_id) {
216   AddInput(graph_builder_->GetTensorName(tf_input_id));
217 }
218 
AddOutput()219 TensorID OpBuilder::AddOutput() {
220   auto tensor_id = TensorID(GetID(), num_outputs_++);
221   *layer_->mutable_output()->Add() = tensor_id.ToString();
222   return tensor_id;
223 }
224 
SetDebugName(const char * name,int id)225 void OpBuilder::SetDebugName(const char* name, int id) {
226   debug_name_ = std::string(name) + "_" + std::to_string(id);
227 }
228 
229 }  // namespace coreml
230 }  // namespace delegates
231 }  // namespace tflite
232