blob: 657eebfa7dfaa7abf32819ff44acdc2ef58153e4 [file] [log] [blame]
Eric Kunzee5e26762020-10-13 16:11:07 -07001
Kevin Cheng3a478572021-01-22 17:21:02 -08002// Copyright (c) 2020-2021, ARM Limited.
Eric Kunzee5e26762020-10-13 16:11:07 -07003//
4// Licensed under the Apache License, Version 2.0 (the "License");
5// you may not use this file except in compliance with the License.
6// You may obtain a copy of the License at
7//
8// http://www.apache.org/licenses/LICENSE-2.0
9//
10// Unless required by applicable law or agreed to in writing, software
11// distributed under the License is distributed on an "AS IS" BASIS,
12// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13// See the License for the specific language governing permissions and
14// limitations under the License.
15
16#include "type_conversion.h"
17#include "quant_util.h"
18#include "template_types.h"
19#include <cmath>
20
21using namespace TosaReference;
22using namespace Eigen;
23using namespace tosa;
24
25template <int Rank, DType InDtype, DType OutDtype>
Kevin Chengacb550f2021-06-29 15:32:19 -070026OpRescale<Rank, InDtype, OutDtype>::OpRescale(SubgraphTraverser* sgt_,
27 TosaAttributeBase* attribute_,
28 TosaQuantInfoBase* qinfo_,
29 uint64_t id_)
30 : GraphNode(sgt_, Op_RESCALE, id_)
Eric Kunzee5e26762020-10-13 16:11:07 -070031{
32 setRequiredOperands(1, 1);
33 setRequiredRank(0, 6);
34 INIT_ATTRIBUTE(Rescale);
35}
36
37template <int Rank, DType InDtype, DType OutDtype>
38OpRescale<Rank, InDtype, OutDtype>::~OpRescale()
39{
40 if (attribute)
41 delete attribute;
42}
43
44template <int Rank, DType InDtype, DType OutDtype>
45int OpRescale<Rank, InDtype, OutDtype>::checkTensorAttributes()
46{
47 if (validateRequiredOperands())
48 return 1;
49
50 if (validateRequiredRank(inputs[0]) || validateRequiredRank(outputs[0]))
51 {
52 return 1;
53 }
54
55 // output and input must be the same rank and size
56 if (inputs[0]->matchRankSize(*outputs[0]))
57 {
58 printNodeValidationError("OpRescale: input and output rank/size must match");
59 return 1;
60 }
61
62 in = dynamic_cast<TosaReference::TensorTemplate<TIn>*>(inputs[0]);
63 out = dynamic_cast<TosaReference::TensorTemplate<TOut>*>(outputs[0]);
64
65 ASSERT_MEM(in && out);
66
67 return 0;
68}
69
70template <int Rank, DType InDtype, DType OutDtype>
71int OpRescale<Rank, InDtype, OutDtype>::eval()
72{
73 int32_t input_zp = attribute->input_zp();
74 int32_t output_zp = attribute->output_zp();
75 std::vector<int32_t> multiplier = attribute->multiplier();
76 std::vector<int32_t> shift = attribute->shift();
Kevin Cheng0f87c952021-03-18 17:41:39 -070077 bool scale32 = attribute->scale32();
78 bool double_round = attribute->double_round();
79 bool per_channel = attribute->per_channel();
Eric Kunzee5e26762020-10-13 16:11:07 -070080
Eric Kunzee5e26762020-10-13 16:11:07 -070081 // reshape [d0, d1, ..., dn] into [d0 * d1 ..., dn]
82 Eigen::array<Eigen::Index, 2> shape_2d;
83 shape_2d[0] = 1;
84 if (Rank > 0)
85 {
86 for (int i = 0; i < Rank - 1; i++)
87 {
88 shape_2d[0] *= this->in->getShape()[i];
89 }
90 shape_2d[1] = this->in->getShape()[Rank - 1];
91 }
92 else
93 {
94 shape_2d[1] = 1;
95 }
96 ETensor2<InEigenType> input_reshaped = this->in->getTensor().reshape(shape_2d);
97
98 ETensor2<OutEigenType> output_2d(shape_2d);
99
Eric Kunzee5e26762020-10-13 16:11:07 -0700100 if (per_channel)
101 {
102 ETensor2<InEigenType> curr_channel_slice_prescaled;
103 ETensor2<OutEigenType> curr_channel_slice_postscaled;
104 int32_t channel_multiplier, channel_shift;
105 Eigen::array<Eigen::Index, 2> begin, size;
106 size = Eigen::array<Eigen::Index, 2>({ shape_2d[0], 1 });
Kevin Chengacb550f2021-06-29 15:32:19 -0700107 try
Eric Kunzee5e26762020-10-13 16:11:07 -0700108 {
Kevin Chengacb550f2021-06-29 15:32:19 -0700109 for (int32_t i = 0; i < shape_2d[1]; i++)
Eric Kunzee5e26762020-10-13 16:11:07 -0700110 {
Kevin Chengacb550f2021-06-29 15:32:19 -0700111 begin = Eigen::array<Eigen::Index, 2>({ 0, i });
112 curr_channel_slice_prescaled = input_reshaped.slice(begin, size);
113 channel_multiplier = multiplier[i];
114 channel_shift = shift[i];
115 curr_channel_slice_postscaled =
116 curr_channel_slice_prescaled.unaryExpr([input_zp, output_zp, channel_multiplier, channel_shift,
117 double_round, scale32](InEigenType in_val) -> OutEigenType {
118 InEigenType input_zp_shifted = in_val - (InEigenType)input_zp;
119 int32_t scaled;
120 if (scale32)
121 scaled = TosaReference::QuantUtil::apply_scale_32(input_zp_shifted, channel_multiplier,
122 channel_shift, double_round);
123 else
124 scaled = TosaReference::QuantUtil::apply_scale_16(input_zp_shifted, channel_multiplier,
125 channel_shift);
126 OutEigenType out_val = (OutEigenType)(scaled + output_zp);
127 out_val = std::max<OutEigenType>(out_val, QMin);
128 out_val = std::min<OutEigenType>(out_val, QMax);
129 return out_val;
130 });
131
132 for (int32_t j = 0; j < shape_2d[0]; j++)
133 {
134 output_2d(j, i) = curr_channel_slice_postscaled(j, 0);
135 }
Eric Kunzee5e26762020-10-13 16:11:07 -0700136 }
137 }
Kevin Chengacb550f2021-06-29 15:32:19 -0700138 catch (std::string desc)
139 {
140 REQUIRE(false, "OpRescale apply_scale_32/16() fails: %s.", desc.c_str());
141 }
Eric Kunzee5e26762020-10-13 16:11:07 -0700142 }
143 else
144 {
145 int32_t tensor_multiplier = multiplier[0];
146 int32_t tensor_shift = shift[0];
Kevin Chengacb550f2021-06-29 15:32:19 -0700147 try
148 {
149 output_2d = input_reshaped.unaryExpr([input_zp, output_zp, tensor_multiplier, tensor_shift, double_round,
150 scale32](InEigenType in_val) -> OutEigenType {
151 InEigenType input_zp_shifted = in_val - (InEigenType)input_zp;
152 int32_t scaled;
153 if (scale32)
154 scaled = TosaReference::QuantUtil::apply_scale_32(input_zp_shifted, tensor_multiplier, tensor_shift,
155 double_round);
156 else
157 scaled =
158 TosaReference::QuantUtil::apply_scale_16(input_zp_shifted, tensor_multiplier, tensor_shift);
159 OutEigenType out_val = (OutEigenType)(scaled + output_zp);
160 out_val = std::max<OutEigenType>(out_val, QMin);
161 out_val = std::min<OutEigenType>(out_val, QMax);
162 return out_val;
163 });
164 }
165 catch (std::string desc)
166 {
167 REQUIRE(false, "OpRescale apply_scale_32/16() fails: %s.", desc.c_str());
168 }
Eric Kunzee5e26762020-10-13 16:11:07 -0700169 }
170
171 // reshape [d0 * d1 ..., dn] back to [d0, d1, ..., dn]
172 Eigen::array<Eigen::Index, Rank> output_shape;
173 for (int i = 0; i < Rank; i++)
174 {
175 output_shape[i] = this->out->getShape()[i];
176 }
177 this->out->getTensor() = output_2d.reshape(output_shape);
178
179 return GraphNode::eval();
180}
181
182template <int Rank, DType InDtype, DType OutDtype>
Kevin Chengacb550f2021-06-29 15:32:19 -0700183OpCast<Rank, InDtype, OutDtype>::OpCast(SubgraphTraverser* sgt_,
184 TosaAttributeBase* attribute_,
185 TosaQuantInfoBase* qinfo_,
186 uint64_t id_)
187 : GraphNode(sgt_, Op_CAST, id_)
Eric Kunzee5e26762020-10-13 16:11:07 -0700188{
189 setRequiredOperands(1, 1);
190 setRequiredRank(0, 6);
191}
192
193template <int Rank, DType InDtype, DType OutDtype>
194OpCast<Rank, InDtype, OutDtype>::~OpCast()
195{}
196
197template <int Rank, DType InDtype, DType OutDtype>
198int OpCast<Rank, InDtype, OutDtype>::checkTensorAttributes()
199{
200 if (validateRequiredOperands())
201 return 1;
202
203 if (validateRequiredRank(inputs[0]) || validateRequiredRank(outputs[0]))
204 {
205 return 1;
206 }
207
208 // output and input must be the same rank and size
209 if (inputs[0]->matchRankSize(*outputs[0]))
210 {
211 printNodeValidationError("OpCast: input and output rank/size must match");
212 return 1;
213 }
214
215 in = dynamic_cast<TosaReference::TensorTemplate<TIn>*>(inputs[0]);
216 out = dynamic_cast<TosaReference::TensorTemplate<TOut>*>(outputs[0]);
217
218 ASSERT_MEM(in && out);
219
220 return 0;
221}
222
223template <int Rank, DType InDtype, DType OutDtype>
224int OpCast<Rank, InDtype, OutDtype>::eval()
225{
226 this->out->getTensor() = this->in->getTensor().unaryExpr(cast_helper.get_fcn());
227
228 return GraphNode::eval();
229}
230
231template <DType InDtype, DType OutDtype>
232CastHelper<InDtype, OutDtype>::CastHelper()
233{
234 fcn = [](InEigenType in) -> OutEigenType {
235 OutEigenType out = (OutEigenType)in; // implicit sign_extend() if sizeof(out_t) >= sizeof(in_t)
Eric Kunzee5e26762020-10-13 16:11:07 -0700236 return out;
237 };
238}
239
240template <DType InDtype>
241CastHelper<InDtype, DType_BOOL>::CastHelper()
242{
243 fcn = [](InEigenType in) -> bool { return (in != 0) ? true : false; };
244}
245
246template <DType OutDtype>
247CastHelper<DType_BOOL, OutDtype>::CastHelper()
248{
249 fcn = [](bool in) -> OutEigenType {
250 OutEigenType out = in ? (OutEigenType)1 : (OutEigenType)0;
251 return out;
252 };
253}
254
255template <DType InDtype>
256CastHelper<InDtype, DType_FLOAT>::CastHelper()
257{
258 fcn = [](InEigenType in) -> float {
259 float out = (OutEigenType)in; // default cast to float is round_to_nearest_float()
260 return out;
261 };
262}
263
264template <DType OutDtype>
265CastHelper<DType_FLOAT, OutDtype>::CastHelper()
266{
267 fcn = [](float in) -> OutEigenType {
268 OutEigenType out = std::round(in);
269 out = std::max<OutEigenType>(out, OutMin);
270 out = std::min<OutEigenType>(out, OutMax);
271 return out;
272 };
273}
274
275// template explicit instantiation
276DEF_INSTANTIATE_RANK0_6_ONE_RANK_TWO_TYPE(OpCast, BOOL, INT8);
277DEF_INSTANTIATE_RANK0_6_ONE_RANK_TWO_TYPE(OpCast, BOOL, INT16);
278DEF_INSTANTIATE_RANK0_6_ONE_RANK_TWO_TYPE(OpCast, BOOL, INT32);
279DEF_INSTANTIATE_RANK0_6_ONE_RANK_TWO_TYPE(OpCast, INT8, BOOL);
280DEF_INSTANTIATE_RANK0_6_ONE_RANK_TWO_TYPE(OpCast, INT8, INT16);
281DEF_INSTANTIATE_RANK0_6_ONE_RANK_TWO_TYPE(OpCast, INT8, INT32);
282DEF_INSTANTIATE_RANK0_6_ONE_RANK_TWO_TYPE(OpCast, INT8, FLOAT);
283DEF_INSTANTIATE_RANK0_6_ONE_RANK_TWO_TYPE(OpCast, INT16, BOOL);
284DEF_INSTANTIATE_RANK0_6_ONE_RANK_TWO_TYPE(OpCast, INT16, INT8);
285DEF_INSTANTIATE_RANK0_6_ONE_RANK_TWO_TYPE(OpCast, INT16, INT32);
286DEF_INSTANTIATE_RANK0_6_ONE_RANK_TWO_TYPE(OpCast, INT16, FLOAT);
287DEF_INSTANTIATE_RANK0_6_ONE_RANK_TWO_TYPE(OpCast, INT32, BOOL);
288DEF_INSTANTIATE_RANK0_6_ONE_RANK_TWO_TYPE(OpCast, INT32, INT8);
289DEF_INSTANTIATE_RANK0_6_ONE_RANK_TWO_TYPE(OpCast, INT32, INT16);
290DEF_INSTANTIATE_RANK0_6_ONE_RANK_TWO_TYPE(OpCast, INT32, FLOAT);
291DEF_INSTANTIATE_RANK0_6_ONE_RANK_TWO_TYPE(OpCast, FLOAT, INT8);
292DEF_INSTANTIATE_RANK0_6_ONE_RANK_TWO_TYPE(OpCast, FLOAT, INT16);
293DEF_INSTANTIATE_RANK0_6_ONE_RANK_TWO_TYPE(OpCast, FLOAT, INT32);
294
Kevin Cheng3a478572021-01-22 17:21:02 -0800295DEF_INSTANTIATE_RANK0_6_ONE_RANK_TWO_TYPE(OpRescale, INT8, INT8);
296DEF_INSTANTIATE_RANK0_6_ONE_RANK_TWO_TYPE(OpRescale, INT8, INT16);
297DEF_INSTANTIATE_RANK0_6_ONE_RANK_TWO_TYPE(OpRescale, INT8, INT32);
298DEF_INSTANTIATE_RANK0_6_ONE_RANK_TWO_TYPE(OpRescale, INT16, INT8);
Eric Kunzee5e26762020-10-13 16:11:07 -0700299DEF_INSTANTIATE_RANK0_6_ONE_RANK_TWO_TYPE(OpRescale, INT16, INT16);
300DEF_INSTANTIATE_RANK0_6_ONE_RANK_TWO_TYPE(OpRescale, INT16, INT32);
Kevin Cheng3a478572021-01-22 17:21:02 -0800301DEF_INSTANTIATE_RANK0_6_ONE_RANK_TWO_TYPE(OpRescale, INT32, INT8);
Eric Kunzee5e26762020-10-13 16:11:07 -0700302DEF_INSTANTIATE_RANK0_6_ONE_RANK_TWO_TYPE(OpRescale, INT32, INT16);
303DEF_INSTANTIATE_RANK0_6_ONE_RANK_TWO_TYPE(OpRescale, INT32, INT32);
Kevin Cheng3a478572021-01-22 17:21:02 -0800304DEF_INSTANTIATE_RANK0_6_ONE_RANK_TWO_TYPE(OpRescale, INT48, INT8);
Eric Kunzee5e26762020-10-13 16:11:07 -0700305DEF_INSTANTIATE_RANK0_6_ONE_RANK_TWO_TYPE(OpRescale, INT48, INT16);
306DEF_INSTANTIATE_RANK0_6_ONE_RANK_TWO_TYPE(OpRescale, INT48, INT32);
Kevin Cheng3a478572021-01-22 17:21:02 -0800307DEF_INSTANTIATE_RANK0_6_ONE_RANK_TWO_TYPE(OpRescale, UINT8, INT8);
308DEF_INSTANTIATE_RANK0_6_ONE_RANK_TWO_TYPE(OpRescale, INT8, UINT8);