blob: f48af32e21a2feef7f3715994bdca372b080d1fd [file] [log] [blame]
Sadik Armagan8f397a12022-06-17 15:38:22 +01001//
2// Copyright © 2022 Arm Ltd and Contributors. All rights reserved.
3// SPDX-License-Identifier: MIT
4//
5
6#include "ConversionUtils.hpp"
7#include <armnnUtils/Permute.hpp>
8
9///
10/// Helper classes
11///
12
13namespace armnn_driver
14{
15
16LayerInputHandle::LayerInputHandle()
17 : m_OutputSlot(nullptr)
18 , m_Valid(false)
19{}
20
21LayerInputHandle::LayerInputHandle(bool valid, armnn::IOutputSlot* outputSlot, armnn::TensorInfo tensorInfo)
22 : m_OutputSlot(outputSlot)
23 , m_Valid(valid)
24 , m_TensorInfo(tensorInfo)
25{}
26
27bool LayerInputHandle::IsValid() const
28{
29 return m_Valid;
30}
31
32void LayerInputHandle::Connect(armnn::IInputSlot& inputSlot)
33{
34 ARMNN_ASSERT(IsValid());
35 if (m_OutputSlot)
36 {
37 m_OutputSlot->Connect(inputSlot);
38 }
39}
40
41void LayerInputHandle::Disconnect(armnn::IInputSlot& inputSlot)
42{
43 ARMNN_ASSERT(IsValid());
44 if (m_OutputSlot)
45 {
46 m_OutputSlot->Disconnect(inputSlot);
47 }
48}
49
50const armnn::TensorInfo& LayerInputHandle::GetTensorInfo() const
51{
52 return m_TensorInfo;
53}
54
55void LayerInputHandle::SanitizeQuantizationScale(LayerInputHandle& weight, LayerInputHandle& input)
56{
57 if (m_OutputSlot)
58 {
59 armnn::TensorInfo weightInfo = weight.GetTensorInfo();
60 armnn::TensorInfo inputInfo = input.GetTensorInfo();
61 armnn::TensorInfo biasInfo = GetTensorInfo();
62
63 SanitizeBiasQuantizationScale(biasInfo, weightInfo, inputInfo);
64
65 m_TensorInfo = biasInfo;
66 m_OutputSlot->SetTensorInfo(biasInfo);
67 }
68}
69
Sadik Armaganb0161572022-08-03 11:27:05 +010070armnn::IOutputSlot* LayerInputHandle::GetOutputSlot() const
71{
72 return m_OutputSlot;
73}
74
Sadik Armagan8f397a12022-06-17 15:38:22 +010075ConstTensorPin::ConstTensorPin(bool optional)
76 : m_Optional(optional)
77{}
78
79ConstTensorPin::ConstTensorPin(armnn::TensorInfo& tensorInfo,
80 const void* valueStart,
81 uint32_t numBytes,
82 const armnn::PermutationVector& mappings)
83 : m_Optional(false)
84{
85 armnn::IgnoreUnused(numBytes);
86 if (tensorInfo.GetNumBytes() != numBytes)
87 {
88 VLOG(DRIVER) << "The size of ConstTensor does not match its TensorInfo.";
89 }
90
91 const bool needsSwizzling = (mappings.GetSize() > 0);
92 if (needsSwizzling)
93 {
94 m_SwizzledTensorData.resize(tensorInfo.GetNumBytes());
95 SwizzleAndroidNn4dTensorToArmNn(tensorInfo, valueStart, m_SwizzledTensorData.data(), mappings);
96
97 m_ConstTensor = armnn::ConstTensor(tensorInfo, m_SwizzledTensorData.data());
98 }
99 else
100 {
101 m_ConstTensor = armnn::ConstTensor(tensorInfo, valueStart);
102 }
103}
104
105bool ConstTensorPin::IsValid() const
106{
107 return m_ConstTensor.GetMemoryArea() != nullptr;
108}
109
110bool ConstTensorPin::IsOptional() const
111{
112 return m_Optional;
113}
114
115const armnn::ConstTensor& ConstTensorPin::GetConstTensor() const
116{
117 return m_ConstTensor;
118}
119
120const armnn::ConstTensor* ConstTensorPin::GetConstTensorPtr() const
121{
122 if (IsValid() && m_ConstTensor.GetNumElements() > 0)
123 {
124 return &m_ConstTensor;
125 }
126 // tensor is either invalid, or has no elements (indicating an optional tensor that was not provided)
127 return nullptr;
128}
129
130///
131/// Utility functions
132///
133
134bool IsWeightsValid(const Operation& operation,
135 uint32_t inputIndex,
136 const Model& model)
137{
138 const Operand* operand = GetInputOperand(operation, inputIndex, model);
139 if (!operand)
140 {
141 Fail("%s: failed to get input operand %i", __func__, inputIndex);
142 return false;
143 }
144
145 if (operand->lifetime != OperandLifeTime::CONSTANT_COPY
146 && operand->lifetime != OperandLifeTime::CONSTANT_REFERENCE
147 && operand->lifetime != OperandLifeTime::NO_VALUE)
148 {
149 return false;
150 }
151 return true;
152}
153
154ConstTensorPin ConvertOperandToConstTensorPin(const Operand& operand,
155 const Model& model,
156 const ConversionData& data,
157 const armnn::PermutationVector& dimensionMappings,
158 const armnn::TensorShape* overrideTensorShape,
Sadik Armagan1e276f32022-07-19 12:37:20 +0100159 bool optional,
160 const armnn::DataType* overrideDataType)
Sadik Armagan8f397a12022-06-17 15:38:22 +0100161{
162 if (!IsOperandTypeSupportedForTensors(operand.type))
163 {
164 VLOG(DRIVER) << __func__ << ": unsupported operand type for tensor" << operand.type;
165 return ConstTensorPin();
166 }
167
168 if (!optional && !IsOperandConstant(operand))
169 {
170 VLOG(DRIVER) << __func__ << ": lifetime for input tensor: r" << operand.lifetime;
171 return ConstTensorPin();
172 }
173
174 const void* const valueStart = GetOperandValueReadOnlyAddress(operand, model, data, optional);
175 if (!valueStart)
176 {
177 if (optional)
178 {
179 // optional tensor with no values is not really an error; return it as invalid, but marked as optional
180 return ConstTensorPin(true);
181 }
182 // mandatory tensor with no values
183 Fail("%s: failed to get operand address", __func__);
184 return ConstTensorPin();
185 }
186
187 armnn::TensorInfo tensorInfo = GetTensorInfoForOperand(operand);
188
Sadik Armagan1e276f32022-07-19 12:37:20 +0100189 if (overrideTensorShape)
Sadik Armagan8f397a12022-06-17 15:38:22 +0100190 {
191 tensorInfo.SetShape(*overrideTensorShape);
192 }
Sadik Armagan1e276f32022-07-19 12:37:20 +0100193
194 if (overrideDataType)
195 {
196 tensorInfo.SetDataType(*overrideDataType);
197 }
198
199 // Make sure isConstant flag is set.
200 tensorInfo.SetConstant();
Sadik Armagan8f397a12022-06-17 15:38:22 +0100201 return ConstTensorPin(tensorInfo, valueStart, operand.location.length, dimensionMappings);
202}
203
204LayerInputHandle ConvertToLayerInputHandle(const Operation& operation,
205 uint32_t inputIndex,
206 const Model& model,
207 ConversionData& data,
Sadik Armagan1e276f32022-07-19 12:37:20 +0100208 const armnn::PermutationVector& dimensionMappings,
209 const LayerInputHandle* inputHandle)
Sadik Armagan8f397a12022-06-17 15:38:22 +0100210{
211
212 const Operand* operand = GetInputOperand(operation, inputIndex, model);
213 if (!operand)
214 {
215 Fail("%s: failed to get input operand %i", __func__, inputIndex);
216 return LayerInputHandle();
217 }
218
219 if (!IsOperandTypeSupportedForTensors(operand->type))
220 {
221 VLOG(DRIVER) << __func__ << ": unsupported operand type for tensor: " << operand->type;
222 return LayerInputHandle();
223 }
224
225 try
226 {
227 armnn::TensorInfo operandTensorInfo = GetTensorInfoForOperand(*operand);
228
229 if (IsDynamicTensor(operandTensorInfo))
230 {
231 data.m_DynamicInputsEncountered = true;
232
233 const uint32_t operandIndex = operation.inputs[inputIndex];
234
235 // Check if the dynamic input tensors have been inferred by one of the previous layers
236 // If not we can't support them
237 if (data.m_OutputSlotForOperand.size() >= operandIndex && data.m_OutputSlotForOperand[operandIndex])
238 {
239 operandTensorInfo = data.m_OutputSlotForOperand[operandIndex]->GetTensorInfo();
240 }
241 else
242 {
243 Fail("%s: Type 2 dynamic input tensors are not supported", __func__);
244 return LayerInputHandle();
245 }
246 }
247
248 switch (operand->lifetime)
249 {
250 case OperandLifeTime::SUBGRAPH_INPUT:
251 {
252 // NOTE: We must check whether we can support the input tensor on at least one
253 // of the provided backends; otherwise we cannot convert the operation
254 bool isInputSupported = false;
255 FORWARD_LAYER_SUPPORT_FUNC(__func__,
256 IsInputSupported,
257 data.m_Backends,
258 isInputSupported,
259 operandTensorInfo);
260
261 if (!isInputSupported)
262 {
263 Fail("%s: unsupported input tensor", __func__);
264 return LayerInputHandle();
265 }
266
267 [[clang::fallthrough]]; // intentional fallthrough
268 }
269 case OperandLifeTime::TEMPORARY_VARIABLE: // intentional fallthrough
270 case OperandLifeTime::SUBGRAPH_OUTPUT:
271 {
272 // The tensor is either an operand internal to the model, or a model input.
273 // It can be associated with an ArmNN output slot for an existing layer.
274
275 // m_OutputSlotForOperand[...] can be nullptr if the previous layer could not be converted
276 const uint32_t operandIndex = operation.inputs[inputIndex];
277 return LayerInputHandle(true, data.m_OutputSlotForOperand[operandIndex], operandTensorInfo);
278 }
279 case OperandLifeTime::CONSTANT_COPY: // intentional fallthrough
280 case OperandLifeTime::POINTER:
281 case OperandLifeTime::CONSTANT_REFERENCE:
282 {
Sadik Armagan1e276f32022-07-19 12:37:20 +0100283 auto constantTensorDataType = operandTensorInfo.GetDataType();
Sadik Armagan8f397a12022-06-17 15:38:22 +0100284 // The tensor has an already known constant value, and can be converted into an ArmNN Constant layer.
Sadik Armagan1e276f32022-07-19 12:37:20 +0100285 ConstTensorPin tensorPin = ConvertOperandToConstTensorPin(*operand,
286 model,
287 data,
288 dimensionMappings,
289 nullptr,
290 false,
291 &constantTensorDataType);
Sadik Armagan8f397a12022-06-17 15:38:22 +0100292 if (tensorPin.IsValid())
293 {
294 bool isSupported = false;
295 FORWARD_LAYER_SUPPORT_FUNC(__func__,
296 IsConstantSupported,
297 data.m_Backends,
298 isSupported,
299 tensorPin.GetConstTensor().GetInfo());
300 if (!isSupported)
301 {
302 return LayerInputHandle();
303 }
304
305 armnn::IConnectableLayer* constantLayer =
306 data.m_Network->AddConstantLayer(tensorPin.GetConstTensor());
307 armnn::IOutputSlot& outputSlot = constantLayer->GetOutputSlot(0);
Sadik Armaganb9570c12022-06-21 11:39:41 +0100308 armnn::TensorInfo constantTensorInfo = tensorPin.GetConstTensor().GetInfo();
309 outputSlot.SetTensorInfo(constantTensorInfo);
Sadik Armagan8f397a12022-06-17 15:38:22 +0100310
Sadik Armaganb9570c12022-06-21 11:39:41 +0100311 return LayerInputHandle(true, &outputSlot, constantTensorInfo);
Sadik Armagan8f397a12022-06-17 15:38:22 +0100312 }
313 else
314 {
315 Fail("%s: invalid operand tensor", __func__);
316 return LayerInputHandle();
317 }
318 break;
319 }
320 default:
321 {
322 VLOG(DRIVER) << __func__ << ": unsupported lifetime for input tensor: " << operand->lifetime;
323 return LayerInputHandle();
324 }
325 }
326 }
327 catch (UnsupportedOperand<OperandType>& e)
328 {
329 VLOG(DRIVER) << __func__ << ": Operand type: " << e.m_type << " not supported in ArmnnDriver";
330 return LayerInputHandle();
331 }
332}
333
334bool ConvertPaddings(const Operation& operation,
335 const Model& model,
336 ConversionData& data,
337 unsigned int rank,
338 armnn::PadDescriptor& padDescriptor)
339{
340 const Operand* paddingsOperand = GetInputOperand(operation, 1, model);
341 if (!paddingsOperand)
342 {
343 return Fail("%s: Could not read paddings operand", __func__);
344 }
345
346 armnn::TensorShape paddingsOperandShape = GetTensorShapeForOperand(*paddingsOperand);
347 if (paddingsOperandShape.GetNumDimensions() != 2 || paddingsOperandShape.GetNumElements() != rank * 2)
348 {
349 return Fail("%s: Operation has invalid paddings operand: expected shape [%d, 2]", __func__, rank);
350 }
351
352 std::vector<int32_t> paddings;
353 if (!GetTensorInt32Values(*paddingsOperand, paddings, model, data))
354 {
355 return Fail("%s: Operation has invalid or unsupported paddings operand", __func__);
356 }
357
358 // add padding for each dimension of input tensor.
359 for (unsigned int i = 0; i < paddings.size() - 1; i += 2)
360 {
361 int paddingBeforeInput = paddings[i];
362 int paddingAfterInput = paddings[i + 1];
363
364 if (paddingBeforeInput < 0 || paddingAfterInput < 0)
365 {
366 return Fail("%s: Operation has invalid paddings operand, invalid padding values.", __func__);
367 }
368
369 padDescriptor.m_PadList.emplace_back((unsigned int) paddingBeforeInput, (unsigned int) paddingAfterInput);
370 }
371
372 return true;
373}
374
375
376bool ConvertPooling2d(const Operation& operation,
377 const char* operationName,
378 armnn::PoolingAlgorithm poolType,
379 const Model& model,
380 ConversionData& data)
381{
382
383 VLOG(DRIVER) << "Converter::ConvertL2Pool2d()";
384
385 LayerInputHandle input = ConvertToLayerInputHandle(operation, 0, model, data);
386 if (!input.IsValid())
387 {
388 return Fail("%s: Operation Could not read input 0", operationName);
389 }
390
391 const Operand* output = GetOutputOperand(operation, 0, model);
392 if (!output)
393 {
394 return Fail("%s: Could not read output 0", __func__);
395 }
396
397 const armnn::TensorInfo& inputInfo = input.GetTensorInfo();
398 const armnn::TensorInfo& outputInfo = GetTensorInfoForOperand(*output);
399
400 armnn::Pooling2dDescriptor desc;
401 desc.m_PoolType = poolType;
402 desc.m_OutputShapeRounding = armnn::OutputShapeRounding::Floor;
403 desc.m_DataLayout = armnn::DataLayout::NHWC;
404
405 ActivationFn activation;
406
407 auto inputSize = operation.inputs.size();
408
409 if (inputSize >= 10)
410 {
411 // one input, 9 parameters (padding l r t b, stridex, stridey, width, height, activation type)
412 if (!GetInputScalar(operation, 1, OperandType::INT32, desc.m_PadLeft, model, data) ||
413 !GetInputScalar(operation, 2, OperandType::INT32, desc.m_PadRight, model, data) ||
414 !GetInputScalar(operation, 3, OperandType::INT32, desc.m_PadTop, model, data) ||
415 !GetInputScalar(operation, 4, OperandType::INT32, desc.m_PadBottom, model, data) ||
416 !GetInputScalar(operation, 5, OperandType::INT32, desc.m_StrideX, model, data) ||
417 !GetInputScalar(operation, 6, OperandType::INT32, desc.m_StrideY, model, data) ||
418 !GetInputScalar(operation, 7, OperandType::INT32, desc.m_PoolWidth, model, data) ||
419 !GetInputScalar(operation, 8, OperandType::INT32, desc.m_PoolHeight, model, data) ||
420 !GetInputActivationFunction(operation, 9, activation, model, data))
421 {
422 return Fail("%s: Operation has invalid inputs", operationName);
423 }
424
425 if (Is12OrLaterOperand(*output))
426 {
427 desc.m_DataLayout = OptionalDataLayout(operation, 10, model, data);
428 }
429 }
430 else
431 {
432 // one input, 6 parameters (padding, stridex, stridey, width, height, activation type)
433 ::android::nn::PaddingScheme scheme;
434 if (!GetInputPaddingScheme(operation, 1, scheme, model, data) ||
435 !GetInputScalar(operation, 2, OperandType::INT32, desc.m_StrideX, model, data) ||
436 !GetInputScalar(operation, 3, OperandType::INT32, desc.m_StrideY, model, data) ||
437 !GetInputScalar(operation, 4, OperandType::INT32, desc.m_PoolWidth, model, data) ||
438 !GetInputScalar(operation, 5, OperandType::INT32, desc.m_PoolHeight, model, data) ||
439 !GetInputActivationFunction(operation, 6, activation, model, data))
440 {
441 return Fail("%s: Operation has invalid inputs", operationName);
442 }
443
444 if (Is12OrLaterOperand(*output))
445 {
446 desc.m_DataLayout = OptionalDataLayout(operation, 7, model, data);
447 }
448
449 const armnnUtils::DataLayoutIndexed dataLayout(desc.m_DataLayout);
450 const unsigned int inputWidth = inputInfo.GetShape()[dataLayout.GetWidthIndex()];
451 const unsigned int inputHeight = inputInfo.GetShape()[dataLayout.GetHeightIndex()];
452
453 CalcPadding(inputWidth, desc.m_PoolWidth, desc.m_StrideX, desc.m_PadLeft, desc.m_PadRight, scheme);
454 CalcPadding(inputHeight, desc.m_PoolHeight, desc.m_StrideY, desc.m_PadTop, desc.m_PadBottom, scheme);
455 }
456
457 bool isSupported = false;
458
459 auto validateFunc = [&](const armnn::TensorInfo& outputInfo, bool& isSupported)
460 {
461 FORWARD_LAYER_SUPPORT_FUNC(__func__,
462 IsPooling2dSupported,
463 data.m_Backends,
464 isSupported,
465 inputInfo,
466 outputInfo,
467 desc);
468
469 };
470
471 if(IsDynamicTensor(outputInfo))
472 {
473 isSupported = AreDynamicTensorsSupported();
474 }
475 else
476 {
477 validateFunc(outputInfo, isSupported);
478 }
479
480 if (!isSupported)
481 {
482 return false;
483 }
484
485 armnn::IConnectableLayer* pooling2dLayer = data.m_Network->AddPooling2dLayer(desc);
486 if (!pooling2dLayer)
487 {
488 return Fail("%s: AddPooling2dLayer failed", __func__);
489 }
490
491 input.Connect(pooling2dLayer->GetInputSlot(0));
492
493 if (!isSupported)
494 {
495 return false;
496 }
497
498 return SetupAndTrackLayerOutputSlot(operation, 0, *pooling2dLayer, model,
499 data, nullptr, validateFunc, activation);
500}
501
502bool ConvertReduce(const Operation& operation,
503 const Model& model,
504 ConversionData& data,
505 armnn::ReduceOperation reduceOperation)
506{
507 armnn::ReduceDescriptor descriptor;
508 descriptor.m_ReduceOperation = reduceOperation;
509
510 LayerInputHandle input = ConvertToLayerInputHandle(operation, 0, model, data);
511 if (!input.IsValid())
512 {
513 return Fail("%s: Operation has invalid inputs", __func__);
514 }
515 const armnn::TensorInfo& inputInfo = input.GetTensorInfo();
516
517 const Operand* output = GetOutputOperand(operation, 0, model);
518 if (!output)
519 {
520 return Fail("%s: Could not read output 0", __func__);
521 }
522 const armnn::TensorInfo& outputInfo = GetTensorInfoForOperand(*output);
523
524 const Operand* axisOperand = GetInputOperand(operation, 1, model);
525 if (!axisOperand)
526 {
527 return Fail("%s: Could not read input 1", __func__);
528 }
529 std::vector<int32_t> axis;
530 if (!GetTensorInt32Values(*axisOperand, axis, model, data))
531 {
532 return Fail("%s: Input 1 has invalid values", __func__);
533 }
534
535 // Convert the axis to unsigned int and remove duplicates.
536 unsigned int rank = inputInfo.GetNumDimensions();
537 std::set<unsigned int> uniqueAxis;
538 std::transform(axis.begin(), axis.end(),
539 std::inserter(uniqueAxis, uniqueAxis.begin()),
540 [rank](int i) -> unsigned int { return (i + rank) % rank; });
541 descriptor.m_vAxis.assign(uniqueAxis.begin(), uniqueAxis.end());
542
543 // Get the "keep dims" flag.
544 if (!GetInputScalar(operation, 2, OperandType::BOOL, descriptor.m_KeepDims, model, data))
545 {
546 return Fail("%s: Could not read input 2", __func__);
547 }
548
549 bool isSupported = false;
550 auto validateFunc = [&](const armnn::TensorInfo& outputInfo, bool& isSupported)
551 {
552 FORWARD_LAYER_SUPPORT_FUNC(__func__,
553 IsReduceSupported,
554 data.m_Backends,
555 isSupported,
556 inputInfo,
557 outputInfo,
558 descriptor);
559 };
560
561 if(!IsDynamicTensor(outputInfo))
562 {
563 validateFunc(outputInfo, isSupported);
564 }
565 else
566 {
567 isSupported = AreDynamicTensorsSupported();
568 }
569
570 if (!isSupported)
571 {
572 return false;
573 }
574
575 armnn::IConnectableLayer* const layer = data.m_Network->AddReduceLayer(descriptor);
576 assert(layer != nullptr);
577 input.Connect(layer->GetInputSlot(0));
578
579 return SetupAndTrackLayerOutputSlot(operation, 0, *layer, model, data, nullptr, validateFunc);
580}
581
582
583bool ConvertToActivation(const Operation& operation,
584 const char* operationName,
585 const armnn::ActivationDescriptor& activationDesc,
586 const Model& model,
587 ConversionData& data)
588{
589 LayerInputHandle input = ConvertToLayerInputHandle(operation, 0, model, data);
590 if (!input.IsValid())
591 {
592 return Fail("%s: Input 0 is invalid", operationName);
593 }
594
595 const Operand* outputOperand = GetOutputOperand(operation, 0, model);
596 if (!outputOperand)
597 {
598 return false;
599 }
600
601 const armnn::TensorInfo& outInfo = GetTensorInfoForOperand(*outputOperand);
602
603 bool isSupported = false;
604
605 auto validateFunc = [&](const armnn::TensorInfo& outInfo, bool& isSupported)
606 {
607 FORWARD_LAYER_SUPPORT_FUNC(__func__,
608 IsActivationSupported,
609 data.m_Backends,
610 isSupported,
611 input.GetTensorInfo(),
612 outInfo,
613 activationDesc);
614 };
615
616 if(IsDynamicTensor(outInfo))
617 {
618 isSupported = AreDynamicTensorsSupported();
619 }
620 else
621 {
622 validateFunc(outInfo, isSupported);
623 }
624
625 if (!isSupported)
626 {
627 return false;
628 }
629
630 armnn::IConnectableLayer* layer = data.m_Network->AddActivationLayer(activationDesc);
631 ARMNN_ASSERT(layer != nullptr);
632 input.Connect(layer->GetInputSlot(0));
633
634 return SetupAndTrackLayerOutputSlot(operation, 0, *layer, model, data, nullptr, validateFunc);
635}
636
637DequantizeResult DequantizeIfRequired(size_t operand_index,
638 const Operation& operation,
639 const Model& model,
640 const ConversionData& data)
641{
642 const Operand* weightsOperand = GetInputOperand(operation, operand_index, model);
643 if (!weightsOperand)
644 {
645 return { nullptr, 0, armnn::TensorInfo(), DequantizeStatus::INVALID_OPERAND };
646 }
647
648 if (IsOperandConstant(*weightsOperand))
649 {
650 // Weights are already constant
651 return { nullptr, 0, armnn::TensorInfo(), DequantizeStatus::NOT_REQUIRED };
652 }
653
654 const size_t weightsInputIndex = operation.inputs[operand_index];
655
656 // The weights are a non const tensor, this indicates they might be the output of a dequantize op.
657 // Iterate over the nodes and find the previous operation which should be DEQUANTIZE
658 for (uint32_t operationIdx = 0; operationIdx < getMainModel(model).operations.size(); ++operationIdx)
659 {
660 // Search for the DEQUANTIZE op which has the operand with index equal to operandIndex
661 const auto& operationIt = getMainModel(model).operations[operationIdx];
662 if (operationIt.type != OperationType::DEQUANTIZE)
663 {
664 continue;
665 }
666
667 size_t outOpIndex = weightsInputIndex + 1;
668 for (size_t i = 0; outOpIndex != weightsInputIndex && i < operationIt.outputs.size(); ++i)
669 {
670 outOpIndex = operationIt.outputs[i];
671 }
672
673 if (outOpIndex != weightsInputIndex)
674 {
675 continue;
676 }
677
678 const Operand* operand = GetInputOperand(operationIt, 0, model);
679 ARMNN_ASSERT(operand);
680
681 if (!IsQSymm8(*operand))
682 {
683 // Only supporting dequantize from QSYMM8 to FLOAT
684 break;
685 }
686
687 // Allocate a new buffer for the dequantized data and manually dequantize
688 const void* startValue = GetOperandValueReadOnlyAddress(*operand, model, data);
689 if (!startValue)
690 {
691 // Failed to get the operand address
692 break;
693 }
694
695 const uint8_t* quantizedBuffer = reinterpret_cast<const uint8_t*>(startValue);
696 size_t dequantizedBufferLength = operand->location.length;
697 const float quantizationScale = operand->scale;
698
699 auto dequantizedBuffer = std::make_unique<float[]>(dequantizedBufferLength + 1);
700 for (size_t i = 0; i < dequantizedBufferLength; ++i)
701 {
702 float* dstPtr = dequantizedBuffer.get();
703 ARMNN_ASSERT(dstPtr);
704 *dstPtr++ = quantizedBuffer[i] * quantizationScale;
705 }
706
707 // Construct tensor info for dequantized ConstTensor
708 armnn::TensorInfo tensorInfo(operand->dimensions.size(),
709 operand->dimensions.data(),
710 armnn::DataType::Float32);
711
712 return { std::move(dequantizedBuffer), dequantizedBufferLength * sizeof(float),
713 std::move(tensorInfo),
714 DequantizeStatus::SUCCESS };
715 }
716
717 return { nullptr, 0, armnn::TensorInfo() , DequantizeStatus::NOT_REQUIRED};
718}
719
720ConstTensorPin DequantizeAndMakeConstTensorPin(const Operation& operation,
721 const Model& model,
722 const ConversionData& data,
723 size_t operandIndex,
724 bool optional)
725{
726 DequantizeResult dequantized = DequantizeIfRequired(operandIndex,operation, model, data);
727
728 DequantizeStatus status = std::get<3>(dequantized);
729 switch (status)
730 {
731 case DequantizeStatus::INVALID_OPERAND:
732 {
733 // return invalid const tensor pin
734 return ConstTensorPin();
735 }
736 case DequantizeStatus::NOT_REQUIRED:
737 {
738 return ConvertOperationInputToConstTensorPin(
739 operation, operandIndex, model, data, g_DontPermute, nullptr, optional);
740 }
741 case DequantizeStatus::SUCCESS:
742 default:
743 {
744 return ConstTensorPin(
745 std::get<2>(dequantized), std::get<0>(dequantized).get(), std::get<1>(dequantized), g_DontPermute);
746 }
747 }
748}
749
750bool GetInputPaddingScheme(const Operation& operation,
751 uint32_t inputIndex,
752 PaddingScheme& outPaddingScheme,
753 const Model& model,
754 const ConversionData& data)
755{
756 int32_t paddingSchemeAsInt;
757 if (!GetInputInt32(operation, inputIndex, paddingSchemeAsInt, model, data))
758 {
759 return Fail("%s: failed to get padding scheme input value", __func__);
760 }
761
762 outPaddingScheme = static_cast<::android::nn::PaddingScheme>(paddingSchemeAsInt);
763 return true;
764}
765
766const void* GetOperandValueReadOnlyAddress(const Operand& operand,
767 const Model& model,
768 const ConversionData& data,
769 bool optional)
770{
771 const void* valueStart = nullptr;
772 switch (operand.lifetime)
773 {
774 case OperandLifeTime::CONSTANT_COPY:
775 {
776 valueStart = model.operandValues.data() + operand.location.offset;
777 break;
778 }
779 case OperandLifeTime::POINTER:
780 {
781 // Pointer specified in the model
782 valueStart = std::get<const void*>(operand.location.pointer);
783 break;
784 }
785 case OperandLifeTime::CONSTANT_REFERENCE:
786 {
787 // Constant specified via a Memory object
788 valueStart = GetMemoryFromPool(operand.location, data.m_MemPools);
789 break;
790 }
791 case OperandLifeTime::NO_VALUE:
792 {
793 // An optional input tensor with no values is not an error so should not register as a fail
794 if (optional)
795 {
796 valueStart = nullptr;
797 break;
798 }
799 [[fallthrough]];
800 }
801 default:
802 {
803 VLOG(DRIVER) << __func__ << ": unsupported/invalid operand lifetime:: " << operand.lifetime;
804 valueStart = nullptr;
805 }
806 }
807
808 return valueStart;
809}
810
811bool GetTensorInt32Values(const Operand& operand,
812 std::vector<int32_t>& outValues,
813 const Model& model,
814 const ConversionData& data)
815{
816 if (operand.type != OperandType::TENSOR_INT32)
817 {
818 VLOG(DRIVER) << __func__ << ": invalid operand type: " << operand.type;
819 return false;
820 }
821
822 const void* startAddress = GetOperandValueReadOnlyAddress(operand, model, data);
823 if (!startAddress)
824 {
825 VLOG(DRIVER) << __func__ << ": failed to get operand address " << operand.type;
826 return false;
827 }
828
829 // Check number of bytes is sensible
830 const uint32_t numBytes = operand.location.length;
831 if (numBytes % sizeof(int32_t) != 0)
832 {
833 return Fail("%s: invalid number of bytes: %i, expected to be a multiple of %i",
834 __func__, numBytes, sizeof(int32_t));
835 }
836
837 outValues.resize(numBytes / sizeof(int32_t));
838 memcpy(outValues.data(), startAddress, numBytes);
839 return true;
840}
841
842armnn::DataLayout OptionalDataLayout(const Operation& operation,
843 uint32_t inputIndex,
844 const Model& model,
845 ConversionData& data)
846{
847 const Operand* operand = GetInputOperand(operation, inputIndex, model);
848 if (!operand)
849 {
850 return armnn::DataLayout::NHWC;
851 }
852
853 if (!IsBool(*operand))
854 {
855 return armnn::DataLayout::NHWC;
856 }
857
858 const void* valueAddress = GetOperandValueReadOnlyAddress(*operand, model, data);
859 if (!valueAddress)
860 {
861 return armnn::DataLayout::NHWC;
862 }
863
864 if (*(static_cast<const bool*>(valueAddress)))
865 {
866 return armnn::DataLayout::NCHW;
867 }
868 else
869 {
870 return armnn::DataLayout::NHWC;
871 }
872}
873
874armnn::IConnectableLayer* ProcessActivation(const armnn::TensorInfo& tensorInfo,
875 ActivationFn activation,
876 armnn::IConnectableLayer* prevLayer,
877 ConversionData& data)
878{
879 ARMNN_ASSERT(prevLayer->GetNumOutputSlots() == 1);
880
881 prevLayer->GetOutputSlot(0).SetTensorInfo(tensorInfo);
882
883 armnn::IConnectableLayer* activationLayer = prevLayer;
884
885 if (activation != ActivationFn::kActivationNone)
886 {
887 armnn::ActivationDescriptor activationDesc;
888 switch (activation)
889 {
890 case ActivationFn::kActivationRelu:
891 {
892 activationDesc.m_Function = armnn::ActivationFunction::ReLu;
893 break;
894 }
895 case ActivationFn::kActivationRelu1:
896 {
897 activationDesc.m_Function = armnn::ActivationFunction::BoundedReLu;
898 activationDesc.m_A = 1.0f;
899 activationDesc.m_B = -1.0f;
900 break;
901 }
902 case ActivationFn::kActivationRelu6:
903 {
904 activationDesc.m_Function = armnn::ActivationFunction::BoundedReLu;
905 activationDesc.m_A = 6.0f;
906 break;
907 }
908 case ActivationFn::kActivationSigmoid:
909 {
910 activationDesc.m_Function = armnn::ActivationFunction::Sigmoid;
911 break;
912 }
913 case ActivationFn::kActivationTanh:
914 {
915 activationDesc.m_Function = armnn::ActivationFunction::TanH;
916 activationDesc.m_A = 1.0f;
917 activationDesc.m_B = 1.0f;
918 break;
919 }
920 default:
921 {
922 Fail("%s: Invalid activation enum value %i", __func__, activation);
923 return nullptr;
924 }
925 }
926
927 bool isSupported = false;
928 FORWARD_LAYER_SUPPORT_FUNC(__func__,
929 IsActivationSupported,
930 data.m_Backends,
931 isSupported,
932 prevLayer->GetOutputSlot(0).GetTensorInfo(),
933 tensorInfo,
934 activationDesc);
935 if (!isSupported)
936 {
937 return nullptr;
938 }
939
940 activationLayer = data.m_Network->AddActivationLayer(activationDesc);
941
942 prevLayer->GetOutputSlot(0).Connect(activationLayer->GetInputSlot(0));
943 activationLayer->GetOutputSlot(0).SetTensorInfo(tensorInfo);
944 }
945
946 return activationLayer;
947}
948
949bool SetupAndTrackLayerOutputSlot(const Operation& operation,
950 uint32_t operationOutputIndex,
951 armnn::IConnectableLayer& layer,
952 uint32_t layerOutputIndex,
953 const Model& model,
954 ConversionData& data,
955 const armnn::TensorInfo* overrideOutputInfo,
956 const std::function <void (const armnn::TensorInfo&, bool&)>& validateFunc,
957 const ActivationFn& activationFunction,
958 bool inferOutputShapes)
959{
960 const Operand* outputOperand = GetOutputOperand(operation, operationOutputIndex, model);
961 if ((outputOperand == nullptr) || (operationOutputIndex >= layer.GetNumOutputSlots()))
962 {
963 return false;
964 }
965
966 armnn::IOutputSlot& outputSlot = layer.GetOutputSlot(layerOutputIndex);
967 if (overrideOutputInfo == nullptr)
968 {
969 outputSlot.SetTensorInfo(GetTensorInfoForOperand(*outputOperand));
970 }
971 else
972 {
973 outputSlot.SetTensorInfo(*overrideOutputInfo);
974 }
975
976 bool isSupported = false;
977 if (validateFunc && (IsDynamicTensor(outputSlot.GetTensorInfo()) || inferOutputShapes))
978 {
979 // Type one dynamic tensors require the previous layer's output shape for inference
980 for (unsigned int inputSlotIndex = 0; inputSlotIndex < layer.GetNumInputSlots(); ++inputSlotIndex)
981 {
982 if(!layer.GetInputSlot(inputSlotIndex).GetConnection())
983 {
984 return false;
985 }
986 }
987 // IsTensorInfoSet will infer the dynamic output shape
988 outputSlot.IsTensorInfoSet();
989 // Once the shape is inferred we can validate it
990 validateFunc(outputSlot.GetTensorInfo(), isSupported);
991
992 if(!isSupported)
993 {
994 for (unsigned int inputSlotIndex = 0; inputSlotIndex < layer.GetNumInputSlots(); ++inputSlotIndex)
995 {
996 layer.GetInputSlot(inputSlotIndex).GetConnection()->Disconnect(layer.GetInputSlot(inputSlotIndex));
997 }
998 return false;
999 }
1000 }
1001
1002 const uint32_t operandIndex = operation.outputs[operationOutputIndex];
1003
1004 if (activationFunction != ActivationFn::kActivationNone)
1005 {
1006 const armnn::TensorInfo& activationOutputInfo = outputSlot.GetTensorInfo();
1007 armnn::IConnectableLayer* const endLayer = ProcessActivation(activationOutputInfo, activationFunction,
1008 &layer, data);
1009
1010 if (!endLayer)
1011 {
1012 return Fail("%s: ProcessActivation failed", __func__);
1013 }
1014
1015 armnn::IOutputSlot& activationOutputSlot = endLayer->GetOutputSlot(layerOutputIndex);
1016 data.m_OutputSlotForOperand[operandIndex] = &activationOutputSlot;
1017 }
1018 else
1019 {
1020 data.m_OutputSlotForOperand[operandIndex] = &outputSlot;
1021 }
1022
1023 return true;
1024}
1025
Sadik Armaganb0161572022-08-03 11:27:05 +01001026bool IsConnectedToDequantize(armnn::IOutputSlot* ioutputSlot)
1027{
1028 VLOG(DRIVER) << "ConversionUtils::IsConnectedToDequantize()";
1029 if (!ioutputSlot)
1030 {
1031 return false;
1032 }
1033 VLOG(DRIVER) << "ConversionUtils::IsConnectedToDequantize() ioutputSlot is valid.";
1034 // Find the connections and layers..
1035 armnn::IConnectableLayer& owningLayer = ioutputSlot->GetOwningIConnectableLayer();
1036 if (owningLayer.GetType() == armnn::LayerType::Dequantize)
1037 {
1038 VLOG(DRIVER) << "ConversionUtils::IsConnectedToDequantize() connected to Dequantize Layer.";
1039 armnn::IInputSlot& inputSlot = owningLayer.GetInputSlot(0);
1040 armnn::IOutputSlot* connection = inputSlot.GetConnection();
1041 if (connection)
1042 {
1043 VLOG(DRIVER) << "ConversionUtils::IsConnectedToDequantize() Dequantize Layer has a connection.";
1044 armnn::IConnectableLayer& connectedLayer =
1045 connection->GetOwningIConnectableLayer();
1046 if (connectedLayer.GetType() == armnn::LayerType::Constant)
1047 {
1048 VLOG(DRIVER) << "ConversionUtils::IsConnectedToDequantize() Dequantize Layer connected to Constant";
1049 return true;
1050 }
1051 }
1052 }
1053 return false;
1054}
1055
Sadik Armagan8f397a12022-06-17 15:38:22 +01001056} // namespace armnn_driver