blob: f8fd32372e15b82b379d7ee7a3dcbd8cb511c7ca [file] [log] [blame]
Eric Kunzee5e26762020-10-13 16:11:07 -07001
Luke Hutton261b7b62023-01-10 14:50:31 +00002// Copyright (c) 2020-2023, 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"
James Ward8b390432022-08-12 20:48:56 +010019#include "half.hpp"
Eric Kunzee5e26762020-10-13 16:11:07 -070020
21using namespace TosaReference;
22using namespace Eigen;
23using namespace tosa;
24
Kevin Cheng9fe17242021-11-10 01:04:39 +000025int check_pool2d_attribute(tosa::TosaPoolAttribute* attribute,
26 std::vector<int32_t> input_shape,
27 std::vector<int32_t> output_shape,
28 std::string& msg)
Kevin Cheng7eb93d72021-10-09 01:26:08 +000029{
TatWai Chong86c403b2022-06-06 20:46:01 -070030 if (attribute->pad().size() != 4)
Kevin Cheng7eb93d72021-10-09 01:26:08 +000031 {
32 msg = "illegal size for attribute padding";
33 return 1;
34 }
35
36 if (attribute->kernel().size() != 2)
37 {
38 msg = "illegal size for attribute kernel";
39 return 1;
40 }
41
42 if (attribute->stride().size() != 2)
43 {
44 msg = "illegal size for attribute stride";
45 return 1;
46 }
47
TatWai Chong86c403b2022-06-06 20:46:01 -070048 for (int32_t i : attribute->pad())
Kevin Cheng7eb93d72021-10-09 01:26:08 +000049 {
50 if (i < 0)
51 {
52 msg = "At least one pad is smaller than zero";
53 return 1;
54 }
55 }
56
57 for (int32_t i : attribute->kernel())
58 {
59 if (i < 1)
60 {
Kevin Cheng9fe17242021-11-10 01:04:39 +000061 msg = "At least one kernel dimension is smaller than one";
Kevin Cheng7eb93d72021-10-09 01:26:08 +000062 return 1;
63 }
64 }
65
66 for (int32_t i : attribute->stride())
67 {
68 if (i < 1)
69 {
Kevin Cheng9fe17242021-11-10 01:04:39 +000070 msg = "At least one stride dimension is smaller than one";
Kevin Cheng7eb93d72021-10-09 01:26:08 +000071 return 1;
72 }
73 }
74
75 int32_t IH = input_shape[1];
76 int32_t IW = input_shape[2];
77 int32_t OH = output_shape[1];
78 int32_t OW = output_shape[2];
79
TatWai Chong86c403b2022-06-06 20:46:01 -070080 int32_t pad_top = attribute->pad()[0];
81 int32_t pad_bottom = attribute->pad()[1];
82 int32_t pad_left = attribute->pad()[2];
83 int32_t pad_right = attribute->pad()[3];
Kevin Cheng7eb93d72021-10-09 01:26:08 +000084
85 int32_t stride_y = attribute->stride()[0];
86 int32_t stride_x = attribute->stride()[1];
87 int32_t kernel_y = attribute->kernel()[0];
88 int32_t kernel_x = attribute->kernel()[1];
89
90 if (pad_top >= kernel_y || pad_bottom >= kernel_y || pad_left >= kernel_x || pad_right >= kernel_x)
91 {
92 msg = "At least one pad is >= kernel dimension";
93 return 1;
94 }
95
Jeremy Johnson4a6fb9b2022-04-26 15:47:21 +010096 int32_t full_H = IH + pad_top + pad_bottom - kernel_y;
97 int32_t full_W = IW + pad_left + pad_right - kernel_x;
98
99 if ((full_H % stride_y != 0) ||
100 (full_W % stride_x != 0))
Kevin Cheng7eb93d72021-10-09 01:26:08 +0000101 {
Jeremy Johnson4a6fb9b2022-04-26 15:47:21 +0100102 msg = "Parameters must yield exact integer output dimensions";
103 return 1;
104 }
105
106 if ((OH != (full_H / stride_y) + 1) ||
107 (OW != (full_W / stride_x) + 1))
108 {
109 msg = "Mismatch between output shape provided and expected output shape (" +
110 std::to_string((full_H / stride_y) + 1) + "," +
111 std::to_string((full_W / stride_x) + 1) + ")";
Kevin Cheng7eb93d72021-10-09 01:26:08 +0000112 return 1;
113 }
114
115 return 0;
116}
117
Eric Kunzeb5fabec2022-06-07 05:20:44 +0000118int check_conv_attribute(tosa::TosaConvAttribute* attribute,
Tai Lya4d748b2023-03-28 22:06:56 +0000119 uint32_t conv_dimension,
120 std::vector<int32_t> input_shape,
121 std::vector<int32_t> output_shape,
122 std::vector<int32_t> weights,
123 uint32_t offset_kernel,
124 TOSA_REF_TYPE InDtype,
125 TOSA_REF_TYPE WeightDtype,
126 std::string& msg)
Kevin Cheng9fe17242021-11-10 01:04:39 +0000127{
TatWai Chong86c403b2022-06-06 20:46:01 -0700128 if (attribute->pad().size() != (2 * conv_dimension))
Kevin Cheng9fe17242021-11-10 01:04:39 +0000129 {
TatWai Chong86c403b2022-06-06 20:46:01 -0700130 msg = "Illegal size for attribute pad";
Kevin Cheng9fe17242021-11-10 01:04:39 +0000131 return 1;
132 }
133
134 if (attribute->stride().size() != conv_dimension)
135 {
136 msg = "Illegal size for attribute stride";
137 return 1;
138 }
139
140 if (attribute->dilation().size() != conv_dimension)
141 {
142 msg = "Illegal size for attribute dilation";
143 return 1;
144 }
145
TatWai Chong86c403b2022-06-06 20:46:01 -0700146 for (int32_t i : attribute->pad())
Kevin Cheng9fe17242021-11-10 01:04:39 +0000147 {
148 if (i < 0)
149 {
150 msg = "At least one pad is smaller than zero";
151 return 1;
152 }
153 }
154
155 for (int32_t i : attribute->stride())
156 {
157 if (i < 1)
158 {
159 msg = "At least one stride dimension is smaller than one";
160 return 1;
161 }
162 }
163
164 for (int32_t i : attribute->dilation())
165 {
166 if (i < 1)
167 {
168 msg = "At least one dilation dimension is smaller than one";
169 return 1;
170 }
171 }
172
Jeremy Johnson4a6fb9b2022-04-26 15:47:21 +0100173 ASSERT_MSG(conv_dimension == 2 || conv_dimension == 3, "Unsupported convolution dimension")
174
TatWai Chongfd629052022-07-25 04:01:58 +0000175 int32_t offset_d = conv_dimension == 3 ? 1 : 0;
Jeremy Johnson4a6fb9b2022-04-26 15:47:21 +0100176 int32_t ID = conv_dimension == 3 ? input_shape[1] : 1;
177 int32_t IH = input_shape[1 + offset_d];
178 int32_t IW = input_shape[2 + offset_d];
179 int32_t OD = conv_dimension == 3 ? output_shape[1] : 1;
180 int32_t OH = output_shape[1 + offset_d];
181 int32_t OW = output_shape[2 + offset_d];
182
183 int32_t stride_d = conv_dimension == 3 ? attribute->stride()[0] : 1;
184 int32_t stride_y = attribute->stride()[0 + offset_d];
185 int32_t stride_x = attribute->stride()[1 + offset_d];
186 int32_t kernel_d = conv_dimension == 3 ? weights[offset_kernel] : 1;
187 int32_t kernel_h = weights[offset_kernel + offset_d];
188 int32_t kernel_w = weights[offset_kernel + 1 + offset_d];
189 int32_t dilation_d = conv_dimension == 3 ? attribute->dilation()[0] : 1;
190 int32_t dilation_y = attribute->dilation()[0 + offset_d];
191 int32_t dilation_x = attribute->dilation()[1 + offset_d];
192
193 offset_d *= 2;
TatWai Chong86c403b2022-06-06 20:46:01 -0700194 int32_t pad_d0 = conv_dimension == 3 ? attribute->pad()[0] : 0;
195 int32_t pad_d1 = conv_dimension == 3 ? attribute->pad()[1] : 0;
196 int32_t pad_top = attribute->pad()[0 + offset_d];
197 int32_t pad_bottom = attribute->pad()[1 + offset_d];
198 int32_t pad_left = attribute->pad()[2 + offset_d];
199 int32_t pad_right = attribute->pad()[3 + offset_d];
Jeremy Johnson4a6fb9b2022-04-26 15:47:21 +0100200
201 int32_t full_D = ID - 1 + pad_d0 + pad_d1 - (kernel_d - 1) * dilation_d;
202 int32_t full_H = IH - 1 + pad_top + pad_bottom - (kernel_h - 1) * dilation_y;
203 int32_t full_W = IW - 1 + pad_left + pad_right - (kernel_w - 1) * dilation_x;
204
205 if ((full_H % stride_y != 0) ||
206 (full_W % stride_x != 0) ||
207 (full_D % stride_d != 0))
208 {
209 msg = "Parameters must yield exact integer output dimensions";
210 return 1;
211 }
212
213 if ((OH != (full_H / stride_y) + 1) ||
214 (OW != (full_W / stride_x) + 1) ||
215 (OD != (full_D / stride_d) + 1))
216 {
217 std::string msg_d = "";
218 if (conv_dimension == 3)
219 {
220 msg_d += std::to_string((full_D / stride_d) + 1) + ",";
221 }
222 msg = "Mismatch between output shape provided and expected output shape (" +
223 msg_d +
224 std::to_string((full_H / stride_y) + 1) + "," +
225 std::to_string((full_W / stride_x) + 1) + ")";
226 return 1;
227 }
228
Tai Lya4d748b2023-03-28 22:06:56 +0000229 if (InDtype != TOSA_REF_TYPE_INT8 && attribute->input_zp() != 0)
230 {
Eric Kunzeb5fabec2022-06-07 05:20:44 +0000231 msg = "Input zero point must be zero for non-int8 data";
232 return 1;
233 }
Tai Lya4d748b2023-03-28 22:06:56 +0000234 if (WeightDtype != TOSA_REF_TYPE_INT8 && attribute->weight_zp() != 0)
235 {
Eric Kunzeb5fabec2022-06-07 05:20:44 +0000236 msg = "Weight zero point must be zero for non-int8 data";
237 return 1;
Kevin Cheng9fe17242021-11-10 01:04:39 +0000238 }
239
240 return 0;
241}
242
Luke Hutton57287132023-02-06 14:54:18 +0000243int check_fft_shape(const std::vector<int32_t>& in_real,
244 const std::vector<int32_t>& in_imag,
245 const std::vector<int32_t>& out_real,
246 const std::vector<int32_t>& out_imag,
247 std::string& msg) {
248 const bool is_rfft = in_imag.empty();
249 auto is_power_of_two = [](int32_t n) -> bool
250 {
251 return (n & (n-1)) == 0 && n > 0;
252 };
253
254 if (!is_power_of_two(in_real[1]) || !is_power_of_two(in_real[2]))
255 {
256 msg = "Input height and width must be a power of two";
257 return 1;
258 }
259
260 // RFFT does not have a second input
261 if (!is_rfft)
262 {
263 bool input_check = true;
264 for (size_t i = 0; i < in_real.size(); i++)
265 {
266 if (in_real[i] != in_imag[i])
267 {
268 input_check = false;
269 break;
270 }
271 }
272 if (!input_check)
273 {
274 msg = "Mismatch between real input shape and imaginary input shape";
275 return 1;
276 }
277 }
278
279 bool output_check = true;
280 for (size_t i = 0; i < out_real.size(); i++)
281 {
282 if (out_real[i] != out_imag[i])
283 {
284 output_check = false;
285 break;
286 }
287 }
288 if (!output_check)
289 {
290 msg = "Mismatch between real output shape and imaginary output shape";
291 return 1;
292 }
293
294 if (in_real[0] != out_real[0])
295 {
296 msg = "Input and output batch size don't match";
297 return 1;
298 }
299 if (in_real[1] != out_real[1])
300 {
301 msg = "Input and output height don't match";
302 return 1;
303 }
304
305 if (is_rfft)
306 {
307 if (in_real[2] / 2 + 1 != out_real[2])
308 {
309 msg = "Output width is expected to match input width / 2 + 1";
310 return 1;
311 }
312 } else {
313 if (in_real[2] != out_real[2])
314 {
315 msg = "Input and output width don't match";
316 return 1;
317 }
318 }
319
320 return 0;
321}
322
Tai Lya4d748b2023-03-28 22:06:56 +0000323template <int Rank, TOSA_REF_TYPE Dtype>
Kevin Chengacb550f2021-06-29 15:32:19 -0700324OpArgMax<Rank, Dtype>::OpArgMax(SubgraphTraverser* sgt_,
325 TosaAttributeBase* attribute_,
Kevin Chengacb550f2021-06-29 15:32:19 -0700326 uint64_t id_)
327 : GraphNode(sgt_, Op_ARGMAX, id_)
Eric Kunzee5e26762020-10-13 16:11:07 -0700328{
329 setRequiredOperands(1, 1);
Kevin Chengcc61be32021-10-14 17:09:57 -0700330 setRequiredRank(1, 4);
Eric Kunzee5e26762020-10-13 16:11:07 -0700331
332 INIT_ATTRIBUTE(Axis);
333}
334
Tai Lya4d748b2023-03-28 22:06:56 +0000335template <int Rank, TOSA_REF_TYPE Dtype>
Eric Kunzee5e26762020-10-13 16:11:07 -0700336OpArgMax<Rank, Dtype>::~OpArgMax()
337{
338 if (attribute)
339 delete attribute;
340}
341
Tai Lya4d748b2023-03-28 22:06:56 +0000342template <int Rank, TOSA_REF_TYPE Dtype>
Eric Kunzee5e26762020-10-13 16:11:07 -0700343int OpArgMax<Rank, Dtype>::checkTensorAttributes()
344{
345 if (validateRequiredOperands())
346 return 1;
347
Kevin Chengcc61be32021-10-14 17:09:57 -0700348 if (validateRequiredRank(inputs[0]))
Eric Kunzee5e26762020-10-13 16:11:07 -0700349 {
350 return 1;
351 }
352
Kevin Chengcc61be32021-10-14 17:09:57 -0700353 int32_t output_rank = inputs[0]->getRank() - 1;
354 if (output_rank != outputs[0]->getRank())
355 {
356 printNodeValidationError("OpArgMax: Output rank needs to be rank(input) - 1");
357 return 1;
358 }
359
Tai Lya4d748b2023-03-28 22:06:56 +0000360 if (outputs[0]->getDtype() != TOSA_REF_TYPE_INT32)
Kevin Chengcc61be32021-10-14 17:09:57 -0700361 {
362 printNodeValidationError("OpArgMax: Output data type not supported for this configuration of operator");
363 return 1;
364 }
365
Eric Kunzee5e26762020-10-13 16:11:07 -0700366 input = dynamic_cast<TosaReference::TensorTemplate<TIn>*>(inputs[0]);
367 output = dynamic_cast<TosaReference::TensorTemplate<TOut>*>(outputs[0]);
368
Kevin Chengcc61be32021-10-14 17:09:57 -0700369 if (attribute->axis() < 0 || attribute->axis() >= input->getRank())
370 {
371 printNodeValidationError("OpArgMax: Axis needs to be within [0, rank(input)]");
372 return 1;
373 }
374
375 bool shape_check = true;
376 for (int32_t i = 0; i < input->getRank(); i++)
377 {
378 if (i < attribute->axis())
379 {
380 if (input->getShape()[i] != output->getShape()[i])
381 {
382 shape_check = false;
383 break;
384 }
385 }
386 else if (i > attribute->axis())
387 {
388 if (input->getShape()[i] != output->getShape()[i - 1])
389 {
390 shape_check = false;
391 break;
392 }
393 }
394 // No need to check i == axis
395 }
396 if (!shape_check)
397 {
398 printNodeValidationError("OpArgMax: Mismatch between output shape provided and expected output shape");
399 return 1;
400 }
401
Eric Kunzee5e26762020-10-13 16:11:07 -0700402 return 0;
403}
404
Tai Lya4d748b2023-03-28 22:06:56 +0000405template <int Rank, TOSA_REF_TYPE Dtype>
Eric Kunzee5e26762020-10-13 16:11:07 -0700406int OpArgMax<Rank, Dtype>::eval()
407{
408 Eigen::Tensor<DenseIndex, Rank - 1> index = this->input->getTensor().argmax(attribute->axis());
409
410 this->output->getTensor() = index.unaryExpr([](DenseIndex in) -> OutEigenType { return (OutEigenType)in; });
411
412 return GraphNode::eval();
413}
414
Tai Lya4d748b2023-03-28 22:06:56 +0000415template <TOSA_REF_TYPE Dtype, TOSA_REF_TYPE AccDtype>
James Ward8b390432022-08-12 20:48:56 +0100416OpAvgPool2d<Dtype, AccDtype>::OpAvgPool2d(SubgraphTraverser* sgt_,
Kevin Chengacb550f2021-06-29 15:32:19 -0700417 TosaAttributeBase* attribute_,
Kevin Chengacb550f2021-06-29 15:32:19 -0700418 uint64_t id_)
419 : GraphNode(sgt_, Op_AVG_POOL2D, id_)
Eric Kunzee5e26762020-10-13 16:11:07 -0700420{
421 setRequiredOperands(1, 1);
422 setRequiredRank(4);
423
Kevin Cheng93a16282021-08-31 16:14:03 -0700424 INIT_ATTRIBUTE(Pool);
Eric Kunzee5e26762020-10-13 16:11:07 -0700425}
426
Tai Lya4d748b2023-03-28 22:06:56 +0000427template <TOSA_REF_TYPE Dtype, TOSA_REF_TYPE AccDtype>
James Ward8b390432022-08-12 20:48:56 +0100428OpAvgPool2d<Dtype, AccDtype>::~OpAvgPool2d()
Eric Kunzee5e26762020-10-13 16:11:07 -0700429{
430 if (attribute)
431 delete attribute;
432}
433
Tai Lya4d748b2023-03-28 22:06:56 +0000434template <TOSA_REF_TYPE Dtype, TOSA_REF_TYPE AccDtype>
James Ward8b390432022-08-12 20:48:56 +0100435int OpAvgPool2d<Dtype, AccDtype>::checkTensorAttributes()
Eric Kunzee5e26762020-10-13 16:11:07 -0700436{
437 if (validateRequiredOperands())
438 return 1;
439
440 if (validateRequiredRank(inputs[0]) || validateRequiredRank(outputs[0]))
441 {
442 return 1;
443 }
444
445 if (inputs[0]->matchType(*outputs[0]))
446 {
447 printNodeValidationError("OpAvgPool2d: input and output tensor type mismatch");
448 return 1;
449 }
450
451 in = dynamic_cast<TosaReference::TensorTemplate<TIn>*>(inputs[0]);
452 out = dynamic_cast<TosaReference::TensorTemplate<TOut>*>(outputs[0]);
453
Tai Lya4d748b2023-03-28 22:06:56 +0000454 ERROR_IF(Dtype != TOSA_REF_TYPE_INT8 && attribute->input_zp() != 0,
455 "OpAvgPool2d: Input zeropoint must be zero for non int8_t data");
456 ERROR_IF(Dtype != TOSA_REF_TYPE_INT8 && attribute->output_zp() != 0,
457 "OpAvgPool2d: Output zeropoint must be zero for non int8_t data");
Eric Kunzee5e26762020-10-13 16:11:07 -0700458
Kevin Cheng7eb93d72021-10-09 01:26:08 +0000459 std::string msg;
Kevin Cheng9fe17242021-11-10 01:04:39 +0000460 if (check_pool2d_attribute(attribute, in->getShape(), out->getShape(), msg))
Eric Kunzee5e26762020-10-13 16:11:07 -0700461 {
Kevin Cheng7eb93d72021-10-09 01:26:08 +0000462 msg = "OpAvgPool2d: " + msg;
463 printNodeValidationError(msg.c_str());
Eric Kunzee5e26762020-10-13 16:11:07 -0700464 return 1;
465 }
466
467 return 0;
468}
469
Eric Kunze830add42022-01-25 22:56:46 -0800470// This calculates the number of padding elements used for each location along an axis
471// Average pooling only divides by the number of elements used, not including padding.
472// This function uses left/right, but is also used for vertical padding with top/bottom
Tai Lya4d748b2023-03-28 22:06:56 +0000473template <TOSA_REF_TYPE Dtype, TOSA_REF_TYPE AccDtype>
474ETensor1<int32_t> OpAvgPool2d<Dtype, AccDtype>::calculate_div_map_1d(
475 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 -0700476{
477 ETensor1<int32_t> result(out_size);
478
Eric Kunzee5e26762020-10-13 16:11:07 -0700479 result.setConstant(kernel_size);
480
Eric Kunze830add42022-01-25 22:56:46 -0800481 // adjust divisors on the left side for padding
482 // We start at the leftmost output element, and remove pad_left - (index * stride) elements
483 // until we have no more padding being used
Eric Kunze67a91552022-02-02 11:27:21 -0800484 for(int index = 0; (index <= pad_left / stride) && (index < out_size); index++) {
Eric Kunze830add42022-01-25 22:56:46 -0800485 int32_t adjust = pad_left - (index * stride);
486 result(index) -= adjust;
Eric Kunzee5e26762020-10-13 16:11:07 -0700487 }
488
Eric Kunze830add42022-01-25 22:56:46 -0800489 // The process repeats on the right side. Padding starts taking effect as we
490 // near the rightmost input element. The first output element which touches
491 // padding is defined in the initialization of index below. Then we keep moving
492 // to the right, increasing padding until we get to the last output element.
493 int index = std::max(0, ((pad_left + in_size - kernel_size) / stride) + 1);
494 for (; index < out_size; index++) {
495 int32_t adjust = ((index * stride) + kernel_size) - (pad_left + in_size);
496 result(index) -= adjust;
Eric Kunzee5e26762020-10-13 16:11:07 -0700497 }
Eric Kunzee5e26762020-10-13 16:11:07 -0700498 return result;
499}
500
501// assuming input and output tensor have same scales like tflite reference
502// so no need to scale input and output
Tai Lya4d748b2023-03-28 22:06:56 +0000503template <TOSA_REF_TYPE Dtype, TOSA_REF_TYPE AccDtype>
James Ward8b390432022-08-12 20:48:56 +0100504int OpAvgPool2d<Dtype, AccDtype>::eval()
Eric Kunzee5e26762020-10-13 16:11:07 -0700505{
506 int in_batch = this->in->getShape()[0];
507 int in_height = this->in->getShape()[1];
508 int in_width = this->in->getShape()[2];
509 int in_channels = this->in->getShape()[3];
510
511 int out_batch = this->out->getShape()[0];
512 int out_height = this->out->getShape()[1];
513 int out_width = this->out->getShape()[2];
514 int out_channels = this->out->getShape()[3];
515
Kevin Chengacb550f2021-06-29 15:32:19 -0700516 ERROR_IF(in_batch != out_batch, "OpAvgPool2d: tensor batch mismatch %d != %d", in_batch, out_batch);
517 ERROR_IF(in_channels != out_channels, "OpAvgPool2d: tensor channel mismatch %d != %d", in_channels, out_channels);
Eric Kunzee5e26762020-10-13 16:11:07 -0700518
TatWai Chong86c403b2022-06-06 20:46:01 -0700519 int pad_top = this->attribute->pad()[0];
520 int pad_bottom = this->attribute->pad()[1];
521 int pad_left = this->attribute->pad()[2];
522 int pad_right = this->attribute->pad()[3];
Jerry Gea793f462023-04-11 00:05:02 +0000523 int kernel_y = this->attribute->kernel()[0];
524 int kernel_x = this->attribute->kernel()[1];
525 int stride_y = this->attribute->stride()[0];
526 int stride_x = this->attribute->stride()[1];
527
528 // Check Tosa Level
529 auto tosa_level = g_func_config.tosa_level;
530 LEVEL_CHECK(kernel_y <= tosa_level.MAX_KERNEL, "kernel_y should be smaller than or equal to MAX_KERNEL");
531 LEVEL_CHECK(kernel_x <= tosa_level.MAX_KERNEL, "kernel_x should be smaller than or equal to MAX_KERNEL");
532 LEVEL_CHECK(stride_y <= tosa_level.MAX_STRIDE, "stride_y should be smaller than or equal to MAX_STRIDE");
533 LEVEL_CHECK(stride_x <= tosa_level.MAX_STRIDE, "stride_x should be smaller than or equal to MAX_STRIDE");
534 LEVEL_CHECK(pad_top <= tosa_level.MAX_KERNEL, "pad_top should be smaller than or equal to MAX_KERNEL");
535 LEVEL_CHECK(pad_bottom <= tosa_level.MAX_KERNEL, "pad_bottom should be smaller than or equal to MAX_KERNEL");
536 LEVEL_CHECK(pad_left <= tosa_level.MAX_KERNEL, "pad_left should be smaller than or equal to MAX_KERNEL");
537 LEVEL_CHECK(pad_right <= tosa_level.MAX_KERNEL, "pad_right should be smaller than or equal to MAX_KERNEL");
Eric Kunzee5e26762020-10-13 16:11:07 -0700538
Tai Lya4d748b2023-03-28 22:06:56 +0000539 TOSA_REF_TYPE accum_dtype = ConvertDType(this->attribute->accum_dtype());
James Ward8b390432022-08-12 20:48:56 +0100540
Eric Kunzee5e26762020-10-13 16:11:07 -0700541 DEBUG_INFO(OP,
542 "perform AvgPool2d, input.shape=[%d,%d,%d,%d], output.shape=[%d,%d,%d,%d], kernel=[%d,%d], "
James Ward8b390432022-08-12 20:48:56 +0100543 "stride=[%d,%d], pad=[%d,%d,%d,%d], accum_dtype=%s",
Jerry Gea793f462023-04-11 00:05:02 +0000544 in_batch, in_height, in_width, in_channels, out_batch, out_height, out_width, out_channels, kernel_y,
545 kernel_x, stride_y, stride_x, pad_top, pad_bottom, pad_left, pad_right, EnumNamesDType()[accum_dtype]);
Eric Kunzee5e26762020-10-13 16:11:07 -0700546
547 Eigen::array<Eigen::Index, 2> im2col_input_dims;
Jerry Gea793f462023-04-11 00:05:02 +0000548 im2col_input_dims[0] = kernel_y * kernel_x;
Eric Kunzee5e26762020-10-13 16:11:07 -0700549 im2col_input_dims[1] = out_batch * out_height * out_width * out_channels;
550
551 Eigen::array<Eigen::Index, 4> col2im_output_dims;
552 col2im_output_dims[0] = out_batch;
553 col2im_output_dims[1] = out_height;
554 col2im_output_dims[2] = out_width;
555 col2im_output_dims[3] = out_channels;
556
TatWai Chong86c403b2022-06-06 20:46:01 -0700557 Eigen::array<std::pair<int32_t, int32_t>, 4> pad;
558 pad[0] = std::make_pair(0, 0);
559 pad[1] = std::make_pair(pad_top, pad_bottom);
560 pad[2] = std::make_pair(pad_left, pad_right);
561 pad[3] = std::make_pair(0, 0);
Eric Kunzee5e26762020-10-13 16:11:07 -0700562
563 ETensor4<InEigenType> input_val = this->in->getTensor();
Tai Lya4d748b2023-03-28 22:06:56 +0000564 if (Dtype == TOSA_REF_TYPE_INT8)
Eric Kunzee5e26762020-10-13 16:11:07 -0700565 {
Eric Kunzeb5fabec2022-06-07 05:20:44 +0000566 input_val = input_val - (InEigenType)attribute->input_zp();
Eric Kunzee5e26762020-10-13 16:11:07 -0700567 }
568
TatWai Chong86c403b2022-06-06 20:46:01 -0700569 ETensor4<InEigenType> input_padded = input_val.pad(pad);
Eric Kunzee5e26762020-10-13 16:11:07 -0700570
571 // assuming input and output have same scales
572 // so input and output scaling is not required
573 // TODO: check if this assumption TOSA made
574
575 // extract_image_patches() output [N, KH, KW, H * W, C]
576 // transpose to [KH, KW, N, H * W, C]
577 // reshape to [KH * KW, N * H * W * C]
578 ETensor2<InEigenType> input_extract_patches =
Jerry Gea793f462023-04-11 00:05:02 +0000579 input_padded.extract_image_patches(kernel_y, kernel_x, stride_y, stride_x, 1, 1, Eigen::PADDING_VALID)
Eric Kunzee5e26762020-10-13 16:11:07 -0700580 .shuffle(Eigen::array<Eigen::Index, 5>{ 1, 2, 0, 3, 4 })
581 .reshape(im2col_input_dims);
582
583 // 1D result with [N * H * W * C]
584 ETensor1<AccEigenType> out_1d(this->out->getElementCount());
585 out_1d.setZero();
586
587 // sum pool
588 for (size_t i = 0; i < this->out->getElementCount(); i++)
589 {
Jerry Gea793f462023-04-11 00:05:02 +0000590 for (int32_t j = 0; j < kernel_y * kernel_x; j++)
Eric Kunzee5e26762020-10-13 16:11:07 -0700591 {
592 out_1d(i) += (AccEigenType)input_extract_patches(j, i);
593 }
594 }
595
596 // reshape result to [N, H, W, C] and divide with div_map
597 ETensor4<AccEigenType> sum = out_1d.reshape(col2im_output_dims);
598
599 // calculate 1d height/width div_map (number of elements this pooling window covers)
600 // and outer product to get 2d div_map, then reshape/broadcast to [N, H, W, C]
Jeremy Johnson44eb88d2023-04-24 09:49:58 +0100601 ETensor1<int32_t> div_map_h = calculate_div_map_1d(in_height, out_height, kernel_y, stride_y, pad_top, pad_bottom);
Jerry Gea793f462023-04-11 00:05:02 +0000602 ETensor1<int32_t> div_map_w = calculate_div_map_1d(in_width, out_width, kernel_x, stride_x, pad_left, pad_right);
Eric Kunzee5e26762020-10-13 16:11:07 -0700603 Eigen::array<Eigen::IndexPair<Eigen::Index>, 1> contract_dims = { Eigen::IndexPair<Eigen::Index>(1, 0) };
604 Eigen::array<Eigen::Index, 4> bcast{ out_batch, 1, 1, out_channels };
605
James Ward24dbc422022-10-19 12:20:31 +0100606 ETensor2<int32_t> dm2_w = div_map_w.reshape(Eigen::array<Eigen::Index, 2>{ 1, out_width });
607 ETensor2<int32_t> dm2_h = div_map_h.reshape(Eigen::array<Eigen::Index, 2>{ out_height, 1 });
Eric Kunzee5e26762020-10-13 16:11:07 -0700608 ETensor4<int32_t> div_map =
James Ward24dbc422022-10-19 12:20:31 +0100609 dm2_h.contract(dm2_w, contract_dims)
Eric Kunzee5e26762020-10-13 16:11:07 -0700610 .reshape(Eigen::array<Eigen::Index, 4>{ 1, out_height, out_width, 1 })
611 .broadcast(bcast);
Tai Lya4d748b2023-03-28 22:06:56 +0000612 if (Dtype != TOSA_REF_TYPE_FP32 && Dtype != TOSA_REF_TYPE_FP16 && Dtype != TOSA_REF_TYPE_BF16 &&
613 Dtype != TOSA_REF_TYPE_FP64)
Eric Kunzee5e26762020-10-13 16:11:07 -0700614 {
Kevin Chengacb550f2021-06-29 15:32:19 -0700615 try
616 {
617 this->out->getTensor() = sum.binaryExpr(div_map, [](AccEigenType value, int32_t div) -> OutEigenType {
618 int32_t multiplier, shift;
619 TosaReference::QuantUtil::reciprocal_scale(div, multiplier, shift);
Eric Kunzee5e26762020-10-13 16:11:07 -0700620
Kevin Chengacb550f2021-06-29 15:32:19 -0700621 return (OutEigenType)TosaReference::QuantUtil::apply_scale_32(value, multiplier, shift, false);
622 });
623 }
624 catch (std::string desc)
625 {
626 REQUIRE(false, "OpAvgPool2d apply_scale_32() fails: %s.", desc.c_str());
627 }
Eric Kunzeb5fabec2022-06-07 05:20:44 +0000628 this->out->getTensor() = this->out->getTensor() + (OutEigenType)(attribute->output_zp());
Eric Kunzee5e26762020-10-13 16:11:07 -0700629 this->out->getTensor() = this->out->getTensor().cwiseMax((OutEigenType)QMin);
630 this->out->getTensor() = this->out->getTensor().cwiseMin((OutEigenType)QMax);
631 }
632 else
633 {
James Ward24dbc422022-10-19 12:20:31 +0100634 // Case for float-types
Eric Kunzee5e26762020-10-13 16:11:07 -0700635 this->out->getTensor() = (sum / div_map.template cast<AccEigenType>()).template cast<OutEigenType>();
636 }
637
638 return GraphNode::eval();
639}
640
Tai Lya4d748b2023-03-28 22:06:56 +0000641template <TOSA_REF_TYPE InDtype, TOSA_REF_TYPE WeightDtype, TOSA_REF_TYPE OutDtype>
James Wardd34b3fc2023-01-18 14:51:25 +0000642OpConv2d<InDtype, WeightDtype, OutDtype>::OpConv2d(SubgraphTraverser* sgt_,
Kevin Chengacb550f2021-06-29 15:32:19 -0700643 TosaAttributeBase* attribute_,
Kevin Chengacb550f2021-06-29 15:32:19 -0700644 uint64_t id_)
645 : GraphNode(sgt_, Op_CONV2D, id_)
Eric Kunzee5e26762020-10-13 16:11:07 -0700646{
647 setRequiredOperands(3, 1);
648 setRequiredRank(4);
649
Kevin Cheng93a16282021-08-31 16:14:03 -0700650 INIT_ATTRIBUTE(Conv);
Eric Kunzee5e26762020-10-13 16:11:07 -0700651}
652
Tai Lya4d748b2023-03-28 22:06:56 +0000653template <TOSA_REF_TYPE InDtype, TOSA_REF_TYPE WeightDtype, TOSA_REF_TYPE OutDtype>
James Wardd34b3fc2023-01-18 14:51:25 +0000654OpConv2d<InDtype, WeightDtype, OutDtype>::~OpConv2d()
Eric Kunzee5e26762020-10-13 16:11:07 -0700655{
656 if (attribute)
657 delete attribute;
Eric Kunzee5e26762020-10-13 16:11:07 -0700658}
659
Tai Lya4d748b2023-03-28 22:06:56 +0000660template <TOSA_REF_TYPE InDtype, TOSA_REF_TYPE WeightDtype, TOSA_REF_TYPE OutDtype>
James Wardd34b3fc2023-01-18 14:51:25 +0000661int OpConv2d<InDtype, WeightDtype, OutDtype>::checkTensorAttributes()
Eric Kunzee5e26762020-10-13 16:11:07 -0700662{
663 if (validateRequiredOperands())
664 return 1;
665
666 if (validateRequiredRank(inputs[0]) || validateRequiredRank(inputs[1]) || validateRequiredRank(outputs[0]))
667 {
668 return 1;
669 }
670
671 // 'bias' checked separatedly since it doens't make sense to make required rank ranging from 1 to 4
672 if (inputs[2]->getRank() != 1)
673 {
674 printNodeValidationError("OpConv2d: bias tensor must be rank 1");
675 }
676
James Wardd34b3fc2023-01-18 14:51:25 +0000677 ERROR_IF(outputs[0]->getDtype() != OutDtype,
James Ward8b390432022-08-12 20:48:56 +0100678 "OpConv2d: Output data type not supported for this configuration of operator");
Kevin Chengcc61be32021-10-14 17:09:57 -0700679
Eric Kunzee5e26762020-10-13 16:11:07 -0700680 input = dynamic_cast<TosaReference::TensorTemplate<TIn>*>(inputs[0]);
681 weight = dynamic_cast<TosaReference::TensorTemplate<TWeight>*>(inputs[1]);
682 bias = dynamic_cast<TosaReference::TensorTemplate<TBias>*>(inputs[2]);
James Ward8b390432022-08-12 20:48:56 +0100683 output = dynamic_cast<TosaReference::TensorTemplate<TOut>*>(outputs[0]);
Eric Kunzee5e26762020-10-13 16:11:07 -0700684
Kevin Cheng9fe17242021-11-10 01:04:39 +0000685 std::string msg;
Eric Kunzeb5fabec2022-06-07 05:20:44 +0000686 if (check_conv_attribute(attribute, 2 /* conv_dimension */, input->getShape(), output->getShape(),
Jeremy Johnson4a6fb9b2022-04-26 15:47:21 +0100687 weight->getShape(), 1 /* offset_kernel */, InDtype, WeightDtype, msg))
Eric Kunzee5e26762020-10-13 16:11:07 -0700688 {
Kevin Cheng9fe17242021-11-10 01:04:39 +0000689 msg = "OpConv2d: " + msg;
690 printNodeValidationError(msg.c_str());
Eric Kunzee5e26762020-10-13 16:11:07 -0700691 return 1;
692 }
693
Eric Kunzee5e26762020-10-13 16:11:07 -0700694 return 0;
695}
696
Tai Lya4d748b2023-03-28 22:06:56 +0000697template <TOSA_REF_TYPE InDtype, TOSA_REF_TYPE WeightDtype, TOSA_REF_TYPE OutDtype>
James Wardd34b3fc2023-01-18 14:51:25 +0000698int OpConv2d<InDtype, WeightDtype, OutDtype>::eval()
Eric Kunzee5e26762020-10-13 16:11:07 -0700699{
700 int in_batch = this->input->getShape()[0];
701 int in_height = this->input->getShape()[1];
702 int in_width = this->input->getShape()[2];
703 int in_channels = this->input->getShape()[3];
704
705 int f_out_channels = this->weight->getShape()[0];
706 int f_height = this->weight->getShape()[1];
707 int f_width = this->weight->getShape()[2];
708 int f_in_channels = this->weight->getShape()[3];
709
710 int b_out_channels = this->bias->getShape()[0];
711
712 int out_batch = this->output->getShape()[0];
713 int out_height = this->output->getShape()[1];
714 int out_width = this->output->getShape()[2];
715 int out_channels = this->output->getShape()[3];
716
Kevin Chengacb550f2021-06-29 15:32:19 -0700717 ERROR_IF(in_batch != out_batch, "OpConv2d: tensor batch mismatch %d != %d", in_batch, out_batch);
718 ERROR_IF(f_in_channels != in_channels, "OpConv2d: tensor input channel mismatch %d != %d", f_in_channels,
719 in_channels);
720 ERROR_IF(f_out_channels != out_channels, "OpConv2d: tensor output channel mismatch %d != %d", f_out_channels,
721 out_channels);
722 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 -0700723
TatWai Chong86c403b2022-06-06 20:46:01 -0700724 int pad_top = this->attribute->pad()[0];
725 int pad_bottom = this->attribute->pad()[1];
726 int pad_left = this->attribute->pad()[2];
727 int pad_right = this->attribute->pad()[3];
728
Jerry Gea793f462023-04-11 00:05:02 +0000729 int stride_y = this->attribute->stride()[0];
730 int stride_x = this->attribute->stride()[1];
731 int dilation_y = this->attribute->dilation()[0];
732 int dilation_x = this->attribute->dilation()[1];
733
734 // Check Tosa Level
735 auto tosa_level = g_func_config.tosa_level;
736 LEVEL_CHECK(dilation_y * f_height <= tosa_level.MAX_KERNEL, "dilation_y * KH should be smaller than or equal to MAX_KERNEL");
737 LEVEL_CHECK(dilation_x * f_width <= tosa_level.MAX_KERNEL, "dilation_x * KW should be smaller than or equal to MAX_KERNEL");
738 LEVEL_CHECK(pad_top <= tosa_level.MAX_KERNEL, "pad_top should be smaller than or equal to MAX_KERNEL");
739 LEVEL_CHECK(pad_bottom <= tosa_level.MAX_KERNEL, "pad_bottom should be smaller than or equal to MAX_KERNEL");
740 LEVEL_CHECK(pad_left <= tosa_level.MAX_KERNEL, "pad_left should be smaller than or equal to MAX_KERNEL");
741 LEVEL_CHECK(pad_right <= tosa_level.MAX_KERNEL, "pad_right should be smaller than or equal to MAX_KERNEL");
742 LEVEL_CHECK(stride_y <= tosa_level.MAX_STRIDE, "stride_y should be smaller than or equal to MAX_STRIDE");
743 LEVEL_CHECK(stride_x <= tosa_level.MAX_STRIDE, "stride_x should be smaller than or equal to MAX_STRIDE");
Eric Kunzee5e26762020-10-13 16:11:07 -0700744
745 DEBUG_INFO(OP,
746 "perform OpConv2d, input.shape=[%d,%d,%d,%d], weight.shape=[%d,%d,%d,%d], output.shape=[%d,%d,%d,%d], "
James Wardd34b3fc2023-01-18 14:51:25 +0000747 "stride=[%d,%d], dilation=[%d,%d], pad=[%d,%d,%d,%d]",
Eric Kunzee5e26762020-10-13 16:11:07 -0700748 in_batch, in_height, in_width, in_channels, f_height, f_width, f_in_channels, f_out_channels, out_batch,
Jerry Gea793f462023-04-11 00:05:02 +0000749 out_height, out_width, out_channels, stride_y, stride_x, dilation_y, dilation_x, pad_top,
James Wardd34b3fc2023-01-18 14:51:25 +0000750 pad_bottom, pad_left, pad_right);
Eric Kunzee5e26762020-10-13 16:11:07 -0700751
752 // GEMM-conv2d, left matrix is input, right matrix is weight
753 Eigen::array<Eigen::Index, 2> im2col_input_dims;
754 im2col_input_dims[0] = out_batch * out_height * out_width;
755 im2col_input_dims[1] = f_height * f_width * f_in_channels;
756
757 Eigen::array<Eigen::Index, 2> im2col_weight_dims;
758 im2col_weight_dims[0] = f_height * f_width * f_in_channels;
759 im2col_weight_dims[1] = f_out_channels;
760
761 Eigen::array<Eigen::Index, 2> bias_reshaped_dims;
762 bias_reshaped_dims[0] = 1;
763 bias_reshaped_dims[1] = b_out_channels;
764
765 Eigen::array<Eigen::Index, 4> weight_zp_bcast_dims;
766 weight_zp_bcast_dims[0] = f_height;
767 weight_zp_bcast_dims[1] = f_width;
768 weight_zp_bcast_dims[2] = f_in_channels;
769
770 Eigen::array<Eigen::Index, 2> bias_bcast_dims;
771 bias_bcast_dims[0] = out_batch * out_height * out_width;
772 bias_bcast_dims[1] = 1;
773
774 Eigen::array<Eigen::Index, 4> col2im_output_dims;
775 col2im_output_dims[0] = out_batch;
776 col2im_output_dims[1] = out_height;
777 col2im_output_dims[2] = out_width;
778 col2im_output_dims[3] = out_channels;
779
780 Eigen::array<Eigen::IndexPair<Eigen::Index>, 1> contract_dims = { Eigen::IndexPair<Eigen::Index>(1, 0) };
781
TatWai Chong86c403b2022-06-06 20:46:01 -0700782 Eigen::array<std::pair<int32_t, int32_t>, 4> pad;
783 pad[0] = std::make_pair(0, 0);
784 pad[1] = std::make_pair(pad_top, pad_bottom);
785 pad[2] = std::make_pair(pad_left, pad_right);
786 pad[3] = std::make_pair(0, 0);
Eric Kunzee5e26762020-10-13 16:11:07 -0700787
788 TIn input_val = this->input->getTensor();
789 TWeight weight_val = this->weight->getTensor();
Tai Lya4d748b2023-03-28 22:06:56 +0000790 if (InDtype == TOSA_REF_TYPE_INT8 || WeightDtype == TOSA_REF_TYPE_INT8)
Eric Kunzee5e26762020-10-13 16:11:07 -0700791 {
Eric Kunzeb5fabec2022-06-07 05:20:44 +0000792 input_val = input_val - (InEigenType)attribute->input_zp();
793 weight_val = weight_val - (WeightEigenType)attribute->weight_zp();
Eric Kunzee5e26762020-10-13 16:11:07 -0700794 }
795
TatWai Chong86c403b2022-06-06 20:46:01 -0700796 ETensor4<InEigenType> input_padded = input_val.pad(pad);
Eric Kunzee5e26762020-10-13 16:11:07 -0700797
798 // extract_image_patches() output [N, KH, KW, H * W, C]
799 // need to transpose to [N, H * W, KH, KW, C]
800 ETensor5<InEigenType> input_extract_patches =
801 input_padded
Jerry Gea793f462023-04-11 00:05:02 +0000802 .extract_image_patches(f_height, f_width, stride_y, stride_x, dilation_y, dilation_x, Eigen::PADDING_VALID)
Eric Kunzee5e26762020-10-13 16:11:07 -0700803 .shuffle(Eigen::array<Eigen::Index, 5>{ 0, 3, 1, 2, 4 });
804
805 // reshape input to [N * H * W, KH * KW * C]
806 ETensor2<InEigenType> im2col_input = input_extract_patches.reshape(im2col_input_dims);
807
808 // transpose and reshape weight from [OC, H, W, IC] to [H * W * IC, OC]
809 ETensor2<WeightEigenType> im2col_weight =
James Ward8b390432022-08-12 20:48:56 +0100810 weight_val.shuffle(Eigen::array<Eigen::Index, 4>({ 1, 2, 3, 0 })).reshape(im2col_weight_dims);
Eric Kunzee5e26762020-10-13 16:11:07 -0700811
812 // don't need to apply bias_multiplier ( * bias_scale and >> bias_shift) since tflite already scale it
813 // and reshaped from [C] to [1, C], and broadcast to [N * H * W, C]
James Ward8b390432022-08-12 20:48:56 +0100814 ETensor2<OutEigenType> bias_2d = (this->bias->getTensor().reshape(bias_reshaped_dims).broadcast(bias_bcast_dims)).template cast<OutEigenType>();
Eric Kunzee5e26762020-10-13 16:11:07 -0700815
816 // output matrix is [N * H * W, C]
James Ward8b390432022-08-12 20:48:56 +0100817 ETensor2<OutEigenType> contracted_result =
818 (im2col_input.template cast<AccEigenType>().contract(im2col_weight.template cast<AccEigenType>(), contract_dims)).template cast<OutEigenType>();
Eric Kunzee5e26762020-10-13 16:11:07 -0700819
820 // adding bias
James Ward8b390432022-08-12 20:48:56 +0100821 ETensor2<OutEigenType> biased_output = contracted_result + bias_2d;
Eric Kunzee5e26762020-10-13 16:11:07 -0700822
823 // reshape back to [N, H, W, C]
824 this->output->getTensor() = biased_output.reshape(col2im_output_dims);
825
Tai Lya4d748b2023-03-28 22:06:56 +0000826 if (OutDtype == TOSA_REF_TYPE_INT48)
Eric Kunzee5e26762020-10-13 16:11:07 -0700827 {
James Ward8b390432022-08-12 20:48:56 +0100828 this->output->getTensor() = this->output->getTensor().cwiseMax((OutEigenType)AccQMin);
829 this->output->getTensor() = this->output->getTensor().cwiseMin((OutEigenType)AccQMax);
Eric Kunzee5e26762020-10-13 16:11:07 -0700830 }
831
832 return GraphNode::eval();
833}
834
Tai Lya4d748b2023-03-28 22:06:56 +0000835template <TOSA_REF_TYPE InDtype, TOSA_REF_TYPE WeightDtype, TOSA_REF_TYPE OutDtype>
James Wardd34b3fc2023-01-18 14:51:25 +0000836OpConv3d<InDtype, WeightDtype, OutDtype>::OpConv3d(SubgraphTraverser* sgt_,
Kevin Cheng1533b852021-09-01 12:51:58 -0700837 TosaAttributeBase* attribute_,
Kevin Cheng1533b852021-09-01 12:51:58 -0700838 uint64_t id_)
839 : GraphNode(sgt_, Op_CONV3D, id_)
840{
841 setRequiredOperands(3, 1);
842 setRequiredRank(5);
843
844 INIT_ATTRIBUTE(Conv);
Kevin Cheng1533b852021-09-01 12:51:58 -0700845}
846
Tai Lya4d748b2023-03-28 22:06:56 +0000847template <TOSA_REF_TYPE InDtype, TOSA_REF_TYPE WeightDtype, TOSA_REF_TYPE OutDtype>
James Wardd34b3fc2023-01-18 14:51:25 +0000848OpConv3d<InDtype, WeightDtype, OutDtype>::~OpConv3d()
Kevin Cheng1533b852021-09-01 12:51:58 -0700849{
850 if (attribute)
851 delete attribute;
Kevin Cheng1533b852021-09-01 12:51:58 -0700852}
853
Tai Lya4d748b2023-03-28 22:06:56 +0000854template <TOSA_REF_TYPE InDtype, TOSA_REF_TYPE WeightDtype, TOSA_REF_TYPE OutDtype>
James Wardd34b3fc2023-01-18 14:51:25 +0000855int OpConv3d<InDtype, WeightDtype, OutDtype>::checkTensorAttributes()
Kevin Cheng1533b852021-09-01 12:51:58 -0700856{
857 if (validateRequiredOperands())
858 return 1;
859
860 if (validateRequiredRank(inputs[0]) || validateRequiredRank(inputs[1]) || validateRequiredRank(outputs[0]))
861 {
862 return 1;
863 }
864
865 // 'bias' checked separatedly since it doens't make sense to make required rank ranging from 1 to 4
866 if (inputs[2]->getRank() != 1)
867 {
868 printNodeValidationError("OpConv3d: bias tensor must be rank 1");
869 }
870
James Wardd34b3fc2023-01-18 14:51:25 +0000871 ERROR_IF(outputs[0]->getDtype() != OutDtype,
James Ward8b390432022-08-12 20:48:56 +0100872 "OpConv3d: Output data type not supported for this configuration of operator");
Kevin Chengcc61be32021-10-14 17:09:57 -0700873
Kevin Cheng1533b852021-09-01 12:51:58 -0700874 input = dynamic_cast<TosaReference::TensorTemplate<TIn>*>(inputs[0]);
875 weight = dynamic_cast<TosaReference::TensorTemplate<TWeight>*>(inputs[1]);
876 bias = dynamic_cast<TosaReference::TensorTemplate<TBias>*>(inputs[2]);
James Ward8b390432022-08-12 20:48:56 +0100877 output = dynamic_cast<TosaReference::TensorTemplate<TOut>*>(outputs[0]);
Kevin Cheng1533b852021-09-01 12:51:58 -0700878
Kevin Cheng9fe17242021-11-10 01:04:39 +0000879 std::string msg;
Eric Kunzeb5fabec2022-06-07 05:20:44 +0000880 if (check_conv_attribute(attribute, 3 /* conv_dimension */, input->getShape(), output->getShape(),
Jeremy Johnson4a6fb9b2022-04-26 15:47:21 +0100881 weight->getShape(), 1 /* offset_kernel */, InDtype, WeightDtype, msg))
Kevin Cheng1533b852021-09-01 12:51:58 -0700882 {
Kevin Cheng9fe17242021-11-10 01:04:39 +0000883 msg = "OpConv3d: " + msg;
884 printNodeValidationError(msg.c_str());
Kevin Cheng1533b852021-09-01 12:51:58 -0700885 return 1;
886 }
887
Kevin Cheng1533b852021-09-01 12:51:58 -0700888 return 0;
889}
890
Tai Lya4d748b2023-03-28 22:06:56 +0000891template <TOSA_REF_TYPE InDtype, TOSA_REF_TYPE WeightDtype, TOSA_REF_TYPE OutDtype>
James Wardd34b3fc2023-01-18 14:51:25 +0000892int OpConv3d<InDtype, WeightDtype, OutDtype>::eval()
Kevin Cheng1533b852021-09-01 12:51:58 -0700893{
894 int in_batch = this->input->getShape()[0];
895 int in_depth = this->input->getShape()[1];
896 int in_height = this->input->getShape()[2];
897 int in_width = this->input->getShape()[3];
898 int in_channels = this->input->getShape()[4];
899
900 int f_out_channels = this->weight->getShape()[0];
901 int f_depth = this->weight->getShape()[1];
902 int f_height = this->weight->getShape()[2];
903 int f_width = this->weight->getShape()[3];
904 int f_in_channels = this->weight->getShape()[4];
905
906 int b_out_channels = this->bias->getShape()[0];
907
908 int out_batch = this->output->getShape()[0];
909 int out_depth = this->output->getShape()[1];
910 int out_height = this->output->getShape()[2];
911 int out_width = this->output->getShape()[3];
912 int out_channels = this->output->getShape()[4];
913
914 ERROR_IF(in_batch != out_batch, "OpConv3d: tensor batch mismatch %d != %d", in_batch, out_batch);
915 ERROR_IF(f_in_channels != in_channels, "OpConv3d: tensor input channel mismatch %d != %d", f_in_channels,
916 in_channels);
917 ERROR_IF(f_out_channels != out_channels, "OpConv3d: tensor output channel mismatch %d != %d", f_out_channels,
918 out_channels);
919 ERROR_IF(b_out_channels != out_channels, "OpConv3d: bias channel mismatch %d != %d", b_out_channels, out_channels);
920
TatWai Chong86c403b2022-06-06 20:46:01 -0700921 int pad_d0 = this->attribute->pad()[0];
922 int pad_d1 = this->attribute->pad()[1];
923 int pad_top = this->attribute->pad()[2];
924 int pad_bottom = this->attribute->pad()[3];
925 int pad_left = this->attribute->pad()[4];
926 int pad_right = this->attribute->pad()[5];
927
Kevin Cheng1533b852021-09-01 12:51:58 -0700928 int stride_d = this->attribute->stride()[0];
Jerry Gea793f462023-04-11 00:05:02 +0000929 int stride_y = this->attribute->stride()[1];
930 int stride_x = this->attribute->stride()[2];
TatWai Chong86c403b2022-06-06 20:46:01 -0700931
Kevin Cheng1533b852021-09-01 12:51:58 -0700932 int dilation_d = this->attribute->dilation()[0];
Jerry Gea793f462023-04-11 00:05:02 +0000933 int dilation_y = this->attribute->dilation()[1];
934 int dilation_x = this->attribute->dilation()[2];
935
936 // Check Tosa Level
937 auto tosa_level = g_func_config.tosa_level;
938 LEVEL_CHECK(dilation_d * f_depth <= tosa_level.MAX_KERNEL, "dilation_d * KD should be smaller than or equal to MAX_KERNEL");
939 LEVEL_CHECK(dilation_y * f_height <= tosa_level.MAX_KERNEL, "dilation_y * KH should be smaller than or equal to MAX_KERNEL");
940 LEVEL_CHECK(dilation_x * f_width <= tosa_level.MAX_KERNEL, "dilation_x * KW should be smaller than or equal to MAX_KERNEL");
941 LEVEL_CHECK(pad_d0 <= tosa_level.MAX_KERNEL, "pad_d0 should be smaller than or equal to MAX_KERNEL");
942 LEVEL_CHECK(pad_d1 <= tosa_level.MAX_KERNEL, "pad_d1 should be smaller than or equal to MAX_KERNEL");
943 LEVEL_CHECK(pad_top <= tosa_level.MAX_KERNEL, "pad_top should be smaller than or equal to MAX_KERNEL");
944 LEVEL_CHECK(pad_bottom <= tosa_level.MAX_KERNEL, "pad_bottom should be smaller than or equal to MAX_KERNEL");
945 LEVEL_CHECK(pad_left <= tosa_level.MAX_KERNEL, "pad_left should be smaller than or equal to MAX_KERNEL");
946 LEVEL_CHECK(pad_right <= tosa_level.MAX_KERNEL, "pad_right should be smaller than or equal to MAX_KERNEL");
947 LEVEL_CHECK(stride_y <= tosa_level.MAX_STRIDE, "stride_y should be smaller than or equal to MAX_STRIDE");
948 LEVEL_CHECK(stride_x <= tosa_level.MAX_STRIDE, "stride_x should be smaller than or equal to MAX_STRIDE");
949 LEVEL_CHECK(stride_d <= tosa_level.MAX_STRIDE, "stride_d should be smaller than or equal to MAX_STRIDE");
Kevin Cheng1533b852021-09-01 12:51:58 -0700950
951 DEBUG_INFO(
952 OP,
953 "perform OpConv3d, input.shape=[%d,%d,%d,%d,%d], weight.shape=[%d,%d,%d,%d,%d], output.shape=[%d,%d,%d,%d,%d], "
James Wardd34b3fc2023-01-18 14:51:25 +0000954 "stride=[%d,%d,%d], dilation=[%d,%d,%d], pad=[%d,%d,%d,%d,%d,%d]",
Kevin Cheng1533b852021-09-01 12:51:58 -0700955 in_batch, in_depth, in_height, in_width, in_channels, f_out_channels, f_depth, f_height, f_width, f_in_channels,
Jerry Gea793f462023-04-11 00:05:02 +0000956 out_batch, out_depth, out_height, out_width, out_channels, stride_d, stride_y, stride_x, dilation_d, dilation_y,
957 dilation_x, pad_d0, pad_d1, pad_top, pad_bottom, pad_left, pad_right);
Kevin Cheng1533b852021-09-01 12:51:58 -0700958
TatWai Chong86c403b2022-06-06 20:46:01 -0700959 Eigen::array<std::pair<int32_t, int32_t>, 5> pad;
960 pad[0] = std::make_pair(0, 0);
961 pad[1] = std::make_pair(pad_d0, pad_d1);
962 pad[2] = std::make_pair(pad_top, pad_bottom);
963 pad[3] = std::make_pair(pad_left, pad_right);
964 pad[4] = std::make_pair(0, 0);
Kevin Cheng1533b852021-09-01 12:51:58 -0700965
966 TIn input_val = this->input->getTensor();
967 TWeight weight_val = this->weight->getTensor();
Tai Lya4d748b2023-03-28 22:06:56 +0000968 if (InDtype == TOSA_REF_TYPE_INT8 || WeightDtype == TOSA_REF_TYPE_INT8)
Kevin Cheng1533b852021-09-01 12:51:58 -0700969 {
Eric Kunzeb5fabec2022-06-07 05:20:44 +0000970 input_val = input_val - (InEigenType)attribute->input_zp();
971 weight_val = weight_val - (WeightEigenType)attribute->weight_zp();
Kevin Cheng1533b852021-09-01 12:51:58 -0700972 }
973
TatWai Chong86c403b2022-06-06 20:46:01 -0700974 ETensor5<InEigenType> input_padded = input_val.pad(pad);
Kevin Cheng1533b852021-09-01 12:51:58 -0700975
976 // 1. initialize with bias
977 Eigen::array<Eigen::Index, 5> reshape_dim;
978 reshape_dim.fill(1);
979 reshape_dim[4] = b_out_channels;
980
981 Eigen::array<Eigen::Index, 5> bcast;
982 bcast[0] = out_batch;
983 bcast[1] = out_depth;
984 bcast[2] = out_height;
985 bcast[3] = out_width;
986 bcast[4] = 1;
987 this->output->getTensor() = this->bias->getTensor().reshape(reshape_dim).broadcast(bcast);
988
989 // 2. direct convolution
James Ward8b390432022-08-12 20:48:56 +0100990 AccEigenType acc(0.0);
Kevin Cheng1533b852021-09-01 12:51:58 -0700991 int d_idx, h_idx, w_idx;
992
993 for (int ob = 0; ob < out_batch; ob++)
994 {
995 for (int od = 0; od < out_depth; od++)
996 {
997 for (int oh = 0; oh < out_height; oh++)
998 {
999 for (int ow = 0; ow < out_width; ow++)
1000 {
1001 for (int oc = 0; oc < out_channels; oc++)
1002 {
Eric Kunze7edb34c2022-05-16 17:34:40 -07001003 // Initialize accumulator with bias value
James Ward8b390432022-08-12 20:48:56 +01001004 acc = (AccEigenType)this->output->getTensor()(ob, od, oh, ow, oc);
Kevin Cheng1533b852021-09-01 12:51:58 -07001005 for (int fd = 0; fd < f_depth; fd++)
1006 {
1007 d_idx = od * stride_d + fd * dilation_d;
1008 for (int fh = 0; fh < f_height; fh++)
1009 {
Jerry Gea793f462023-04-11 00:05:02 +00001010 h_idx = oh * stride_y + fh * dilation_y;
Kevin Cheng1533b852021-09-01 12:51:58 -07001011 for (int fw = 0; fw < f_width; fw++)
1012 {
Jerry Gea793f462023-04-11 00:05:02 +00001013 w_idx = ow * stride_x + fw * dilation_x;
Kevin Cheng1533b852021-09-01 12:51:58 -07001014 for (int ic = 0; ic < in_channels; ic++)
1015 {
1016 acc += ((AccEigenType)input_padded(ob, d_idx, h_idx, w_idx, ic) *
1017 (AccEigenType)weight_val(oc, fd, fh, fw, ic));
1018 }
1019 }
1020 }
1021 }
James Ward8b390432022-08-12 20:48:56 +01001022 this->output->getTensor()(ob, od, oh, ow, oc) = (OutEigenType)acc;
Kevin Cheng1533b852021-09-01 12:51:58 -07001023 }
1024 }
1025 }
1026 }
1027 }
1028
Tai Lya4d748b2023-03-28 22:06:56 +00001029 if (OutDtype == TOSA_REF_TYPE_INT48)
Kevin Cheng1533b852021-09-01 12:51:58 -07001030 {
James Ward8b390432022-08-12 20:48:56 +01001031 this->output->getTensor() = this->output->getTensor().cwiseMax((OutEigenType)AccQMin);
1032 this->output->getTensor() = this->output->getTensor().cwiseMin((OutEigenType)AccQMax);
Kevin Cheng1533b852021-09-01 12:51:58 -07001033 }
1034
1035 return GraphNode::eval();
1036}
1037
Tai Lya4d748b2023-03-28 22:06:56 +00001038template <TOSA_REF_TYPE InDtype, TOSA_REF_TYPE WeightDtype, TOSA_REF_TYPE OutDtype>
James Wardd34b3fc2023-01-18 14:51:25 +00001039OpDepthwiseConv2d<InDtype, WeightDtype, OutDtype>::OpDepthwiseConv2d(SubgraphTraverser* sgt_,
Tai Lya4d748b2023-03-28 22:06:56 +00001040 TosaAttributeBase* attribute_,
1041 uint64_t id_)
Kevin Chengacb550f2021-06-29 15:32:19 -07001042 : GraphNode(sgt_, Op_DEPTHWISE_CONV2D, id_)
Eric Kunzee5e26762020-10-13 16:11:07 -07001043{
1044 setRequiredOperands(3, 1);
1045 setRequiredRank(4);
1046
Kevin Cheng93a16282021-08-31 16:14:03 -07001047 INIT_ATTRIBUTE(Conv);
Eric Kunzee5e26762020-10-13 16:11:07 -07001048}
1049
Tai Lya4d748b2023-03-28 22:06:56 +00001050template <TOSA_REF_TYPE InDtype, TOSA_REF_TYPE WeightDtype, TOSA_REF_TYPE OutDtype>
James Wardd34b3fc2023-01-18 14:51:25 +00001051OpDepthwiseConv2d<InDtype, WeightDtype, OutDtype>::~OpDepthwiseConv2d()
Eric Kunzee5e26762020-10-13 16:11:07 -07001052{
1053 if (attribute)
1054 delete attribute;
Eric Kunzee5e26762020-10-13 16:11:07 -07001055}
1056
Tai Lya4d748b2023-03-28 22:06:56 +00001057template <TOSA_REF_TYPE InDtype, TOSA_REF_TYPE WeightDtype, TOSA_REF_TYPE OutDtype>
James Wardd34b3fc2023-01-18 14:51:25 +00001058int OpDepthwiseConv2d<InDtype, WeightDtype, OutDtype>::checkTensorAttributes()
Eric Kunzee5e26762020-10-13 16:11:07 -07001059{
1060 if (validateRequiredOperands())
1061 return 1;
1062
1063 if (validateRequiredRank(inputs[0]) || validateRequiredRank(inputs[1]) || validateRequiredRank(outputs[0]))
1064 {
1065 return 1;
1066 }
1067
1068 // 'bias' checked separatedly since it doens't make sense to make required rank ranging from 1 to 4
1069 if (inputs[2]->getRank() != 1)
1070 {
1071 printNodeValidationError("OpDepthwiseConv2d: bias tensor must be rank 1");
1072 }
1073
James Wardd34b3fc2023-01-18 14:51:25 +00001074 ERROR_IF(outputs[0]->getDtype() != OutDtype,
James Ward8b390432022-08-12 20:48:56 +01001075 "OpDepthwiseConv2d: Output data type not supported for this configuration of operator");
Kevin Chengcc61be32021-10-14 17:09:57 -07001076
Eric Kunzee5e26762020-10-13 16:11:07 -07001077 input = dynamic_cast<TosaReference::TensorTemplate<TIn>*>(inputs[0]);
1078 weight = dynamic_cast<TosaReference::TensorTemplate<TWeight>*>(inputs[1]);
1079 bias = dynamic_cast<TosaReference::TensorTemplate<TBias>*>(inputs[2]);
James Ward8b390432022-08-12 20:48:56 +01001080 output = dynamic_cast<TosaReference::TensorTemplate<TOut>*>(outputs[0]);
Eric Kunzee5e26762020-10-13 16:11:07 -07001081
Kevin Cheng9fe17242021-11-10 01:04:39 +00001082 std::string msg;
Eric Kunzeb5fabec2022-06-07 05:20:44 +00001083 if (check_conv_attribute(attribute, 2 /* conv_dimension */, input->getShape(), output->getShape(),
Jeremy Johnson4a6fb9b2022-04-26 15:47:21 +01001084 weight->getShape(), 0 /* offset_kernel */, InDtype, WeightDtype, msg))
Eric Kunzee5e26762020-10-13 16:11:07 -07001085 {
Kevin Cheng9fe17242021-11-10 01:04:39 +00001086 msg = "OpDepthwiseConv2d: " + msg;
1087 printNodeValidationError(msg.c_str());
Eric Kunzee5e26762020-10-13 16:11:07 -07001088 return 1;
1089 }
1090
Eric Kunzee5e26762020-10-13 16:11:07 -07001091 return 0;
1092}
1093
Tai Lya4d748b2023-03-28 22:06:56 +00001094template <TOSA_REF_TYPE InDtype, TOSA_REF_TYPE WeightDtype, TOSA_REF_TYPE OutDtype>
James Wardd34b3fc2023-01-18 14:51:25 +00001095int OpDepthwiseConv2d<InDtype, WeightDtype, OutDtype>::eval()
Eric Kunzee5e26762020-10-13 16:11:07 -07001096{
1097 int in_batch = this->input->getShape()[0];
1098 int in_height = this->input->getShape()[1];
1099 int in_width = this->input->getShape()[2];
1100 int in_channels = this->input->getShape()[3];
1101
1102 int f_height = this->weight->getShape()[0];
1103 int f_width = this->weight->getShape()[1];
1104 int f_in_channels = this->weight->getShape()[2];
1105 int f_multiplier = this->weight->getShape()[3];
1106
1107 int b_out_channels = this->bias->getShape()[0];
1108
1109 int out_batch = this->output->getShape()[0];
1110 int out_height = this->output->getShape()[1];
1111 int out_width = this->output->getShape()[2];
1112 int out_channels = this->output->getShape()[3];
1113
Kevin Chengacb550f2021-06-29 15:32:19 -07001114 ERROR_IF(in_batch != out_batch, "OpDepthwiseConv2d: tensor batch mismatch %d != %d", in_batch, out_batch);
1115 ERROR_IF(f_in_channels != in_channels, "OpDepthwiseConv2d: tensor input channel mismatch %d != %d", f_in_channels,
1116 in_channels);
1117 ERROR_IF(in_channels * f_multiplier != out_channels, "OpDepthwiseConv2d: tensor output channel mismatch %d != %d",
1118 in_channels * f_multiplier, out_channels);
1119 ERROR_IF(b_out_channels != out_channels, "OpDepthwiseConv2d: bias channels mismatch %d != %d", b_out_channels,
1120 out_channels);
Eric Kunzee5e26762020-10-13 16:11:07 -07001121
TatWai Chong86c403b2022-06-06 20:46:01 -07001122 int pad_top = this->attribute->pad()[0];
1123 int pad_bottom = this->attribute->pad()[1];
1124 int pad_left = this->attribute->pad()[2];
1125 int pad_right = this->attribute->pad()[3];
1126
Jerry Gea793f462023-04-11 00:05:02 +00001127 int stride_y = this->attribute->stride()[0];
1128 int stride_x = this->attribute->stride()[1];
1129 int dilation_y = this->attribute->dilation()[0];
1130 int dilation_x = this->attribute->dilation()[1];
1131
1132 // Check Tosa Level
1133 auto tosa_level = g_func_config.tosa_level;
1134 LEVEL_CHECK(dilation_y * f_height <= tosa_level.MAX_KERNEL, "dilation_y * KH should be smaller than or equal to MAX_KERNEL");
1135 LEVEL_CHECK(dilation_x * f_width <= tosa_level.MAX_KERNEL, "dilation_x * KW should be smaller than or equal to MAX_KERNEL");
1136 LEVEL_CHECK(pad_top <= tosa_level.MAX_KERNEL, "pad_top should be smaller than or equal to MAX_KERNEL");
1137 LEVEL_CHECK(pad_bottom <= tosa_level.MAX_KERNEL, "pad_bottom should be smaller than or equal to MAX_KERNEL");
1138 LEVEL_CHECK(pad_left <= tosa_level.MAX_KERNEL, "pad_left should be smaller than or equal to MAX_KERNEL");
1139 LEVEL_CHECK(pad_right <= tosa_level.MAX_KERNEL, "pad_right should be smaller than or equal to MAX_KERNEL");
1140 LEVEL_CHECK(stride_y <= tosa_level.MAX_STRIDE, "stride_y should be smaller than or equal to MAX_STRIDE");
1141 LEVEL_CHECK(stride_x <= tosa_level.MAX_STRIDE, "stride_x should be smaller than or equal to MAX_STRIDE");
Eric Kunzee5e26762020-10-13 16:11:07 -07001142
1143 DEBUG_INFO(OP,
1144 "perform OpDepthwiseConv2d, input.shape=[%d,%d,%d,%d], weight.shape=[%d,%d,%d,%d], "
James Wardd34b3fc2023-01-18 14:51:25 +00001145 "output.shape=[%d,%d,%d,%d], stride=[%d,%d], dilation=[%d,%d], pad=[%d,%d,%d,%d]",
Eric Kunzee5e26762020-10-13 16:11:07 -07001146 in_batch, in_height, in_width, in_channels, f_height, f_width, f_in_channels, f_multiplier, out_batch,
Jerry Gea793f462023-04-11 00:05:02 +00001147 out_height, out_width, out_channels, stride_y, stride_x, dilation_y, dilation_x, pad_top,
James Wardd34b3fc2023-01-18 14:51:25 +00001148 pad_bottom, pad_left, pad_right);
Eric Kunzee5e26762020-10-13 16:11:07 -07001149
TatWai Chong86c403b2022-06-06 20:46:01 -07001150 Eigen::array<std::pair<int32_t, int32_t>, 4> pad;
1151 pad[0] = std::make_pair(0, 0);
1152 pad[1] = std::make_pair(pad_top, pad_bottom);
1153 pad[2] = std::make_pair(pad_left, pad_right);
1154 pad[3] = std::make_pair(0, 0);
Eric Kunzee5e26762020-10-13 16:11:07 -07001155
1156 TIn input_val = this->input->getTensor();
1157 TWeight weight_val = this->weight->getTensor();
Tai Lya4d748b2023-03-28 22:06:56 +00001158 if (InDtype == TOSA_REF_TYPE_INT8 || WeightDtype == TOSA_REF_TYPE_INT8)
Eric Kunzee5e26762020-10-13 16:11:07 -07001159 {
Eric Kunzeb5fabec2022-06-07 05:20:44 +00001160 input_val = input_val - (InEigenType)attribute->input_zp();
1161 weight_val = weight_val - (WeightEigenType)attribute->weight_zp();
Eric Kunzee5e26762020-10-13 16:11:07 -07001162 }
1163
TatWai Chong86c403b2022-06-06 20:46:01 -07001164 ETensor4<InEigenType> input_padded = input_val.pad(pad);
Eric Kunzee5e26762020-10-13 16:11:07 -07001165
1166 // GEMM doesn't fit well with DepthwiseConv2d
TatWai Chong86c403b2022-06-06 20:46:01 -07001167 // 1. use extract_image_patches() to handle stride/dilation/pad
Eric Kunzee5e26762020-10-13 16:11:07 -07001168 // 2. perform direct convolution
1169
1170 // 1. extract_image_patches() output [N, KH, KW, OH * OW, IC]
1171 ETensor5<InEigenType> input_extract_patches = input_padded.extract_image_patches(
Jerry Gea793f462023-04-11 00:05:02 +00001172 f_height, f_width, stride_y, stride_x, dilation_y, dilation_x, Eigen::PADDING_VALID);
Eric Kunzee5e26762020-10-13 16:11:07 -07001173
1174 Eigen::array<Eigen::Index, 4> reshape_dim;
1175 reshape_dim.fill(1);
1176 reshape_dim[3] = b_out_channels;
1177
1178 Eigen::array<Eigen::Index, 4> bcast;
1179 bcast[0] = out_batch;
1180 bcast[1] = out_height;
1181 bcast[2] = out_width;
1182 bcast[3] = 1;
1183
1184 // initialize with bias
1185 this->output->getTensor() = this->bias->getTensor().reshape(reshape_dim).broadcast(bcast);
1186
1187 // 2. direct depthwise convolution
1188 for (int ob = 0; ob < out_batch; ob++)
1189 {
1190 for (int oh = 0; oh < out_height; oh++)
1191 {
1192 for (int ow = 0; ow < out_width; ow++)
1193 {
1194 for (int ic = 0; ic < in_channels; ic++)
1195 {
1196 for (int cm = 0; cm < f_multiplier; cm++)
1197 {
1198 for (int fh = 0; fh < f_height; fh++)
1199 {
1200 for (int fw = 0; fw < f_width; fw++)
1201 {
James Ward8b390432022-08-12 20:48:56 +01001202 // Perform multiplication in AccEigenType then cast to OutEigenType
Eric Kunzee5e26762020-10-13 16:11:07 -07001203 this->output->getTensor()(ob, oh, ow, ic * f_multiplier + cm) +=
James Ward8b390432022-08-12 20:48:56 +01001204 (OutEigenType)((AccEigenType)input_extract_patches(ob, fh, fw, ow * out_height + oh, ic) *
1205 (AccEigenType)weight_val(fh, fw, ic, cm));
Eric Kunzee5e26762020-10-13 16:11:07 -07001206 }
1207 }
1208 }
1209 }
1210 }
1211 }
1212 }
1213
Tai Lya4d748b2023-03-28 22:06:56 +00001214 if (OutDtype == TOSA_REF_TYPE_INT48)
Eric Kunzee5e26762020-10-13 16:11:07 -07001215 {
James Ward8b390432022-08-12 20:48:56 +01001216 this->output->getTensor() = this->output->getTensor().cwiseMax((OutEigenType)AccQMin);
1217 this->output->getTensor() = this->output->getTensor().cwiseMin((OutEigenType)AccQMax);
Eric Kunzee5e26762020-10-13 16:11:07 -07001218 }
1219
1220 return GraphNode::eval();
1221}
1222
Tai Lya4d748b2023-03-28 22:06:56 +00001223template <TOSA_REF_TYPE InDtype, TOSA_REF_TYPE WeightDtype, TOSA_REF_TYPE OutDtype>
James Wardd34b3fc2023-01-18 14:51:25 +00001224OpFullyConnected<InDtype, WeightDtype, OutDtype>::OpFullyConnected(SubgraphTraverser* sgt_,
Tai Lya4d748b2023-03-28 22:06:56 +00001225 TosaAttributeBase* attribute_,
1226 uint64_t id_)
Kevin Chengacb550f2021-06-29 15:32:19 -07001227 : GraphNode(sgt_, Op_FULLY_CONNECTED, id_)
Eric Kunzee5e26762020-10-13 16:11:07 -07001228{
1229 setRequiredOperands(3, 1);
1230 setRequiredRank(2);
1231
Eric Kunzeb5fabec2022-06-07 05:20:44 +00001232 INIT_ATTRIBUTE(FullyConnected);
Eric Kunzee5e26762020-10-13 16:11:07 -07001233}
1234
Tai Lya4d748b2023-03-28 22:06:56 +00001235template <TOSA_REF_TYPE InDtype, TOSA_REF_TYPE WeightDtype, TOSA_REF_TYPE OutDtype>
James Wardd34b3fc2023-01-18 14:51:25 +00001236OpFullyConnected<InDtype, WeightDtype, OutDtype>::~OpFullyConnected()
Eric Kunzee5e26762020-10-13 16:11:07 -07001237{
Eric Kunzeb5fabec2022-06-07 05:20:44 +00001238 if (attribute)
1239 delete attribute;
Eric Kunzee5e26762020-10-13 16:11:07 -07001240}
1241
Tai Lya4d748b2023-03-28 22:06:56 +00001242template <TOSA_REF_TYPE InDtype, TOSA_REF_TYPE WeightDtype, TOSA_REF_TYPE OutDtype>
James Wardd34b3fc2023-01-18 14:51:25 +00001243int OpFullyConnected<InDtype, WeightDtype, OutDtype>::checkTensorAttributes()
Eric Kunzee5e26762020-10-13 16:11:07 -07001244{
1245 if (validateRequiredOperands())
1246 return 1;
1247
1248 if (validateRequiredRank(inputs[0]) || validateRequiredRank(inputs[1]) || validateRequiredRank(outputs[0]))
1249 {
1250 return 1;
1251 }
1252
1253 input = dynamic_cast<TosaReference::TensorTemplate<TIn>*>(inputs[0]);
1254 weight = dynamic_cast<TosaReference::TensorTemplate<TWeight>*>(inputs[1]);
1255 bias = dynamic_cast<TosaReference::TensorTemplate<TBias>*>(inputs[2]);
1256
1257 if (input->getShape()[1] != weight->getShape()[1])
1258 {
1259 printNodeValidationError("OpFullyConnected operator input.shape[1] should match weight.shape[1]");
1260 return 1;
1261 }
1262
1263 if (weight->getShape()[0] != bias->getShape()[0])
1264 {
1265 printNodeValidationError("OpFullyConnected operator bias.shape[0] should match weight.shape[0]");
1266 return 1;
1267 }
1268
James Wardd34b3fc2023-01-18 14:51:25 +00001269 ERROR_IF(outputs[0]->getDtype() != OutDtype,
James Ward8b390432022-08-12 20:48:56 +01001270 "OpFullyConnected: Output data type not supported for this configuration of operator");
Kevin Chengcc61be32021-10-14 17:09:57 -07001271
James Ward8b390432022-08-12 20:48:56 +01001272 output = dynamic_cast<TosaReference::TensorTemplate<TOut>*>(outputs[0]);
Eric Kunzee5e26762020-10-13 16:11:07 -07001273
Tai Lya4d748b2023-03-28 22:06:56 +00001274 ERROR_IF(InDtype != TOSA_REF_TYPE_INT8 && attribute->input_zp() != 0,
1275 "OpFullyConnected: Input zeropoint must be zero for non int8_t data");
1276 ERROR_IF(WeightDtype != TOSA_REF_TYPE_INT8 && attribute->weight_zp() != 0,
1277 "OpFullyConnected: Weight zeropoint must be zero for non int8_t data");
Kevin Chengcc61be32021-10-14 17:09:57 -07001278
Eric Kunzee5e26762020-10-13 16:11:07 -07001279 return 0;
1280}
1281
Tai Lya4d748b2023-03-28 22:06:56 +00001282template <TOSA_REF_TYPE InDtype, TOSA_REF_TYPE WeightDtype, TOSA_REF_TYPE OutDtype>
James Wardd34b3fc2023-01-18 14:51:25 +00001283int OpFullyConnected<InDtype, WeightDtype, OutDtype>::eval()
Eric Kunzee5e26762020-10-13 16:11:07 -07001284{
1285 typedef Eigen::Tensor<int, 1>::DimensionPair DimPair;
1286 Eigen::array<DimPair, 1> dims{ { DimPair(1, 0) } };
1287
1288 Eigen::array<Eigen::Index, 2> weight_shuffle{ 1, 0 };
1289
1290 Eigen::array<Eigen::Index, 2> bias_reshape;
1291 bias_reshape[0] = 1;
1292 bias_reshape[1] = this->bias->getShape()[0];
1293
1294 Eigen::array<Eigen::Index, 2> bias_bcast;
1295 bias_bcast[0] = this->input->getShape()[0];
1296 bias_bcast[1] = 1;
1297
1298 TIn input_val = this->input->getTensor();
1299 TWeight weight_val = this->weight->getTensor().shuffle(weight_shuffle);
Tai Lya4d748b2023-03-28 22:06:56 +00001300 if (InDtype == TOSA_REF_TYPE_INT8 || WeightDtype == TOSA_REF_TYPE_INT8)
Eric Kunzee5e26762020-10-13 16:11:07 -07001301 {
Eric Kunzeb5fabec2022-06-07 05:20:44 +00001302 input_val = input_val - (InEigenType)attribute->input_zp();
1303 weight_val = weight_val - (WeightEigenType)attribute->weight_zp();
Eric Kunzee5e26762020-10-13 16:11:07 -07001304 }
1305
1306 this->output->getTensor() =
James Ward8b390432022-08-12 20:48:56 +01001307 input_val.template cast<AccEigenType>().contract(weight_val.template cast<AccEigenType>(), dims).template cast<OutEigenType>() +
1308 this->bias->getTensor().reshape(bias_reshape).broadcast(bias_bcast);
Eric Kunzee5e26762020-10-13 16:11:07 -07001309
Tai Lya4d748b2023-03-28 22:06:56 +00001310 if (OutDtype == TOSA_REF_TYPE_INT48)
Eric Kunzee5e26762020-10-13 16:11:07 -07001311 {
James Ward8b390432022-08-12 20:48:56 +01001312 this->output->getTensor() = this->output->getTensor().cwiseMax((OutEigenType)AccQMin);
1313 this->output->getTensor() = this->output->getTensor().cwiseMin((OutEigenType)AccQMax);
Eric Kunzee5e26762020-10-13 16:11:07 -07001314 }
1315 return GraphNode::eval();
1316}
1317
Tai Lya4d748b2023-03-28 22:06:56 +00001318template <TOSA_REF_TYPE Dtype, TOSA_REF_TYPE OutDtype>
James Wardd34b3fc2023-01-18 14:51:25 +00001319OpMatMul<Dtype, OutDtype>::OpMatMul(SubgraphTraverser* sgt_,
Kevin Chengacb550f2021-06-29 15:32:19 -07001320 TosaAttributeBase* attribute_,
Kevin Chengacb550f2021-06-29 15:32:19 -07001321 uint64_t id_)
1322 : GraphNode(sgt_, Op_MATMUL, id_)
Eric Kunzee5e26762020-10-13 16:11:07 -07001323{
1324 setRequiredOperands(2, 1);
Kevin Cheng2d60f002021-06-09 14:18:32 -07001325 setRequiredRank(3);
Eric Kunzee5e26762020-10-13 16:11:07 -07001326
Eric Kunzeb5fabec2022-06-07 05:20:44 +00001327 INIT_ATTRIBUTE(MatMul);
Eric Kunzee5e26762020-10-13 16:11:07 -07001328}
1329
Tai Lya4d748b2023-03-28 22:06:56 +00001330template <TOSA_REF_TYPE Dtype, TOSA_REF_TYPE OutDtype>
James Wardd34b3fc2023-01-18 14:51:25 +00001331OpMatMul<Dtype, OutDtype>::~OpMatMul()
Eric Kunzee5e26762020-10-13 16:11:07 -07001332{
Eric Kunzeb5fabec2022-06-07 05:20:44 +00001333 if (attribute)
1334 delete attribute;
Eric Kunzee5e26762020-10-13 16:11:07 -07001335}
1336
Tai Lya4d748b2023-03-28 22:06:56 +00001337template <TOSA_REF_TYPE Dtype, TOSA_REF_TYPE OutDtype>
James Wardd34b3fc2023-01-18 14:51:25 +00001338int OpMatMul<Dtype, OutDtype>::checkTensorAttributes()
Eric Kunzee5e26762020-10-13 16:11:07 -07001339{
1340 if (validateRequiredOperands())
1341 return 1;
1342
1343 if (validateRequiredRank(inputs[0]) || validateRequiredRank(inputs[1]) || validateRequiredRank(outputs[0]))
1344 {
1345 return 1;
1346 }
1347
James Wardd34b3fc2023-01-18 14:51:25 +00001348 ERROR_IF(outputs[0]->getDtype() != OutDtype,
James Ward8b390432022-08-12 20:48:56 +01001349 "OpMatMul: Output data type not supported for this configuration of operator");
Kevin Chengcc61be32021-10-14 17:09:57 -07001350
Kevin Cheng2d60f002021-06-09 14:18:32 -07001351 a = dynamic_cast<TosaReference::TensorTemplate<TIn>*>(inputs[0]);
1352 b = dynamic_cast<TosaReference::TensorTemplate<TIn>*>(inputs[1]);
James Ward8b390432022-08-12 20:48:56 +01001353 output = dynamic_cast<TosaReference::TensorTemplate<TOut>*>(outputs[0]);
Eric Kunzee5e26762020-10-13 16:11:07 -07001354
Kevin Cheng2d60f002021-06-09 14:18:32 -07001355 ASSERT_MEM(a && b && output);
1356
1357 // a: [N, H, C]
1358 // b: [N, C, W]
1359 // c: [N, H, W]
1360
1361 // Check N
1362 if (a->getShape()[0] != b->getShape()[0] || a->getShape()[0] != output->getShape()[0])
Eric Kunzee5e26762020-10-13 16:11:07 -07001363 {
Kevin Cheng2d60f002021-06-09 14:18:32 -07001364 printNodeValidationError("OpMatMul operator a.shape[0], b.shape[0] and output.shape[0] should match");
Eric Kunzee5e26762020-10-13 16:11:07 -07001365 return 1;
1366 }
Kevin Cheng2d60f002021-06-09 14:18:32 -07001367 N = a->getShape()[0];
Eric Kunzee5e26762020-10-13 16:11:07 -07001368
Kevin Cheng2d60f002021-06-09 14:18:32 -07001369 // Check C
1370 if (a->getShape()[2] != b->getShape()[1])
1371 {
1372 printNodeValidationError("OpMatMul operator a.shape[2] should match b.shape[1]");
1373 return 1;
1374 }
1375 C = a->getShape()[2];
1376
1377 // Check H
1378 if (a->getShape()[1] != output->getShape()[1])
1379 {
1380 printNodeValidationError("OpMatMul operator a.shape[1] should match output.shape[1]");
1381 return 1;
1382 }
1383 H = a->getShape()[1];
1384
1385 // Check W
1386 if (b->getShape()[2] != output->getShape()[2])
1387 {
1388 printNodeValidationError("OpMatMul operator output.shape[2] should match output.shape[2]");
1389 return 1;
1390 }
1391 W = b->getShape()[2];
Eric Kunzee5e26762020-10-13 16:11:07 -07001392
Tai Lya4d748b2023-03-28 22:06:56 +00001393 ERROR_IF(Dtype != TOSA_REF_TYPE_INT8 && attribute->a_zp() != 0,
1394 "OpMatMul: A zeropoint must be zero for non int8_t data");
1395 ERROR_IF(Dtype != TOSA_REF_TYPE_INT8 && attribute->b_zp() != 0,
1396 "OpMatMul: B zeropoint must be zero for non int8_t data");
Kevin Chengcc61be32021-10-14 17:09:57 -07001397
Eric Kunzee5e26762020-10-13 16:11:07 -07001398 return 0;
1399}
1400
Tai Lya4d748b2023-03-28 22:06:56 +00001401template <TOSA_REF_TYPE Dtype, TOSA_REF_TYPE OutDtype>
James Wardd34b3fc2023-01-18 14:51:25 +00001402int OpMatMul<Dtype, OutDtype>::eval()
Eric Kunzee5e26762020-10-13 16:11:07 -07001403{
1404 typedef Eigen::Tensor<int, 1>::DimensionPair DimPair;
1405 Eigen::array<DimPair, 1> dims{ { DimPair(1, 0) } };
1406
1407 TIn a_val = this->a->getTensor();
1408 TIn b_val = this->b->getTensor();
Tai Lya4d748b2023-03-28 22:06:56 +00001409 if (Dtype == TOSA_REF_TYPE_INT8)
Eric Kunzee5e26762020-10-13 16:11:07 -07001410 {
Eric Kunzeb5fabec2022-06-07 05:20:44 +00001411 a_val = a_val - (InEigenType)attribute->a_zp();
1412 b_val = b_val - (InEigenType)attribute->b_zp();
Eric Kunzee5e26762020-10-13 16:11:07 -07001413 }
1414
Kevin Cheng2d60f002021-06-09 14:18:32 -07001415 Eigen::array<Eigen::Index, 2> a_rank2_shape({ H, C });
1416 Eigen::array<Eigen::Index, 2> b_rank2_shape({ C, W });
1417 Eigen::array<Eigen::Index, 3> output_rank3_shape({ 1, H, W });
1418
1419 Eigen::array<Eigen::Index, 3> a_size_array({ 1, H, C });
1420 Eigen::array<Eigen::Index, 3> b_size_array({ 1, C, W });
1421
1422 Eigen::array<Eigen::Index, 3> a_begin_array({ 0, 0, 0 });
1423 Eigen::array<Eigen::Index, 3> b_begin_array({ 0, 0, 0 });
1424
1425 // Iterate N dimension.
1426 for (int i = 0; i < N; i++)
1427 {
1428 a_begin_array[0] = i;
1429 b_begin_array[0] = i;
1430
1431 TInRank2 a_rank2_val = a_val.slice(a_begin_array, a_size_array).reshape(a_rank2_shape);
1432 TInRank2 b_rank2_val = b_val.slice(b_begin_array, b_size_array).reshape(b_rank2_shape);
1433 TAccRank2 output_rank2_val =
1434 a_rank2_val.template cast<AccEigenType>().contract(b_rank2_val.template cast<AccEigenType>(), dims);
James Ward8b390432022-08-12 20:48:56 +01001435 TOut output_rank3_val = output_rank2_val.reshape(output_rank3_shape).template cast<OutEigenType>();
Kevin Cheng2d60f002021-06-09 14:18:32 -07001436 if (i == 0)
1437 {
1438 this->output->getTensor() = output_rank3_val;
1439 }
1440 else
1441 {
James Ward8b390432022-08-12 20:48:56 +01001442 TOut temp = this->output->getTensor().concatenate(output_rank3_val, 0);
Kevin Cheng2d60f002021-06-09 14:18:32 -07001443 this->output->getTensor() = temp;
1444 }
1445 }
Eric Kunzee5e26762020-10-13 16:11:07 -07001446
Tai Lya4d748b2023-03-28 22:06:56 +00001447 if (OutDtype == TOSA_REF_TYPE_INT48)
Eric Kunzee5e26762020-10-13 16:11:07 -07001448 {
James Ward8b390432022-08-12 20:48:56 +01001449 this->output->getTensor() = this->output->getTensor().cwiseMax((OutEigenType)AccQMin);
1450 this->output->getTensor() = this->output->getTensor().cwiseMin((OutEigenType)AccQMax);
Eric Kunzee5e26762020-10-13 16:11:07 -07001451 }
1452
1453 return GraphNode::eval();
1454}
1455
Tai Lya4d748b2023-03-28 22:06:56 +00001456template <TOSA_REF_TYPE Dtype>
Kevin Chengacb550f2021-06-29 15:32:19 -07001457OpMaxPool2d<Dtype>::OpMaxPool2d(SubgraphTraverser* sgt_,
1458 TosaAttributeBase* attribute_,
Kevin Chengacb550f2021-06-29 15:32:19 -07001459 uint64_t id_)
1460 : GraphNode(sgt_, Op_MAX_POOL2D, id_)
Eric Kunzee5e26762020-10-13 16:11:07 -07001461{
1462 setRequiredOperands(1, 1);
1463 setRequiredRank(4);
1464
Kevin Cheng93a16282021-08-31 16:14:03 -07001465 INIT_ATTRIBUTE(Pool);
Eric Kunzee5e26762020-10-13 16:11:07 -07001466}
1467
Tai Lya4d748b2023-03-28 22:06:56 +00001468template <TOSA_REF_TYPE Dtype>
Eric Kunzee5e26762020-10-13 16:11:07 -07001469OpMaxPool2d<Dtype>::~OpMaxPool2d()
1470{
1471 if (attribute)
1472 delete attribute;
1473}
1474
Tai Lya4d748b2023-03-28 22:06:56 +00001475template <TOSA_REF_TYPE Dtype>
Eric Kunzee5e26762020-10-13 16:11:07 -07001476int OpMaxPool2d<Dtype>::checkTensorAttributes()
1477{
1478 if (validateRequiredOperands())
1479 return 1;
1480
1481 if (validateRequiredRank(inputs[0]) || validateRequiredRank(outputs[0]))
1482 {
1483 return 1;
1484 }
1485
1486 if (inputs[0]->matchType(*outputs[0]))
1487 {
1488 printNodeValidationError("OpMaxPool2d: input and output tensor type mismatch");
1489 return 1;
1490 }
1491
1492 in = dynamic_cast<TosaReference::TensorTemplate<TIn>*>(inputs[0]);
1493 out = dynamic_cast<TosaReference::TensorTemplate<TOut>*>(outputs[0]);
1494
Kevin Cheng7eb93d72021-10-09 01:26:08 +00001495 std::string msg;
Kevin Cheng9fe17242021-11-10 01:04:39 +00001496 if (check_pool2d_attribute(attribute, in->getShape(), out->getShape(), msg))
Eric Kunzee5e26762020-10-13 16:11:07 -07001497 {
Kevin Cheng7eb93d72021-10-09 01:26:08 +00001498 msg = "OpMaxPool2d: " + msg;
1499 printNodeValidationError(msg.c_str());
Eric Kunzee5e26762020-10-13 16:11:07 -07001500 return 1;
1501 }
1502
1503 return 0;
1504}
1505
Tai Lya4d748b2023-03-28 22:06:56 +00001506template <TOSA_REF_TYPE Dtype>
Eric Kunzee5e26762020-10-13 16:11:07 -07001507int OpMaxPool2d<Dtype>::eval()
1508{
1509 int in_batch = this->in->getShape()[0];
1510 int in_height = this->in->getShape()[1];
1511 int in_width = this->in->getShape()[2];
1512 int in_channels = this->in->getShape()[3];
1513
1514 int out_batch = this->out->getShape()[0];
1515 int out_height = this->out->getShape()[1];
1516 int out_width = this->out->getShape()[2];
1517 int out_channels = this->out->getShape()[3];
1518
Kevin Chengacb550f2021-06-29 15:32:19 -07001519 ERROR_IF(in_batch != out_batch, "OpMaxPool2d: tensor batch mismatch %d != %d", in_batch, out_batch);
1520 ERROR_IF(in_channels != out_channels, "OpMaxPool2d: tensor channel mismatch %d != %d", in_channels, out_channels);
Eric Kunzee5e26762020-10-13 16:11:07 -07001521
TatWai Chong86c403b2022-06-06 20:46:01 -07001522 int pad_top = this->attribute->pad()[0];
1523 int pad_bottom = this->attribute->pad()[1];
1524 int pad_left = this->attribute->pad()[2];
1525 int pad_right = this->attribute->pad()[3];
1526
Jerry Gea793f462023-04-11 00:05:02 +00001527 int kernel_y = this->attribute->kernel()[0];
1528 int kernel_x = this->attribute->kernel()[1];
1529 int stride_y = this->attribute->stride()[0];
1530 int stride_x = this->attribute->stride()[1];
1531
1532 // Check Tosa Level
1533 auto tosa_level = g_func_config.tosa_level;
1534 LEVEL_CHECK(kernel_y <= tosa_level.MAX_KERNEL, "kernel_y should be smaller than or equal to MAX_KERNEL");
1535 LEVEL_CHECK(kernel_x <= tosa_level.MAX_KERNEL, "kernel_x should be smaller than or equal to MAX_KERNEL");
1536 LEVEL_CHECK(stride_y <= tosa_level.MAX_STRIDE, "stride_y should be smaller than or equal to MAX_STRIDE");
1537 LEVEL_CHECK(stride_x <= tosa_level.MAX_STRIDE, "stride_x should be smaller than or equal to MAX_STRIDE");
1538 LEVEL_CHECK(pad_top <= tosa_level.MAX_KERNEL, "pad_top should be smaller than or equal to MAX_KERNEL");
1539 LEVEL_CHECK(pad_bottom <= tosa_level.MAX_KERNEL, "pad_bottom should be smaller than or equal to MAX_KERNEL");
1540 LEVEL_CHECK(pad_left <= tosa_level.MAX_KERNEL, "pad_left should be smaller than or equal to MAX_KERNEL");
1541 LEVEL_CHECK(pad_right <= tosa_level.MAX_KERNEL, "pad_right should be smaller than or equal to MAX_KERNEL");
Eric Kunzee5e26762020-10-13 16:11:07 -07001542
1543 DEBUG_INFO(OP,
1544 "perform MaxPool2d, input.shape=[%d,%d,%d,%d], output.shape=[%d,%d,%d,%d], kernel=[%d,%d], "
TatWai Chong86c403b2022-06-06 20:46:01 -07001545 "stride=[%d,%d], pad=[%d,%d,%d,%d]",
Jerry Gea793f462023-04-11 00:05:02 +00001546 in_batch, in_height, in_width, in_channels, out_batch, out_height, out_width, out_channels, kernel_y,
1547 kernel_x, stride_y, stride_x, pad_top, pad_bottom, pad_left, pad_right);
Eric Kunzee5e26762020-10-13 16:11:07 -07001548
1549 Eigen::array<Eigen::Index, 2> im2col_input_dims;
Jerry Gea793f462023-04-11 00:05:02 +00001550 im2col_input_dims[0] = kernel_y * kernel_x;
Eric Kunzee5e26762020-10-13 16:11:07 -07001551 im2col_input_dims[1] = out_batch * out_height * out_width * out_channels;
1552
1553 Eigen::array<Eigen::Index, 4> col2im_output_dims;
1554 col2im_output_dims[0] = out_batch;
1555 col2im_output_dims[1] = out_height;
1556 col2im_output_dims[2] = out_width;
1557 col2im_output_dims[3] = out_channels;
1558
TatWai Chong86c403b2022-06-06 20:46:01 -07001559 Eigen::array<std::pair<int32_t, int32_t>, 4> pad;
1560 pad[0] = std::make_pair(0, 0);
1561 pad[1] = std::make_pair(pad_top, pad_bottom);
1562 pad[2] = std::make_pair(pad_left, pad_right);
1563 pad[3] = std::make_pair(0, 0);
Eric Kunzee5e26762020-10-13 16:11:07 -07001564
TatWai Chong86c403b2022-06-06 20:46:01 -07001565 ETensor4<InEigenType> input_padded = this->in->getTensor().pad(pad, std::numeric_limits<InEigenType>::lowest());
Eric Kunzee5e26762020-10-13 16:11:07 -07001566
1567 // extract_image_patches() output [N, KH, KW, H * W, C]
1568 // transpose to [KH, KW, N, H * W, C]
1569 // reshape to [KH * KW, N * H * W * C]
1570 //
1571 // Set the padding value to be the most negative value that can be
1572 // represented by the datatype to ensure that any padding values will be equal
1573 // to or smaller than the actual maximum in the KH x KW patch.
1574 ETensor2<InEigenType> input_extract_patches =
1575 input_padded
Jerry Gea793f462023-04-11 00:05:02 +00001576 .extract_image_patches(kernel_y, kernel_x, stride_y, stride_x, 1, 1, Eigen::PADDING_VALID,
Eric Kunzee5e26762020-10-13 16:11:07 -07001577 std::numeric_limits<InEigenType>::lowest())
1578 .shuffle(Eigen::array<Eigen::Index, 5>{ 1, 2, 0, 3, 4 })
1579 .reshape(im2col_input_dims);
1580
1581 // Get the maximum of the KHxHW patches along axis 0
1582 Eigen::Tensor<DenseIndex, 1> tensor_argmax = input_extract_patches.argmax(0);
1583
1584 // 1D result with [N * H * W * C]
1585 ETensor1<OutEigenType> out_1d(this->out->getElementCount());
1586
1587 // index input_patches with argmax array should give the result
1588 for (size_t i = 0; i < this->out->getElementCount(); i++)
1589 {
1590 out_1d(i) = (OutEigenType)input_extract_patches(tensor_argmax(i), i);
1591 }
1592
1593 // reshape result to [N, H, W, C]
1594 this->out->getTensor() = out_1d.reshape(col2im_output_dims);
1595
1596 return GraphNode::eval();
1597}
1598
Tai Lya4d748b2023-03-28 22:06:56 +00001599template <TOSA_REF_TYPE Dtype>
1600OpFFT2d<Dtype>::OpFFT2d(SubgraphTraverser* sgt_, TosaAttributeBase* attribute_, uint64_t id_)
Luke Hutton57287132023-02-06 14:54:18 +00001601 : GraphNode(sgt_, Op_FFT2D, id_)
1602{
1603 setRequiredOperands(2, 2);
1604 setRequiredRank(3);
1605
1606 INIT_ATTRIBUTE(FFT);
1607}
1608
Tai Lya4d748b2023-03-28 22:06:56 +00001609template <TOSA_REF_TYPE Dtype>
1610OpFFT2d<Dtype>::~OpFFT2d()
1611{
Luke Hutton57287132023-02-06 14:54:18 +00001612 if (attribute)
1613 delete attribute;
1614}
1615
Tai Lya4d748b2023-03-28 22:06:56 +00001616template <TOSA_REF_TYPE Dtype>
Luke Hutton57287132023-02-06 14:54:18 +00001617int OpFFT2d<Dtype>::checkTensorAttributes()
1618{
1619 if (validateRequiredOperands())
1620 return 1;
1621
1622 if (validateRequiredRank(inputs[0]) || validateRequiredRank(inputs[1]) ||
1623 validateRequiredRank(outputs[0]) || validateRequiredRank(outputs[1]))
1624 {
1625 return 1;
1626 }
1627
1628 if (inputs[0]->matchType(*outputs[0]) || inputs[1]->matchType(*outputs[1]) ||
1629 inputs[0]->matchType(*inputs[1]))
1630 {
1631 printNodeValidationError("OpFFT2d: input and output tensor type mismatch");
1632 return 1;
1633 }
1634
1635 in_real = dynamic_cast<TosaReference::TensorTemplate<TIn>*>(inputs[0]);
1636 in_imag = dynamic_cast<TosaReference::TensorTemplate<TIn>*>(inputs[1]);
1637 out_real = dynamic_cast<TosaReference::TensorTemplate<TOut>*>(outputs[0]);
1638 out_imag = dynamic_cast<TosaReference::TensorTemplate<TOut>*>(outputs[1]);
1639
1640 ASSERT_MEM(in_real && in_imag && out_real && out_imag);
1641
1642 std::string msg;
1643 if (check_fft_shape(in_real->getShape(), in_imag->getShape(),
1644 out_real->getShape(), out_imag->getShape(), msg))
1645 {
1646 msg = "OpFFT2d: " + msg;
1647 printNodeValidationError(msg.c_str());
1648 return 1;
1649 }
1650
1651 return 0;
1652}
1653
Tai Lya4d748b2023-03-28 22:06:56 +00001654template <TOSA_REF_TYPE Dtype>
Luke Hutton57287132023-02-06 14:54:18 +00001655int OpFFT2d<Dtype>::eval()
1656{
1657 int in_real_batch = this->in_real->getShape()[0];
1658 int in_real_height = this->in_real->getShape()[1];
1659 int in_real_width = this->in_real->getShape()[2];
1660
1661 int in_imag_batch = this->in_imag->getShape()[0];
1662 int in_imag_height = this->in_imag->getShape()[1];
1663 int in_imag_width = this->in_imag->getShape()[2];
1664
1665 int out_real_batch = this->out_real->getShape()[0];
1666 int out_real_height = this->out_real->getShape()[1];
1667 int out_real_width = this->out_real->getShape()[2];
1668
1669 int out_imag_batch = this->out_imag->getShape()[0];
1670 int out_imag_height = this->out_imag->getShape()[1];
1671 int out_imag_width = this->out_imag->getShape()[2];
1672
Jerry Gea793f462023-04-11 00:05:02 +00001673 // Check Tosa Level
1674 auto tosa_level = g_func_config.tosa_level;
1675 LEVEL_CHECK(in_real_height <= tosa_level.MAX_KERNEL, "H should be smaller than or equal to MAX_KERNEL");
1676 LEVEL_CHECK(in_real_width <= tosa_level.MAX_KERNEL, "W should be smaller than or equal to MAX_KERNEL");
1677
Luke Hutton57287132023-02-06 14:54:18 +00001678 DEBUG_INFO(OP,
1679 "perform OpFFT2d, input.shapes=[[%d,%d,%d],[%d,%d,%d]], output.shapes=[[%d,%d,%d],[%d,%d,%d]]",
1680 in_real_batch, in_real_height, in_real_width,
1681 in_imag_batch, in_imag_height, in_imag_width,
1682 out_real_batch, out_real_height, out_real_width,
1683 out_imag_batch, out_imag_height, out_imag_width);
1684
1685 OutEigenType sum_real, sum_imag, a, sign_val = 1.0;
1686
1687 if (attribute->inverse()) {
1688 sign_val = -1.0;
1689 }
1690
1691 for (int n = 0; n < in_real_batch; n++)
1692 {
1693 for (int oy = 0; oy < out_real_height; oy++)
1694 {
1695 for (int ox = 0; ox < out_real_width; ox++)
1696 {
1697 sum_real = 0.0;
1698 sum_imag = 0.0;
1699 for (int iy = 0; iy < in_real_height; iy++)
1700 {
1701 for (int ix = 0; ix < in_real_width; ix++)
1702 {
1703 OutEigenType val_real = this->in_real->getTensor()(n, iy, ix);
1704 OutEigenType val_imag = this->in_imag->getTensor()(n, iy, ix);
1705 // Use explicit cast to ensure intermmediate calculations are completed using OutEigenType
1706 a = sign_val * 2 * M_PI * ((iy * (OutEigenType)oy) / in_real_height + (ix * (OutEigenType)ox) / in_real_width);
1707 sum_real += val_real * cos(a) + val_imag * sin(a);
1708 sum_imag += -val_real * sin(a) + val_imag * cos(a);
1709 }
1710 }
1711 this->out_real->getTensor()(n, oy, ox) = sum_real;
1712 this->out_imag->getTensor()(n, oy, ox) = sum_imag;
1713 }
1714 }
1715 }
1716
1717 return GraphNode::eval();
1718}
1719
Tai Lya4d748b2023-03-28 22:06:56 +00001720template <TOSA_REF_TYPE Dtype>
Luke Hutton261b7b62023-01-10 14:50:31 +00001721OpRFFT2d<Dtype>::OpRFFT2d(SubgraphTraverser* sgt_,
1722 TosaAttributeBase* attribute_,
1723 uint64_t id_)
1724 : GraphNode(sgt_, Op_RFFT2D, id_)
1725{
1726 setRequiredOperands(1, 2);
1727 setRequiredRank(3);
1728}
1729
Tai Lya4d748b2023-03-28 22:06:56 +00001730template <TOSA_REF_TYPE Dtype>
Luke Hutton261b7b62023-01-10 14:50:31 +00001731OpRFFT2d<Dtype>::~OpRFFT2d() {}
1732
1733
Tai Lya4d748b2023-03-28 22:06:56 +00001734template <TOSA_REF_TYPE Dtype>
Luke Hutton261b7b62023-01-10 14:50:31 +00001735int OpRFFT2d<Dtype>::checkTensorAttributes()
1736{
1737 if (validateRequiredOperands())
1738 return 1;
1739
1740 if (validateRequiredRank(inputs[0]) || validateRequiredRank(outputs[0]) ||
1741 validateRequiredRank(outputs[1]))
1742 {
1743 return 1;
1744 }
1745
1746 if (inputs[0]->matchType(*outputs[0]) || inputs[0]->matchType(*outputs[1]))
1747 {
1748 printNodeValidationError("OpRFFT2d: input and output tensor type mismatch");
1749 return 1;
1750 }
1751
1752 in = dynamic_cast<TosaReference::TensorTemplate<TIn>*>(inputs[0]);
1753 out_real = dynamic_cast<TosaReference::TensorTemplate<TOut>*>(outputs[0]);
1754 out_imag = dynamic_cast<TosaReference::TensorTemplate<TOut>*>(outputs[1]);
1755
1756 ASSERT_MEM(in && out_real && out_imag);
1757
Luke Hutton57287132023-02-06 14:54:18 +00001758 std::string msg;
1759 if (check_fft_shape(in->getShape(), {},
1760 out_real->getShape(), out_imag->getShape(), msg))
Luke Hutton261b7b62023-01-10 14:50:31 +00001761 {
Luke Hutton57287132023-02-06 14:54:18 +00001762 msg = "OpRFFT2d: " + msg;
1763 printNodeValidationError(msg.c_str());
Luke Hutton261b7b62023-01-10 14:50:31 +00001764 return 1;
1765 }
1766
1767 return 0;
1768}
1769
Tai Lya4d748b2023-03-28 22:06:56 +00001770template <TOSA_REF_TYPE Dtype>
Luke Hutton261b7b62023-01-10 14:50:31 +00001771int OpRFFT2d<Dtype>::eval()
1772{
1773 int32_t in_batch = in->getShape()[0];
1774 int32_t in_height = in->getShape()[1];
1775 int32_t in_width = in->getShape()[2];
1776
1777 int32_t out_real_batch = out_real->getShape()[0];
1778 int32_t out_real_height = out_real->getShape()[1];
1779 int32_t out_real_width = out_real->getShape()[2];
1780
1781 int32_t out_imag_batch = out_imag->getShape()[0];
1782 int32_t out_imag_height = out_imag->getShape()[1];
1783 int32_t out_imag_width = out_imag->getShape()[2];
1784
Jerry Gea793f462023-04-11 00:05:02 +00001785 // Check Tosa Level
1786 auto tosa_level = g_func_config.tosa_level;
1787 LEVEL_CHECK(in_height <= tosa_level.MAX_KERNEL, "H should be smaller than or equal to MAX_KERNEL");
1788 LEVEL_CHECK(in_width <= tosa_level.MAX_KERNEL, "W should be smaller than or equal to MAX_KERNEL");
1789
Luke Hutton261b7b62023-01-10 14:50:31 +00001790 DEBUG_INFO(OP,
1791 "perform OpRFFT2d, input.shape=[%d,%d,%d], output_real.shape=[%d,%d,%d], "
1792 "output_imag.shape=[%d,%d,%d]",
1793 in_batch, in_height, in_width,
1794 out_real_batch, out_real_height, out_real_width,
1795 out_imag_batch, out_imag_height, out_imag_width);
1796
1797 OutEigenType sum_real, sum_imag, a;
1798
1799 for (int n = 0; n < in_batch; n++)
1800 {
1801 for (int oy = 0; oy < out_real_height; oy++)
1802 {
1803 for (int ox = 0; ox < out_real_width; ox++)
1804 {
1805 sum_real = 0.0;
1806 sum_imag = 0.0;
1807 for (int iy = 0; iy < in_height; iy++)
1808 {
1809 for (int ix = 0; ix < in_width; ix++)
1810 {
1811 // Use explicit cast to ensure intermmediate calculations are completed using OutEigenType
1812 a = 2 * M_PI * ((iy * (OutEigenType)oy) / in_height + (ix * (OutEigenType)ox) / in_width);
1813 sum_real += this->in->getTensor()(n, iy, ix) * cos(a);
1814 sum_imag += -this->in->getTensor()(n, iy, ix) * sin(a);
1815 }
1816 }
1817 this->out_real->getTensor()(n, oy, ox) = sum_real;
1818 this->out_imag->getTensor()(n, oy, ox) = sum_imag;
1819 }
1820 }
1821 }
1822
1823 return GraphNode::eval();
1824}
1825
Tai Lya4d748b2023-03-28 22:06:56 +00001826template <TOSA_REF_TYPE InDtype, TOSA_REF_TYPE WeightDtype, TOSA_REF_TYPE OutDtype>
James Wardd34b3fc2023-01-18 14:51:25 +00001827OpTransposeConv2d<InDtype, WeightDtype, OutDtype>::OpTransposeConv2d(SubgraphTraverser* sgt_,
Tai Lya4d748b2023-03-28 22:06:56 +00001828 TosaAttributeBase* attribute_,
1829 uint64_t id_)
Kevin Chengacb550f2021-06-29 15:32:19 -07001830 : GraphNode(sgt_, Op_TRANSPOSE_CONV2D, id_)
Eric Kunzee5e26762020-10-13 16:11:07 -07001831{
1832 setRequiredOperands(3, 1);
1833 setRequiredRank(4);
1834
Kevin Cheng93a16282021-08-31 16:14:03 -07001835 INIT_ATTRIBUTE(TransposeConv);
Eric Kunzee5e26762020-10-13 16:11:07 -07001836}
1837
Tai Lya4d748b2023-03-28 22:06:56 +00001838template <TOSA_REF_TYPE InDtype, TOSA_REF_TYPE WeightDtype, TOSA_REF_TYPE OutDtype>
James Wardd34b3fc2023-01-18 14:51:25 +00001839OpTransposeConv2d<InDtype, WeightDtype, OutDtype>::~OpTransposeConv2d()
Eric Kunzee5e26762020-10-13 16:11:07 -07001840{
1841 if (attribute)
1842 delete attribute;
Eric Kunzee5e26762020-10-13 16:11:07 -07001843}
1844
Tai Lya4d748b2023-03-28 22:06:56 +00001845template <TOSA_REF_TYPE InDtype, TOSA_REF_TYPE WeightDtype, TOSA_REF_TYPE OutDtype>
James Wardd34b3fc2023-01-18 14:51:25 +00001846int OpTransposeConv2d<InDtype, WeightDtype, OutDtype>::checkTensorAttributes()
Eric Kunzee5e26762020-10-13 16:11:07 -07001847{
1848 if (validateRequiredOperands())
1849 return 1;
1850
1851 if (validateRequiredRank(inputs[0]) || validateRequiredRank(inputs[1]) || validateRequiredRank(outputs[0]))
1852 {
1853 return 1;
1854 }
1855
James Wardd34b3fc2023-01-18 14:51:25 +00001856 ERROR_IF(outputs[0]->getDtype() != OutDtype,
James Ward8b390432022-08-12 20:48:56 +01001857 "OpTransposeConv2d: Output data type not supported for this configuration of operator");
Kevin Chengcc61be32021-10-14 17:09:57 -07001858
Eric Kunzee5e26762020-10-13 16:11:07 -07001859 input = dynamic_cast<TosaReference::TensorTemplate<TIn>*>(inputs[0]);
1860 weight = dynamic_cast<TosaReference::TensorTemplate<TWeight>*>(inputs[1]);
1861 bias = dynamic_cast<TosaReference::TensorTemplate<TBias>*>(inputs[2]);
James Ward8b390432022-08-12 20:48:56 +01001862 output = dynamic_cast<TosaReference::TensorTemplate<TOut>*>(outputs[0]);
Eric Kunzee5e26762020-10-13 16:11:07 -07001863
TatWai Chong24594f52022-06-08 00:48:04 -07001864 if (attribute->out_pad().size() != 4)
Eric Kunzee5e26762020-10-13 16:11:07 -07001865 {
TatWai Chong24594f52022-06-08 00:48:04 -07001866 printNodeValidationError("OpTransposeConv2d: illegal size for attribute out_pad");
Eric Kunzee5e26762020-10-13 16:11:07 -07001867 return 1;
1868 }
1869
1870 if (attribute->stride().size() != 2)
1871 {
1872 printNodeValidationError("OpTransposeConv2d: illegal size for attribute stride");
1873 return 1;
1874 }
1875
Eric Kunzee5e26762020-10-13 16:11:07 -07001876 if (attribute->output_shape().size() != 4)
1877 {
1878 printNodeValidationError("OpTransposeConv2d: illegal size for attribute output_shape");
1879 return 1;
1880 }
1881
Eric Kunzec1a97832022-07-01 16:56:09 -07001882
Kevin Cheng9fe17242021-11-10 01:04:39 +00001883
1884 for (int32_t i : attribute->stride())
1885 {
1886 if (i < 1)
1887 {
1888 printNodeValidationError("OpTransposeConv2d: At least one stride is smaller than one");
1889 return 1;
1890 }
1891 }
1892
Eric Kunzee5e26762020-10-13 16:11:07 -07001893 for (int d = 0; d < 4; d++)
1894 {
1895 if (attribute->output_shape()[d] != this->output->getShape()[d])
1896 {
1897 printNodeValidationError("OpTransposeConv2d: illegal size for attribute output_shape");
1898 return 1;
1899 }
1900 }
1901
Jeremy Johnson4a6fb9b2022-04-26 15:47:21 +01001902 int32_t IH = input->getShape()[1];
1903 int32_t IW = input->getShape()[2];
1904 int32_t OH = output->getShape()[1];
1905 int32_t OW = output->getShape()[2];
1906
1907 int32_t stride_y = attribute->stride()[0];
1908 int32_t stride_x = attribute->stride()[1];
1909 int32_t kernel_h = weight->getShape()[1];
1910 int32_t kernel_w = weight->getShape()[2];
1911
TatWai Chong24594f52022-06-08 00:48:04 -07001912 int32_t out_pad_top = attribute->out_pad()[0];
1913 int32_t out_pad_bottom = attribute->out_pad()[1];
1914 int32_t out_pad_left = attribute->out_pad()[2];
1915 int32_t out_pad_right = attribute->out_pad()[3];
Jeremy Johnson4a6fb9b2022-04-26 15:47:21 +01001916
Eric Kunzec1a97832022-07-01 16:56:09 -07001917 for (size_t i = 0; i < attribute->out_pad().size(); i++)
1918 {
1919 ERROR_IF(attribute->out_pad()[i] <= -(weight->getShape()[(i / 2) + 1]), "OpTransposeConv2d: At least one out_pad value is larger than kernel size");
1920 }
1921
1922 int32_t H = (IH - 1) * stride_y + out_pad_top + out_pad_bottom + kernel_h;
1923 int32_t W = (IW - 1) * stride_x + out_pad_left + out_pad_right + kernel_w;
Jeremy Johnson4a6fb9b2022-04-26 15:47:21 +01001924
1925 if ((OH != H) || (OW != W))
1926 {
1927 std::string msg = "OpTransposeConv2d: Mismatch between output shape provided and expected output shape (" +
1928 std::to_string(H) + "," +
1929 std::to_string(W) + ")";
1930 printNodeValidationError(msg.c_str());
1931 return 1;
1932 }
1933
Tai Lya4d748b2023-03-28 22:06:56 +00001934 ERROR_IF(InDtype != TOSA_REF_TYPE_INT8 && attribute->input_zp() != 0,
1935 "OpTransposeConv2d: Input zeropoint must be zero for non int8_t data");
1936 ERROR_IF(WeightDtype != TOSA_REF_TYPE_INT8 && attribute->weight_zp() != 0,
1937 "OpTransposeConv2d: Weight zeropoint must be zero for non int8_t data");
Kevin Chengcc61be32021-10-14 17:09:57 -07001938
Eric Kunzee5e26762020-10-13 16:11:07 -07001939 return 0;
1940}
1941
Tai Lya4d748b2023-03-28 22:06:56 +00001942template <TOSA_REF_TYPE InDtype, TOSA_REF_TYPE WeightDtype, TOSA_REF_TYPE OutDtype>
James Wardd34b3fc2023-01-18 14:51:25 +00001943int OpTransposeConv2d<InDtype, WeightDtype, OutDtype>::eval()
Eric Kunzee5e26762020-10-13 16:11:07 -07001944{
1945 int in_batch = this->input->getShape()[0];
1946 int in_height = this->input->getShape()[1];
1947 int in_width = this->input->getShape()[2];
1948 int in_channels = this->input->getShape()[3];
1949
1950 int f_out_channels = this->weight->getShape()[0];
1951 int f_height = this->weight->getShape()[1];
1952 int f_width = this->weight->getShape()[2];
1953 int f_in_channels = this->weight->getShape()[3];
1954
1955 int b_out_channels = this->bias->getShape()[0];
1956
1957 int out_batch = this->output->getShape()[0];
1958 int out_height = this->output->getShape()[1];
1959 int out_width = this->output->getShape()[2];
1960 int out_channels = this->output->getShape()[3];
1961
TatWai Chong24594f52022-06-08 00:48:04 -07001962 int out_pad_top = this->attribute->out_pad()[0];
1963 int out_pad_bottom = this->attribute->out_pad()[1];
1964 int out_pad_left = this->attribute->out_pad()[2];
1965 int out_pad_right = this->attribute->out_pad()[3];
Jeremy Johnson4a6fb9b2022-04-26 15:47:21 +01001966
Jerry Gea793f462023-04-11 00:05:02 +00001967 int stride_y = this->attribute->stride()[0];
1968 int stride_x = this->attribute->stride()[1];
Eric Kunzee5e26762020-10-13 16:11:07 -07001969
Kevin Chengacb550f2021-06-29 15:32:19 -07001970 ERROR_IF(in_batch != out_batch, "OpTransposeConv2d: tensor batch mismatch %d != %d", in_batch, out_batch);
1971 ERROR_IF(f_in_channels != in_channels, "OpTransposeConv2d: tensor input channel mismatch %d != %d", f_in_channels,
1972 in_channels);
1973 ERROR_IF(f_out_channels != out_channels, "OpTransposeConv2d: tensor output channel mismatch %d != %d",
1974 f_out_channels, out_channels);
1975 ERROR_IF(b_out_channels != out_channels, "OpDepthwiseConv2d: bias channels mismatch %d != %d", b_out_channels,
1976 out_channels);
Eric Kunzee5e26762020-10-13 16:11:07 -07001977
Jerry Gea793f462023-04-11 00:05:02 +00001978 // Check Tosa Level
1979 auto tosa_level = g_func_config.tosa_level;
1980 LEVEL_CHECK(f_height <= tosa_level.MAX_KERNEL, "KH should be smaller than or equal to MAX_KERNEL");
1981 LEVEL_CHECK(f_width <= tosa_level.MAX_KERNEL, "KW should be smaller than or equal to MAX_KERNEL");
1982 LEVEL_CHECK(out_pad_top <= tosa_level.MAX_KERNEL, "out_pad_top should be smaller than or equal to MAX_KERNEL");
1983 LEVEL_CHECK(out_pad_bottom <= tosa_level.MAX_KERNEL, "out_pad_bottom should be smaller than or equal to MAX_KERNEL");
1984 LEVEL_CHECK(out_pad_left <= tosa_level.MAX_KERNEL, "out_pad_left should be smaller than or equal to MAX_KERNEL");
1985 LEVEL_CHECK(out_pad_right <= tosa_level.MAX_KERNEL, "out_pad_right should be smaller than or equal to MAX_KERNEL");
1986 LEVEL_CHECK(stride_y <= tosa_level.MAX_STRIDE, "stride_y should be smaller than or equal to MAX_STRIDE");
1987 LEVEL_CHECK(stride_x <= tosa_level.MAX_STRIDE, "stride_x should be smaller than or equal to MAX_STRIDE");
1988
Eric Kunzee5e26762020-10-13 16:11:07 -07001989 DEBUG_INFO(OP,
1990 "perform OpTransposeConv2d, input.shape=[%d,%d,%d,%d], weight.shape=[%d,%d,%d,%d], "
James Wardd34b3fc2023-01-18 14:51:25 +00001991 "output.shape=[%d,%d,%d,%d], stride=[%d,%d], out_pad=[%d,%d,%d,%d]",
Jeremy Johnson4a6fb9b2022-04-26 15:47:21 +01001992 in_batch, in_height, in_width, in_channels, f_height, f_width, f_out_channels, f_in_channels,
Jerry Gea793f462023-04-11 00:05:02 +00001993 out_batch, out_height, out_width, out_channels, stride_y, stride_x, out_pad_top,
James Wardd34b3fc2023-01-18 14:51:25 +00001994 out_pad_bottom, out_pad_left, out_pad_right);
Eric Kunzee5e26762020-10-13 16:11:07 -07001995
1996 TIn input_val = this->input->getTensor();
1997 TWeight weight_val = this->weight->getTensor();
Tai Lya4d748b2023-03-28 22:06:56 +00001998 if (InDtype == TOSA_REF_TYPE_INT8 || WeightDtype == TOSA_REF_TYPE_INT8)
Eric Kunzee5e26762020-10-13 16:11:07 -07001999 {
Eric Kunzeb5fabec2022-06-07 05:20:44 +00002000 input_val = input_val - (InEigenType)attribute->input_zp();
2001 weight_val = weight_val - (WeightEigenType)attribute->weight_zp();
Eric Kunzee5e26762020-10-13 16:11:07 -07002002 }
2003
2004 Eigen::array<Eigen::Index, 4> reshape_dim;
2005 reshape_dim.fill(1);
2006 reshape_dim[3] = b_out_channels;
2007
2008 Eigen::array<Eigen::Index, 4> bcast;
2009 bcast[0] = out_batch;
2010 bcast[1] = out_height;
2011 bcast[2] = out_width;
2012 bcast[3] = 1;
2013
2014 // initialize with bias
2015 this->output->getTensor() = this->bias->getTensor().reshape(reshape_dim).broadcast(bcast);
2016
2017 int out_x_origin, out_y_origin;
2018 int out_x, out_y;
2019
2020 // reference implementation from: tensorflow/tensorflow/lite/kernels/internal/reference/reference_ops.h
2021 for (int ob = 0; ob < out_batch; ob++)
2022 {
2023 for (int ih = 0; ih < in_height; ih++)
2024 {
2025 for (int iw = 0; iw < in_width; iw++)
2026 {
Jerry Gea793f462023-04-11 00:05:02 +00002027 out_x_origin = iw * stride_x + out_pad_left;
2028 out_y_origin = ih * stride_y + out_pad_top;
Eric Kunzee5e26762020-10-13 16:11:07 -07002029 for (int ic = 0; ic < in_channels; ic++)
2030 {
2031 for (int fh = 0; fh < f_height; fh++)
2032 {
2033 for (int fw = 0; fw < f_width; fw++)
2034 {
Jeremy Johnson4a6fb9b2022-04-26 15:47:21 +01002035 out_x = out_x_origin + fw;
2036 out_y = out_y_origin + fh;
Eric Kunzee5e26762020-10-13 16:11:07 -07002037 for (int oc = 0; oc < out_channels; oc++)
2038 {
2039 if ((out_x >= 0 && out_x < out_width) && (out_y >= 0 && out_y < out_height))
2040 {
2041 this->output->getTensor()(ob, out_y, out_x, oc) +=
James Ward8b390432022-08-12 20:48:56 +01002042 (OutEigenType) ((AccEigenType)input_val(ob, ih, iw, ic) *
2043 (AccEigenType)weight_val(oc, fh, fw, ic));
Eric Kunzee5e26762020-10-13 16:11:07 -07002044 }
2045 }
2046 }
2047 }
2048 }
2049 }
2050 }
2051 }
2052
Tai Lya4d748b2023-03-28 22:06:56 +00002053 if (OutDtype == TOSA_REF_TYPE_INT48)
Eric Kunzee5e26762020-10-13 16:11:07 -07002054 {
James Ward8b390432022-08-12 20:48:56 +01002055 this->output->getTensor() = this->output->getTensor().cwiseMax((OutEigenType)AccQMin);
2056 this->output->getTensor() = this->output->getTensor().cwiseMin((OutEigenType)AccQMax);
Eric Kunzee5e26762020-10-13 16:11:07 -07002057 }
2058
2059 return GraphNode::eval();
2060}
2061
2062// template explicit instantiation
James Ward8b390432022-08-12 20:48:56 +01002063DEF_INSTANTIATE_RANK1_6_ONE_RANK_ONE_TYPE(OpArgMax, FP16);
James Ward24dbc422022-10-19 12:20:31 +01002064DEF_INSTANTIATE_RANK1_6_ONE_RANK_ONE_TYPE(OpArgMax, BF16);
Jeremy Johnsonbc2a3db2022-09-27 13:50:00 +01002065DEF_INSTANTIATE_RANK1_6_ONE_RANK_ONE_TYPE(OpArgMax, FP32);
Kevin Cheng3a478572021-01-22 17:21:02 -08002066DEF_INSTANTIATE_RANK1_6_ONE_RANK_ONE_TYPE(OpArgMax, INT8);
Eric Kunzee5e26762020-10-13 16:11:07 -07002067DEF_INSTANTIATE_RANK1_6_ONE_RANK_ONE_TYPE(OpArgMax, INT16);
Tai Lya4d748b2023-03-28 22:06:56 +00002068DEF_INSTANTIATE_RANK1_6_ONE_RANK_ONE_TYPE(OpArgMax, FP64);
Eric Kunzee5e26762020-10-13 16:11:07 -07002069
James Wardd34b3fc2023-01-18 14:51:25 +00002070DEF_INSTANTIATE_TWO_TYPE(OpAvgPool2d, FP16, FP16);
2071DEF_INSTANTIATE_TWO_TYPE(OpAvgPool2d, FP16, FP32);
2072DEF_INSTANTIATE_TWO_TYPE(OpAvgPool2d, BF16, FP32);
2073DEF_INSTANTIATE_TWO_TYPE(OpAvgPool2d, FP32, FP32);
2074DEF_INSTANTIATE_TWO_TYPE(OpAvgPool2d, INT8, INT32);
2075DEF_INSTANTIATE_TWO_TYPE(OpAvgPool2d, INT16, INT32);
Tai Lya4d748b2023-03-28 22:06:56 +00002076DEF_INSTANTIATE_TWO_TYPE(OpAvgPool2d, FP64, FP64);
Eric Kunzee5e26762020-10-13 16:11:07 -07002077
James Wardd34b3fc2023-01-18 14:51:25 +00002078 // [in_t, weight_t, out_t]
2079DEF_INSTANTIATE_THREE_TYPE(OpConv2d, FP16, FP16, FP16);
2080DEF_INSTANTIATE_THREE_TYPE(OpConv2d, FP16, FP16, FP32);
2081DEF_INSTANTIATE_THREE_TYPE(OpConv2d, BF16, BF16, FP32);
2082DEF_INSTANTIATE_THREE_TYPE(OpConv2d, FP32, FP32, FP32);
2083DEF_INSTANTIATE_THREE_TYPE(OpConv2d, INT8, INT4, INT32);
2084DEF_INSTANTIATE_THREE_TYPE(OpConv2d, INT8, INT8, INT32);
2085DEF_INSTANTIATE_THREE_TYPE(OpConv2d, INT16, INT8, INT48);
Tai Lya4d748b2023-03-28 22:06:56 +00002086DEF_INSTANTIATE_THREE_TYPE(OpConv2d, FP64, FP64, FP64);
Eric Kunzee5e26762020-10-13 16:11:07 -07002087
James Wardd34b3fc2023-01-18 14:51:25 +00002088DEF_INSTANTIATE_THREE_TYPE(OpConv3d, FP16, FP16, FP16);
2089DEF_INSTANTIATE_THREE_TYPE(OpConv3d, FP16, FP16, FP32);
2090DEF_INSTANTIATE_THREE_TYPE(OpConv3d, BF16, BF16, FP32);
2091DEF_INSTANTIATE_THREE_TYPE(OpConv3d, FP32, FP32, FP32);
2092DEF_INSTANTIATE_THREE_TYPE(OpConv3d, INT8, INT4, INT32);
2093DEF_INSTANTIATE_THREE_TYPE(OpConv3d, INT8, INT8, INT32);
2094DEF_INSTANTIATE_THREE_TYPE(OpConv3d, INT16, INT8, INT48);
Tai Lya4d748b2023-03-28 22:06:56 +00002095DEF_INSTANTIATE_THREE_TYPE(OpConv3d, FP64, FP64, FP64);
Kevin Cheng1533b852021-09-01 12:51:58 -07002096
James Wardd34b3fc2023-01-18 14:51:25 +00002097DEF_INSTANTIATE_THREE_TYPE(OpDepthwiseConv2d, FP16, FP16, FP16);
2098DEF_INSTANTIATE_THREE_TYPE(OpDepthwiseConv2d, FP16, FP16, FP32);
2099DEF_INSTANTIATE_THREE_TYPE(OpDepthwiseConv2d, BF16, BF16, FP32);
2100DEF_INSTANTIATE_THREE_TYPE(OpDepthwiseConv2d, FP32, FP32, FP32);
2101DEF_INSTANTIATE_THREE_TYPE(OpDepthwiseConv2d, INT8, INT4, INT32);
2102DEF_INSTANTIATE_THREE_TYPE(OpDepthwiseConv2d, INT8, INT8, INT32);
2103DEF_INSTANTIATE_THREE_TYPE(OpDepthwiseConv2d, INT16, INT8, INT48);
Tai Lya4d748b2023-03-28 22:06:56 +00002104DEF_INSTANTIATE_THREE_TYPE(OpDepthwiseConv2d, FP64, FP64, FP64);
Eric Kunzee5e26762020-10-13 16:11:07 -07002105
Luke Hutton57287132023-02-06 14:54:18 +00002106DEF_INSTANTIATE_ONE_TYPE(OpFFT2d, FP32);
Tai Lya4d748b2023-03-28 22:06:56 +00002107DEF_INSTANTIATE_ONE_TYPE(OpFFT2d, FP64);
Luke Hutton57287132023-02-06 14:54:18 +00002108
James Wardd34b3fc2023-01-18 14:51:25 +00002109DEF_INSTANTIATE_THREE_TYPE(OpFullyConnected, FP16, FP16, FP16);
2110DEF_INSTANTIATE_THREE_TYPE(OpFullyConnected, FP16, FP16, FP32);
2111DEF_INSTANTIATE_THREE_TYPE(OpFullyConnected, BF16, BF16, FP32);
2112DEF_INSTANTIATE_THREE_TYPE(OpFullyConnected, FP32, FP32, FP32);
2113DEF_INSTANTIATE_THREE_TYPE(OpFullyConnected, INT8, INT4, INT32);
2114DEF_INSTANTIATE_THREE_TYPE(OpFullyConnected, INT8, INT8, INT32);
2115DEF_INSTANTIATE_THREE_TYPE(OpFullyConnected, INT16, INT8, INT48);
Tai Lya4d748b2023-03-28 22:06:56 +00002116DEF_INSTANTIATE_THREE_TYPE(OpFullyConnected, FP64, FP64, FP64);
Eric Kunzee5e26762020-10-13 16:11:07 -07002117
James Wardd34b3fc2023-01-18 14:51:25 +00002118DEF_INSTANTIATE_TWO_TYPE(OpMatMul, INT8, INT32);
2119DEF_INSTANTIATE_TWO_TYPE(OpMatMul, INT16, INT48);
2120DEF_INSTANTIATE_TWO_TYPE(OpMatMul, FP16, FP16);
2121DEF_INSTANTIATE_TWO_TYPE(OpMatMul, FP16, FP32);
2122DEF_INSTANTIATE_TWO_TYPE(OpMatMul, BF16, FP32);
2123DEF_INSTANTIATE_TWO_TYPE(OpMatMul, FP32, FP32);
Tai Lya4d748b2023-03-28 22:06:56 +00002124DEF_INSTANTIATE_TWO_TYPE(OpMatMul, FP64, FP64);
Eric Kunzee5e26762020-10-13 16:11:07 -07002125
James Ward8b390432022-08-12 20:48:56 +01002126DEF_INSTANTIATE_ONE_TYPE(OpMaxPool2d, FP16);
James Ward24dbc422022-10-19 12:20:31 +01002127DEF_INSTANTIATE_ONE_TYPE(OpMaxPool2d, BF16);
Jeremy Johnsonbc2a3db2022-09-27 13:50:00 +01002128DEF_INSTANTIATE_ONE_TYPE(OpMaxPool2d, FP32);
Kevin Cheng3a478572021-01-22 17:21:02 -08002129DEF_INSTANTIATE_ONE_TYPE(OpMaxPool2d, INT8);
Eric Kunzee5e26762020-10-13 16:11:07 -07002130DEF_INSTANTIATE_ONE_TYPE(OpMaxPool2d, INT16);
Tai Lya4d748b2023-03-28 22:06:56 +00002131DEF_INSTANTIATE_ONE_TYPE(OpMaxPool2d, FP64);
Eric Kunzee5e26762020-10-13 16:11:07 -07002132
Luke Hutton261b7b62023-01-10 14:50:31 +00002133DEF_INSTANTIATE_ONE_TYPE(OpRFFT2d, FP32);
Tai Lya4d748b2023-03-28 22:06:56 +00002134DEF_INSTANTIATE_ONE_TYPE(OpRFFT2d, FP64);
Luke Hutton261b7b62023-01-10 14:50:31 +00002135
James Wardd34b3fc2023-01-18 14:51:25 +00002136DEF_INSTANTIATE_THREE_TYPE(OpTransposeConv2d, FP16, FP16, FP16);
2137DEF_INSTANTIATE_THREE_TYPE(OpTransposeConv2d, FP16, FP16, FP32);
2138DEF_INSTANTIATE_THREE_TYPE(OpTransposeConv2d, BF16, BF16, FP32);
2139DEF_INSTANTIATE_THREE_TYPE(OpTransposeConv2d, FP32, FP32, FP32);
2140DEF_INSTANTIATE_THREE_TYPE(OpTransposeConv2d, INT8, INT4, INT32);
2141DEF_INSTANTIATE_THREE_TYPE(OpTransposeConv2d, INT8, INT8, INT32);
2142DEF_INSTANTIATE_THREE_TYPE(OpTransposeConv2d, INT16, INT8, INT48);
Tai Lya4d748b2023-03-28 22:06:56 +00002143DEF_INSTANTIATE_THREE_TYPE(OpTransposeConv2d, FP64, FP64, FP64);