blob: c647812d5f3e471f0224f4caf65fb7e49daeab9a [file] [log] [blame]
arovir01b0717b52018-09-05 17:03:25 +01001//
2// Copyright © 2017 Arm Ltd. All rights reserved.
3// SPDX-License-Identifier: MIT
4//
5
6#include "HalPolicy.hpp"
7
8#include "../1.0/HalPolicy.hpp"
9
10namespace armnn_driver
11{
12namespace hal_1_1
13{
14
15bool HalPolicy::ConvertOperation(const Operation& operation, const Model& model, ConversionData& data)
16{
17 if (compliantWithV1_0(operation))
18 {
19 hal_1_0::HalPolicy::Operation v10Operation = convertToV1_0(operation);
20 hal_1_0::HalPolicy::Model v10Model = convertToV1_0(model);
21
22 return hal_1_0::HalPolicy::ConvertOperation(v10Operation, v10Model, data);
23 }
24 else
25 {
26 switch (operation.type)
27 {
28 case V1_1::OperationType::DIV:
29 return ConvertDiv(operation, model, data);
David Beck38e12942018-09-12 16:02:24 +010030 case V1_1::OperationType::SUB:
31 return ConvertSub(operation, model, data);
narpra013c052562018-09-17 14:25:04 +010032 case V1_1::OperationType::MEAN:
33 return ConvertMean(operation, model, data);
Nina Drozd62a4a9f2018-10-01 14:20:25 +010034 case V1_1::OperationType::PAD:
35 return ConvertPad(operation, model, data);
saoste01b8471482018-10-10 09:44:51 +010036 case V1_1::OperationType::SQUEEZE:
37 return ConvertSqueeze(operation, model, data);
saoste01fe463152018-10-18 17:49:56 +010038 case V1_1::OperationType::TRANSPOSE:
39 return ConvertTranspose(operation, model, data);
arovir01b0717b52018-09-05 17:03:25 +010040 default:
41 return Fail("%s: Operation type %s not supported in ArmnnDriver",
42 __func__, toString(operation.type).c_str());
43 }
44 }
45}
46
47bool HalPolicy::ConvertDiv(const Operation& operation, const Model& model, ConversionData& data)
48{
49 LayerInputHandle input0 = ConvertToLayerInputHandle(operation, 0, model, data);
50 LayerInputHandle input1 = ConvertToLayerInputHandle(operation, 1, model, data);
51
52 if (!input0.IsValid() || !input1.IsValid())
53 {
54 return Fail("%s: Operation has invalid inputs", __func__);
55 }
56
57 // The FuseActivation parameter is always the input index 2
58 // and it should be optional
59 ActivationFn activationFunction;
60 if (!GetOptionalInputActivation(operation, 2, activationFunction, model, data))
61 {
62 return Fail("%s: Operation has invalid inputs", __func__);
63 }
64
65 const Operand* outputOperand = GetOutputOperand(operation, 0, model);
66 if (!outputOperand)
67 {
68 return false;
69 }
70
71 const armnn::TensorInfo& outInfo = GetTensorInfoForOperand(*outputOperand);
72
73 if (!IsLayerSupported(__func__,
74 armnn::IsDivisionSupported,
75 data.m_Compute,
76 input0.GetTensorInfo(),
77 input1.GetTensorInfo(),
78 outInfo))
79 {
80 return false;
81 }
82
83 armnn::IConnectableLayer* const startLayer = data.m_Network->AddDivisionLayer();
84 armnn::IConnectableLayer* const endLayer = ProcessActivation(outInfo, activationFunction, startLayer, data);
85
86 const armnn::TensorInfo& inputTensorInfo0 = input0.GetTensorInfo();
87 const armnn::TensorInfo& inputTensorInfo1 = input1.GetTensorInfo();
88
89 if (endLayer)
90 {
91 BroadcastTensor(input0, input1, startLayer, *data.m_Network);
92 return SetupAndTrackLayerOutputSlot(operation, 0, *endLayer, model, data);
93 }
94
95 return Fail("%s: ProcessActivation failed", __func__);
96}
97
David Beck38e12942018-09-12 16:02:24 +010098bool HalPolicy::ConvertSub(const Operation& operation, const Model& model, ConversionData& data)
99{
100 LayerInputHandle input0 = ConvertToLayerInputHandle(operation, 0, model, data);
101 LayerInputHandle input1 = ConvertToLayerInputHandle(operation, 1, model, data);
102
103 if (!input0.IsValid() || !input1.IsValid())
104 {
105 return Fail("%s: Operation has invalid inputs", __func__);
106 }
107
108 // The FuseActivation parameter is always the input index 2
109 // and it should be optional
110 ActivationFn activationFunction;
111 if (!GetOptionalInputActivation(operation, 2, activationFunction, model, data))
112 {
113 return Fail("%s: Operation has invalid inputs", __func__);
114 }
115
116 const Operand* outputOperand = GetOutputOperand(operation, 0, model);
117 if (!outputOperand)
118 {
119 return false;
120 }
121
122 const armnn::TensorInfo& outInfo = GetTensorInfoForOperand(*outputOperand);
123
124 if (!IsLayerSupported(__func__,
125 armnn::IsSubtractionSupported,
126 data.m_Compute,
127 input0.GetTensorInfo(),
128 input1.GetTensorInfo(),
129 outInfo))
130 {
131 return false;
132 }
133
134 armnn::IConnectableLayer* const startLayer = data.m_Network->AddSubtractionLayer();
135 armnn::IConnectableLayer* const endLayer = ProcessActivation(outInfo, activationFunction, startLayer, data);
136
137 const armnn::TensorInfo& inputTensorInfo0 = input0.GetTensorInfo();
138 const armnn::TensorInfo& inputTensorInfo1 = input1.GetTensorInfo();
139
140 if (endLayer)
141 {
142 BroadcastTensor(input0, input1, startLayer, *data.m_Network);
143 return SetupAndTrackLayerOutputSlot(operation, 0, *endLayer, model, data);
144 }
145
146 return Fail("%s: ProcessActivation failed", __func__);
147}
148
narpra013c052562018-09-17 14:25:04 +0100149bool HalPolicy::ConvertMean(const Operation& operation, const Model& model, ConversionData& data)
150{
151 LayerInputHandle input = ConvertToLayerInputHandle(operation, 0, model, data);
narpra013c052562018-09-17 14:25:04 +0100152 if (!input.IsValid())
153 {
154 return Fail("%s: Operation has invalid inputs", __func__);
155 }
156
Matteo Martincighae622b72018-10-23 18:25:38 +0100157 const Operand* axisOperand = GetInputOperand(operation, 1, model);
158 if (!axisOperand)
159 {
160 return Fail("%s: Could not read input 1", __func__);
161 }
162
163 std::vector<int32_t> axis;
164 if (!GetTensorInt32Values(*axisOperand, axis, model, data))
165 {
166 return Fail("%s: Input 1 has invalid values", __func__);
167 }
168
169 const armnn::TensorInfo& inputInfo = input.GetTensorInfo();
170
171 // Convert the axis to unsigned int and remove duplicates.
172 unsigned int rank = inputInfo.GetNumDimensions();
173 std::set<unsigned int> uniqueAxis;
174 std::transform(axis.begin(), axis.end(),
175 std::inserter(uniqueAxis, uniqueAxis.begin()),
176 [rank](int i) -> unsigned int { return (i + rank) % rank; });
177
178 // Get the "keep dims" flag.
179 int32_t keepDims = 0;
180 if (!GetInputInt32(operation, 2, keepDims, model, data))
181 {
182 return Fail("%s: Could not read input 2", __func__);
183 }
narpra013c052562018-09-17 14:25:04 +0100184
185 armnn::MeanDescriptor descriptor;
Matteo Martincighae622b72018-10-23 18:25:38 +0100186 descriptor.m_Axis.assign(uniqueAxis.begin(), uniqueAxis.end());
187 descriptor.m_KeepDims = keepDims > 0;
narpra013c052562018-09-17 14:25:04 +0100188
189 const Operand* output = GetOutputOperand(operation, 0, model);
190 if (!output)
191 {
192 return Fail("%s: Could not read output 0", __func__);
193 }
194
195 const armnn::TensorInfo& outputInfo = GetTensorInfoForOperand(*output);
196
197 if (!IsLayerSupported(__func__,
198 armnn::IsMeanSupported,
199 data.m_Compute,
200 inputInfo,
201 outputInfo,
202 descriptor))
203 {
204 return false;
205 }
206
207 armnn::IConnectableLayer* const layer = data.m_Network->AddMeanLayer(descriptor);
narpra0196bedf02018-09-26 16:57:28 +0100208 assert(layer != nullptr);
209 input.Connect(layer->GetInputSlot(0));
narpra013c052562018-09-17 14:25:04 +0100210
211 return SetupAndTrackLayerOutputSlot(operation, 0, *layer, model, data);
212}
213
Nina Drozd62a4a9f2018-10-01 14:20:25 +0100214bool HalPolicy::ConvertPad(const Operation& operation, const Model& model, ConversionData& data)
215{
216 LayerInputHandle input = ConvertToLayerInputHandle(operation, 0, model, data);
217
218 if (!input.IsValid())
219 {
220 return Fail("%s: Operation has invalid inputs", __func__);
221 }
222
223 const armnn::TensorInfo& inputInfo = input.GetTensorInfo();
224
225 const Operand* paddingsOperand = GetInputOperand(operation, 1, model);
226
227 if (!paddingsOperand)
228 {
229 return Fail("%s: Could not read paddings operand", __func__);
230 }
231
232 unsigned int rank = inputInfo.GetNumDimensions();
233 armnn::TensorShape paddingsOperandShape = GetTensorShapeForOperand(*paddingsOperand);
234 if (paddingsOperandShape.GetNumDimensions() != rank || paddingsOperandShape.GetNumElements() != 2)
235 {
236 return Fail("%s: Operation has invalid paddings operand: expected shape [%d, 2]", __func__, rank);
237 }
238
239 std::vector<int32_t> paddings;
240 GetTensorInt32Values(*paddingsOperand, paddings, model, data);
241
242 // add padding for each dimension of input tensor.
243 armnn::PadDescriptor descriptor;
244 for (unsigned int i = 0; i < paddings.size() - 1; i += 2)
245 {
246 int paddingBeforeInput = paddings[i];
247 int paddingAfterInput = paddings[i + 1];
248 if (paddingBeforeInput < 0 || paddingAfterInput < 0)
249 {
250 return Fail("%s: Operation has invalid paddings operand, invalid padding values.", __func__);
251 }
252 descriptor.m_PadList.emplace_back((unsigned int) paddingBeforeInput, (unsigned int) paddingAfterInput);
253 }
254
255 const Operand* output = GetOutputOperand(operation, 0, model);
256 if (!output)
257 {
258 return Fail("%s: Could not read output 0", __func__);
259 }
260
261 const armnn::TensorInfo& outputInfo = GetTensorInfoForOperand(*output);
262
263 if (!IsLayerSupported(__func__,
264 armnn::IsPadSupported,
265 data.m_Compute,
266 inputInfo,
267 outputInfo,
268 descriptor))
269 {
270 return false;
271 }
272
273 armnn::IConnectableLayer* const layer = data.m_Network->AddPadLayer(descriptor);
274 assert(layer != nullptr);
275 input.Connect(layer->GetInputSlot(0));
276 layer->GetOutputSlot(0).SetTensorInfo(outputInfo);
277
278 return SetupAndTrackLayerOutputSlot(operation, 0, *layer, model, data);
279}
280
saoste01b8471482018-10-10 09:44:51 +0100281bool HalPolicy::ConvertSqueeze(const Operation& operation, const Model& model, ConversionData& data)
282{
saoste01b8471482018-10-10 09:44:51 +0100283 LayerInputHandle input = ConvertToLayerInputHandle(operation, 0, model, data);
284
285 if (!input.IsValid())
286 {
287 return Fail("%s: Operation has invalid inputs", __func__);
288 }
289
290 const armnn::TensorInfo& inputInfo = input.GetTensorInfo();
291
292 unsigned int rank = inputInfo.GetNumDimensions();
saoste01fe463152018-10-18 17:49:56 +0100293 if (rank > 4)
saoste01b8471482018-10-10 09:44:51 +0100294 {
saoste01fe463152018-10-18 17:49:56 +0100295 Fail("%s: Inputs with rank greater than 4 are not supported", __func__);
saoste01b8471482018-10-10 09:44:51 +0100296 }
297
298 // NOTE: Axis is an optional parameter to SQUEEZE, therefore we do not want to generate a failure
299 // if the operand index is out of bounds.
300 const Operand* axisOperand = GetInputOperand(operation, 1, model, false);
301
saoste01fe463152018-10-18 17:49:56 +0100302 const uint32_t dimensionSequence[] = { 0, 1, 2, 3 };
303
saoste01b8471482018-10-10 09:44:51 +0100304 std::vector<int32_t> axis;
saoste01fe463152018-10-18 17:49:56 +0100305 if (!axisOperand)
saoste01b8471482018-10-10 09:44:51 +0100306 {
307 axis.assign(dimensionSequence,
saoste01fe463152018-10-18 17:49:56 +0100308 dimensionSequence + rank);
saoste01b8471482018-10-10 09:44:51 +0100309 }
310 else
311 {
312 GetTensorInt32Values(*axisOperand, axis, model, data);
313 }
314
saoste01b8471482018-10-10 09:44:51 +0100315
saoste01a893efa2018-10-13 11:56:12 +0100316 std::vector<uint32_t> outputDims;
saoste01fe463152018-10-18 17:49:56 +0100317 for (unsigned int i = 0; i < rank; i++)
saoste01a893efa2018-10-13 11:56:12 +0100318 {
319 bool skipSqueeze = (std::find(axis.begin(), axis.end(), i) == axis.end());
320 auto currentDimension = inputInfo.GetShape()[i];
saoste01b8471482018-10-10 09:44:51 +0100321 if (skipSqueeze || currentDimension != 1)
322 {
323 outputDims.push_back(currentDimension);
324 }
325 }
326
saoste01fe463152018-10-18 17:49:56 +0100327 armnn::TensorShape outShape = armnn::TensorShape(outputDims.size(), outputDims.data());
saoste01b8471482018-10-10 09:44:51 +0100328
329 armnn::TensorInfo outputInfo = inputInfo;
330 outputInfo.SetShape(outShape);
331
332 armnn::ReshapeDescriptor reshapeDesc;
333 reshapeDesc.m_TargetShape = outputInfo.GetShape();
334
335 const Operand* output = GetOutputOperand(operation, 0, model);
336 if (!output)
337 {
338 return Fail("%s: Could not read output 0", __func__);
339 }
340
341 if (!IsLayerSupported(__func__,
342 armnn::IsReshapeSupported,
343 data.m_Compute,
344 inputInfo))
345 {
346 return false;
347 }
348
349 armnn::IConnectableLayer* const layer = data.m_Network->AddReshapeLayer(reshapeDesc);
350 assert(layer != nullptr);
351 input.Connect(layer->GetInputSlot(0));
saoste01fe463152018-10-18 17:49:56 +0100352
353 return SetupAndTrackLayerOutputSlot(operation, 0, *layer, model, data);
354}
355
356bool HalPolicy::ConvertTranspose(const Operation& operation, const Model& model, ConversionData& data)
357{
358 LayerInputHandle input = ConvertToLayerInputHandle(operation, 0, model, data);
359
360 if (!input.IsValid())
361 {
362 return Fail("%s: Operation has invalid inputs", __func__);
363 }
364
365 const armnn::TensorInfo& inputInfo = input.GetTensorInfo();
366
367 unsigned int rank = inputInfo.GetNumDimensions();
368 if (rank > 4)
369 {
370 Fail("%s: Inputs with rank greater than 4 are not supported", __func__);
371 }
372
373 // NOTE: Axis is an optional parameter to TRANSPOSE, therefore we do not want to generate a failure
374 // if the operand index is out of bounds.
375 const Operand* permOperand = GetInputOperand(operation, 1, model, false);
376
377 std::vector<int32_t> perm(rank);
378 if (!permOperand)
379 {
380 // NOTE: If perm is not given, it is set to (n-1...0), where n is the rank of the tensor
381 for (unsigned int i = rank; i > 0; i--)
382 {
383 perm[rank - i] = boost::numeric_cast<int> (i - 1);
384 }
385 }
386 else
387 {
388 GetTensorInt32Values(*permOperand, perm, model, data);
389 }
390
391 std::vector<uint32_t> outputDims(perm.begin(), perm.begin() + rank);
392
393 auto permutationVector = armnn::PermutationVector(outputDims.data(), outputDims.size());
394 if (!permutationVector.IsEqual(NHWCToArmNN)
395 && !permutationVector.IsEqual(ArmNNToNHWC)
396 && !permutationVector.IsEqual({ 3, 2, 0, 1 }))
397 {
398 return Fail("%s: Only [0, 3, 1, 2], [0, 2, 3, 1] and [3, 2, 0, 1] permutations are supported.", __func__);
399 }
400
401 armnn::PermuteDescriptor permuteDesc;
402 permuteDesc.m_DimMappings = permutationVector;
403
404 const Operand* output = GetOutputOperand(operation, 0, model);
405 if (!output)
406 {
407 return Fail("%s: Could not read output 0", __func__);
408 }
409
410 const armnn::TensorInfo& outputInfo = GetTensorInfoForOperand(*output);
411
412 if (!IsLayerSupported(__func__,
413 armnn::IsPermuteSupported,
414 data.m_Compute,
415 inputInfo,
416 outputInfo,
417 permuteDesc))
418 {
419 return false;
420 }
421
422 armnn::IConnectableLayer* const layer = data.m_Network->AddPermuteLayer(permuteDesc);
423 assert(layer != nullptr);
424 input.Connect(layer->GetInputSlot(0));
saoste01b8471482018-10-10 09:44:51 +0100425
426 return SetupAndTrackLayerOutputSlot(operation, 0, *layer, model, data);
427}
428
arovir01b0717b52018-09-05 17:03:25 +0100429} // namespace hal_1_1
saoste01b8471482018-10-10 09:44:51 +0100430} // namespace armnn_driver