2 // Copyright © 2017 Arm Ltd. All rights reserved.
3 // SPDX-License-Identifier: MIT
6 #include "WorkloadTestUtils.hpp"
8 #include <armnn/Exceptions.hpp>
10 #include <backendsCommon/CpuTensorHandle.hpp>
11 #include <backendsCommon/Workload.hpp>
13 #include <reference/workloads/RefWorkloads.hpp>
14 #include <reference/RefWorkloadFactory.hpp>
16 #include <boost/test/unit_test.hpp>
18 using namespace armnn;
20 BOOST_AUTO_TEST_SUITE(WorkloadInfoValidation)
24 BOOST_AUTO_TEST_CASE(QueueDescriptor_Validate_WrongNumOfInputsOutputs)
26 InputQueueDescriptor invalidData;
27 WorkloadInfo invalidInfo;
28 //Invalid argument exception is expected, because no inputs and no outputs were defined.
29 BOOST_CHECK_THROW(RefWorkloadFactory().CreateInput(invalidData, invalidInfo), armnn::InvalidArgumentException);
32 BOOST_AUTO_TEST_CASE(RefPooling2dFloat32Workload_Validate_WrongDimTensor)
34 armnn::TensorInfo inputTensorInfo;
35 armnn::TensorInfo outputTensorInfo;
37 unsigned int inputShape[] = {2, 3, 4}; // <- Invalid - input tensor has to be 4D.
38 unsigned int outputShape[] = {2, 3, 4, 5};
40 outputTensorInfo = armnn::TensorInfo(4, outputShape, armnn::DataType::Float32);
41 inputTensorInfo = armnn::TensorInfo(3, inputShape, armnn::DataType::Float32);
43 Pooling2dQueueDescriptor invalidData;
44 WorkloadInfo invalidInfo;
46 AddOutputToWorkload(invalidData, invalidInfo, outputTensorInfo, nullptr);
47 AddInputToWorkload(invalidData, invalidInfo, inputTensorInfo, nullptr);
49 // Invalid argument exception is expected, input tensor has to be 4D.
50 BOOST_CHECK_THROW(RefPooling2dWorkload(invalidData, invalidInfo), armnn::InvalidArgumentException);
53 BOOST_AUTO_TEST_CASE(SoftmaxQueueDescriptor_Validate_WrongInputHeight)
55 unsigned int inputHeight = 1;
56 unsigned int inputWidth = 1;
57 unsigned int inputChannels = 4;
58 unsigned int inputNum = 2;
60 unsigned int outputChannels = inputChannels;
61 unsigned int outputHeight = inputHeight + 1; //Makes data invalid - Softmax expects height and width to be 1.
62 unsigned int outputWidth = inputWidth;
63 unsigned int outputNum = inputNum;
65 armnn::TensorInfo inputTensorInfo;
66 armnn::TensorInfo outputTensorInfo;
68 unsigned int inputShape[] = { inputNum, inputChannels, inputHeight, inputWidth };
69 unsigned int outputShape[] = { outputNum, outputChannels, outputHeight, outputWidth };
71 inputTensorInfo = armnn::TensorInfo(4, inputShape, armnn::DataType::Float32);
72 outputTensorInfo = armnn::TensorInfo(4, outputShape, armnn::DataType::Float32);
74 SoftmaxQueueDescriptor invalidData;
75 WorkloadInfo invalidInfo;
77 AddInputToWorkload(invalidData, invalidInfo, inputTensorInfo, nullptr);
78 AddOutputToWorkload(invalidData, invalidInfo, outputTensorInfo, nullptr);
80 //Invalid argument exception is expected, because height != 1.
81 BOOST_CHECK_THROW(RefSoftmaxWorkload(invalidData, invalidInfo), armnn::InvalidArgumentException);
84 BOOST_AUTO_TEST_CASE(FullyConnectedQueueDescriptor_Validate_RequiredDataMissing)
86 unsigned int inputWidth = 1;
87 unsigned int inputHeight = 1;
88 unsigned int inputChannels = 5;
89 unsigned int inputNum = 2;
91 unsigned int outputWidth = 1;
92 unsigned int outputHeight = 1;
93 unsigned int outputChannels = 3;
94 unsigned int outputNum = 2;
96 // Define the tensor descriptors.
97 armnn::TensorInfo inputTensorInfo;
98 armnn::TensorInfo outputTensorInfo;
99 armnn::TensorInfo weightsDesc;
100 armnn::TensorInfo biasesDesc;
102 unsigned int inputShape[] = { inputNum, inputChannels, inputHeight, inputWidth };
103 unsigned int outputShape[] = { outputNum, outputChannels, outputHeight, outputWidth };
104 unsigned int weightsShape[] = { 1, 1, inputChannels, outputChannels };
105 unsigned int biasShape[] = { 1, outputChannels, outputHeight, outputWidth };
107 inputTensorInfo = armnn::TensorInfo(4, inputShape, armnn::DataType::Float32);
108 outputTensorInfo = armnn::TensorInfo(4, outputShape, armnn::DataType::Float32);
109 weightsDesc = armnn::TensorInfo(4, weightsShape, armnn::DataType::Float32);
110 biasesDesc = armnn::TensorInfo(4, biasShape, armnn::DataType::Float32);
112 FullyConnectedQueueDescriptor invalidData;
113 WorkloadInfo invalidInfo;
115 ScopedCpuTensorHandle weightTensor(weightsDesc);
116 ScopedCpuTensorHandle biasTensor(biasesDesc);
118 AddInputToWorkload(invalidData, invalidInfo, inputTensorInfo, nullptr);
119 AddOutputToWorkload(invalidData, invalidInfo, outputTensorInfo, nullptr);
120 invalidData.m_Weight = &weightTensor;
121 invalidData.m_Bias = &biasTensor;
122 invalidData.m_Parameters.m_BiasEnabled = true;
123 invalidData.m_Parameters.m_TransposeWeightMatrix = false;
126 //Invalid argument exception is expected, because not all required fields have been provided.
127 //In particular inputsData[0], outputsData[0] and weightsData can not be null.
128 BOOST_CHECK_THROW(RefFullyConnectedWorkload(invalidData, invalidInfo), armnn::InvalidArgumentException);
132 BOOST_AUTO_TEST_CASE(NormalizationQueueDescriptor_Validate_WrongInputHeight)
134 constexpr unsigned int inputNum = 5;
135 constexpr unsigned int inputHeight = 32;
136 constexpr unsigned int inputWidth = 24;
137 constexpr unsigned int inputChannels = 3;
139 constexpr unsigned int outputNum = inputNum;
140 constexpr unsigned int outputChannels = inputChannels;
141 constexpr unsigned int outputHeight = inputHeight + 1; //Makes data invalid - normalization requires.
142 //Input and output to have the same dimensions.
143 constexpr unsigned int outputWidth = inputWidth;
146 armnn::TensorInfo inputTensorInfo;
147 armnn::TensorInfo outputTensorInfo;
149 unsigned int inputShape[] = {inputNum, inputChannels, inputHeight, inputWidth};
150 unsigned int outputShape[] = {outputNum, outputChannels, outputHeight, outputWidth};
152 inputTensorInfo = armnn::TensorInfo(4, inputShape, armnn::DataType::Float32);
153 outputTensorInfo = armnn::TensorInfo(4, outputShape, armnn::DataType::Float32);
156 armnn::NormalizationAlgorithmMethod normMethod = armnn::NormalizationAlgorithmMethod::LocalBrightness;
157 armnn::NormalizationAlgorithmChannel normChannel = armnn::NormalizationAlgorithmChannel::Across;
161 uint32_t normSize = 5;
163 NormalizationQueueDescriptor invalidData;
164 WorkloadInfo invalidInfo;
166 AddInputToWorkload(invalidData, invalidInfo, inputTensorInfo, nullptr);
167 AddOutputToWorkload(invalidData, invalidInfo, outputTensorInfo, nullptr);
168 invalidData.m_Parameters.m_NormChannelType = normChannel;
169 invalidData.m_Parameters.m_NormMethodType = normMethod;
170 invalidData.m_Parameters.m_NormSize = normSize;
171 invalidData.m_Parameters.m_Alpha = alpha;
172 invalidData.m_Parameters.m_Beta = beta;
173 invalidData.m_Parameters.m_K = kappa;
175 //Invalid argument exception is expected, because input height != output height.
176 BOOST_CHECK_THROW(RefNormalizationWorkload(invalidData, invalidInfo), armnn::InvalidArgumentException);
179 BOOST_AUTO_TEST_CASE(SplitterQueueDescriptor_Validate_WrongWindow)
181 constexpr unsigned int inputNum = 1;
182 constexpr unsigned int inputHeight = 32;
183 constexpr unsigned int inputWidth = 24;
184 constexpr unsigned int inputChannels = 3;
186 constexpr unsigned int outputNum = inputNum;
187 constexpr unsigned int outputChannels = inputChannels;
188 constexpr unsigned int outputHeight = 18;
189 constexpr unsigned int outputWidth = inputWidth;
192 armnn::TensorInfo inputTensorInfo;
193 armnn::TensorInfo outputTensorInfo;
195 unsigned int inputShape[] = {inputNum, inputChannels, inputHeight, inputWidth};
196 unsigned int outputShape[] = {outputNum, outputChannels, outputHeight, outputWidth};
198 inputTensorInfo = armnn::TensorInfo(4, inputShape, armnn::DataType::Float32);
199 outputTensorInfo = armnn::TensorInfo(4, outputShape, armnn::DataType::Float32);
201 SplitterQueueDescriptor invalidData;
202 WorkloadInfo invalidInfo;
204 AddInputToWorkload(invalidData, invalidInfo, inputTensorInfo, nullptr);
205 AddOutputToWorkload(invalidData, invalidInfo, outputTensorInfo, nullptr);
207 // Invalid, since it has only 3 dimensions while the input tensor is 4d.
208 std::vector<unsigned int> wOrigin = {0, 0, 0};
209 armnn::SplitterQueueDescriptor::ViewOrigin window(wOrigin);
210 invalidData.m_ViewOrigins.push_back(window);
212 BOOST_TEST_INFO("Invalid argument exception is expected, because split window dimensionality does not "
214 BOOST_CHECK_THROW(RefSplitterWorkload(invalidData, invalidInfo), armnn::InvalidArgumentException);
216 // Invalid, since window extends past the boundary of input tensor.
217 std::vector<unsigned int> wOrigin3 = {0, 0, 15, 0};
218 armnn::SplitterQueueDescriptor::ViewOrigin window3(wOrigin3);
219 invalidData.m_ViewOrigins[0] = window3;
220 BOOST_TEST_INFO("Invalid argument exception is expected (wOrigin3[2]+ outputHeight > inputHeight");
221 BOOST_CHECK_THROW(RefSplitterWorkload(invalidData, invalidInfo), armnn::InvalidArgumentException);
224 std::vector<unsigned int> wOrigin4 = {0, 0, 0, 0};
225 armnn::SplitterQueueDescriptor::ViewOrigin window4(wOrigin4);
226 invalidData.m_ViewOrigins[0] = window4;
228 std::vector<unsigned int> wOrigin5 = {1, 16, 20, 2};
229 armnn::SplitterQueueDescriptor::ViewOrigin window5(wOrigin4);
230 invalidData.m_ViewOrigins.push_back(window5);
232 BOOST_TEST_INFO("Invalid exception due to number of split windows not matching number of outputs.");
233 BOOST_CHECK_THROW(RefSplitterWorkload(invalidData, invalidInfo), armnn::InvalidArgumentException);
237 BOOST_AUTO_TEST_CASE(ConcatQueueDescriptor_Validate_WrongWindow)
239 constexpr unsigned int inputNum = 1;
240 constexpr unsigned int inputChannels = 3;
241 constexpr unsigned int inputHeight = 32;
242 constexpr unsigned int inputWidth = 24;
244 constexpr unsigned int outputNum = 1;
245 constexpr unsigned int outputChannels = 3;
246 constexpr unsigned int outputHeight = 32;
247 constexpr unsigned int outputWidth = 24;
250 armnn::TensorInfo inputTensorInfo;
251 armnn::TensorInfo outputTensorInfo;
253 unsigned int inputShape[] = {inputNum, inputChannels, inputHeight, inputWidth};
254 unsigned int outputShape[] = {outputNum, outputChannels, outputHeight, outputWidth};
256 inputTensorInfo = armnn::TensorInfo(4, inputShape, armnn::DataType::Float32);
257 outputTensorInfo = armnn::TensorInfo(4, outputShape, armnn::DataType::Float32);
259 ConcatQueueDescriptor invalidData;
260 WorkloadInfo invalidInfo;
262 AddInputToWorkload(invalidData, invalidInfo, inputTensorInfo, nullptr);
263 AddOutputToWorkload(invalidData, invalidInfo, outputTensorInfo, nullptr);
265 // Invalid, since it has only 3 dimensions while the input tensor is 4d.
266 std::vector<unsigned int> wOrigin = {0, 0, 0};
267 armnn::ConcatQueueDescriptor::ViewOrigin window(wOrigin);
268 invalidData.m_ViewOrigins.push_back(window);
270 BOOST_TEST_INFO("Invalid argument exception is expected, because merge window dimensionality does not "
272 BOOST_CHECK_THROW(RefConcatWorkload(invalidData, invalidInfo), armnn::InvalidArgumentException);
274 // Invalid, since window extends past the boundary of output tensor.
275 std::vector<unsigned int> wOrigin3 = {0, 0, 15, 0};
276 armnn::ConcatQueueDescriptor::ViewOrigin window3(wOrigin3);
277 invalidData.m_ViewOrigins[0] = window3;
278 BOOST_TEST_INFO("Invalid argument exception is expected (wOrigin3[2]+ inputHeight > outputHeight");
279 BOOST_CHECK_THROW(RefConcatWorkload(invalidData, invalidInfo), armnn::InvalidArgumentException);
282 std::vector<unsigned int> wOrigin4 = {0, 0, 0, 0};
283 armnn::ConcatQueueDescriptor::ViewOrigin window4(wOrigin4);
284 invalidData.m_ViewOrigins[0] = window4;
286 std::vector<unsigned int> wOrigin5 = {1, 16, 20, 2};
287 armnn::ConcatQueueDescriptor::ViewOrigin window5(wOrigin4);
288 invalidData.m_ViewOrigins.push_back(window5);
290 BOOST_TEST_INFO("Invalid exception due to number of merge windows not matching number of inputs.");
291 BOOST_CHECK_THROW(RefConcatWorkload(invalidData, invalidInfo), armnn::InvalidArgumentException);
294 BOOST_AUTO_TEST_CASE(AdditionQueueDescriptor_Validate_InputNumbers)
296 armnn::TensorInfo input1TensorInfo;
297 armnn::TensorInfo input2TensorInfo;
298 armnn::TensorInfo input3TensorInfo;
299 armnn::TensorInfo outputTensorInfo;
301 unsigned int shape[] = {1, 1, 1, 1};
303 input1TensorInfo = armnn::TensorInfo(4, shape, armnn::DataType::Float32);
304 input2TensorInfo = armnn::TensorInfo(4, shape, armnn::DataType::Float32);
305 input3TensorInfo = armnn::TensorInfo(4, shape, armnn::DataType::Float32);
306 outputTensorInfo = armnn::TensorInfo(4, shape, armnn::DataType::Float32);
308 AdditionQueueDescriptor invalidData;
309 WorkloadInfo invalidInfo;
311 AddInputToWorkload(invalidData, invalidInfo, input1TensorInfo, nullptr);
312 AddOutputToWorkload(invalidData, invalidInfo, outputTensorInfo, nullptr);
315 BOOST_CHECK_THROW(RefAdditionWorkload(invalidData, invalidInfo), armnn::InvalidArgumentException);
317 AddInputToWorkload(invalidData, invalidInfo, input2TensorInfo, nullptr);
320 BOOST_CHECK_NO_THROW(RefAdditionWorkload(invalidData, invalidInfo));
322 AddInputToWorkload(invalidData, invalidInfo, input3TensorInfo, nullptr);
325 BOOST_CHECK_THROW(RefAdditionWorkload(invalidData, invalidInfo), armnn::InvalidArgumentException);
328 BOOST_AUTO_TEST_CASE(AdditionQueueDescriptor_Validate_InputShapes)
330 armnn::TensorInfo input1TensorInfo;
331 armnn::TensorInfo input2TensorInfo;
332 armnn::TensorInfo outputTensorInfo;
334 unsigned int shape1[] = {1, 1, 2, 1};
335 unsigned int shape2[] = {1, 1, 3, 2};
337 // Incompatible shapes even with broadcasting.
339 input1TensorInfo = armnn::TensorInfo(4, shape1, armnn::DataType::Float32);
340 input2TensorInfo = armnn::TensorInfo(4, shape2, armnn::DataType::Float32);
341 outputTensorInfo = armnn::TensorInfo(4, shape1, armnn::DataType::Float32);
343 AdditionQueueDescriptor invalidData;
344 WorkloadInfo invalidInfo;
346 AddInputToWorkload(invalidData, invalidInfo, input1TensorInfo, nullptr);
347 AddInputToWorkload(invalidData, invalidInfo, input2TensorInfo, nullptr);
348 AddOutputToWorkload(invalidData, invalidInfo, outputTensorInfo, nullptr);
350 BOOST_CHECK_THROW(RefAdditionWorkload(invalidData, invalidInfo), armnn::InvalidArgumentException);
353 // Output size not compatible with input sizes.
355 input1TensorInfo = armnn::TensorInfo(4, shape1, armnn::DataType::Float32);
356 input2TensorInfo = armnn::TensorInfo(4, shape1, armnn::DataType::Float32);
357 outputTensorInfo = armnn::TensorInfo(4, shape2, armnn::DataType::Float32);
359 AdditionQueueDescriptor invalidData;
360 WorkloadInfo invalidInfo;
362 AddInputToWorkload(invalidData, invalidInfo, input1TensorInfo, nullptr);
363 AddInputToWorkload(invalidData, invalidInfo, input2TensorInfo, nullptr);
364 AddOutputToWorkload(invalidData, invalidInfo, outputTensorInfo, nullptr);
367 BOOST_CHECK_THROW(RefAdditionWorkload(invalidData, invalidInfo), armnn::InvalidArgumentException);
371 BOOST_AUTO_TEST_CASE(MultiplicationQueueDescriptor_Validate_InputTensorDimensionMismatch)
373 armnn::TensorInfo input0TensorInfo;
374 armnn::TensorInfo input1TensorInfo;
375 armnn::TensorInfo outputTensorInfo;
377 constexpr unsigned int input0Shape[] = { 2, 2, 4, 4 };
378 constexpr std::size_t dimensionCount = std::extent<decltype(input0Shape)>::value;
380 // Checks dimension consistency for input tensors.
381 for (unsigned int dimIndex = 0; dimIndex < dimensionCount; ++dimIndex)
383 unsigned int input1Shape[dimensionCount];
384 for (unsigned int i = 0; i < dimensionCount; ++i)
386 input1Shape[i] = input0Shape[i];
389 ++input1Shape[dimIndex];
391 input0TensorInfo = armnn::TensorInfo(dimensionCount, input0Shape, armnn::DataType::Float32);
392 input1TensorInfo = armnn::TensorInfo(dimensionCount, input1Shape, armnn::DataType::Float32);
393 outputTensorInfo = armnn::TensorInfo(dimensionCount, input0Shape, armnn::DataType::Float32);
395 MultiplicationQueueDescriptor invalidData;
396 WorkloadInfo invalidInfo;
398 AddOutputToWorkload(invalidData, invalidInfo, outputTensorInfo, nullptr);
399 AddInputToWorkload(invalidData, invalidInfo, input0TensorInfo, nullptr);
400 AddInputToWorkload(invalidData, invalidInfo, input1TensorInfo, nullptr);
402 BOOST_CHECK_THROW(RefMultiplicationWorkload(invalidData, invalidInfo), armnn::InvalidArgumentException);
405 // Checks dimension consistency for input and output tensors.
406 for (unsigned int dimIndex = 0; dimIndex < dimensionCount; ++dimIndex)
408 unsigned int outputShape[dimensionCount];
409 for (unsigned int i = 0; i < dimensionCount; ++i)
411 outputShape[i] = input0Shape[i];
414 ++outputShape[dimIndex];
416 input0TensorInfo = armnn::TensorInfo(dimensionCount, input0Shape, armnn::DataType::Float32);
417 input1TensorInfo = armnn::TensorInfo(dimensionCount, input0Shape, armnn::DataType::Float32);
418 outputTensorInfo = armnn::TensorInfo(dimensionCount, outputShape, armnn::DataType::Float32);
420 MultiplicationQueueDescriptor invalidData;
421 WorkloadInfo invalidInfo;
423 AddOutputToWorkload(invalidData, invalidInfo, outputTensorInfo, nullptr);
424 AddInputToWorkload(invalidData, invalidInfo, input0TensorInfo, nullptr);
425 AddInputToWorkload(invalidData, invalidInfo, input1TensorInfo, nullptr);
427 BOOST_CHECK_THROW(RefMultiplicationWorkload(invalidData, invalidInfo), armnn::InvalidArgumentException);
431 BOOST_AUTO_TEST_CASE(ReshapeQueueDescriptor_Validate_MismatchingNumElements)
433 armnn::TensorInfo inputTensorInfo;
434 armnn::TensorInfo outputTensorInfo;
436 // The input and output shapes should have the same number of elements, but these don't.
437 unsigned int inputShape[] = { 1, 1, 2, 3 };
438 unsigned int outputShape[] = { 1, 1, 1, 2 };
440 inputTensorInfo = armnn::TensorInfo(4, inputShape, armnn::DataType::Float32);
441 outputTensorInfo = armnn::TensorInfo(4, outputShape, armnn::DataType::Float32);
443 ReshapeQueueDescriptor invalidData;
444 WorkloadInfo invalidInfo;
446 AddInputToWorkload(invalidData, invalidInfo, inputTensorInfo, nullptr);
447 AddOutputToWorkload(invalidData, invalidInfo, outputTensorInfo, nullptr);
449 // InvalidArgumentException is expected, because the number of elements don't match.
450 BOOST_CHECK_THROW(RefReshapeWorkload(invalidData, invalidInfo), armnn::InvalidArgumentException);
454 BOOST_AUTO_TEST_CASE(LstmQueueDescriptor_Validate)
456 armnn::DataType dataType = armnn::DataType::Float32;
461 unsigned int batchSize = 2;
462 unsigned int outputSize = 3;
463 unsigned int inputSize = 5;
464 unsigned numUnits = 4;
466 armnn::TensorInfo inputTensorInfo({batchSize , inputSize}, dataType, qScale, qOffset );
467 armnn::TensorInfo outputStateInTensorInfo({batchSize , outputSize}, dataType, qScale, qOffset);
468 armnn::TensorInfo cellStateInTensorInfo({batchSize , numUnits}, dataType, qScale, qOffset);
470 // Scratch buffer size with CIFG [batchSize, numUnits * 4]
471 armnn::TensorInfo scratchBufferTensorInfo({batchSize, numUnits * 4}, dataType, qScale, qOffset);
472 armnn::TensorInfo cellStateOutTensorInfo({batchSize, numUnits}, dataType, qScale, qOffset);
473 armnn::TensorInfo outputStateOutTensorInfo({batchSize, outputSize}, dataType, qScale, qOffset);
474 armnn::TensorInfo outputTensorInfo({batchSize, outputSize}, dataType, qScale, qOffset);
476 armnn::TensorInfo tensorInfo3({outputSize}, dataType, qScale, qOffset);
477 armnn::TensorInfo tensorInfo4({numUnits}, dataType, qScale, qOffset);
478 armnn::TensorInfo tensorInfo4x5({numUnits, inputSize}, dataType, qScale, qOffset);
479 armnn::TensorInfo tensorInfo4x3({numUnits, outputSize}, dataType, qScale, qOffset);
480 armnn::TensorInfo tensorInfo3x4({outputSize, numUnits}, dataType, qScale, qOffset);
482 LstmQueueDescriptor data;
485 AddInputToWorkload(data, info, inputTensorInfo, nullptr);
486 AddInputToWorkload(data, info, outputStateInTensorInfo, nullptr);
487 AddInputToWorkload(data, info, cellStateInTensorInfo, nullptr);
489 AddOutputToWorkload(data, info, scratchBufferTensorInfo, nullptr);
490 AddOutputToWorkload(data, info, outputStateOutTensorInfo, nullptr);
491 AddOutputToWorkload(data, info, cellStateOutTensorInfo, nullptr);
492 // AddOutputToWorkload(data, info, outputTensorInfo, nullptr); is left out
494 armnn::ScopedCpuTensorHandle inputToInputWeightsTensor(tensorInfo4x5);
495 armnn::ScopedCpuTensorHandle inputToForgetWeightsTensor(tensorInfo4x5);
496 armnn::ScopedCpuTensorHandle inputToCellWeightsTensor(tensorInfo4x5);
497 armnn::ScopedCpuTensorHandle inputToOutputWeightsTensor(tensorInfo4x5);
498 armnn::ScopedCpuTensorHandle recurrentToForgetWeightsTensor(tensorInfo4x3);
499 armnn::ScopedCpuTensorHandle recurrentToInputWeightsTensor(tensorInfo4x3);
500 armnn::ScopedCpuTensorHandle recurrentToCellWeightsTensor(tensorInfo4x3);
501 armnn::ScopedCpuTensorHandle recurrentToOutputWeightsTensor(tensorInfo4x3);
502 armnn::ScopedCpuTensorHandle cellToInputWeightsTensor(tensorInfo4);
503 armnn::ScopedCpuTensorHandle inputGateBiasTensor(tensorInfo4);
504 armnn::ScopedCpuTensorHandle forgetGateBiasTensor(tensorInfo4);
505 armnn::ScopedCpuTensorHandle cellBiasTensor(tensorInfo4);
506 armnn::ScopedCpuTensorHandle outputGateBiasTensor(tensorInfo4);
507 armnn::ScopedCpuTensorHandle cellToForgetWeightsTensor(tensorInfo4);
508 armnn::ScopedCpuTensorHandle cellToOutputWeightsTensor(tensorInfo4);
509 armnn::ScopedCpuTensorHandle projectionWeightsTensor(tensorInfo3x4);
510 armnn::ScopedCpuTensorHandle projectionBiasTensor(tensorInfo3);
511 armnn::ScopedCpuTensorHandle inputLayerNormWeightsTensor(tensorInfo4);
512 armnn::ScopedCpuTensorHandle forgetLayerNormWeightsTensor(tensorInfo4);
513 armnn::ScopedCpuTensorHandle cellLayerNormWeightsTensor(tensorInfo4);
514 armnn::ScopedCpuTensorHandle outputLayerNormWeightsTensor(tensorInfo4);
516 data.m_InputToInputWeights = &inputToInputWeightsTensor;
517 data.m_InputToForgetWeights = &inputToForgetWeightsTensor;
518 data.m_InputToCellWeights = &inputToCellWeightsTensor;
519 data.m_InputToOutputWeights = &inputToOutputWeightsTensor;
520 data.m_RecurrentToInputWeights = &recurrentToInputWeightsTensor;
521 data.m_RecurrentToForgetWeights = &recurrentToForgetWeightsTensor;
522 data.m_RecurrentToCellWeights = &recurrentToCellWeightsTensor;
523 data.m_RecurrentToOutputWeights = &recurrentToOutputWeightsTensor;
524 data.m_CellToInputWeights = &cellToInputWeightsTensor;
525 data.m_InputGateBias = &inputGateBiasTensor;
526 data.m_ForgetGateBias = &forgetGateBiasTensor;
527 data.m_CellBias = &cellBiasTensor;
528 data.m_OutputGateBias = &outputGateBiasTensor;
529 data.m_CellToForgetWeights = &cellToForgetWeightsTensor;
530 data.m_CellToOutputWeights = &cellToOutputWeightsTensor;
531 data.m_ProjectionWeights = &projectionWeightsTensor;
532 data.m_ProjectionBias = &projectionBiasTensor;
534 data.m_InputLayerNormWeights = &inputLayerNormWeightsTensor;
535 data.m_ForgetLayerNormWeights = &forgetLayerNormWeightsTensor;
536 data.m_CellLayerNormWeights = &cellLayerNormWeightsTensor;
537 data.m_OutputLayerNormWeights = &outputLayerNormWeightsTensor;
539 // Flags to set test configuration
540 data.m_Parameters.m_ActivationFunc = 4;
541 data.m_Parameters.m_CifgEnabled = false;
542 data.m_Parameters.m_PeepholeEnabled = true;
543 data.m_Parameters.m_ProjectionEnabled = true;
544 data.m_Parameters.m_LayerNormEnabled = true;
546 // check wrong number of outputs
547 BOOST_CHECK_THROW(data.Validate(info), armnn::InvalidArgumentException);
548 AddOutputToWorkload(data, info, outputTensorInfo, nullptr);
550 // check wrong cifg parameter configuration
551 data.m_Parameters.m_CifgEnabled = true;
552 armnn::TensorInfo scratchBufferTensorInfo2({batchSize, numUnits * 3}, dataType, qScale, qOffset);
553 SetWorkloadOutput(data, info, 0, scratchBufferTensorInfo2, nullptr);
554 BOOST_CHECK_THROW(data.Validate(info), armnn::InvalidArgumentException);
555 data.m_Parameters.m_CifgEnabled = false;
556 SetWorkloadOutput(data, info, 0, scratchBufferTensorInfo, nullptr);
558 // check wrong inputGateBias configuration
559 data.m_InputGateBias = nullptr;
560 BOOST_CHECK_THROW(data.Validate(info), armnn::InvalidArgumentException);
561 data.m_InputGateBias = &inputGateBiasTensor;
563 // check inconsistant projection parameters
564 data.m_Parameters.m_ProjectionEnabled = false;
565 BOOST_CHECK_THROW(data.Validate(info), armnn::InvalidArgumentException);
566 data.m_Parameters.m_ProjectionEnabled = true;
567 data.m_ProjectionWeights = nullptr;
568 BOOST_CHECK_THROW(data.Validate(info), armnn::InvalidArgumentException);
569 data.m_ProjectionWeights = &projectionWeightsTensor;
571 // check missing input layer normalisation weights
572 data.m_InputLayerNormWeights = nullptr;
573 BOOST_CHECK_THROW(data.Validate(info), armnn::InvalidArgumentException);
574 data.m_InputLayerNormWeights = &inputLayerNormWeightsTensor;
576 // layer norm disabled but normalisation weights are present
577 data.m_Parameters.m_LayerNormEnabled = false;
578 BOOST_CHECK_THROW(data.Validate(info), armnn::InvalidArgumentException);
579 data.m_Parameters.m_LayerNormEnabled = true;
581 // check invalid outputTensor shape
582 armnn::TensorInfo incorrectOutputTensorInfo({batchSize, outputSize + 1}, dataType, qScale, qOffset);
583 SetWorkloadOutput(data, info, 3, incorrectOutputTensorInfo, nullptr);
584 BOOST_CHECK_THROW(data.Validate(info), armnn::InvalidArgumentException);
585 SetWorkloadOutput(data, info, 3, outputTensorInfo, nullptr);
587 // check invalid cell clipping parameters
588 data.m_Parameters.m_ClippingThresCell = -1.0f;
589 BOOST_CHECK_THROW(data.Validate(info), armnn::InvalidArgumentException);
590 data.m_Parameters.m_ClippingThresCell = 0.0f;
592 // check invalid projection clipping parameters
593 data.m_Parameters.m_ClippingThresProj = -1.0f;
594 BOOST_CHECK_THROW(data.Validate(info), armnn::InvalidArgumentException);
595 data.m_Parameters.m_ClippingThresProj = 0.0f;
597 // check correct configuration
598 BOOST_CHECK_NO_THROW(data.Validate(info));
601 BOOST_AUTO_TEST_CASE(BiasPerAxisQuantization_Validate)
603 constexpr unsigned int nInput = 1u;
604 constexpr unsigned int cInput = 3u;
605 constexpr unsigned int hInput = 3u;
606 constexpr unsigned int wInput = 3u;
608 constexpr unsigned int nOutput = nInput;
609 constexpr unsigned int cOutput = cInput;
610 constexpr unsigned int hOutput = 1u;
611 constexpr unsigned int wOutput = 1u;
613 const TensorShape inputShape { nInput, cInput, hInput, wInput };
614 const TensorShape outputShape{ nOutput, cOutput, hOutput, wOutput };
615 const TensorShape weightShape{ cOutput, cInput, hInput, wInput };
616 const TensorShape biasShape { cOutput };
618 constexpr DataType inputType = DataType::QuantisedAsymm8;
619 constexpr DataType weightType = DataType::QuantizedSymm8PerAxis;
620 constexpr DataType biasType = DataType::Signed32;
622 constexpr float perTensorScale = 1.5f;
623 const TensorInfo inputInfo (inputShape, inputType, perTensorScale);
624 const TensorInfo outputInfo(outputShape, inputType, perTensorScale);
626 const std::vector<float> weightPerAxisScales = { 2.50f, 3.50f };
627 const TensorInfo weightInfo(weightShape, weightType, weightPerAxisScales, 0);
629 Convolution2dQueueDescriptor queueDescriptor;
630 queueDescriptor.m_Parameters.m_BiasEnabled = true;
632 WorkloadInfo workloadInfo;
633 AddInputToWorkload(queueDescriptor, workloadInfo, inputInfo, nullptr);
634 AddOutputToWorkload(queueDescriptor, workloadInfo, outputInfo, nullptr);
636 ScopedCpuTensorHandle weightTensor(weightInfo);
637 queueDescriptor.m_Weight = &weightTensor;
639 // Test 1: correct per-axis quantization values
640 const std::vector<float> biasPerAxisScales1 = { 3.75f, 5.25f };
641 const TensorInfo biasInfo1(biasShape, biasType, biasPerAxisScales1, 0);
643 ScopedCpuTensorHandle biasHandle1(biasInfo1);
644 queueDescriptor.m_Bias = &biasHandle1;
646 BOOST_CHECK_NO_THROW(queueDescriptor.Validate(workloadInfo));
648 // Test 2: wrong per-axis quantization values
649 const std::vector<float> biasPerAxisScales2 = { 4.00f, 5.00f };
650 const TensorInfo biasInfo2(biasShape, biasType, biasPerAxisScales2, 0);
652 ScopedCpuTensorHandle biasHandle2(biasInfo2);
653 queueDescriptor.m_Bias = &biasHandle2;
655 BOOST_CHECK_THROW(queueDescriptor.Validate(workloadInfo), InvalidArgumentException);
657 // Test 3: mismatched number of quantization scales
658 const std::vector<float> biasPerAxisScales3 = { 3.75f, 5.25f, 5.25f };
659 const TensorInfo biasInfo3(biasShape, biasType, biasPerAxisScales3, 0);
661 ScopedCpuTensorHandle biasHandle3(biasInfo3);
662 queueDescriptor.m_Bias = &biasHandle3;
664 BOOST_CHECK_THROW(queueDescriptor.Validate(workloadInfo), InvalidArgumentException);
667 BOOST_AUTO_TEST_SUITE_END()