blob: 03cb9fb779d2898ba297d1beea29cc6b4b2c77dc [file] [log] [blame]
Eric Kunzee5e26762020-10-13 16:11:07 -07001
Kevin Cheng3a478572021-01-22 17:21:02 -08002// Copyright (c) 2020-2021, ARM Limited.
Eric Kunzee5e26762020-10-13 16:11:07 -07003//
4// Licensed under the Apache License, Version 2.0 (the "License");
5// you may not use this file except in compliance with the License.
6// You may obtain a copy of the License at
7//
8// http://www.apache.org/licenses/LICENSE-2.0
9//
10// Unless required by applicable law or agreed to in writing, software
11// distributed under the License is distributed on an "AS IS" BASIS,
12// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13// See the License for the specific language governing permissions and
14// limitations under the License.
15
16#include "tensor_ops.h"
17#include "quant_util.h"
18#include "template_types.h"
19
20using namespace TosaReference;
21using namespace Eigen;
22using namespace tosa;
23
Kevin Cheng9fe17242021-11-10 01:04:39 +000024int check_pool2d_attribute(tosa::TosaPoolAttribute* attribute,
25 std::vector<int32_t> input_shape,
26 std::vector<int32_t> output_shape,
27 std::string& msg)
Kevin Cheng7eb93d72021-10-09 01:26:08 +000028{
TatWai Chong86c403b2022-06-06 20:46:01 -070029 if (attribute->pad().size() != 4)
Kevin Cheng7eb93d72021-10-09 01:26:08 +000030 {
31 msg = "illegal size for attribute padding";
32 return 1;
33 }
34
35 if (attribute->kernel().size() != 2)
36 {
37 msg = "illegal size for attribute kernel";
38 return 1;
39 }
40
41 if (attribute->stride().size() != 2)
42 {
43 msg = "illegal size for attribute stride";
44 return 1;
45 }
46
TatWai Chong86c403b2022-06-06 20:46:01 -070047 for (int32_t i : attribute->pad())
Kevin Cheng7eb93d72021-10-09 01:26:08 +000048 {
49 if (i < 0)
50 {
51 msg = "At least one pad is smaller than zero";
52 return 1;
53 }
54 }
55
56 for (int32_t i : attribute->kernel())
57 {
58 if (i < 1)
59 {
Kevin Cheng9fe17242021-11-10 01:04:39 +000060 msg = "At least one kernel dimension is smaller than one";
Kevin Cheng7eb93d72021-10-09 01:26:08 +000061 return 1;
62 }
63 }
64
65 for (int32_t i : attribute->stride())
66 {
67 if (i < 1)
68 {
Kevin Cheng9fe17242021-11-10 01:04:39 +000069 msg = "At least one stride dimension is smaller than one";
Kevin Cheng7eb93d72021-10-09 01:26:08 +000070 return 1;
71 }
72 }
73
74 int32_t IH = input_shape[1];
75 int32_t IW = input_shape[2];
76 int32_t OH = output_shape[1];
77 int32_t OW = output_shape[2];
78
TatWai Chong86c403b2022-06-06 20:46:01 -070079 int32_t pad_top = attribute->pad()[0];
80 int32_t pad_bottom = attribute->pad()[1];
81 int32_t pad_left = attribute->pad()[2];
82 int32_t pad_right = attribute->pad()[3];
Kevin Cheng7eb93d72021-10-09 01:26:08 +000083
84 int32_t stride_y = attribute->stride()[0];
85 int32_t stride_x = attribute->stride()[1];
86 int32_t kernel_y = attribute->kernel()[0];
87 int32_t kernel_x = attribute->kernel()[1];
88
89 if (pad_top >= kernel_y || pad_bottom >= kernel_y || pad_left >= kernel_x || pad_right >= kernel_x)
90 {
91 msg = "At least one pad is >= kernel dimension";
92 return 1;
93 }
94
Jeremy Johnson4a6fb9b2022-04-26 15:47:21 +010095 int32_t full_H = IH + pad_top + pad_bottom - kernel_y;
96 int32_t full_W = IW + pad_left + pad_right - kernel_x;
97
98 if ((full_H % stride_y != 0) ||
99 (full_W % stride_x != 0))
Kevin Cheng7eb93d72021-10-09 01:26:08 +0000100 {
Jeremy Johnson4a6fb9b2022-04-26 15:47:21 +0100101 msg = "Parameters must yield exact integer output dimensions";
102 return 1;
103 }
104
105 if ((OH != (full_H / stride_y) + 1) ||
106 (OW != (full_W / stride_x) + 1))
107 {
108 msg = "Mismatch between output shape provided and expected output shape (" +
109 std::to_string((full_H / stride_y) + 1) + "," +
110 std::to_string((full_W / stride_x) + 1) + ")";
Kevin Cheng7eb93d72021-10-09 01:26:08 +0000111 return 1;
112 }
113
114 return 0;
115}
116
Eric Kunzeb5fabec2022-06-07 05:20:44 +0000117int check_conv_attribute(tosa::TosaConvAttribute* attribute,
Kevin Cheng9fe17242021-11-10 01:04:39 +0000118 uint32_t conv_dimension,
119 std::vector<int32_t> input_shape,
120 std::vector<int32_t> output_shape,
Jeremy Johnson4a6fb9b2022-04-26 15:47:21 +0100121 std::vector<int32_t> weights,
122 uint32_t offset_kernel,
Kevin Cheng9fe17242021-11-10 01:04:39 +0000123 DType InDtype,
124 DType WeightDtype,
125 std::string& msg)
126{
TatWai Chong86c403b2022-06-06 20:46:01 -0700127 if (attribute->pad().size() != (2 * conv_dimension))
Kevin Cheng9fe17242021-11-10 01:04:39 +0000128 {
TatWai Chong86c403b2022-06-06 20:46:01 -0700129 msg = "Illegal size for attribute pad";
Kevin Cheng9fe17242021-11-10 01:04:39 +0000130 return 1;
131 }
132
133 if (attribute->stride().size() != conv_dimension)
134 {
135 msg = "Illegal size for attribute stride";
136 return 1;
137 }
138
139 if (attribute->dilation().size() != conv_dimension)
140 {
141 msg = "Illegal size for attribute dilation";
142 return 1;
143 }
144
TatWai Chong86c403b2022-06-06 20:46:01 -0700145 for (int32_t i : attribute->pad())
Kevin Cheng9fe17242021-11-10 01:04:39 +0000146 {
147 if (i < 0)
148 {
149 msg = "At least one pad is smaller than zero";
150 return 1;
151 }
152 }
153
154 for (int32_t i : attribute->stride())
155 {
156 if (i < 1)
157 {
158 msg = "At least one stride dimension is smaller than one";
159 return 1;
160 }
161 }
162
163 for (int32_t i : attribute->dilation())
164 {
165 if (i < 1)
166 {
167 msg = "At least one dilation dimension is smaller than one";
168 return 1;
169 }
170 }
171
Jeremy Johnson4a6fb9b2022-04-26 15:47:21 +0100172 ASSERT_MSG(conv_dimension == 2 || conv_dimension == 3, "Unsupported convolution dimension")
173
174 int32_t offset_d = 1 ? conv_dimension == 3 : 0;
175 int32_t ID = conv_dimension == 3 ? input_shape[1] : 1;
176 int32_t IH = input_shape[1 + offset_d];
177 int32_t IW = input_shape[2 + offset_d];
178 int32_t OD = conv_dimension == 3 ? output_shape[1] : 1;
179 int32_t OH = output_shape[1 + offset_d];
180 int32_t OW = output_shape[2 + offset_d];
181
182 int32_t stride_d = conv_dimension == 3 ? attribute->stride()[0] : 1;
183 int32_t stride_y = attribute->stride()[0 + offset_d];
184 int32_t stride_x = attribute->stride()[1 + offset_d];
185 int32_t kernel_d = conv_dimension == 3 ? weights[offset_kernel] : 1;
186 int32_t kernel_h = weights[offset_kernel + offset_d];
187 int32_t kernel_w = weights[offset_kernel + 1 + offset_d];
188 int32_t dilation_d = conv_dimension == 3 ? attribute->dilation()[0] : 1;
189 int32_t dilation_y = attribute->dilation()[0 + offset_d];
190 int32_t dilation_x = attribute->dilation()[1 + offset_d];
191
192 offset_d *= 2;
TatWai Chong86c403b2022-06-06 20:46:01 -0700193 int32_t pad_d0 = conv_dimension == 3 ? attribute->pad()[0] : 0;
194 int32_t pad_d1 = conv_dimension == 3 ? attribute->pad()[1] : 0;
195 int32_t pad_top = attribute->pad()[0 + offset_d];
196 int32_t pad_bottom = attribute->pad()[1 + offset_d];
197 int32_t pad_left = attribute->pad()[2 + offset_d];
198 int32_t pad_right = attribute->pad()[3 + offset_d];
Jeremy Johnson4a6fb9b2022-04-26 15:47:21 +0100199
200 int32_t full_D = ID - 1 + pad_d0 + pad_d1 - (kernel_d - 1) * dilation_d;
201 int32_t full_H = IH - 1 + pad_top + pad_bottom - (kernel_h - 1) * dilation_y;
202 int32_t full_W = IW - 1 + pad_left + pad_right - (kernel_w - 1) * dilation_x;
203
204 if ((full_H % stride_y != 0) ||
205 (full_W % stride_x != 0) ||
206 (full_D % stride_d != 0))
207 {
208 msg = "Parameters must yield exact integer output dimensions";
209 return 1;
210 }
211
212 if ((OH != (full_H / stride_y) + 1) ||
213 (OW != (full_W / stride_x) + 1) ||
214 (OD != (full_D / stride_d) + 1))
215 {
216 std::string msg_d = "";
217 if (conv_dimension == 3)
218 {
219 msg_d += std::to_string((full_D / stride_d) + 1) + ",";
220 }
221 msg = "Mismatch between output shape provided and expected output shape (" +
222 msg_d +
223 std::to_string((full_H / stride_y) + 1) + "," +
224 std::to_string((full_W / stride_x) + 1) + ")";
225 return 1;
226 }
227
Eric Kunzeb5fabec2022-06-07 05:20:44 +0000228 if (InDtype != DType_INT8 && attribute->input_zp() != 0) {
229 msg = "Input zero point must be zero for non-int8 data";
230 return 1;
231 }
232 if (WeightDtype != DType_INT8 && attribute->weight_zp() != 0) {
233 msg = "Weight zero point must be zero for non-int8 data";
234 return 1;
Kevin Cheng9fe17242021-11-10 01:04:39 +0000235 }
236
237 return 0;
238}
239
Eric Kunzee5e26762020-10-13 16:11:07 -0700240template <int Rank, DType Dtype>
Kevin Chengacb550f2021-06-29 15:32:19 -0700241OpArgMax<Rank, Dtype>::OpArgMax(SubgraphTraverser* sgt_,
242 TosaAttributeBase* attribute_,
Kevin Chengacb550f2021-06-29 15:32:19 -0700243 uint64_t id_)
244 : GraphNode(sgt_, Op_ARGMAX, id_)
Eric Kunzee5e26762020-10-13 16:11:07 -0700245{
246 setRequiredOperands(1, 1);
Kevin Chengcc61be32021-10-14 17:09:57 -0700247 setRequiredRank(1, 4);
Eric Kunzee5e26762020-10-13 16:11:07 -0700248
249 INIT_ATTRIBUTE(Axis);
250}
251
252template <int Rank, DType Dtype>
253OpArgMax<Rank, Dtype>::~OpArgMax()
254{
255 if (attribute)
256 delete attribute;
257}
258
259template <int Rank, DType Dtype>
260int OpArgMax<Rank, Dtype>::checkTensorAttributes()
261{
262 if (validateRequiredOperands())
263 return 1;
264
Kevin Chengcc61be32021-10-14 17:09:57 -0700265 if (validateRequiredRank(inputs[0]))
Eric Kunzee5e26762020-10-13 16:11:07 -0700266 {
267 return 1;
268 }
269
Kevin Chengcc61be32021-10-14 17:09:57 -0700270 int32_t output_rank = inputs[0]->getRank() - 1;
271 if (output_rank != outputs[0]->getRank())
272 {
273 printNodeValidationError("OpArgMax: Output rank needs to be rank(input) - 1");
274 return 1;
275 }
276
277 if (outputs[0]->getDtype() != DType_INT32)
278 {
279 printNodeValidationError("OpArgMax: Output data type not supported for this configuration of operator");
280 return 1;
281 }
282
Eric Kunzee5e26762020-10-13 16:11:07 -0700283 input = dynamic_cast<TosaReference::TensorTemplate<TIn>*>(inputs[0]);
284 output = dynamic_cast<TosaReference::TensorTemplate<TOut>*>(outputs[0]);
285
Kevin Chengcc61be32021-10-14 17:09:57 -0700286 if (attribute->axis() < 0 || attribute->axis() >= input->getRank())
287 {
288 printNodeValidationError("OpArgMax: Axis needs to be within [0, rank(input)]");
289 return 1;
290 }
291
292 bool shape_check = true;
293 for (int32_t i = 0; i < input->getRank(); i++)
294 {
295 if (i < attribute->axis())
296 {
297 if (input->getShape()[i] != output->getShape()[i])
298 {
299 shape_check = false;
300 break;
301 }
302 }
303 else if (i > attribute->axis())
304 {
305 if (input->getShape()[i] != output->getShape()[i - 1])
306 {
307 shape_check = false;
308 break;
309 }
310 }
311 // No need to check i == axis
312 }
313 if (!shape_check)
314 {
315 printNodeValidationError("OpArgMax: Mismatch between output shape provided and expected output shape");
316 return 1;
317 }
318
Eric Kunzee5e26762020-10-13 16:11:07 -0700319 return 0;
320}
321
322template <int Rank, DType Dtype>
323int OpArgMax<Rank, Dtype>::eval()
324{
325 Eigen::Tensor<DenseIndex, Rank - 1> index = this->input->getTensor().argmax(attribute->axis());
326
327 this->output->getTensor() = index.unaryExpr([](DenseIndex in) -> OutEigenType { return (OutEigenType)in; });
328
329 return GraphNode::eval();
330}
331
332template <DType Dtype>
Kevin Chengacb550f2021-06-29 15:32:19 -0700333OpAvgPool2d<Dtype>::OpAvgPool2d(SubgraphTraverser* sgt_,
334 TosaAttributeBase* attribute_,
Kevin Chengacb550f2021-06-29 15:32:19 -0700335 uint64_t id_)
336 : GraphNode(sgt_, Op_AVG_POOL2D, id_)
Eric Kunzee5e26762020-10-13 16:11:07 -0700337{
338 setRequiredOperands(1, 1);
339 setRequiredRank(4);
340
Kevin Cheng93a16282021-08-31 16:14:03 -0700341 INIT_ATTRIBUTE(Pool);
Eric Kunzee5e26762020-10-13 16:11:07 -0700342}
343
344template <DType Dtype>
345OpAvgPool2d<Dtype>::~OpAvgPool2d()
346{
347 if (attribute)
348 delete attribute;
349}
350
351template <DType Dtype>
352int OpAvgPool2d<Dtype>::checkTensorAttributes()
353{
354 if (validateRequiredOperands())
355 return 1;
356
357 if (validateRequiredRank(inputs[0]) || validateRequiredRank(outputs[0]))
358 {
359 return 1;
360 }
361
362 if (inputs[0]->matchType(*outputs[0]))
363 {
364 printNodeValidationError("OpAvgPool2d: input and output tensor type mismatch");
365 return 1;
366 }
367
368 in = dynamic_cast<TosaReference::TensorTemplate<TIn>*>(inputs[0]);
369 out = dynamic_cast<TosaReference::TensorTemplate<TOut>*>(outputs[0]);
370
Eric Kunzeb5fabec2022-06-07 05:20:44 +0000371 ERROR_IF(Dtype != DType_INT8 && attribute->input_zp() != 0, "OpAvgPool2d: Input zeropoint must be zero for non int8_t data");
372 ERROR_IF(Dtype != DType_INT8 && attribute->output_zp() != 0, "OpAvgPool2d: Output zeropoint must be zero for non int8_t data");
Eric Kunzee5e26762020-10-13 16:11:07 -0700373
Kevin Cheng7eb93d72021-10-09 01:26:08 +0000374 std::string msg;
Kevin Cheng9fe17242021-11-10 01:04:39 +0000375 if (check_pool2d_attribute(attribute, in->getShape(), out->getShape(), msg))
Eric Kunzee5e26762020-10-13 16:11:07 -0700376 {
Kevin Cheng7eb93d72021-10-09 01:26:08 +0000377 msg = "OpAvgPool2d: " + msg;
378 printNodeValidationError(msg.c_str());
Eric Kunzee5e26762020-10-13 16:11:07 -0700379 return 1;
380 }
381
382 return 0;
383}
384
Eric Kunze830add42022-01-25 22:56:46 -0800385// This calculates the number of padding elements used for each location along an axis
386// Average pooling only divides by the number of elements used, not including padding.
387// This function uses left/right, but is also used for vertical padding with top/bottom
Eric Kunzee5e26762020-10-13 16:11:07 -0700388template <DType Dtype>
Eric Kunze830add42022-01-25 22:56:46 -0800389ETensor1<int32_t> OpAvgPool2d<Dtype>::calculate_div_map_1d(int in_size, int out_size, int kernel_size, int stride, int32_t pad_left, int32_t pad_right)
Eric Kunzee5e26762020-10-13 16:11:07 -0700390{
391 ETensor1<int32_t> result(out_size);
392
Eric Kunzee5e26762020-10-13 16:11:07 -0700393 result.setConstant(kernel_size);
394
Eric Kunze830add42022-01-25 22:56:46 -0800395 // adjust divisors on the left side for padding
396 // We start at the leftmost output element, and remove pad_left - (index * stride) elements
397 // until we have no more padding being used
Eric Kunze67a91552022-02-02 11:27:21 -0800398 for(int index = 0; (index <= pad_left / stride) && (index < out_size); index++) {
Eric Kunze830add42022-01-25 22:56:46 -0800399 int32_t adjust = pad_left - (index * stride);
400 result(index) -= adjust;
Eric Kunzee5e26762020-10-13 16:11:07 -0700401 }
402
Eric Kunze830add42022-01-25 22:56:46 -0800403 // The process repeats on the right side. Padding starts taking effect as we
404 // near the rightmost input element. The first output element which touches
405 // padding is defined in the initialization of index below. Then we keep moving
406 // to the right, increasing padding until we get to the last output element.
407 int index = std::max(0, ((pad_left + in_size - kernel_size) / stride) + 1);
408 for (; index < out_size; index++) {
409 int32_t adjust = ((index * stride) + kernel_size) - (pad_left + in_size);
410 result(index) -= adjust;
Eric Kunzee5e26762020-10-13 16:11:07 -0700411 }
Eric Kunzee5e26762020-10-13 16:11:07 -0700412 return result;
413}
414
415// assuming input and output tensor have same scales like tflite reference
416// so no need to scale input and output
417template <DType Dtype>
418int OpAvgPool2d<Dtype>::eval()
419{
420 int in_batch = this->in->getShape()[0];
421 int in_height = this->in->getShape()[1];
422 int in_width = this->in->getShape()[2];
423 int in_channels = this->in->getShape()[3];
424
425 int out_batch = this->out->getShape()[0];
426 int out_height = this->out->getShape()[1];
427 int out_width = this->out->getShape()[2];
428 int out_channels = this->out->getShape()[3];
429
Kevin Chengacb550f2021-06-29 15:32:19 -0700430 ERROR_IF(in_batch != out_batch, "OpAvgPool2d: tensor batch mismatch %d != %d", in_batch, out_batch);
431 ERROR_IF(in_channels != out_channels, "OpAvgPool2d: tensor channel mismatch %d != %d", in_channels, out_channels);
Eric Kunzee5e26762020-10-13 16:11:07 -0700432
TatWai Chong86c403b2022-06-06 20:46:01 -0700433 int pad_top = this->attribute->pad()[0];
434 int pad_bottom = this->attribute->pad()[1];
435 int pad_left = this->attribute->pad()[2];
436 int pad_right = this->attribute->pad()[3];
Eric Kunzee5e26762020-10-13 16:11:07 -0700437 int kernel_h = this->attribute->kernel()[0];
438 int kernel_w = this->attribute->kernel()[1];
439 int stride_h = this->attribute->stride()[0];
440 int stride_w = this->attribute->stride()[1];
441
442 DEBUG_INFO(OP,
443 "perform AvgPool2d, input.shape=[%d,%d,%d,%d], output.shape=[%d,%d,%d,%d], kernel=[%d,%d], "
TatWai Chong86c403b2022-06-06 20:46:01 -0700444 "stride=[%d,%d], pad=[%d,%d,%d,%d]",
Eric Kunzee5e26762020-10-13 16:11:07 -0700445 in_batch, in_height, in_width, in_channels, out_batch, out_height, out_width, out_channels, kernel_h,
TatWai Chong86c403b2022-06-06 20:46:01 -0700446 kernel_w, stride_h, stride_w, pad_top, pad_bottom, pad_left, pad_right);
Eric Kunzee5e26762020-10-13 16:11:07 -0700447
448 Eigen::array<Eigen::Index, 2> im2col_input_dims;
449 im2col_input_dims[0] = kernel_h * kernel_w;
450 im2col_input_dims[1] = out_batch * out_height * out_width * out_channels;
451
452 Eigen::array<Eigen::Index, 4> col2im_output_dims;
453 col2im_output_dims[0] = out_batch;
454 col2im_output_dims[1] = out_height;
455 col2im_output_dims[2] = out_width;
456 col2im_output_dims[3] = out_channels;
457
TatWai Chong86c403b2022-06-06 20:46:01 -0700458 Eigen::array<std::pair<int32_t, int32_t>, 4> pad;
459 pad[0] = std::make_pair(0, 0);
460 pad[1] = std::make_pair(pad_top, pad_bottom);
461 pad[2] = std::make_pair(pad_left, pad_right);
462 pad[3] = std::make_pair(0, 0);
Eric Kunzee5e26762020-10-13 16:11:07 -0700463
464 ETensor4<InEigenType> input_val = this->in->getTensor();
Eric Kunzeb5fabec2022-06-07 05:20:44 +0000465 if (Dtype == DType_INT8)
Eric Kunzee5e26762020-10-13 16:11:07 -0700466 {
Eric Kunzeb5fabec2022-06-07 05:20:44 +0000467 input_val = input_val - (InEigenType)attribute->input_zp();
Eric Kunzee5e26762020-10-13 16:11:07 -0700468 }
469
TatWai Chong86c403b2022-06-06 20:46:01 -0700470 ETensor4<InEigenType> input_padded = input_val.pad(pad);
Eric Kunzee5e26762020-10-13 16:11:07 -0700471
472 // assuming input and output have same scales
473 // so input and output scaling is not required
474 // TODO: check if this assumption TOSA made
475
476 // extract_image_patches() output [N, KH, KW, H * W, C]
477 // transpose to [KH, KW, N, H * W, C]
478 // reshape to [KH * KW, N * H * W * C]
479 ETensor2<InEigenType> input_extract_patches =
480 input_padded.extract_image_patches(kernel_h, kernel_w, stride_h, stride_w, 1, 1, Eigen::PADDING_VALID)
481 .shuffle(Eigen::array<Eigen::Index, 5>{ 1, 2, 0, 3, 4 })
482 .reshape(im2col_input_dims);
483
484 // 1D result with [N * H * W * C]
485 ETensor1<AccEigenType> out_1d(this->out->getElementCount());
486 out_1d.setZero();
487
488 // sum pool
489 for (size_t i = 0; i < this->out->getElementCount(); i++)
490 {
491 for (int32_t j = 0; j < kernel_h * kernel_w; j++)
492 {
493 out_1d(i) += (AccEigenType)input_extract_patches(j, i);
494 }
495 }
496
497 // reshape result to [N, H, W, C] and divide with div_map
498 ETensor4<AccEigenType> sum = out_1d.reshape(col2im_output_dims);
499
500 // calculate 1d height/width div_map (number of elements this pooling window covers)
501 // and outer product to get 2d div_map, then reshape/broadcast to [N, H, W, C]
TatWai Chong86c403b2022-06-06 20:46:01 -0700502 ETensor1<int32_t> div_map_h = calculate_div_map_1d(in_height, out_height, kernel_h, stride_h, pad_top, pad_bottom);
503 ETensor1<int32_t> div_map_w = calculate_div_map_1d(in_width, out_width, kernel_w, stride_w, pad_left, pad_right);
Eric Kunzee5e26762020-10-13 16:11:07 -0700504 Eigen::array<Eigen::IndexPair<Eigen::Index>, 1> contract_dims = { Eigen::IndexPair<Eigen::Index>(1, 0) };
505 Eigen::array<Eigen::Index, 4> bcast{ out_batch, 1, 1, out_channels };
506
507 ETensor4<int32_t> div_map =
508 div_map_h.reshape(Eigen::array<Eigen::Index, 2>{ out_height, 1 })
509 .contract(div_map_w.reshape(Eigen::array<Eigen::Index, 2>{ 1, out_width }), contract_dims)
510 .reshape(Eigen::array<Eigen::Index, 4>{ 1, out_height, out_width, 1 })
511 .broadcast(bcast);
512
513 if (Dtype != DType_FLOAT)
514 {
Kevin Chengacb550f2021-06-29 15:32:19 -0700515 try
516 {
517 this->out->getTensor() = sum.binaryExpr(div_map, [](AccEigenType value, int32_t div) -> OutEigenType {
518 int32_t multiplier, shift;
519 TosaReference::QuantUtil::reciprocal_scale(div, multiplier, shift);
Eric Kunzee5e26762020-10-13 16:11:07 -0700520
Kevin Chengacb550f2021-06-29 15:32:19 -0700521 return (OutEigenType)TosaReference::QuantUtil::apply_scale_32(value, multiplier, shift, false);
522 });
523 }
524 catch (std::string desc)
525 {
526 REQUIRE(false, "OpAvgPool2d apply_scale_32() fails: %s.", desc.c_str());
527 }
Eric Kunzeb5fabec2022-06-07 05:20:44 +0000528 this->out->getTensor() = this->out->getTensor() + (OutEigenType)(attribute->output_zp());
Eric Kunzee5e26762020-10-13 16:11:07 -0700529 this->out->getTensor() = this->out->getTensor().cwiseMax((OutEigenType)QMin);
530 this->out->getTensor() = this->out->getTensor().cwiseMin((OutEigenType)QMax);
531 }
532 else
533 {
534 this->out->getTensor() = (sum / div_map.template cast<AccEigenType>()).template cast<OutEigenType>();
535 }
536
537 return GraphNode::eval();
538}
539
540template <DType InDtype, DType WeightDtype>
Kevin Chengacb550f2021-06-29 15:32:19 -0700541OpConv2d<InDtype, WeightDtype>::OpConv2d(SubgraphTraverser* sgt_,
542 TosaAttributeBase* attribute_,
Kevin Chengacb550f2021-06-29 15:32:19 -0700543 uint64_t id_)
544 : GraphNode(sgt_, Op_CONV2D, id_)
Eric Kunzee5e26762020-10-13 16:11:07 -0700545{
546 setRequiredOperands(3, 1);
547 setRequiredRank(4);
548
Kevin Cheng93a16282021-08-31 16:14:03 -0700549 INIT_ATTRIBUTE(Conv);
Eric Kunzee5e26762020-10-13 16:11:07 -0700550}
551
552template <DType InDtype, DType WeightDtype>
553OpConv2d<InDtype, WeightDtype>::~OpConv2d()
554{
555 if (attribute)
556 delete attribute;
Eric Kunzee5e26762020-10-13 16:11:07 -0700557}
558
559template <DType InDtype, DType WeightDtype>
560int OpConv2d<InDtype, WeightDtype>::checkTensorAttributes()
561{
562 if (validateRequiredOperands())
563 return 1;
564
565 if (validateRequiredRank(inputs[0]) || validateRequiredRank(inputs[1]) || validateRequiredRank(outputs[0]))
566 {
567 return 1;
568 }
569
570 // 'bias' checked separatedly since it doens't make sense to make required rank ranging from 1 to 4
571 if (inputs[2]->getRank() != 1)
572 {
573 printNodeValidationError("OpConv2d: bias tensor must be rank 1");
574 }
575
Kevin Chengcc61be32021-10-14 17:09:57 -0700576 ERROR_IF(outputs[0]->getDtype() != AccDtype,
Kevin Cheng80794802021-11-01 11:14:13 -0700577 "OpConv2d: Output data type not supported for this configuration of operator");
Kevin Chengcc61be32021-10-14 17:09:57 -0700578
Eric Kunzee5e26762020-10-13 16:11:07 -0700579 input = dynamic_cast<TosaReference::TensorTemplate<TIn>*>(inputs[0]);
580 weight = dynamic_cast<TosaReference::TensorTemplate<TWeight>*>(inputs[1]);
581 bias = dynamic_cast<TosaReference::TensorTemplate<TBias>*>(inputs[2]);
582 output = dynamic_cast<TosaReference::TensorTemplate<TAcc>*>(outputs[0]);
583
Kevin Cheng9fe17242021-11-10 01:04:39 +0000584 std::string msg;
Eric Kunzeb5fabec2022-06-07 05:20:44 +0000585 if (check_conv_attribute(attribute, 2 /* conv_dimension */, input->getShape(), output->getShape(),
Jeremy Johnson4a6fb9b2022-04-26 15:47:21 +0100586 weight->getShape(), 1 /* offset_kernel */, InDtype, WeightDtype, msg))
Eric Kunzee5e26762020-10-13 16:11:07 -0700587 {
Kevin Cheng9fe17242021-11-10 01:04:39 +0000588 msg = "OpConv2d: " + msg;
589 printNodeValidationError(msg.c_str());
Eric Kunzee5e26762020-10-13 16:11:07 -0700590 return 1;
591 }
592
Eric Kunzee5e26762020-10-13 16:11:07 -0700593 return 0;
594}
595
596template <DType InDtype, DType WeightDtype>
597int OpConv2d<InDtype, WeightDtype>::eval()
598{
599 int in_batch = this->input->getShape()[0];
600 int in_height = this->input->getShape()[1];
601 int in_width = this->input->getShape()[2];
602 int in_channels = this->input->getShape()[3];
603
604 int f_out_channels = this->weight->getShape()[0];
605 int f_height = this->weight->getShape()[1];
606 int f_width = this->weight->getShape()[2];
607 int f_in_channels = this->weight->getShape()[3];
608
609 int b_out_channels = this->bias->getShape()[0];
610
611 int out_batch = this->output->getShape()[0];
612 int out_height = this->output->getShape()[1];
613 int out_width = this->output->getShape()[2];
614 int out_channels = this->output->getShape()[3];
615
Kevin Chengacb550f2021-06-29 15:32:19 -0700616 ERROR_IF(in_batch != out_batch, "OpConv2d: tensor batch mismatch %d != %d", in_batch, out_batch);
617 ERROR_IF(f_in_channels != in_channels, "OpConv2d: tensor input channel mismatch %d != %d", f_in_channels,
618 in_channels);
619 ERROR_IF(f_out_channels != out_channels, "OpConv2d: tensor output channel mismatch %d != %d", f_out_channels,
620 out_channels);
621 ERROR_IF(b_out_channels != out_channels, "OpConv2d: bias channel mismatch %d != %d", b_out_channels, out_channels);
Eric Kunzee5e26762020-10-13 16:11:07 -0700622
TatWai Chong86c403b2022-06-06 20:46:01 -0700623 int pad_top = this->attribute->pad()[0];
624 int pad_bottom = this->attribute->pad()[1];
625 int pad_left = this->attribute->pad()[2];
626 int pad_right = this->attribute->pad()[3];
627
Eric Kunzee5e26762020-10-13 16:11:07 -0700628 int stride_h = this->attribute->stride()[0];
629 int stride_w = this->attribute->stride()[1];
630 int dilation_h = this->attribute->dilation()[0];
631 int dilation_w = this->attribute->dilation()[1];
632
633 DEBUG_INFO(OP,
634 "perform OpConv2d, input.shape=[%d,%d,%d,%d], weight.shape=[%d,%d,%d,%d], output.shape=[%d,%d,%d,%d], "
TatWai Chong86c403b2022-06-06 20:46:01 -0700635 "stride=[%d,%d], dilation=[%d,%d], pad=[%d,%d,%d,%d]",
Eric Kunzee5e26762020-10-13 16:11:07 -0700636 in_batch, in_height, in_width, in_channels, f_height, f_width, f_in_channels, f_out_channels, out_batch,
TatWai Chong86c403b2022-06-06 20:46:01 -0700637 out_height, out_width, out_channels, stride_h, stride_w, dilation_h, dilation_w, pad_top,
638 pad_bottom, pad_left, pad_right);
Eric Kunzee5e26762020-10-13 16:11:07 -0700639
640 // GEMM-conv2d, left matrix is input, right matrix is weight
641 Eigen::array<Eigen::Index, 2> im2col_input_dims;
642 im2col_input_dims[0] = out_batch * out_height * out_width;
643 im2col_input_dims[1] = f_height * f_width * f_in_channels;
644
645 Eigen::array<Eigen::Index, 2> im2col_weight_dims;
646 im2col_weight_dims[0] = f_height * f_width * f_in_channels;
647 im2col_weight_dims[1] = f_out_channels;
648
649 Eigen::array<Eigen::Index, 2> bias_reshaped_dims;
650 bias_reshaped_dims[0] = 1;
651 bias_reshaped_dims[1] = b_out_channels;
652
653 Eigen::array<Eigen::Index, 4> weight_zp_bcast_dims;
654 weight_zp_bcast_dims[0] = f_height;
655 weight_zp_bcast_dims[1] = f_width;
656 weight_zp_bcast_dims[2] = f_in_channels;
657
658 Eigen::array<Eigen::Index, 2> bias_bcast_dims;
659 bias_bcast_dims[0] = out_batch * out_height * out_width;
660 bias_bcast_dims[1] = 1;
661
662 Eigen::array<Eigen::Index, 4> col2im_output_dims;
663 col2im_output_dims[0] = out_batch;
664 col2im_output_dims[1] = out_height;
665 col2im_output_dims[2] = out_width;
666 col2im_output_dims[3] = out_channels;
667
668 Eigen::array<Eigen::IndexPair<Eigen::Index>, 1> contract_dims = { Eigen::IndexPair<Eigen::Index>(1, 0) };
669
TatWai Chong86c403b2022-06-06 20:46:01 -0700670 Eigen::array<std::pair<int32_t, int32_t>, 4> pad;
671 pad[0] = std::make_pair(0, 0);
672 pad[1] = std::make_pair(pad_top, pad_bottom);
673 pad[2] = std::make_pair(pad_left, pad_right);
674 pad[3] = std::make_pair(0, 0);
Eric Kunzee5e26762020-10-13 16:11:07 -0700675
676 TIn input_val = this->input->getTensor();
677 TWeight weight_val = this->weight->getTensor();
Eric Kunzef7337832022-06-17 08:19:12 -0700678 if (InDtype == DType_INT8 || WeightDtype == DType_INT8)
Eric Kunzee5e26762020-10-13 16:11:07 -0700679 {
Eric Kunzeb5fabec2022-06-07 05:20:44 +0000680 input_val = input_val - (InEigenType)attribute->input_zp();
681 weight_val = weight_val - (WeightEigenType)attribute->weight_zp();
Eric Kunzee5e26762020-10-13 16:11:07 -0700682 }
683
TatWai Chong86c403b2022-06-06 20:46:01 -0700684 ETensor4<InEigenType> input_padded = input_val.pad(pad);
Eric Kunzee5e26762020-10-13 16:11:07 -0700685
686 // extract_image_patches() output [N, KH, KW, H * W, C]
687 // need to transpose to [N, H * W, KH, KW, C]
688 ETensor5<InEigenType> input_extract_patches =
689 input_padded
690 .extract_image_patches(f_height, f_width, stride_h, stride_w, dilation_h, dilation_w, Eigen::PADDING_VALID)
691 .shuffle(Eigen::array<Eigen::Index, 5>{ 0, 3, 1, 2, 4 });
692
693 // reshape input to [N * H * W, KH * KW * C]
694 ETensor2<InEigenType> im2col_input = input_extract_patches.reshape(im2col_input_dims);
695
696 // transpose and reshape weight from [OC, H, W, IC] to [H * W * IC, OC]
697 ETensor2<WeightEigenType> im2col_weight =
698 weight_val.shuffle(Eigen::array<Eigen::Index, 4>({ 1, 2, 3, 0 })).reshape(im2col_weight_dims);
699
700 // don't need to apply bias_multiplier ( * bias_scale and >> bias_shift) since tflite already scale it
701 // and reshaped from [C] to [1, C], and broadcast to [N * H * W, C]
702 ETensor2<AccEigenType> bias_2d = this->bias->getTensor().reshape(bias_reshaped_dims).broadcast(bias_bcast_dims);
703
704 // output matrix is [N * H * W, C]
705 ETensor2<AccEigenType> contracted_result =
706 im2col_input.template cast<AccEigenType>().contract(im2col_weight.template cast<AccEigenType>(), contract_dims);
707
708 // adding bias
709 ETensor2<AccEigenType> biased_output = contracted_result + bias_2d.template cast<AccEigenType>();
710
711 // reshape back to [N, H, W, C]
712 this->output->getTensor() = biased_output.reshape(col2im_output_dims);
713
714 if (AccDtype == DType_INT48)
715 {
716 this->output->getTensor() = this->output->getTensor().cwiseMax((AccEigenType)AccQMin);
717 this->output->getTensor() = this->output->getTensor().cwiseMin((AccEigenType)AccQMax);
718 }
719
720 return GraphNode::eval();
721}
722
723template <DType InDtype, DType WeightDtype>
Kevin Cheng1533b852021-09-01 12:51:58 -0700724OpConv3d<InDtype, WeightDtype>::OpConv3d(SubgraphTraverser* sgt_,
725 TosaAttributeBase* attribute_,
Kevin Cheng1533b852021-09-01 12:51:58 -0700726 uint64_t id_)
727 : GraphNode(sgt_, Op_CONV3D, id_)
728{
729 setRequiredOperands(3, 1);
730 setRequiredRank(5);
731
732 INIT_ATTRIBUTE(Conv);
Kevin Cheng1533b852021-09-01 12:51:58 -0700733}
734
735template <DType InDtype, DType WeightDtype>
736OpConv3d<InDtype, WeightDtype>::~OpConv3d()
737{
738 if (attribute)
739 delete attribute;
Kevin Cheng1533b852021-09-01 12:51:58 -0700740}
741
742template <DType InDtype, DType WeightDtype>
743int OpConv3d<InDtype, WeightDtype>::checkTensorAttributes()
744{
745 if (validateRequiredOperands())
746 return 1;
747
748 if (validateRequiredRank(inputs[0]) || validateRequiredRank(inputs[1]) || validateRequiredRank(outputs[0]))
749 {
750 return 1;
751 }
752
753 // 'bias' checked separatedly since it doens't make sense to make required rank ranging from 1 to 4
754 if (inputs[2]->getRank() != 1)
755 {
756 printNodeValidationError("OpConv3d: bias tensor must be rank 1");
757 }
758
Kevin Chengcc61be32021-10-14 17:09:57 -0700759 ERROR_IF(outputs[0]->getDtype() != AccDtype,
Kevin Cheng80794802021-11-01 11:14:13 -0700760 "OpConv3d: Output data type not supported for this configuration of operator");
Kevin Chengcc61be32021-10-14 17:09:57 -0700761
Kevin Cheng1533b852021-09-01 12:51:58 -0700762 input = dynamic_cast<TosaReference::TensorTemplate<TIn>*>(inputs[0]);
763 weight = dynamic_cast<TosaReference::TensorTemplate<TWeight>*>(inputs[1]);
764 bias = dynamic_cast<TosaReference::TensorTemplate<TBias>*>(inputs[2]);
765 output = dynamic_cast<TosaReference::TensorTemplate<TAcc>*>(outputs[0]);
766
Kevin Cheng9fe17242021-11-10 01:04:39 +0000767 std::string msg;
Eric Kunzeb5fabec2022-06-07 05:20:44 +0000768 if (check_conv_attribute(attribute, 3 /* conv_dimension */, input->getShape(), output->getShape(),
Jeremy Johnson4a6fb9b2022-04-26 15:47:21 +0100769 weight->getShape(), 1 /* offset_kernel */, InDtype, WeightDtype, msg))
Kevin Cheng1533b852021-09-01 12:51:58 -0700770 {
Kevin Cheng9fe17242021-11-10 01:04:39 +0000771 msg = "OpConv3d: " + msg;
772 printNodeValidationError(msg.c_str());
Kevin Cheng1533b852021-09-01 12:51:58 -0700773 return 1;
774 }
775
Kevin Cheng1533b852021-09-01 12:51:58 -0700776 return 0;
777}
778
779template <DType InDtype, DType WeightDtype>
780int OpConv3d<InDtype, WeightDtype>::eval()
781{
782 int in_batch = this->input->getShape()[0];
783 int in_depth = this->input->getShape()[1];
784 int in_height = this->input->getShape()[2];
785 int in_width = this->input->getShape()[3];
786 int in_channels = this->input->getShape()[4];
787
788 int f_out_channels = this->weight->getShape()[0];
789 int f_depth = this->weight->getShape()[1];
790 int f_height = this->weight->getShape()[2];
791 int f_width = this->weight->getShape()[3];
792 int f_in_channels = this->weight->getShape()[4];
793
794 int b_out_channels = this->bias->getShape()[0];
795
796 int out_batch = this->output->getShape()[0];
797 int out_depth = this->output->getShape()[1];
798 int out_height = this->output->getShape()[2];
799 int out_width = this->output->getShape()[3];
800 int out_channels = this->output->getShape()[4];
801
802 ERROR_IF(in_batch != out_batch, "OpConv3d: tensor batch mismatch %d != %d", in_batch, out_batch);
803 ERROR_IF(f_in_channels != in_channels, "OpConv3d: tensor input channel mismatch %d != %d", f_in_channels,
804 in_channels);
805 ERROR_IF(f_out_channels != out_channels, "OpConv3d: tensor output channel mismatch %d != %d", f_out_channels,
806 out_channels);
807 ERROR_IF(b_out_channels != out_channels, "OpConv3d: bias channel mismatch %d != %d", b_out_channels, out_channels);
808
TatWai Chong86c403b2022-06-06 20:46:01 -0700809 int pad_d0 = this->attribute->pad()[0];
810 int pad_d1 = this->attribute->pad()[1];
811 int pad_top = this->attribute->pad()[2];
812 int pad_bottom = this->attribute->pad()[3];
813 int pad_left = this->attribute->pad()[4];
814 int pad_right = this->attribute->pad()[5];
815
Kevin Cheng1533b852021-09-01 12:51:58 -0700816 int stride_d = this->attribute->stride()[0];
817 int stride_h = this->attribute->stride()[1];
818 int stride_w = this->attribute->stride()[2];
TatWai Chong86c403b2022-06-06 20:46:01 -0700819
Kevin Cheng1533b852021-09-01 12:51:58 -0700820 int dilation_d = this->attribute->dilation()[0];
821 int dilation_h = this->attribute->dilation()[1];
822 int dilation_w = this->attribute->dilation()[2];
823
824 DEBUG_INFO(
825 OP,
826 "perform OpConv3d, input.shape=[%d,%d,%d,%d,%d], weight.shape=[%d,%d,%d,%d,%d], output.shape=[%d,%d,%d,%d,%d], "
TatWai Chong86c403b2022-06-06 20:46:01 -0700827 "stride=[%d,%d,%d], dilation=[%d,%d,%d], pad=[%d,%d,%d,%d,%d,%d]",
Kevin Cheng1533b852021-09-01 12:51:58 -0700828 in_batch, in_depth, in_height, in_width, in_channels, f_out_channels, f_depth, f_height, f_width, f_in_channels,
829 out_batch, out_depth, out_height, out_width, out_channels, stride_d, stride_h, stride_w, dilation_d, dilation_h,
TatWai Chong86c403b2022-06-06 20:46:01 -0700830 dilation_w, pad_d0, pad_d1, pad_top, pad_bottom, pad_left, pad_right);
Kevin Cheng1533b852021-09-01 12:51:58 -0700831
TatWai Chong86c403b2022-06-06 20:46:01 -0700832 Eigen::array<std::pair<int32_t, int32_t>, 5> pad;
833 pad[0] = std::make_pair(0, 0);
834 pad[1] = std::make_pair(pad_d0, pad_d1);
835 pad[2] = std::make_pair(pad_top, pad_bottom);
836 pad[3] = std::make_pair(pad_left, pad_right);
837 pad[4] = std::make_pair(0, 0);
Kevin Cheng1533b852021-09-01 12:51:58 -0700838
839 TIn input_val = this->input->getTensor();
840 TWeight weight_val = this->weight->getTensor();
Eric Kunzef7337832022-06-17 08:19:12 -0700841 if (InDtype == DType_INT8 || WeightDtype == DType_INT8)
Kevin Cheng1533b852021-09-01 12:51:58 -0700842 {
Eric Kunzeb5fabec2022-06-07 05:20:44 +0000843 input_val = input_val - (InEigenType)attribute->input_zp();
844 weight_val = weight_val - (WeightEigenType)attribute->weight_zp();
Kevin Cheng1533b852021-09-01 12:51:58 -0700845 }
846
TatWai Chong86c403b2022-06-06 20:46:01 -0700847 ETensor5<InEigenType> input_padded = input_val.pad(pad);
Kevin Cheng1533b852021-09-01 12:51:58 -0700848
849 // 1. initialize with bias
850 Eigen::array<Eigen::Index, 5> reshape_dim;
851 reshape_dim.fill(1);
852 reshape_dim[4] = b_out_channels;
853
854 Eigen::array<Eigen::Index, 5> bcast;
855 bcast[0] = out_batch;
856 bcast[1] = out_depth;
857 bcast[2] = out_height;
858 bcast[3] = out_width;
859 bcast[4] = 1;
860 this->output->getTensor() = this->bias->getTensor().reshape(reshape_dim).broadcast(bcast);
861
862 // 2. direct convolution
863 AccEigenType acc = 0;
864 int d_idx, h_idx, w_idx;
865
866 for (int ob = 0; ob < out_batch; ob++)
867 {
868 for (int od = 0; od < out_depth; od++)
869 {
870 for (int oh = 0; oh < out_height; oh++)
871 {
872 for (int ow = 0; ow < out_width; ow++)
873 {
874 for (int oc = 0; oc < out_channels; oc++)
875 {
Eric Kunze7edb34c2022-05-16 17:34:40 -0700876 // Initialize accumulator with bias value
877 acc = this->output->getTensor()(ob, od, oh, ow, oc);
Kevin Cheng1533b852021-09-01 12:51:58 -0700878 for (int fd = 0; fd < f_depth; fd++)
879 {
880 d_idx = od * stride_d + fd * dilation_d;
881 for (int fh = 0; fh < f_height; fh++)
882 {
883 h_idx = oh * stride_h + fh * dilation_h;
884 for (int fw = 0; fw < f_width; fw++)
885 {
886 w_idx = ow * stride_w + fw * dilation_w;
887 for (int ic = 0; ic < in_channels; ic++)
888 {
889 acc += ((AccEigenType)input_padded(ob, d_idx, h_idx, w_idx, ic) *
890 (AccEigenType)weight_val(oc, fd, fh, fw, ic));
891 }
892 }
893 }
894 }
895 this->output->getTensor()(ob, od, oh, ow, oc) = acc;
896 }
897 }
898 }
899 }
900 }
901
902 if (AccDtype == DType_INT48)
903 {
904 this->output->getTensor() = this->output->getTensor().cwiseMax((AccEigenType)AccQMin);
905 this->output->getTensor() = this->output->getTensor().cwiseMin((AccEigenType)AccQMax);
906 }
907
908 return GraphNode::eval();
909}
910
911template <DType InDtype, DType WeightDtype>
Kevin Chengacb550f2021-06-29 15:32:19 -0700912OpDepthwiseConv2d<InDtype, WeightDtype>::OpDepthwiseConv2d(SubgraphTraverser* sgt_,
913 TosaAttributeBase* attribute_,
Eric Kunzee5e26762020-10-13 16:11:07 -0700914 uint64_t id_)
Kevin Chengacb550f2021-06-29 15:32:19 -0700915 : GraphNode(sgt_, Op_DEPTHWISE_CONV2D, id_)
Eric Kunzee5e26762020-10-13 16:11:07 -0700916{
917 setRequiredOperands(3, 1);
918 setRequiredRank(4);
919
Kevin Cheng93a16282021-08-31 16:14:03 -0700920 INIT_ATTRIBUTE(Conv);
Eric Kunzee5e26762020-10-13 16:11:07 -0700921}
922
923template <DType InDtype, DType WeightDtype>
924OpDepthwiseConv2d<InDtype, WeightDtype>::~OpDepthwiseConv2d()
925{
926 if (attribute)
927 delete attribute;
Eric Kunzee5e26762020-10-13 16:11:07 -0700928}
929
930template <DType InDtype, DType WeightDtype>
931int OpDepthwiseConv2d<InDtype, WeightDtype>::checkTensorAttributes()
932{
933 if (validateRequiredOperands())
934 return 1;
935
936 if (validateRequiredRank(inputs[0]) || validateRequiredRank(inputs[1]) || validateRequiredRank(outputs[0]))
937 {
938 return 1;
939 }
940
941 // 'bias' checked separatedly since it doens't make sense to make required rank ranging from 1 to 4
942 if (inputs[2]->getRank() != 1)
943 {
944 printNodeValidationError("OpDepthwiseConv2d: bias tensor must be rank 1");
945 }
946
Kevin Chengcc61be32021-10-14 17:09:57 -0700947 ERROR_IF(outputs[0]->getDtype() != AccDtype,
Kevin Cheng80794802021-11-01 11:14:13 -0700948 "OpDepthwiseConv2d: Output data type not supported for this configuration of operator");
Kevin Chengcc61be32021-10-14 17:09:57 -0700949
Eric Kunzee5e26762020-10-13 16:11:07 -0700950 input = dynamic_cast<TosaReference::TensorTemplate<TIn>*>(inputs[0]);
951 weight = dynamic_cast<TosaReference::TensorTemplate<TWeight>*>(inputs[1]);
952 bias = dynamic_cast<TosaReference::TensorTemplate<TBias>*>(inputs[2]);
953 output = dynamic_cast<TosaReference::TensorTemplate<TAcc>*>(outputs[0]);
954
Kevin Cheng9fe17242021-11-10 01:04:39 +0000955 std::string msg;
Eric Kunzeb5fabec2022-06-07 05:20:44 +0000956 if (check_conv_attribute(attribute, 2 /* conv_dimension */, input->getShape(), output->getShape(),
Jeremy Johnson4a6fb9b2022-04-26 15:47:21 +0100957 weight->getShape(), 0 /* offset_kernel */, InDtype, WeightDtype, msg))
Eric Kunzee5e26762020-10-13 16:11:07 -0700958 {
Kevin Cheng9fe17242021-11-10 01:04:39 +0000959 msg = "OpDepthwiseConv2d: " + msg;
960 printNodeValidationError(msg.c_str());
Eric Kunzee5e26762020-10-13 16:11:07 -0700961 return 1;
962 }
963
Eric Kunzee5e26762020-10-13 16:11:07 -0700964 return 0;
965}
966
967template <DType InDtype, DType WeightDtype>
968int OpDepthwiseConv2d<InDtype, WeightDtype>::eval()
969{
970 int in_batch = this->input->getShape()[0];
971 int in_height = this->input->getShape()[1];
972 int in_width = this->input->getShape()[2];
973 int in_channels = this->input->getShape()[3];
974
975 int f_height = this->weight->getShape()[0];
976 int f_width = this->weight->getShape()[1];
977 int f_in_channels = this->weight->getShape()[2];
978 int f_multiplier = this->weight->getShape()[3];
979
980 int b_out_channels = this->bias->getShape()[0];
981
982 int out_batch = this->output->getShape()[0];
983 int out_height = this->output->getShape()[1];
984 int out_width = this->output->getShape()[2];
985 int out_channels = this->output->getShape()[3];
986
Kevin Chengacb550f2021-06-29 15:32:19 -0700987 ERROR_IF(in_batch != out_batch, "OpDepthwiseConv2d: tensor batch mismatch %d != %d", in_batch, out_batch);
988 ERROR_IF(f_in_channels != in_channels, "OpDepthwiseConv2d: tensor input channel mismatch %d != %d", f_in_channels,
989 in_channels);
990 ERROR_IF(in_channels * f_multiplier != out_channels, "OpDepthwiseConv2d: tensor output channel mismatch %d != %d",
991 in_channels * f_multiplier, out_channels);
992 ERROR_IF(b_out_channels != out_channels, "OpDepthwiseConv2d: bias channels mismatch %d != %d", b_out_channels,
993 out_channels);
Eric Kunzee5e26762020-10-13 16:11:07 -0700994
TatWai Chong86c403b2022-06-06 20:46:01 -0700995 int pad_top = this->attribute->pad()[0];
996 int pad_bottom = this->attribute->pad()[1];
997 int pad_left = this->attribute->pad()[2];
998 int pad_right = this->attribute->pad()[3];
999
Eric Kunzee5e26762020-10-13 16:11:07 -07001000 int stride_h = this->attribute->stride()[0];
1001 int stride_w = this->attribute->stride()[1];
1002 int dilation_h = this->attribute->dilation()[0];
1003 int dilation_w = this->attribute->dilation()[1];
1004
1005 DEBUG_INFO(OP,
1006 "perform OpDepthwiseConv2d, input.shape=[%d,%d,%d,%d], weight.shape=[%d,%d,%d,%d], "
TatWai Chong86c403b2022-06-06 20:46:01 -07001007 "output.shape=[%d,%d,%d,%d], stride=[%d,%d], dilation=[%d,%d], pad=[%d,%d,%d,%d]",
Eric Kunzee5e26762020-10-13 16:11:07 -07001008 in_batch, in_height, in_width, in_channels, f_height, f_width, f_in_channels, f_multiplier, out_batch,
TatWai Chong86c403b2022-06-06 20:46:01 -07001009 out_height, out_width, out_channels, stride_h, stride_w, dilation_h, dilation_w, pad_top,
1010 pad_bottom, pad_left, pad_right);
Eric Kunzee5e26762020-10-13 16:11:07 -07001011
TatWai Chong86c403b2022-06-06 20:46:01 -07001012 Eigen::array<std::pair<int32_t, int32_t>, 4> pad;
1013 pad[0] = std::make_pair(0, 0);
1014 pad[1] = std::make_pair(pad_top, pad_bottom);
1015 pad[2] = std::make_pair(pad_left, pad_right);
1016 pad[3] = std::make_pair(0, 0);
Eric Kunzee5e26762020-10-13 16:11:07 -07001017
1018 TIn input_val = this->input->getTensor();
1019 TWeight weight_val = this->weight->getTensor();
Eric Kunzef7337832022-06-17 08:19:12 -07001020 if (InDtype == DType_INT8 || WeightDtype == DType_INT8)
Eric Kunzee5e26762020-10-13 16:11:07 -07001021 {
Eric Kunzeb5fabec2022-06-07 05:20:44 +00001022 input_val = input_val - (InEigenType)attribute->input_zp();
1023 weight_val = weight_val - (WeightEigenType)attribute->weight_zp();
Eric Kunzee5e26762020-10-13 16:11:07 -07001024 }
1025
TatWai Chong86c403b2022-06-06 20:46:01 -07001026 ETensor4<InEigenType> input_padded = input_val.pad(pad);
Eric Kunzee5e26762020-10-13 16:11:07 -07001027
1028 // GEMM doesn't fit well with DepthwiseConv2d
TatWai Chong86c403b2022-06-06 20:46:01 -07001029 // 1. use extract_image_patches() to handle stride/dilation/pad
Eric Kunzee5e26762020-10-13 16:11:07 -07001030 // 2. perform direct convolution
1031
1032 // 1. extract_image_patches() output [N, KH, KW, OH * OW, IC]
1033 ETensor5<InEigenType> input_extract_patches = input_padded.extract_image_patches(
1034 f_height, f_width, stride_h, stride_w, dilation_h, dilation_w, Eigen::PADDING_VALID);
1035
1036 Eigen::array<Eigen::Index, 4> reshape_dim;
1037 reshape_dim.fill(1);
1038 reshape_dim[3] = b_out_channels;
1039
1040 Eigen::array<Eigen::Index, 4> bcast;
1041 bcast[0] = out_batch;
1042 bcast[1] = out_height;
1043 bcast[2] = out_width;
1044 bcast[3] = 1;
1045
1046 // initialize with bias
1047 this->output->getTensor() = this->bias->getTensor().reshape(reshape_dim).broadcast(bcast);
1048
1049 // 2. direct depthwise convolution
1050 for (int ob = 0; ob < out_batch; ob++)
1051 {
1052 for (int oh = 0; oh < out_height; oh++)
1053 {
1054 for (int ow = 0; ow < out_width; ow++)
1055 {
1056 for (int ic = 0; ic < in_channels; ic++)
1057 {
1058 for (int cm = 0; cm < f_multiplier; cm++)
1059 {
1060 for (int fh = 0; fh < f_height; fh++)
1061 {
1062 for (int fw = 0; fw < f_width; fw++)
1063 {
1064 this->output->getTensor()(ob, oh, ow, ic * f_multiplier + cm) +=
1065 ((AccEigenType)input_extract_patches(ob, fh, fw, ow * out_height + oh, ic) *
1066 (AccEigenType)weight_val(fh, fw, ic, cm));
1067 }
1068 }
1069 }
1070 }
1071 }
1072 }
1073 }
1074
1075 if (AccDtype == DType_INT48)
1076 {
1077 this->output->getTensor() = this->output->getTensor().cwiseMax((AccEigenType)AccQMin);
1078 this->output->getTensor() = this->output->getTensor().cwiseMin((AccEigenType)AccQMax);
1079 }
1080
1081 return GraphNode::eval();
1082}
1083
1084template <DType InDtype, DType WeightDtype>
Kevin Chengacb550f2021-06-29 15:32:19 -07001085OpFullyConnected<InDtype, WeightDtype>::OpFullyConnected(SubgraphTraverser* sgt_,
1086 TosaAttributeBase* attribute_,
Eric Kunzee5e26762020-10-13 16:11:07 -07001087 uint64_t id_)
Kevin Chengacb550f2021-06-29 15:32:19 -07001088 : GraphNode(sgt_, Op_FULLY_CONNECTED, id_)
Eric Kunzee5e26762020-10-13 16:11:07 -07001089{
1090 setRequiredOperands(3, 1);
1091 setRequiredRank(2);
1092
Eric Kunzeb5fabec2022-06-07 05:20:44 +00001093 INIT_ATTRIBUTE(FullyConnected);
Eric Kunzee5e26762020-10-13 16:11:07 -07001094}
1095
1096template <DType InDtype, DType WeightDtype>
1097OpFullyConnected<InDtype, WeightDtype>::~OpFullyConnected()
1098{
Eric Kunzeb5fabec2022-06-07 05:20:44 +00001099 if (attribute)
1100 delete attribute;
Eric Kunzee5e26762020-10-13 16:11:07 -07001101}
1102
1103template <DType InDtype, DType WeightDtype>
1104int OpFullyConnected<InDtype, WeightDtype>::checkTensorAttributes()
1105{
1106 if (validateRequiredOperands())
1107 return 1;
1108
1109 if (validateRequiredRank(inputs[0]) || validateRequiredRank(inputs[1]) || validateRequiredRank(outputs[0]))
1110 {
1111 return 1;
1112 }
1113
1114 input = dynamic_cast<TosaReference::TensorTemplate<TIn>*>(inputs[0]);
1115 weight = dynamic_cast<TosaReference::TensorTemplate<TWeight>*>(inputs[1]);
1116 bias = dynamic_cast<TosaReference::TensorTemplate<TBias>*>(inputs[2]);
1117
1118 if (input->getShape()[1] != weight->getShape()[1])
1119 {
1120 printNodeValidationError("OpFullyConnected operator input.shape[1] should match weight.shape[1]");
1121 return 1;
1122 }
1123
1124 if (weight->getShape()[0] != bias->getShape()[0])
1125 {
1126 printNodeValidationError("OpFullyConnected operator bias.shape[0] should match weight.shape[0]");
1127 return 1;
1128 }
1129
Kevin Chengcc61be32021-10-14 17:09:57 -07001130 ERROR_IF(outputs[0]->getDtype() != AccDtype,
1131 "OpFullyConnected: Output data type not supported for this configuration of operator");
1132
Eric Kunzee5e26762020-10-13 16:11:07 -07001133 output = dynamic_cast<TosaReference::TensorTemplate<TAcc>*>(outputs[0]);
1134
Eric Kunzeb5fabec2022-06-07 05:20:44 +00001135 ERROR_IF(InDtype != DType_INT8 && attribute->input_zp() != 0, "OpFullyConnected: Input zeropoint must be zero for non int8_t data");
1136 ERROR_IF(WeightDtype != DType_INT8 && attribute->weight_zp() != 0, "OpFullyConnected: Weight zeropoint must be zero for non int8_t data");
Kevin Chengcc61be32021-10-14 17:09:57 -07001137
Eric Kunzee5e26762020-10-13 16:11:07 -07001138 return 0;
1139}
1140
1141template <DType InDtype, DType WeightDtype>
1142int OpFullyConnected<InDtype, WeightDtype>::eval()
1143{
1144 typedef Eigen::Tensor<int, 1>::DimensionPair DimPair;
1145 Eigen::array<DimPair, 1> dims{ { DimPair(1, 0) } };
1146
1147 Eigen::array<Eigen::Index, 2> weight_shuffle{ 1, 0 };
1148
1149 Eigen::array<Eigen::Index, 2> bias_reshape;
1150 bias_reshape[0] = 1;
1151 bias_reshape[1] = this->bias->getShape()[0];
1152
1153 Eigen::array<Eigen::Index, 2> bias_bcast;
1154 bias_bcast[0] = this->input->getShape()[0];
1155 bias_bcast[1] = 1;
1156
1157 TIn input_val = this->input->getTensor();
1158 TWeight weight_val = this->weight->getTensor().shuffle(weight_shuffle);
Eric Kunzef7337832022-06-17 08:19:12 -07001159 if (InDtype == DType_INT8 || WeightDtype == DType_INT8)
Eric Kunzee5e26762020-10-13 16:11:07 -07001160 {
Eric Kunzeb5fabec2022-06-07 05:20:44 +00001161 input_val = input_val - (InEigenType)attribute->input_zp();
1162 weight_val = weight_val - (WeightEigenType)attribute->weight_zp();
Eric Kunzee5e26762020-10-13 16:11:07 -07001163 }
1164
1165 this->output->getTensor() =
1166 input_val.template cast<AccEigenType>().contract(weight_val.template cast<AccEigenType>(), dims) +
1167 this->bias->getTensor().reshape(bias_reshape).broadcast(bias_bcast);
1168
1169 if (AccDtype == DType_INT48)
1170 {
1171 this->output->getTensor() = this->output->getTensor().cwiseMax((AccEigenType)AccQMin);
1172 this->output->getTensor() = this->output->getTensor().cwiseMin((AccEigenType)AccQMax);
1173 }
1174 return GraphNode::eval();
1175}
1176
1177template <DType Dtype>
Kevin Chengacb550f2021-06-29 15:32:19 -07001178OpMatMul<Dtype>::OpMatMul(SubgraphTraverser* sgt_,
1179 TosaAttributeBase* attribute_,
Kevin Chengacb550f2021-06-29 15:32:19 -07001180 uint64_t id_)
1181 : GraphNode(sgt_, Op_MATMUL, id_)
Eric Kunzee5e26762020-10-13 16:11:07 -07001182{
1183 setRequiredOperands(2, 1);
Kevin Cheng2d60f002021-06-09 14:18:32 -07001184 setRequiredRank(3);
Eric Kunzee5e26762020-10-13 16:11:07 -07001185
Eric Kunzeb5fabec2022-06-07 05:20:44 +00001186 INIT_ATTRIBUTE(MatMul);
Eric Kunzee5e26762020-10-13 16:11:07 -07001187}
1188
1189template <DType Dtype>
1190OpMatMul<Dtype>::~OpMatMul()
1191{
Eric Kunzeb5fabec2022-06-07 05:20:44 +00001192 if (attribute)
1193 delete attribute;
Eric Kunzee5e26762020-10-13 16:11:07 -07001194}
1195
1196template <DType Dtype>
1197int OpMatMul<Dtype>::checkTensorAttributes()
1198{
1199 if (validateRequiredOperands())
1200 return 1;
1201
1202 if (validateRequiredRank(inputs[0]) || validateRequiredRank(inputs[1]) || validateRequiredRank(outputs[0]))
1203 {
1204 return 1;
1205 }
1206
Kevin Chengcc61be32021-10-14 17:09:57 -07001207 ERROR_IF(outputs[0]->getDtype() != AccDtype,
Kevin Cheng80794802021-11-01 11:14:13 -07001208 "OpMatMul: Output data type not supported for this configuration of operator");
Kevin Chengcc61be32021-10-14 17:09:57 -07001209
Kevin Cheng2d60f002021-06-09 14:18:32 -07001210 a = dynamic_cast<TosaReference::TensorTemplate<TIn>*>(inputs[0]);
1211 b = dynamic_cast<TosaReference::TensorTemplate<TIn>*>(inputs[1]);
1212 output = dynamic_cast<TosaReference::TensorTemplate<TAcc>*>(outputs[0]);
Eric Kunzee5e26762020-10-13 16:11:07 -07001213
Kevin Cheng2d60f002021-06-09 14:18:32 -07001214 ASSERT_MEM(a && b && output);
1215
1216 // a: [N, H, C]
1217 // b: [N, C, W]
1218 // c: [N, H, W]
1219
1220 // Check N
1221 if (a->getShape()[0] != b->getShape()[0] || a->getShape()[0] != output->getShape()[0])
Eric Kunzee5e26762020-10-13 16:11:07 -07001222 {
Kevin Cheng2d60f002021-06-09 14:18:32 -07001223 printNodeValidationError("OpMatMul operator a.shape[0], b.shape[0] and output.shape[0] should match");
Eric Kunzee5e26762020-10-13 16:11:07 -07001224 return 1;
1225 }
Kevin Cheng2d60f002021-06-09 14:18:32 -07001226 N = a->getShape()[0];
Eric Kunzee5e26762020-10-13 16:11:07 -07001227
Kevin Cheng2d60f002021-06-09 14:18:32 -07001228 // Check C
1229 if (a->getShape()[2] != b->getShape()[1])
1230 {
1231 printNodeValidationError("OpMatMul operator a.shape[2] should match b.shape[1]");
1232 return 1;
1233 }
1234 C = a->getShape()[2];
1235
1236 // Check H
1237 if (a->getShape()[1] != output->getShape()[1])
1238 {
1239 printNodeValidationError("OpMatMul operator a.shape[1] should match output.shape[1]");
1240 return 1;
1241 }
1242 H = a->getShape()[1];
1243
1244 // Check W
1245 if (b->getShape()[2] != output->getShape()[2])
1246 {
1247 printNodeValidationError("OpMatMul operator output.shape[2] should match output.shape[2]");
1248 return 1;
1249 }
1250 W = b->getShape()[2];
Eric Kunzee5e26762020-10-13 16:11:07 -07001251
Eric Kunzeb5fabec2022-06-07 05:20:44 +00001252 ERROR_IF(Dtype != DType_INT8 && attribute->a_zp() != 0, "OpMatMul: A zeropoint must be zero for non int8_t data");
1253 ERROR_IF(Dtype != DType_INT8 && attribute->b_zp() != 0, "OpMatMul: B zeropoint must be zero for non int8_t data");
Kevin Chengcc61be32021-10-14 17:09:57 -07001254
Eric Kunzee5e26762020-10-13 16:11:07 -07001255 return 0;
1256}
1257
1258template <DType Dtype>
1259int OpMatMul<Dtype>::eval()
1260{
1261 typedef Eigen::Tensor<int, 1>::DimensionPair DimPair;
1262 Eigen::array<DimPair, 1> dims{ { DimPair(1, 0) } };
1263
1264 TIn a_val = this->a->getTensor();
1265 TIn b_val = this->b->getTensor();
Eric Kunzeb5fabec2022-06-07 05:20:44 +00001266 if (Dtype == DType_INT8)
Eric Kunzee5e26762020-10-13 16:11:07 -07001267 {
Eric Kunzeb5fabec2022-06-07 05:20:44 +00001268 a_val = a_val - (InEigenType)attribute->a_zp();
1269 b_val = b_val - (InEigenType)attribute->b_zp();
Eric Kunzee5e26762020-10-13 16:11:07 -07001270 }
1271
Kevin Cheng2d60f002021-06-09 14:18:32 -07001272 Eigen::array<Eigen::Index, 2> a_rank2_shape({ H, C });
1273 Eigen::array<Eigen::Index, 2> b_rank2_shape({ C, W });
1274 Eigen::array<Eigen::Index, 3> output_rank3_shape({ 1, H, W });
1275
1276 Eigen::array<Eigen::Index, 3> a_size_array({ 1, H, C });
1277 Eigen::array<Eigen::Index, 3> b_size_array({ 1, C, W });
1278
1279 Eigen::array<Eigen::Index, 3> a_begin_array({ 0, 0, 0 });
1280 Eigen::array<Eigen::Index, 3> b_begin_array({ 0, 0, 0 });
1281
1282 // Iterate N dimension.
1283 for (int i = 0; i < N; i++)
1284 {
1285 a_begin_array[0] = i;
1286 b_begin_array[0] = i;
1287
1288 TInRank2 a_rank2_val = a_val.slice(a_begin_array, a_size_array).reshape(a_rank2_shape);
1289 TInRank2 b_rank2_val = b_val.slice(b_begin_array, b_size_array).reshape(b_rank2_shape);
1290 TAccRank2 output_rank2_val =
1291 a_rank2_val.template cast<AccEigenType>().contract(b_rank2_val.template cast<AccEigenType>(), dims);
1292 TAcc output_rank3_val = output_rank2_val.reshape(output_rank3_shape);
1293 if (i == 0)
1294 {
1295 this->output->getTensor() = output_rank3_val;
1296 }
1297 else
1298 {
1299 TAcc temp = this->output->getTensor().concatenate(output_rank3_val, 0);
1300 this->output->getTensor() = temp;
1301 }
1302 }
Eric Kunzee5e26762020-10-13 16:11:07 -07001303
1304 if (AccDtype == DType_INT48)
1305 {
Kevin Cheng2d60f002021-06-09 14:18:32 -07001306 this->output->getTensor() = this->output->getTensor().cwiseMax((AccEigenType)AccQMin);
1307 this->output->getTensor() = this->output->getTensor().cwiseMin((AccEigenType)AccQMax);
Eric Kunzee5e26762020-10-13 16:11:07 -07001308 }
1309
1310 return GraphNode::eval();
1311}
1312
1313template <DType Dtype>
Kevin Chengacb550f2021-06-29 15:32:19 -07001314OpMaxPool2d<Dtype>::OpMaxPool2d(SubgraphTraverser* sgt_,
1315 TosaAttributeBase* attribute_,
Kevin Chengacb550f2021-06-29 15:32:19 -07001316 uint64_t id_)
1317 : GraphNode(sgt_, Op_MAX_POOL2D, id_)
Eric Kunzee5e26762020-10-13 16:11:07 -07001318{
1319 setRequiredOperands(1, 1);
1320 setRequiredRank(4);
1321
Kevin Cheng93a16282021-08-31 16:14:03 -07001322 INIT_ATTRIBUTE(Pool);
Eric Kunzee5e26762020-10-13 16:11:07 -07001323}
1324
1325template <DType Dtype>
1326OpMaxPool2d<Dtype>::~OpMaxPool2d()
1327{
1328 if (attribute)
1329 delete attribute;
1330}
1331
1332template <DType Dtype>
1333int OpMaxPool2d<Dtype>::checkTensorAttributes()
1334{
1335 if (validateRequiredOperands())
1336 return 1;
1337
1338 if (validateRequiredRank(inputs[0]) || validateRequiredRank(outputs[0]))
1339 {
1340 return 1;
1341 }
1342
1343 if (inputs[0]->matchType(*outputs[0]))
1344 {
1345 printNodeValidationError("OpMaxPool2d: input and output tensor type mismatch");
1346 return 1;
1347 }
1348
1349 in = dynamic_cast<TosaReference::TensorTemplate<TIn>*>(inputs[0]);
1350 out = dynamic_cast<TosaReference::TensorTemplate<TOut>*>(outputs[0]);
1351
Kevin Cheng7eb93d72021-10-09 01:26:08 +00001352 std::string msg;
Kevin Cheng9fe17242021-11-10 01:04:39 +00001353 if (check_pool2d_attribute(attribute, in->getShape(), out->getShape(), msg))
Eric Kunzee5e26762020-10-13 16:11:07 -07001354 {
Kevin Cheng7eb93d72021-10-09 01:26:08 +00001355 msg = "OpMaxPool2d: " + msg;
1356 printNodeValidationError(msg.c_str());
Eric Kunzee5e26762020-10-13 16:11:07 -07001357 return 1;
1358 }
1359
1360 return 0;
1361}
1362
1363template <DType Dtype>
1364int OpMaxPool2d<Dtype>::eval()
1365{
1366 int in_batch = this->in->getShape()[0];
1367 int in_height = this->in->getShape()[1];
1368 int in_width = this->in->getShape()[2];
1369 int in_channels = this->in->getShape()[3];
1370
1371 int out_batch = this->out->getShape()[0];
1372 int out_height = this->out->getShape()[1];
1373 int out_width = this->out->getShape()[2];
1374 int out_channels = this->out->getShape()[3];
1375
Kevin Chengacb550f2021-06-29 15:32:19 -07001376 ERROR_IF(in_batch != out_batch, "OpMaxPool2d: tensor batch mismatch %d != %d", in_batch, out_batch);
1377 ERROR_IF(in_channels != out_channels, "OpMaxPool2d: tensor channel mismatch %d != %d", in_channels, out_channels);
Eric Kunzee5e26762020-10-13 16:11:07 -07001378
TatWai Chong86c403b2022-06-06 20:46:01 -07001379 int pad_top = this->attribute->pad()[0];
1380 int pad_bottom = this->attribute->pad()[1];
1381 int pad_left = this->attribute->pad()[2];
1382 int pad_right = this->attribute->pad()[3];
1383
Eric Kunzee5e26762020-10-13 16:11:07 -07001384 int kernel_h = this->attribute->kernel()[0];
1385 int kernel_w = this->attribute->kernel()[1];
1386 int stride_h = this->attribute->stride()[0];
1387 int stride_w = this->attribute->stride()[1];
1388
1389 DEBUG_INFO(OP,
1390 "perform MaxPool2d, input.shape=[%d,%d,%d,%d], output.shape=[%d,%d,%d,%d], kernel=[%d,%d], "
TatWai Chong86c403b2022-06-06 20:46:01 -07001391 "stride=[%d,%d], pad=[%d,%d,%d,%d]",
Eric Kunzee5e26762020-10-13 16:11:07 -07001392 in_batch, in_height, in_width, in_channels, out_batch, out_height, out_width, out_channels, kernel_h,
TatWai Chong86c403b2022-06-06 20:46:01 -07001393 kernel_w, stride_h, stride_w, pad_top, pad_bottom, pad_left, pad_right);
Eric Kunzee5e26762020-10-13 16:11:07 -07001394
1395 Eigen::array<Eigen::Index, 2> im2col_input_dims;
1396 im2col_input_dims[0] = kernel_h * kernel_w;
1397 im2col_input_dims[1] = out_batch * out_height * out_width * out_channels;
1398
1399 Eigen::array<Eigen::Index, 4> col2im_output_dims;
1400 col2im_output_dims[0] = out_batch;
1401 col2im_output_dims[1] = out_height;
1402 col2im_output_dims[2] = out_width;
1403 col2im_output_dims[3] = out_channels;
1404
TatWai Chong86c403b2022-06-06 20:46:01 -07001405 Eigen::array<std::pair<int32_t, int32_t>, 4> pad;
1406 pad[0] = std::make_pair(0, 0);
1407 pad[1] = std::make_pair(pad_top, pad_bottom);
1408 pad[2] = std::make_pair(pad_left, pad_right);
1409 pad[3] = std::make_pair(0, 0);
Eric Kunzee5e26762020-10-13 16:11:07 -07001410
TatWai Chong86c403b2022-06-06 20:46:01 -07001411 ETensor4<InEigenType> input_padded = this->in->getTensor().pad(pad, std::numeric_limits<InEigenType>::lowest());
Eric Kunzee5e26762020-10-13 16:11:07 -07001412
1413 // extract_image_patches() output [N, KH, KW, H * W, C]
1414 // transpose to [KH, KW, N, H * W, C]
1415 // reshape to [KH * KW, N * H * W * C]
1416 //
1417 // Set the padding value to be the most negative value that can be
1418 // represented by the datatype to ensure that any padding values will be equal
1419 // to or smaller than the actual maximum in the KH x KW patch.
1420 ETensor2<InEigenType> input_extract_patches =
1421 input_padded
1422 .extract_image_patches(kernel_h, kernel_w, stride_h, stride_w, 1, 1, Eigen::PADDING_VALID,
1423 std::numeric_limits<InEigenType>::lowest())
1424 .shuffle(Eigen::array<Eigen::Index, 5>{ 1, 2, 0, 3, 4 })
1425 .reshape(im2col_input_dims);
1426
1427 // Get the maximum of the KHxHW patches along axis 0
1428 Eigen::Tensor<DenseIndex, 1> tensor_argmax = input_extract_patches.argmax(0);
1429
1430 // 1D result with [N * H * W * C]
1431 ETensor1<OutEigenType> out_1d(this->out->getElementCount());
1432
1433 // index input_patches with argmax array should give the result
1434 for (size_t i = 0; i < this->out->getElementCount(); i++)
1435 {
1436 out_1d(i) = (OutEigenType)input_extract_patches(tensor_argmax(i), i);
1437 }
1438
1439 // reshape result to [N, H, W, C]
1440 this->out->getTensor() = out_1d.reshape(col2im_output_dims);
1441
1442 return GraphNode::eval();
1443}
1444
Kevin Chengcc61be32021-10-14 17:09:57 -07001445template <DType InDtype, DType WeightDtype>
1446OpTransposeConv2d<InDtype, WeightDtype>::OpTransposeConv2d(SubgraphTraverser* sgt_,
1447 TosaAttributeBase* attribute_,
Kevin Chengcc61be32021-10-14 17:09:57 -07001448 uint64_t id_)
Kevin Chengacb550f2021-06-29 15:32:19 -07001449 : GraphNode(sgt_, Op_TRANSPOSE_CONV2D, id_)
Eric Kunzee5e26762020-10-13 16:11:07 -07001450{
1451 setRequiredOperands(3, 1);
1452 setRequiredRank(4);
1453
Kevin Cheng93a16282021-08-31 16:14:03 -07001454 INIT_ATTRIBUTE(TransposeConv);
Eric Kunzee5e26762020-10-13 16:11:07 -07001455}
1456
Kevin Chengcc61be32021-10-14 17:09:57 -07001457template <DType InDtype, DType WeightDtype>
1458OpTransposeConv2d<InDtype, WeightDtype>::~OpTransposeConv2d()
Eric Kunzee5e26762020-10-13 16:11:07 -07001459{
1460 if (attribute)
1461 delete attribute;
Eric Kunzee5e26762020-10-13 16:11:07 -07001462}
1463
Kevin Chengcc61be32021-10-14 17:09:57 -07001464template <DType InDtype, DType WeightDtype>
1465int OpTransposeConv2d<InDtype, WeightDtype>::checkTensorAttributes()
Eric Kunzee5e26762020-10-13 16:11:07 -07001466{
1467 if (validateRequiredOperands())
1468 return 1;
1469
1470 if (validateRequiredRank(inputs[0]) || validateRequiredRank(inputs[1]) || validateRequiredRank(outputs[0]))
1471 {
1472 return 1;
1473 }
1474
Kevin Chengcc61be32021-10-14 17:09:57 -07001475 ERROR_IF(outputs[0]->getDtype() != AccDtype,
Kevin Cheng80794802021-11-01 11:14:13 -07001476 "OpTransposeConv2d: Output data type not supported for this configuration of operator");
Kevin Chengcc61be32021-10-14 17:09:57 -07001477
Eric Kunzee5e26762020-10-13 16:11:07 -07001478 input = dynamic_cast<TosaReference::TensorTemplate<TIn>*>(inputs[0]);
1479 weight = dynamic_cast<TosaReference::TensorTemplate<TWeight>*>(inputs[1]);
1480 bias = dynamic_cast<TosaReference::TensorTemplate<TBias>*>(inputs[2]);
1481 output = dynamic_cast<TosaReference::TensorTemplate<TAcc>*>(outputs[0]);
1482
TatWai Chong24594f52022-06-08 00:48:04 -07001483 if (attribute->out_pad().size() != 4)
Eric Kunzee5e26762020-10-13 16:11:07 -07001484 {
TatWai Chong24594f52022-06-08 00:48:04 -07001485 printNodeValidationError("OpTransposeConv2d: illegal size for attribute out_pad");
Eric Kunzee5e26762020-10-13 16:11:07 -07001486 return 1;
1487 }
1488
1489 if (attribute->stride().size() != 2)
1490 {
1491 printNodeValidationError("OpTransposeConv2d: illegal size for attribute stride");
1492 return 1;
1493 }
1494
Eric Kunzee5e26762020-10-13 16:11:07 -07001495 if (attribute->output_shape().size() != 4)
1496 {
1497 printNodeValidationError("OpTransposeConv2d: illegal size for attribute output_shape");
1498 return 1;
1499 }
1500
TatWai Chong24594f52022-06-08 00:48:04 -07001501 for (int32_t i : attribute->out_pad())
Kevin Cheng9fe17242021-11-10 01:04:39 +00001502 {
1503 if (i < 0)
1504 {
1505 printNodeValidationError("OpTransposeConv2d: At least one pad is smaller than zero");
1506 return 1;
1507 }
1508 }
1509
1510 for (int32_t i : attribute->stride())
1511 {
1512 if (i < 1)
1513 {
1514 printNodeValidationError("OpTransposeConv2d: At least one stride is smaller than one");
1515 return 1;
1516 }
1517 }
1518
Eric Kunzee5e26762020-10-13 16:11:07 -07001519 for (int d = 0; d < 4; d++)
1520 {
1521 if (attribute->output_shape()[d] != this->output->getShape()[d])
1522 {
1523 printNodeValidationError("OpTransposeConv2d: illegal size for attribute output_shape");
1524 return 1;
1525 }
1526 }
1527
Jeremy Johnson4a6fb9b2022-04-26 15:47:21 +01001528 int32_t IH = input->getShape()[1];
1529 int32_t IW = input->getShape()[2];
1530 int32_t OH = output->getShape()[1];
1531 int32_t OW = output->getShape()[2];
1532
1533 int32_t stride_y = attribute->stride()[0];
1534 int32_t stride_x = attribute->stride()[1];
1535 int32_t kernel_h = weight->getShape()[1];
1536 int32_t kernel_w = weight->getShape()[2];
1537
TatWai Chong24594f52022-06-08 00:48:04 -07001538 int32_t out_pad_top = attribute->out_pad()[0];
1539 int32_t out_pad_bottom = attribute->out_pad()[1];
1540 int32_t out_pad_left = attribute->out_pad()[2];
1541 int32_t out_pad_right = attribute->out_pad()[3];
Jeremy Johnson4a6fb9b2022-04-26 15:47:21 +01001542
TatWai Chong24594f52022-06-08 00:48:04 -07001543 int32_t H = (IH - 1) * stride_y - out_pad_top - out_pad_bottom + kernel_h;
1544 int32_t W = (IW - 1) * stride_x - out_pad_left - out_pad_right + kernel_w;
Jeremy Johnson4a6fb9b2022-04-26 15:47:21 +01001545
1546 if ((OH != H) || (OW != W))
1547 {
1548 std::string msg = "OpTransposeConv2d: Mismatch between output shape provided and expected output shape (" +
1549 std::to_string(H) + "," +
1550 std::to_string(W) + ")";
1551 printNodeValidationError(msg.c_str());
1552 return 1;
1553 }
1554
Eric Kunzeb5fabec2022-06-07 05:20:44 +00001555 ERROR_IF(InDtype != DType_INT8 && attribute->input_zp() != 0, "OpTransposeConv2d: Input zeropoint must be zero for non int8_t data");
1556 ERROR_IF(WeightDtype != DType_INT8 && attribute->weight_zp() != 0, "OpTransposeConv2d: Weight zeropoint must be zero for non int8_t data");
Kevin Chengcc61be32021-10-14 17:09:57 -07001557
Eric Kunzee5e26762020-10-13 16:11:07 -07001558 return 0;
1559}
1560
Kevin Chengcc61be32021-10-14 17:09:57 -07001561template <DType InDtype, DType WeightDtype>
1562int OpTransposeConv2d<InDtype, WeightDtype>::eval()
Eric Kunzee5e26762020-10-13 16:11:07 -07001563{
1564 int in_batch = this->input->getShape()[0];
1565 int in_height = this->input->getShape()[1];
1566 int in_width = this->input->getShape()[2];
1567 int in_channels = this->input->getShape()[3];
1568
1569 int f_out_channels = this->weight->getShape()[0];
1570 int f_height = this->weight->getShape()[1];
1571 int f_width = this->weight->getShape()[2];
1572 int f_in_channels = this->weight->getShape()[3];
1573
1574 int b_out_channels = this->bias->getShape()[0];
1575
1576 int out_batch = this->output->getShape()[0];
1577 int out_height = this->output->getShape()[1];
1578 int out_width = this->output->getShape()[2];
1579 int out_channels = this->output->getShape()[3];
1580
TatWai Chong24594f52022-06-08 00:48:04 -07001581 int out_pad_top = this->attribute->out_pad()[0];
1582 int out_pad_bottom = this->attribute->out_pad()[1];
1583 int out_pad_left = this->attribute->out_pad()[2];
1584 int out_pad_right = this->attribute->out_pad()[3];
Jeremy Johnson4a6fb9b2022-04-26 15:47:21 +01001585
1586 int stride_h = this->attribute->stride()[0];
1587 int stride_w = this->attribute->stride()[1];
Eric Kunzee5e26762020-10-13 16:11:07 -07001588
Kevin Chengacb550f2021-06-29 15:32:19 -07001589 ERROR_IF(in_batch != out_batch, "OpTransposeConv2d: tensor batch mismatch %d != %d", in_batch, out_batch);
1590 ERROR_IF(f_in_channels != in_channels, "OpTransposeConv2d: tensor input channel mismatch %d != %d", f_in_channels,
1591 in_channels);
1592 ERROR_IF(f_out_channels != out_channels, "OpTransposeConv2d: tensor output channel mismatch %d != %d",
1593 f_out_channels, out_channels);
1594 ERROR_IF(b_out_channels != out_channels, "OpDepthwiseConv2d: bias channels mismatch %d != %d", b_out_channels,
1595 out_channels);
Eric Kunzee5e26762020-10-13 16:11:07 -07001596
1597 DEBUG_INFO(OP,
1598 "perform OpTransposeConv2d, input.shape=[%d,%d,%d,%d], weight.shape=[%d,%d,%d,%d], "
TatWai Chong24594f52022-06-08 00:48:04 -07001599 "output.shape=[%d,%d,%d,%d], stride=[%d,%d], out_pad=[%d,%d,%d,%d]",
Jeremy Johnson4a6fb9b2022-04-26 15:47:21 +01001600 in_batch, in_height, in_width, in_channels, f_height, f_width, f_out_channels, f_in_channels,
TatWai Chong24594f52022-06-08 00:48:04 -07001601 out_batch, out_height, out_width, out_channels, stride_h, stride_w, out_pad_top,
1602 out_pad_bottom, out_pad_left, out_pad_right);
Eric Kunzee5e26762020-10-13 16:11:07 -07001603
1604 TIn input_val = this->input->getTensor();
1605 TWeight weight_val = this->weight->getTensor();
Eric Kunzef7337832022-06-17 08:19:12 -07001606 if (InDtype == DType_INT8 || WeightDtype == DType_INT8)
Eric Kunzee5e26762020-10-13 16:11:07 -07001607 {
Eric Kunzeb5fabec2022-06-07 05:20:44 +00001608 input_val = input_val - (InEigenType)attribute->input_zp();
1609 weight_val = weight_val - (WeightEigenType)attribute->weight_zp();
Eric Kunzee5e26762020-10-13 16:11:07 -07001610 }
1611
1612 Eigen::array<Eigen::Index, 4> reshape_dim;
1613 reshape_dim.fill(1);
1614 reshape_dim[3] = b_out_channels;
1615
1616 Eigen::array<Eigen::Index, 4> bcast;
1617 bcast[0] = out_batch;
1618 bcast[1] = out_height;
1619 bcast[2] = out_width;
1620 bcast[3] = 1;
1621
1622 // initialize with bias
1623 this->output->getTensor() = this->bias->getTensor().reshape(reshape_dim).broadcast(bcast);
1624
1625 int out_x_origin, out_y_origin;
1626 int out_x, out_y;
1627
1628 // reference implementation from: tensorflow/tensorflow/lite/kernels/internal/reference/reference_ops.h
1629 for (int ob = 0; ob < out_batch; ob++)
1630 {
1631 for (int ih = 0; ih < in_height; ih++)
1632 {
1633 for (int iw = 0; iw < in_width; iw++)
1634 {
TatWai Chong24594f52022-06-08 00:48:04 -07001635 out_x_origin = iw * stride_w - out_pad_left;
1636 out_y_origin = ih * stride_h - out_pad_top;
Eric Kunzee5e26762020-10-13 16:11:07 -07001637 for (int ic = 0; ic < in_channels; ic++)
1638 {
1639 for (int fh = 0; fh < f_height; fh++)
1640 {
1641 for (int fw = 0; fw < f_width; fw++)
1642 {
Jeremy Johnson4a6fb9b2022-04-26 15:47:21 +01001643 out_x = out_x_origin + fw;
1644 out_y = out_y_origin + fh;
Eric Kunzee5e26762020-10-13 16:11:07 -07001645 for (int oc = 0; oc < out_channels; oc++)
1646 {
1647 if ((out_x >= 0 && out_x < out_width) && (out_y >= 0 && out_y < out_height))
1648 {
1649 this->output->getTensor()(ob, out_y, out_x, oc) +=
1650 ((AccEigenType)input_val(ob, ih, iw, ic) *
1651 (AccEigenType)weight_val(oc, fh, fw, ic));
1652 }
1653 }
1654 }
1655 }
1656 }
1657 }
1658 }
1659 }
1660
1661 if (AccDtype == DType_INT48)
1662 {
1663 this->output->getTensor() = this->output->getTensor().cwiseMax((AccEigenType)AccQMin);
1664 this->output->getTensor() = this->output->getTensor().cwiseMin((AccEigenType)AccQMax);
1665 }
1666
1667 return GraphNode::eval();
1668}
1669
1670// template explicit instantiation
1671DEF_INSTANTIATE_RANK1_6_ONE_RANK_ONE_TYPE(OpArgMax, FLOAT);
Kevin Cheng3a478572021-01-22 17:21:02 -08001672DEF_INSTANTIATE_RANK1_6_ONE_RANK_ONE_TYPE(OpArgMax, INT8);
Eric Kunzee5e26762020-10-13 16:11:07 -07001673DEF_INSTANTIATE_RANK1_6_ONE_RANK_ONE_TYPE(OpArgMax, INT16);
1674
1675DEF_INSTANTIATE_ONE_TYPE(OpAvgPool2d, FLOAT)
Kevin Cheng3a478572021-01-22 17:21:02 -08001676DEF_INSTANTIATE_ONE_TYPE(OpAvgPool2d, INT8)
Eric Kunzee5e26762020-10-13 16:11:07 -07001677DEF_INSTANTIATE_ONE_TYPE(OpAvgPool2d, INT16)
1678
1679DEF_INSTANTIATE_TWO_TYPE(OpConv2d, FLOAT, FLOAT);
Kevin Cheng3a478572021-01-22 17:21:02 -08001680DEF_INSTANTIATE_TWO_TYPE(OpConv2d, INT8, INT4);
1681DEF_INSTANTIATE_TWO_TYPE(OpConv2d, INT8, INT8);
Eric Kunzee5e26762020-10-13 16:11:07 -07001682DEF_INSTANTIATE_TWO_TYPE(OpConv2d, INT16, INT8);
1683
Kevin Cheng1533b852021-09-01 12:51:58 -07001684DEF_INSTANTIATE_TWO_TYPE(OpConv3d, FLOAT, FLOAT);
1685DEF_INSTANTIATE_TWO_TYPE(OpConv3d, INT8, INT4);
1686DEF_INSTANTIATE_TWO_TYPE(OpConv3d, INT8, INT8);
1687DEF_INSTANTIATE_TWO_TYPE(OpConv3d, INT16, INT8);
1688
Eric Kunzee5e26762020-10-13 16:11:07 -07001689DEF_INSTANTIATE_TWO_TYPE(OpDepthwiseConv2d, FLOAT, FLOAT);
Kevin Cheng3a478572021-01-22 17:21:02 -08001690DEF_INSTANTIATE_TWO_TYPE(OpDepthwiseConv2d, INT8, INT4);
1691DEF_INSTANTIATE_TWO_TYPE(OpDepthwiseConv2d, INT8, INT8);
Eric Kunzee5e26762020-10-13 16:11:07 -07001692DEF_INSTANTIATE_TWO_TYPE(OpDepthwiseConv2d, INT16, INT8);
1693
1694DEF_INSTANTIATE_TWO_TYPE(OpFullyConnected, FLOAT, FLOAT);
Kevin Cheng3a478572021-01-22 17:21:02 -08001695DEF_INSTANTIATE_TWO_TYPE(OpFullyConnected, INT8, INT4);
1696DEF_INSTANTIATE_TWO_TYPE(OpFullyConnected, INT8, INT8);
Eric Kunzee5e26762020-10-13 16:11:07 -07001697DEF_INSTANTIATE_TWO_TYPE(OpFullyConnected, INT16, INT8);
1698
Kevin Cheng3a478572021-01-22 17:21:02 -08001699DEF_INSTANTIATE_ONE_TYPE(OpMatMul, INT8);
Eric Kunzee5e26762020-10-13 16:11:07 -07001700DEF_INSTANTIATE_ONE_TYPE(OpMatMul, INT16);
1701DEF_INSTANTIATE_ONE_TYPE(OpMatMul, FLOAT);
1702
1703DEF_INSTANTIATE_ONE_TYPE(OpMaxPool2d, FLOAT);
Kevin Cheng3a478572021-01-22 17:21:02 -08001704DEF_INSTANTIATE_ONE_TYPE(OpMaxPool2d, INT8);
Eric Kunzee5e26762020-10-13 16:11:07 -07001705DEF_INSTANTIATE_ONE_TYPE(OpMaxPool2d, INT16);
1706
1707DEF_INSTANTIATE_TWO_TYPE(OpTransposeConv2d, FLOAT, FLOAT);
Kevin Cheng3a478572021-01-22 17:21:02 -08001708DEF_INSTANTIATE_TWO_TYPE(OpTransposeConv2d, INT8, INT4);
1709DEF_INSTANTIATE_TWO_TYPE(OpTransposeConv2d, INT8, INT8);
Eric Kunzee5e26762020-10-13 16:11:07 -07001710DEF_INSTANTIATE_TWO_TYPE(OpTransposeConv2d, INT16, INT8);