blob: 059b79f004ab44b939fccd1face578e7e08ece3c [file] [log] [blame]
Sadik Armagan1153d1e2020-04-01 15:09:39 +01001//
Mike Kellye2d611e2021-10-14 12:35:58 +01002// Copyright © 2020 Arm Ltd and Contributors. All rights reserved.
Sadik Armagan1153d1e2020-04-01 15:09:39 +01003// SPDX-License-Identifier: MIT
4//
5
6#pragma once
7
8#include "ConversionUtils_1_2.hpp"
9
10using Half = half_float::half;
11
12namespace armnn_driver
13{
14
15using namespace armnn;
16using namespace android::nn;
17
18template<typename HalPolicy,
19 typename HalOperation = typename HalPolicy::Operation,
20 typename HalModel = typename HalPolicy::Model>
21bool ConvertElu(const HalOperation& operation, const HalModel& model, ConversionData& data)
22{
23 using HalOperandType = typename HalPolicy::OperandType;
24
25 LayerInputHandle input0 = ConvertToLayerInputHandle<HalPolicy>(operation, 0, model, data);
26 if (!input0.IsValid())
27 {
28 return Fail("%s: Operation has invalid inputs", __func__);
29 }
30
31 // Determine data type of input tensor
32 HalOperandType inputType;
33 if (!GetOperandType<HalPolicy>(operation, 0, model, inputType))
34 {
35 return Fail("%s: Operation has invalid inputs", __func__);
36 }
37
38 ActivationDescriptor desc;
39 desc.m_Function = ActivationFunction::Elu;
40
41 // Read alpha
42 if (inputType == HalOperandType::TENSOR_FLOAT16)
43 {
44 Half alpha;
45
46 if (!GetInputScalar<HalPolicy>(operation, 1, HalOperandType::FLOAT16, alpha, model, data))
47 {
48 return Fail("%s: Operation has invalid inputs (FLOAT16)", __func__);
49 }
50
51 desc.m_A = static_cast<float>(alpha);
52 }
53 else if (inputType == HalOperandType::TENSOR_FLOAT32)
54 {
55 if (!GetInputScalar<HalPolicy>(operation, 1, HalOperandType::FLOAT32, desc.m_A, model, data))
56 {
57 return Fail("%s: Operation has invalid inputs (FLOAT32)", __func__);
58 }
59 }
60 else
61 {
62 return Fail("%s: Unsupported input tensor type: %d", __func__, inputType);
63 }
64
65 return ::ConvertToActivation<HalPolicy>(operation, __func__, desc, model, data);
66}
67
Sadik Armagan813f2302020-05-19 14:10:30 +010068template<typename HalPolicy,
Sadik Armagan2e329612020-06-24 10:57:23 +010069 typename HalOperation = typename HalPolicy::Operation,
70 typename HalModel = typename HalPolicy::Model>
71bool ConvertFill(const HalOperation& operation, const HalModel& model, ConversionData& data)
72{
73 using HalOperand = typename HalPolicy::Operand;
74 using HalOperandType = typename HalPolicy::OperandType;
75
76 LayerInputHandle input = ConvertToLayerInputHandle<HalPolicy>(operation, 0, model, data);
77 if (!input.IsValid())
78 {
79 return Fail("%s: Operation has invalid inputs", __func__);
80 }
81
82 const HalOperand* output = GetOutputOperand<HalPolicy>(operation, 0, model);
83 if (!output)
84 {
85 return Fail("%s: Could not read output", __func__);
86 }
87
88 const TensorInfo& inputInfo = input.GetTensorInfo();
89 const TensorInfo& outputInfo = GetTensorInfoForOperand(*output);
90 if (IsDynamicTensor(outputInfo))
91 {
92 return Fail("%s: Dynamic output tensors are not supported", __func__);
93 }
94
95 // Determine data type of output tensor
96 HalOperandType outputType = output->type;
97 FillDescriptor descriptor;
98 // Read the scalar fill value
99 if (outputType == HalOperandType::TENSOR_FLOAT16)
100 {
101 Half value;
102
103 if (!GetInputScalar<HalPolicy>(operation, 1, HalOperandType::FLOAT16, value, model, data))
104 {
105 return Fail("%s: Operation has invalid inputs %d", __func__, outputType);
106 }
107
108 descriptor.m_Value = static_cast<float>(value);
109 }
110 else if (outputType == HalOperandType::TENSOR_FLOAT32)
111 {
112 if (!GetInputScalar<HalPolicy>(operation, 1, HalOperandType::FLOAT32, descriptor.m_Value, model, data))
113 {
114 return Fail("%s: Operation has invalid inputs %d", __func__, outputType);
115 }
116 }
117 else if (outputType == HalOperandType::TENSOR_INT32)
118 {
119 int32_t value;
120
121 if (!GetInputScalar<HalPolicy>(operation, 1, HalOperandType::INT32, value, model, data))
122 {
123 return Fail("%s: Operation has invalid inputs %d", __func__, outputType);
124 }
125
126 descriptor.m_Value = static_cast<float>(value);
127 }
128 else
129 {
130 return Fail("%s: Unsupported input tensor type: %d", __func__, outputType);
131 }
132
133 bool isSupported = false;
134 FORWARD_LAYER_SUPPORT_FUNC(__func__,
135 IsFillSupported,
136 data.m_Backends,
137 isSupported,
138 inputInfo,
139 outputInfo,
140 descriptor);
141 if (!isSupported)
142 {
143 return false;
144 }
145
146 IConnectableLayer* const layer = data.m_Network->AddFillLayer(descriptor);
Mike Kellye2d611e2021-10-14 12:35:58 +0100147 if (!layer)
148 {
149 return Fail("%s: Could not add the FillLayer", __func__);
150 }
Sadik Armagan2e329612020-06-24 10:57:23 +0100151 input.Connect(layer->GetInputSlot(0));
Sadik Armagan2e329612020-06-24 10:57:23 +0100152
153 return SetupAndTrackLayerOutputSlot<HalPolicy>(operation, 0, *layer, model, data);
154}
155
156template<typename HalPolicy,
Sadik Armagan813f2302020-05-19 14:10:30 +0100157 typename HalOperation = typename HalPolicy::Operation,
158 typename HalModel = typename HalPolicy::Model>
Narumol Prangnawarat0629eb82020-11-12 18:27:37 +0000159bool ConvertLogicalBinary(const HalOperation& operation,
160 const HalModel& model,
161 ConversionData& data,
162 LogicalBinaryOperation logicalOperation)
163{
164 using HalOperand = typename HalPolicy::Operand;
165
166 ALOGV("HalPolicy::ConvertLogicalBinary()");
167 ALOGV("logicalOperation = %s", GetLogicalBinaryOperationAsCString(logicalOperation));
168
169 LayerInputHandle input0 = ConvertToLayerInputHandle<HalPolicy>(operation, 0, model, data);
170 LayerInputHandle input1 = ConvertToLayerInputHandle<HalPolicy>(operation, 1, model, data);
171
172 if (!(input0.IsValid() && input1.IsValid()))
173 {
174 return Fail("%s: Operation has invalid inputs", __func__);
175 }
176
177 const HalOperand* output = GetOutputOperand<HalPolicy>(operation, 0, model);
178 if (!output)
179 {
180 return Fail("%s: Could not read output 0", __func__);
181 }
182
183 const TensorInfo& inputInfo0 = input0.GetTensorInfo();
184 const TensorInfo& inputInfo1 = input1.GetTensorInfo();
185 const TensorInfo& outputInfo = GetTensorInfoForOperand(*output);
186
187 LogicalBinaryDescriptor descriptor(logicalOperation);
188
189 bool isSupported = false;
190
191 auto validateFunc = [&](const armnn::TensorInfo& outputInfo, bool& isSupported)
192 {
193 FORWARD_LAYER_SUPPORT_FUNC(__func__,
194 IsLogicalBinarySupported,
195 data.m_Backends,
196 isSupported,
197 inputInfo0,
198 inputInfo1,
199 outputInfo,
200 descriptor);
201 };
202
203 if(!IsDynamicTensor(outputInfo))
204 {
205 validateFunc(outputInfo, isSupported);
206 }
207 else
208 {
209 isSupported = AreDynamicTensorsSupported();
210 }
211
212 if (!isSupported)
213 {
214 return false;
215 }
216
217 IConnectableLayer* layer = data.m_Network->AddLogicalBinaryLayer(descriptor);
Mike Kellye2d611e2021-10-14 12:35:58 +0100218 if (!layer)
219 {
220 return Fail("%s: Could not add the LogicalBinaryLayer", __func__);
221 }
Narumol Prangnawarat0629eb82020-11-12 18:27:37 +0000222
223 bool isReshapeSupported = BroadcastTensor(input0, input1, layer, data);
224 if (!isReshapeSupported)
225 {
226 return false;
227 }
228
229 return SetupAndTrackLayerOutputSlot<HalPolicy>(operation, 0, *layer, model, data, nullptr, validateFunc);
230}
231
232template<typename HalPolicy,
233 typename HalOperation = typename HalPolicy::Operation,
234 typename HalModel = typename HalPolicy::Model>
Sadik Armagan813f2302020-05-19 14:10:30 +0100235bool ConvertQuantizedLstm(const HalOperation& operation, const HalModel& model, ConversionData& data)
236{
237 using HalOperand = typename HalPolicy::Operand;
238 using HalOperandType = typename HalPolicy::OperandType;
239
240 ALOGV("HalPolicy::ConvertQuantizedLstm()");
241
242 //Inputs:
243 // 0: The input: A 2-D tensor of type ANEURALNETWORKS_TENSOR_QUANT8_ASYMM and shape [numBatches, inputSize]
244 // specifying the input to the LSTM cell. Tensor is quantized with a fixed quantization range of -1, 127/128.
245 LayerInputHandle input = ConvertToLayerInputHandle<HalPolicy>(operation, 0, model, data);
246 if (!input.IsValid())
247 {
248 return Fail("%s: Could not read input 0: input", __func__);
249 }
250
251 // 18: The output state: A 2-D tensor of ANEURALNETWORKS_TENSOR_QUANT8_ASYMM, of shape [batch_size, output_size].
252 LayerInputHandle outputStatePrevTimeStep = ConvertToLayerInputHandle<HalPolicy>(operation, 18, model, data);
253 if (!outputStatePrevTimeStep.IsValid())
254 {
255 return Fail("%s: Could not read input 18: outputStatePrevTimeStep", __func__);
256 }
257
258 // 19: The cell state: A 2-D tensor of ANEURALNETWORKS_TENSOR_QUANT16_SYMM, of shape [batch_size, num_units].
259 LayerInputHandle cellStatePrevTimeStep = ConvertToLayerInputHandle<HalPolicy>(operation, 19, model, data);
260 if (!cellStatePrevTimeStep.IsValid())
261 {
262 return Fail("%s: Could not read input 19: cellStatePrevTimeStep", __func__);
263 }
264
265 // Get the mandatory input tensors:
266
267 // 02: The input-to-forget weights: A 2-D tensor of ANEURALNETWORKS_TENSOR_QUANT8_SYMM, of shape
268 // [num_units, input_size].
269 const ConstTensorPin inputToForgetWeightsPin =
270 ConvertOperationInputToConstTensorPin<HalPolicy>(operation, 2, model, data);
271
272 // 03: The input-to-cell weights: A 2-D tensor of ANEURALNETWORKS_TENSOR_QUANT8_SYMM, of shape
273 // [num_units, input_size].
274 const ConstTensorPin inputToCellWeightsPin =
275 ConvertOperationInputToConstTensorPin<HalPolicy>(operation, 3, model, data);
276
277 // 04: The input-to-output weights: A 2-D tensor of ANEURALNETWORKS_TENSOR_QUANT8_SYMM, of shape
278 // [num_units, input_size].
279 const ConstTensorPin inputToOutputWeightsPin =
280 ConvertOperationInputToConstTensorPin<HalPolicy>(operation, 4, model, data);
281
282 // 06: The recurrent-to-forget weights: A 2-D tensor of ANEURALNETWORKS_TENSOR_QUANT8_SYMM, of shape
283 // [num_units, output_size].
284 const ConstTensorPin recurrentToForgetWeightsPin =
285 ConvertOperationInputToConstTensorPin<HalPolicy>(operation, 6, model, data);
286
287 // 07: The recurrent-to-cell weights: A 2-D tensor of ANEURALNETWORKS_TENSOR_QUANT8_SYMM, of shape
288 // [num_units, output_size].
289 const ConstTensorPin recurrentToCellWeightsPin =
290 ConvertOperationInputToConstTensorPin<HalPolicy>(operation, 7, model, data);
291
292 // 08: The recurrent-to-output weights: A 2-D tensor of ANEURALNETWORKS_TENSOR_QUANT8_SYMM, of shape
293 // [num_units, output_size].
294 const ConstTensorPin recurrentToOutputWeightsPin =
295 ConvertOperationInputToConstTensorPin<HalPolicy>(operation, 8, model, data);
296
297 // 13: The forget gate bias: A 1-D tensor of ANEURALNETWORKS_TENSOR_INT32, of shape [num_units].
298 const ConstTensorPin forgetGateBiasPin =
299 ConvertOperationInputToConstTensorPin<HalPolicy>(operation, 13, model, data);
300
301 // 14: The cell bias: A 1-D tensor of ANEURALNETWORKS_TENSOR_INT32, of shape [num_units].
302 const ConstTensorPin cellBiasPin =
303 ConvertOperationInputToConstTensorPin<HalPolicy>(operation, 14, model, data);
304
305 // 15: The output gate bias: A 1-D tensor of ANEURALNETWORKS_TENSOR_INT32, of shape [num_units].
306 const ConstTensorPin outputGateBiasPin =
307 ConvertOperationInputToConstTensorPin<HalPolicy>(operation, 15, model, data);
308
309 if (!inputToForgetWeightsPin.IsValid() ||
310 !inputToCellWeightsPin.IsValid() ||
311 !inputToOutputWeightsPin.IsValid() ||
312 !recurrentToForgetWeightsPin.IsValid() ||
313 !recurrentToCellWeightsPin.IsValid() ||
314 !recurrentToOutputWeightsPin.IsValid() ||
315 !forgetGateBiasPin.IsValid() ||
316 !cellBiasPin.IsValid() ||
317 !outputGateBiasPin.IsValid())
318 {
319 return Fail("%s: Operation has invalid tensor inputs", __func__);
320 }
321
322 // Get the optional input tensors:
323
324 // 01: The input-to-input weights: Optional. A 2-D tensor of ANEURALNETWORKS_TENSOR_QUANT8_SYMM, of shape
325 // [num_units, input_size], where “num_units” corresponds to the number of cell units.
326 const ConstTensorPin inputToInputWeightsPin =
327 ConvertOperationInputToConstTensorPin<HalPolicy>(operation,
328 1,
329 model,
330 data,
331 g_DontPermute,
332 nullptr,
333 true);
334
335 // 05: The recurrent-to-input weights: Optional. A 2-D tensor of ANEURALNETWORKS_TENSOR_QUANT8_SYMM, of shape
336 // [num_units, output_size], where “output_size” corresponds to either the number of cell units (i.e.,
337 // “num_units”), or the second dimension of the “projection_weights”, if defined.
338 const ConstTensorPin recurrentToInputWeightsPin =
339 ConvertOperationInputToConstTensorPin<HalPolicy>(operation,
340 5,
341 model,
342 data,
343 g_DontPermute,
344 nullptr,
345 true);
346
347 // 09: The cell-to-input weights: Optional. A 1-D tensor of ANEURALNETWORKS_TENSOR_QUANT16_SYMM, of shape
348 // [num_units].
349 const ConstTensorPin cellToInputWeightsPin =
350 ConvertOperationInputToConstTensorPin<HalPolicy>(operation,
351 9,
352 model,
353 data,
354 g_DontPermute,
355 nullptr,
356 true);
357
358 // 10: The cell-to-forget weights: Optional. A 1-D tensor of ANEURALNETWORKS_TENSOR_QUANT16_SYMM, of shape
359 // [num_units].
360 const ConstTensorPin cellToForgetWeightsPin =
361 ConvertOperationInputToConstTensorPin<HalPolicy>(operation,
362 10,
363 model,
364 data,
365 g_DontPermute,
366 nullptr,
367 true);
368
369 // 11: The cell-to-output weights: Optional. A 1-D tensor of ANEURALNETWORKS_TENSOR_QUANT16_SYMM, of shape
370 // [num_units].
371 const ConstTensorPin cellToOutputWeightsPin =
372 ConvertOperationInputToConstTensorPin<HalPolicy>(operation,
373 11,
374 model,
375 data,
376 g_DontPermute,
377 nullptr,
378 true);
379
380 // 12: The input gate bias: Optional. A 1-D tensor of ANEURALNETWORKS_TENSOR_INT32, of shape [num_units].
381 const ConstTensorPin inputGateBiasPin =
382 ConvertOperationInputToConstTensorPin<HalPolicy>(operation,
383 12,
384 model,
385 data,
386 g_DontPermute,
387 nullptr,
388 true);
389
390 // 16: The projection weights: Optional. A 2-D tensor of ANEURALNETWORKS_TENSOR_QUANT8_SYMM, of shape
391 // [output_size, num_units].
392 const ConstTensorPin projectionWeightsPin =
393 ConvertOperationInputToConstTensorPin<HalPolicy>(operation,
394 16,
395 model,
396 data,
397 g_DontPermute,
398 nullptr,
399 true);
400
401 // 17: The projection bias: Optional. A 1-D tensor of ANEURALNETWORKS_TENSOR_INT32, of shape [output_size].
402 const ConstTensorPin projectionBiasPin =
403 ConvertOperationInputToConstTensorPin<HalPolicy>(operation,
404 17,
405 model,
406 data,
407 g_DontPermute,
408 nullptr,
409 true);
410
411 if ((!inputToInputWeightsPin.IsValid() && !inputToInputWeightsPin.IsOptional())
412 || (!recurrentToInputWeightsPin.IsValid() && !recurrentToInputWeightsPin.IsOptional())
413 || (!cellToInputWeightsPin.IsValid() && !cellToInputWeightsPin.IsOptional())
414 || (!cellToForgetWeightsPin.IsValid() && !cellToForgetWeightsPin.IsOptional())
415 || (!cellToOutputWeightsPin.IsValid() && !cellToOutputWeightsPin.IsOptional())
416 || (!inputGateBiasPin.IsValid() && !inputGateBiasPin.IsOptional())
417 || (!projectionWeightsPin.IsValid() && !projectionWeightsPin.IsOptional())
418 || (!projectionBiasPin.IsValid() && !projectionBiasPin.IsOptional()))
419 {
420 return Fail("%s: Operation has invalid tensor inputs", __func__);
421 }
422
423
424 // Get the optional normalization tensors
425
426 // 20: The input layer normalization weights. A 1-D tensor of shape [num_units] ANEURALNETWORKS_TENSOR_QUANT16_SYMM.
427 // Used to rescale normalized inputs to activation at input gate.
428 const ConstTensorPin inputLayerNormWeightsPin =
429 ConvertOperationInputToConstTensorPin<HalPolicy>(operation,
430 20,
431 model,
432 data,
433 g_DontPermute,
434 nullptr,
435 true);
436
437 // 21: The forget layer normalization weights. A 1-D tensor of shape [num_units] ANEURALNETWORKS_TENSOR_QUANT16_SYMM
438 // Used to rescale normalized inputs to activation at forget gate.
439 const ConstTensorPin forgetLayerNormWeightsPin =
440 ConvertOperationInputToConstTensorPin<HalPolicy>(operation,
441 21,
442 model,
443 data,
444 g_DontPermute,
445 nullptr,
446 true);
447
448 // 22: The cell layer normalization weights. A 1-D tensor of shape [num_units] ANEURALNETWORKS_TENSOR_QUANT16_SYMM.
449 // Used to rescale normalized inputs to activation at cell gate.
450 const ConstTensorPin cellLayerNormWeightsPin =
451 ConvertOperationInputToConstTensorPin<HalPolicy>(operation,
452 22,
453 model,
454 data,
455 g_DontPermute,
456 nullptr,
457 true);
458
459 // 23: The output layer normalization weights. A 1-D tensor of shape [num_units].
460 // Used to rescale normalized inputs to activation at output gate.
461 const ConstTensorPin outputLayerNormWeightsPin =
462 ConvertOperationInputToConstTensorPin<HalPolicy>(operation,
463 23,
464 model,
465 data,
466 g_DontPermute,
467 nullptr,
468 true);
469
470 if ((!inputLayerNormWeightsPin.IsValid() && !inputLayerNormWeightsPin.IsOptional())
471 || (!forgetLayerNormWeightsPin.IsValid() && !forgetLayerNormWeightsPin.IsOptional())
472 || (!cellLayerNormWeightsPin.IsValid() && !cellLayerNormWeightsPin.IsOptional())
473 || (!outputLayerNormWeightsPin.IsValid() && !outputLayerNormWeightsPin.IsOptional()))
474 {
475 return Fail("%s: Operation has invalid tensor inputs", __func__);
476 }
477
478 // Get the optional input scalars:
479 // 24: The cell clip: If provided the cell state is clipped by this value prior to the cell output activation.
480 // 25: The projection clip: If provided and projection is enabled, this is used for clipping the projected values.
481
482 // Get the mandatory input scalars:
483 // 26: The scale of the intermediate result of matmul, i.e. input to layer normalization, at input gate.
484 // 27: The scale of the intermediate result of matmul, i.e. input to layer normalization, at forget gate.
485 // 28: The scale of the intermediate result of matmul, i.e. input to layer normalization, at cell gate.
486 // 29: The scale of the intermediate result of matmul, i.e. input to layer normalization, at output gate.
487 // 30: The zero point of the hidden state, i.e. input to projection.
488 // 31: The scale of the hidden state, i.e. input to projection.
489 float cellClip, projClip, matMulInputGate, matMulForgetGate, matMulCellGate, matMulOutputGate, projInputScale;
490 int projInputZeroPoint;
491
492 if (!GetInputScalar<HalPolicy>(operation, 24, HalOperandType::FLOAT32, cellClip, model, data, true) ||
493 !GetInputScalar<HalPolicy>(operation, 25, HalOperandType::FLOAT32, projClip, model, data, true) ||
494 !GetInputScalar<HalPolicy>(operation, 26, HalOperandType::FLOAT32, matMulInputGate, model, data) ||
495 !GetInputScalar<HalPolicy>(operation, 27, HalOperandType::FLOAT32, matMulForgetGate, model, data) ||
496 !GetInputScalar<HalPolicy>(operation, 28, HalOperandType::FLOAT32, matMulCellGate, model, data) ||
497 !GetInputScalar<HalPolicy>(operation, 29, HalOperandType::FLOAT32, matMulOutputGate, model, data) ||
Sadik Armagan24af8b22020-05-22 08:34:16 +0100498 !GetInputScalar<HalPolicy>(operation, 30, HalOperandType::INT32, projInputZeroPoint, model, data) ||
499 !GetInputScalar<HalPolicy>(operation, 31, HalOperandType::FLOAT32, projInputScale, model, data))
Sadik Armagan813f2302020-05-19 14:10:30 +0100500 {
501 return Fail("%s: Operation has invalid scalar inputs", __func__);
502 }
503
504 // Outputs:
505 // 0: The output state (out): A 2-D tensor of ANEURALNETWORKS_TENSOR_QUANT8_ASYMM_SIGNED, of shape [batch_size,
506 // output_size].
507 const HalOperand* outputStateOut = GetOutputOperand<HalPolicy>(operation, 0, model);
508 if (!outputStateOut)
509 {
510 return Fail("%s: Could not read output 0: outputStateOut", __func__);
511 }
512
513 // 1: The cell state (out): A 2-D tensor of ANEURALNETWORKS_TENSOR_QUANT16_SYMM, of shape [batch_size, num_units].
514 const HalOperand* cellStateOut = GetOutputOperand<HalPolicy>(operation, 1, model);
515 if (!cellStateOut)
516 {
517 return Fail("%s: Could not read output 1: cellStateOut", __func__);
518 }
519
520 // 2: The output: A 2-D tensor of ANEURALNETWORKS_TENSOR_QUANT8_ASYMM_SIGNED, of shape [batch_size, output_size].
521 // This is effectively the same as the current “output state (out)” value.
522 const HalOperand* output = GetOutputOperand<HalPolicy>(operation, 2, model);
523 if (!output)
524 {
525 return Fail("%s: Could not read output 2: output", __func__);
526 }
527
528 // set the params structure for the AddLstmLayer call
529 LstmInputParams params;
530 params.m_InputToInputWeights = inputToInputWeightsPin.GetConstTensorPtr();
531 params.m_InputToForgetWeights = inputToForgetWeightsPin.GetConstTensorPtr();
532 params.m_InputToCellWeights = inputToCellWeightsPin.GetConstTensorPtr();
533 params.m_InputToOutputWeights = inputToOutputWeightsPin.GetConstTensorPtr();
534 params.m_RecurrentToInputWeights = recurrentToInputWeightsPin.GetConstTensorPtr();
535 params.m_RecurrentToForgetWeights = recurrentToForgetWeightsPin.GetConstTensorPtr();
536 params.m_RecurrentToCellWeights = recurrentToCellWeightsPin.GetConstTensorPtr();
537 params.m_RecurrentToOutputWeights = recurrentToOutputWeightsPin.GetConstTensorPtr();
538 params.m_CellToInputWeights = cellToInputWeightsPin.GetConstTensorPtr();
539 params.m_CellToForgetWeights = cellToForgetWeightsPin.GetConstTensorPtr();
540 params.m_CellToOutputWeights = cellToOutputWeightsPin.GetConstTensorPtr();
541 params.m_InputGateBias = inputGateBiasPin.GetConstTensorPtr();
542 params.m_ForgetGateBias = forgetGateBiasPin.GetConstTensorPtr();
543 params.m_CellBias = cellBiasPin.GetConstTensorPtr();
544 params.m_OutputGateBias = outputGateBiasPin.GetConstTensorPtr();
545 params.m_ProjectionWeights = projectionWeightsPin.GetConstTensorPtr();
546 params.m_ProjectionBias = projectionBiasPin.GetConstTensorPtr();
547 params.m_InputLayerNormWeights = inputLayerNormWeightsPin.GetConstTensorPtr();
548 params.m_ForgetLayerNormWeights = forgetLayerNormWeightsPin.GetConstTensorPtr();
549 params.m_CellLayerNormWeights = cellLayerNormWeightsPin.GetConstTensorPtr();
550 params.m_OutputLayerNormWeights = outputLayerNormWeightsPin.GetConstTensorPtr();
551
552 // set the layer descriptor
553 QLstmDescriptor desc;
554 desc.m_CellClip = cellClip;
555 desc.m_ProjectionClip = projClip;
556 desc.m_CifgEnabled = (params.m_InputToInputWeights == nullptr ||
557 params.m_RecurrentToInputWeights == nullptr ||
558 params.m_InputGateBias == nullptr);
559 desc.m_PeepholeEnabled = (params.m_CellToForgetWeights != nullptr ||
560 params.m_CellToOutputWeights != nullptr);
561 desc.m_ProjectionEnabled = (params.m_ProjectionWeights != nullptr);
562 desc.m_LayerNormEnabled = (params.m_InputLayerNormWeights != nullptr ||
563 params.m_ForgetLayerNormWeights != nullptr ||
564 params.m_CellLayerNormWeights != nullptr ||
565 params.m_OutputLayerNormWeights != nullptr);
566 desc.m_InputIntermediateScale = matMulInputGate;
567 desc.m_ForgetIntermediateScale = matMulForgetGate;
568 desc.m_CellIntermediateScale = matMulCellGate;
569 desc.m_OutputIntermediateScale = matMulOutputGate;
570 desc.m_HiddenStateScale = projInputScale;
571 desc.m_HiddenStateZeroPoint = projInputZeroPoint;
572
573 // validate the optional input groups
574 if (desc.m_CifgEnabled &&
575 (params.m_InputToInputWeights != nullptr ||
576 params.m_RecurrentToInputWeights != nullptr ||
577 params.m_InputGateBias != nullptr))
578 {
579 return Fail("%s: All, or none, of input-to-input weights, recurrent-to-input weights,"
580 " and input gate bias must be provided", __func__);
581 }
582
583 if (!desc.m_ProjectionEnabled && params.m_ProjectionBias != nullptr)
584 {
585 return Fail("%s: projection bias should not be provided without projection weights", __func__);
586 }
587
588 if (desc.m_PeepholeEnabled &&
589 (params.m_CellToForgetWeights == nullptr ||
590 params.m_CellToOutputWeights == nullptr ||
591 (!desc.m_CifgEnabled && params.m_CellToInputWeights == nullptr)))
592 {
593 return Fail("%s: All, or none, of cell-to-forget weights and cell-to-output weights must be provided"
594 " and, if CIFG is not enabled, cell-to-input weights must also be provided", __func__);
595 }
596
597 if (desc.m_LayerNormEnabled &&
598 (params.m_ForgetLayerNormWeights == nullptr ||
599 params.m_CellLayerNormWeights == nullptr ||
600 params.m_OutputLayerNormWeights == nullptr ||
601 (!desc.m_CifgEnabled && params.m_InputLayerNormWeights == nullptr)))
602 {
603 return Fail("%s: All, or none, of forget-norm weights, cell-norm weights and output-norm weights must be"
604 " provided and, if CIFG is not enabled, input-norm weights must also be provided", __func__);
605 }
606
607
608 // Basic parameters
609 LstmInputParamsInfo paramsInfo;
610 paramsInfo.m_InputToForgetWeights = &(params.m_InputToForgetWeights->GetInfo());
611 paramsInfo.m_InputToCellWeights = &(params.m_InputToCellWeights->GetInfo());
612 paramsInfo.m_InputToOutputWeights = &(params.m_InputToOutputWeights->GetInfo());
613 paramsInfo.m_RecurrentToForgetWeights = &(params.m_RecurrentToForgetWeights->GetInfo());
614 paramsInfo.m_RecurrentToCellWeights = &(params.m_RecurrentToCellWeights->GetInfo());
615 paramsInfo.m_RecurrentToOutputWeights = &(params.m_RecurrentToOutputWeights->GetInfo());
616 paramsInfo.m_ForgetGateBias = &(params.m_ForgetGateBias->GetInfo());
617 paramsInfo.m_CellBias = &(params.m_CellBias->GetInfo());
618 paramsInfo.m_OutputGateBias = &(params.m_OutputGateBias->GetInfo());
619
620 // Inputs
621 const TensorInfo& inputInfo = input.GetTensorInfo();
622 const TensorInfo& outputStatePrevTimeStepInfo = outputStatePrevTimeStep.GetTensorInfo();
623 const TensorInfo& cellStatePrevTimeStepInfo = cellStatePrevTimeStep.GetTensorInfo();
624
625 // Outputs
626 TensorInfo outputStateOutInfo = GetTensorInfoForOperand(*outputStateOut);
627 TensorInfo outputInfo = GetTensorInfoForOperand(*output);
628 const TensorInfo& cellStateOutInfo = GetTensorInfoForOperand(*cellStateOut);
629
630 // Optional parameters
631 if (!desc.m_CifgEnabled)
632 {
633 paramsInfo.m_InputToInputWeights = &(params.m_InputToInputWeights->GetInfo());
634 paramsInfo.m_RecurrentToInputWeights = &(params.m_RecurrentToInputWeights->GetInfo());
635 if (desc.m_PeepholeEnabled)
636 {
637 paramsInfo.m_CellToInputWeights = &(params.m_CellToInputWeights->GetInfo());
638 }
639 paramsInfo.m_InputGateBias = &(params.m_InputGateBias->GetInfo());
640 }
641
642
643 if (desc.m_ProjectionEnabled)
644 {
645 paramsInfo.m_ProjectionWeights = &(params.m_ProjectionWeights->GetInfo());
646 if (params.m_ProjectionBias != nullptr)
647 {
648 paramsInfo.m_ProjectionBias = &(params.m_ProjectionBias->GetInfo());
649 }
650 }
651 else
652 {
653 // If Projection is disabled, override non-const outputs to change the quant info with hidden params, then
654 // create a new const TensorInfo based on this
655 outputStateOutInfo.SetQuantizationScale(projInputScale);
656 outputStateOutInfo.SetQuantizationOffset(projInputZeroPoint);
657 outputInfo.SetQuantizationScale(projInputScale);
658 outputInfo.SetQuantizationOffset(projInputZeroPoint);
659 }
660
661 const TensorInfo constOutputStateOutInfo(outputStateOutInfo);
662 const TensorInfo constOutputInfo(outputInfo);
663
664 if (desc.m_PeepholeEnabled)
665 {
666 paramsInfo.m_CellToForgetWeights = &(params.m_CellToForgetWeights->GetInfo());
667 paramsInfo.m_CellToOutputWeights = &(params.m_CellToOutputWeights->GetInfo());
668 }
669
670 if (desc.m_LayerNormEnabled)
671 {
672 if(!desc.m_CifgEnabled)
673 {
674 paramsInfo.m_InputLayerNormWeights = &(params.m_InputLayerNormWeights->GetInfo());
675 }
676 paramsInfo.m_ForgetLayerNormWeights = &(params.m_ForgetLayerNormWeights->GetInfo());
677 paramsInfo.m_CellLayerNormWeights = &(params.m_CellLayerNormWeights->GetInfo());
678 paramsInfo.m_OutputLayerNormWeights = &(params.m_OutputLayerNormWeights->GetInfo());
679 }
680
681 // Check if the layer is supported
Sadik Armagan34db1872020-09-03 15:22:29 +0100682 bool isSupported = false;
683 auto validateFunc = [&](const armnn::TensorInfo& cellStateOutInfo, bool& isSupported)
Sadik Armagan813f2302020-05-19 14:10:30 +0100684 {
Sadik Armagan34db1872020-09-03 15:22:29 +0100685 FORWARD_LAYER_SUPPORT_FUNC(__func__,
686 IsQLstmSupported,
687 data.m_Backends,
688 isSupported,
689 inputInfo,
690 outputStatePrevTimeStepInfo,
691 cellStatePrevTimeStepInfo,
692 constOutputStateOutInfo,
693 cellStateOutInfo,
694 constOutputInfo,
695 desc,
696 paramsInfo);
697 };
698
699 bool isDynamic = false;
700 if (!IsDynamicTensor(constOutputStateOutInfo) &&
701 !IsDynamicTensor(cellStateOutInfo) &&
702 !IsDynamicTensor(constOutputInfo))
703 {
704 validateFunc(outputInfo, isSupported);
705 }
706 else
707 {
708 isDynamic = true;
709 isSupported = AreDynamicTensorsSupported();
Sadik Armagan813f2302020-05-19 14:10:30 +0100710 }
711
Sadik Armagan813f2302020-05-19 14:10:30 +0100712 if (!isSupported)
713 {
714 return false;
715 }
716
717 // Add the layer
718 IConnectableLayer* layer = data.m_Network->AddQLstmLayer(desc, params, "QLstm");
719
720 input.Connect(layer->GetInputSlot(0));
721 outputStatePrevTimeStep.Connect(layer->GetInputSlot(1));
722 cellStatePrevTimeStep.Connect(layer->GetInputSlot(2));
723
Sadik Armagan34db1872020-09-03 15:22:29 +0100724 if (!isDynamic)
725 {
726 return ( SetupAndTrackLayerOutputSlot<HalPolicy>(
727 operation, 0, *layer, 0, model, data, &constOutputStateOutInfo) &&
728 SetupAndTrackLayerOutputSlot<HalPolicy>(operation, 1, *layer, 1, model, data) &&
729 SetupAndTrackLayerOutputSlot<HalPolicy>(operation, 2, *layer, 2, model, data, &constOutputInfo));
730 }
731 else
732 {
733 return ( SetupAndTrackLayerOutputSlot<HalPolicy>(
734 operation, 0, *layer, 0, model, data, &constOutputStateOutInfo) &&
735 SetupAndTrackLayerOutputSlot<HalPolicy>(
Kevin Mayfcf2a152020-09-08 16:06:32 +0100736 operation, 1, *layer, 1, model, data, nullptr, validateFunc,
737 ActivationFn::kActivationNone, true) &&
Sadik Armagan34db1872020-09-03 15:22:29 +0100738 SetupAndTrackLayerOutputSlot<HalPolicy>(operation, 2, *layer, 2, model, data, &constOutputInfo));
739 }
Sadik Armagan813f2302020-05-19 14:10:30 +0100740}
741
Finn Williamsfc884b42020-06-11 17:35:44 +0100742template<typename HalPolicy,
743 typename HalOperation = typename HalPolicy::Operation,
744 typename HalModel = typename HalPolicy::Model>
745bool ConvertRank(const HalOperation& operation, const HalModel& model, ConversionData& data)
746{
747 using HalOperand = typename HalPolicy::Operand;
748
749 const HalOperand* inputOperand = GetInputOperand<HalPolicy>(operation, 0, model);
750 const HalOperand* outputOperand = GetOutputOperand<HalPolicy>(operation, 0, model);
751
752 if (inputOperand == nullptr || outputOperand == nullptr)
753 {
754 return Fail("%s: Operation has invalid inputs", __func__);
755 }
756
757 const Shape inputOperandShape = GetOperandShape(*inputOperand);
758 const Shape outputOperandShape = GetOperandShape(*outputOperand);
759
760 LayerInputHandle input = ConvertToLayerInputHandle<HalPolicy>(operation, 0, model, data);
761 if (!input.IsValid())
762 {
763 return Fail("%s: Could not read input 0", __func__);
764 }
765
766 armnn::TensorInfo outInfo = GetTensorInfoForOperand(*outputOperand);
Teresa Charlin4bd9a742020-08-12 12:58:50 +0100767 if (IsDynamicTensor(outInfo))
768 {
769 return Fail("%s: Dynamic output tensors are not supported", __func__);
770 }
Finn Williamsfc884b42020-06-11 17:35:44 +0100771
772 bool isSupported = false;
773 FORWARD_LAYER_SUPPORT_FUNC(__func__,
774 IsRankSupported,
775 data.m_Backends,
776 isSupported,
777 input.GetTensorInfo(),
778 outInfo);
779 if (!isSupported)
780 {
781 return false;
782 }
783
784 armnn::IConnectableLayer* layer = data.m_Network->AddRankLayer();
Mike Kellye2d611e2021-10-14 12:35:58 +0100785 if (!layer)
786 {
787 return Fail("%s: Could not add the RankLayer", __func__);
788 }
Finn Williamsfc884b42020-06-11 17:35:44 +0100789 input.Connect(layer->GetInputSlot(0));
790
791 return SetupAndTrackLayerOutputSlot<HalPolicy>(operation, 0, *layer, model, data, &outInfo);
792}
793
794} // armnn_driver namespace