2 // Copyright © 2017 Arm Ltd. All rights reserved.
3 // SPDX-License-Identifier: MIT
7 #include <armnn/ArmNN.hpp>
8 #include <armnn/Tensor.hpp>
9 #include <armnn/TypesUtils.hpp>
11 #include <test/TensorHelpers.hpp>
12 #include "QuantizeHelper.hpp"
14 #include <backends/CpuTensorHandle.hpp>
15 #include <backends/WorkloadFactory.hpp>
16 #include "ActivationFixture.hpp"
21 LayerTestResult<T, 4> BoundedReLuTestCommon(armnn::IWorkloadFactory& workloadFactory,
22 float upperBound, float lowerBound,
23 float inputScale, int32_t inputOffset, float outputScale, int32_t outputOffset,
24 const std::vector<T>& inputData, const std::vector<T>& outputExpectedData,
25 unsigned int inputWidth, unsigned int inputHeight,
26 unsigned int inputChannels, unsigned int inputBatchSize)
28 unsigned int outputWidth = inputWidth;
29 unsigned int outputHeight = inputHeight;
30 unsigned int outputChannels = inputChannels;
31 unsigned int outputBatchSize = inputBatchSize;
33 armnn::TensorInfo inputTensorInfo({ inputBatchSize, inputChannels, inputHeight, inputWidth },
34 armnn::GetDataType<T>());
36 armnn::TensorInfo outputTensorInfo({ outputBatchSize, outputChannels, outputHeight, outputWidth },
37 armnn::GetDataType<T>());
39 if(armnn::IsQuantizedType<T>())
41 inputTensorInfo.SetQuantizationScale(inputScale);
42 inputTensorInfo.SetQuantizationOffset(inputOffset);
44 outputTensorInfo.SetQuantizationScale(outputScale);
45 outputTensorInfo.SetQuantizationOffset(outputOffset);
48 LayerTestResult<T, 4> result(inputTensorInfo);
50 auto input = MakeTensor<T, 4>(inputTensorInfo, inputData);
52 std::unique_ptr<armnn::ITensorHandle> inputHandle = workloadFactory.CreateTensorHandle(inputTensorInfo);
53 std::unique_ptr<armnn::ITensorHandle> outputHandle = workloadFactory.CreateTensorHandle(outputTensorInfo);
55 // Setup bounded ReLu.
56 armnn::ActivationQueueDescriptor descriptor;
57 armnn::WorkloadInfo workloadInfo;
58 AddInputToWorkload(descriptor, workloadInfo, inputTensorInfo, inputHandle.get());
59 AddOutputToWorkload(descriptor, workloadInfo, outputTensorInfo, outputHandle.get());
61 descriptor.m_Parameters.m_Function = armnn::ActivationFunction::BoundedReLu;
62 descriptor.m_Parameters.m_A = upperBound;
63 descriptor.m_Parameters.m_B = lowerBound;
65 std::unique_ptr<armnn::IWorkload> workload = workloadFactory.CreateActivation(descriptor, workloadInfo);
67 inputHandle->Allocate();
68 outputHandle->Allocate();
70 CopyDataToITensorHandle(inputHandle.get(), &input[0][0][0][0]);
74 CopyDataFromITensorHandle(&result.output[0][0][0][0], outputHandle.get());
76 result.outputExpected = MakeTensor<T, 4>(outputTensorInfo, outputExpectedData);
81 LayerTestResult<float, 4> BoundedReLuUpperAndLowerBoundTest(armnn::IWorkloadFactory& workloadFactory)
83 unsigned int inputWidth = 4u;
84 unsigned int inputHeight = 5u;
85 unsigned int inputChannels = 1u;
86 unsigned int inputBatchSize = 1;
88 std::vector<float> input = std::vector<float>{
89 -2.0f, 0.1f, 0.5f, 1.25f,
90 0.786f, 0.9875f, -1.5f, 0.384f,
91 1.0001f, 3.5f, 7.5f, 0.896f,
92 2.126f, 2.0f, 0.3f, 0.15f,
93 0.999f, 1.2f, 0.89f, 6.1f,
96 // Calculated manually.
97 std::vector<float> output = std::vector<float>{
98 -1.0f, 0.1f, 0.5f, 1.0f,
99 0.786f, 0.9875f, -1.0f, 0.384f,
100 1.0f, 1.0f, 1.0f, 0.896f,
101 1.0f, 1.0f, 0.3f, 0.15f,
102 0.999f, 1.0f, 0.89f, 1.0f,
105 return BoundedReLuTestCommon(workloadFactory, 1.0f, -1.0f, 1.0f, 0, 1.0f, 0, input, output,
106 inputWidth, inputHeight, inputChannels, inputBatchSize);
109 LayerTestResult<float, 4> BoundedReLuUpperBoundOnlyTest(armnn::IWorkloadFactory& workloadFactory)
111 unsigned int inputWidth = 4u;
112 unsigned int inputHeight = 5u;
113 unsigned int inputChannels = 1u;
114 unsigned int inputBatchSize = 1;
116 std::vector<float> input = std::vector<float>{
117 -1.0f, 0.1f, 0.5f, 6.25f,
118 0.786f, 5.9875f, -0.5f, 0.384f,
119 6.0001f, 3.5f, 7.5f, 0.896f,
120 2.126f, 12.0f, 0.3f, 0.15f,
121 0.999f, 1.2f, 0.89f, 6.1f,
124 // Calculated manually.
125 std::vector<float> output = std::vector<float>{
126 0.0f, 0.1f, 0.5f, 6.0f,
127 0.786f, 5.9875f, 0.0f, 0.384f,
128 6.0f, 3.5f, 6.0f, 0.896f,
129 2.126f, 6.0f, 0.3f, 0.15f,
130 0.999f, 1.2f, 0.89f, 6.0f,
133 return BoundedReLuTestCommon(workloadFactory, 6.0f, 0.0f, 1.0f, 0, 1.0f, 0, input, output,
134 inputWidth, inputHeight, inputChannels, inputBatchSize);
137 LayerTestResult<uint8_t, 4> BoundedReLuUint8UpperBoundOnlyTest(armnn::IWorkloadFactory& workloadFactory)
139 unsigned int inputWidth = 3u;
140 unsigned int inputHeight = 2u;
141 unsigned int inputChannels = 1u;
142 unsigned int inputBatchSize = 1;
144 std::vector<uint8_t> input = std::vector<uint8_t>{
149 // Calculated manually.
150 std::vector<uint8_t> output = std::vector<uint8_t>{
155 float inputScale = 12.0f / 255.0f;
156 int32_t inputOffset = 63;
157 float outputScale = 6.0f / 255.0f;
158 int32_t outputOffset = 0;
160 return BoundedReLuTestCommon(workloadFactory, 6.0f, 0.0f,
161 inputScale, inputOffset, outputScale, outputOffset,
163 inputWidth, inputHeight, inputChannels, inputBatchSize);
166 LayerTestResult<uint8_t, 4> BoundedReLuUint8UpperAndLowerBoundTest(armnn::IWorkloadFactory& workloadFactory)
168 unsigned int inputWidth = 3u;
169 unsigned int inputHeight = 2u;
170 unsigned int inputChannels = 1u;
171 unsigned int inputBatchSize = 1;
173 std::vector<uint8_t> input = std::vector<uint8_t>{
178 // Calculated manually.
179 std::vector<uint8_t> output = std::vector<uint8_t>{
184 int32_t inputOffset = 112;
185 float inputScale = 0.0125f;
187 return BoundedReLuTestCommon(workloadFactory, 1.0f, -1.0f,
188 inputScale, inputOffset, inputScale, inputOffset, // Input/output scale & offset same.
190 inputWidth, inputHeight, inputChannels, inputBatchSize);
196 struct BoundedReLuRandomInputTestTraits
198 constexpr static unsigned int inputHeight = 31u;
199 constexpr static unsigned int inputWidth = 19u;
200 constexpr static unsigned int inputChannels = 4u;
201 constexpr static unsigned int inputBatchSize = 2;
203 constexpr static unsigned int outputHeight = inputHeight;
204 constexpr static unsigned int outputWidth = inputWidth;
205 constexpr static unsigned int outputChannels = inputChannels;
206 constexpr static unsigned int outputBatchSize = inputBatchSize;
208 static armnn::TensorInfo GetInputTensorInfo()
210 return armnn::TensorInfo({ inputBatchSize, inputChannels, inputHeight, inputWidth },
211 armnn::DataType::Float32);
214 static armnn::TensorInfo GetOutputTensorInfo()
216 return armnn::TensorInfo({ outputBatchSize, outputChannels, outputHeight, outputWidth },
217 armnn::DataType::Float32);
221 boost::multi_array<float, 4> BoundedReLuRandomInputTest(armnn::IWorkloadFactory& workloadFactory,
224 const armnn::ActivationDescriptor& activationDescriptor)
226 const armnn::TensorInfo inputTensorInfo = BoundedReLuRandomInputTestTraits::GetInputTensorInfo();
227 const armnn::TensorInfo outputTensorInfo = BoundedReLuRandomInputTestTraits::GetOutputTensorInfo();
229 boost::multi_array<float, 4> output(GetTensorShapeAsArray<4>(outputTensorInfo));
231 // Min/max random values passed to MakeRandomTensor are purposely outside of the ReLu
232 // range [lowerBound, upperBound].
233 auto input = MakeRandomTensor<float, 4>(inputTensorInfo, 4605828, lowerBound - 5.0f, upperBound * 2.0f);
235 std::unique_ptr<armnn::ITensorHandle> inputHandle = workloadFactory.CreateTensorHandle(inputTensorInfo);
236 std::unique_ptr<armnn::ITensorHandle> outputHandle = workloadFactory.CreateTensorHandle(outputTensorInfo);
238 // Set up bounded ReLu.
239 armnn::ActivationQueueDescriptor descriptor;
240 armnn::WorkloadInfo workloadInfo;
241 AddInputToWorkload(descriptor, workloadInfo, inputTensorInfo, inputHandle.get());
242 AddOutputToWorkload(descriptor, workloadInfo, outputTensorInfo, outputHandle.get());
243 descriptor.m_Parameters = activationDescriptor;
245 std::unique_ptr<armnn::IWorkload> workload = workloadFactory.CreateActivation(descriptor, workloadInfo);
247 inputHandle->Allocate();
248 outputHandle->Allocate();
250 CopyDataToITensorHandle(inputHandle.get(), &input[0][0][0][0]);
254 CopyDataFromITensorHandle(&output[0][0][0][0], outputHandle.get());
261 LayerTestResult<float, 4> CompareBoundedReLuTest(armnn::IWorkloadFactory& workloadFactory,
262 armnn::IWorkloadFactory& otherWorkloadFactory,
266 LayerTestResult<float, 4> result(BoundedReLuRandomInputTestTraits::GetOutputTensorInfo());
268 armnn::ActivationDescriptor activationDescriptor;
269 activationDescriptor.m_Function = armnn::ActivationFunction::BoundedReLu;
270 activationDescriptor.m_A = upperBound;
271 activationDescriptor.m_B = lowerBound;
273 result.output = BoundedReLuRandomInputTest(workloadFactory, 0.0f, upperBound, activationDescriptor);
274 result.outputExpected = BoundedReLuRandomInputTest(otherWorkloadFactory, 0.0f, upperBound, activationDescriptor);
280 LayerTestResult<T,4> ConstantLinearActivationTestCommon(armnn::IWorkloadFactory& workloadFactory,
284 unsigned int inputHeight = 20;
285 unsigned int inputWidth = 17;
286 unsigned int inputChannels = 3;
287 unsigned int batchSize = 5;
289 armnn::TensorInfo inputTensorInfo;
290 armnn::TensorInfo outputTensorInfo;
292 unsigned int shape[] = {batchSize, inputChannels, inputHeight, inputWidth};
294 inputTensorInfo = armnn::TensorInfo(4, shape, armnn::GetDataType<T>());
295 outputTensorInfo = armnn::TensorInfo(4, shape, armnn::GetDataType<T>());
297 // Set quantization parameters if the requested type is a quantized type.
298 if(armnn::IsQuantizedType<T>())
300 inputTensorInfo.SetQuantizationScale(qScale);
301 inputTensorInfo.SetQuantizationOffset(qOffset);
302 outputTensorInfo.SetQuantizationScale(qScale);
303 outputTensorInfo.SetQuantizationOffset(qOffset);
306 LayerTestResult<T, 4> ret(outputTensorInfo);
308 std::unique_ptr<armnn::ITensorHandle> inputHandle = workloadFactory.CreateTensorHandle(inputTensorInfo);
309 std::unique_ptr<armnn::ITensorHandle> outputHandle = workloadFactory.CreateTensorHandle(outputTensorInfo);
311 // Do linear activation that should leave the tensor unchanged.
312 armnn::ActivationQueueDescriptor data;
313 armnn::WorkloadInfo info;
314 AddInputToWorkload(data, info, inputTensorInfo, inputHandle.get());
315 AddOutputToWorkload(data, info, outputTensorInfo, outputHandle.get());
316 data.m_Parameters.m_A = 1.0f;
317 data.m_Parameters.m_B = 0.0f;
318 data.m_Parameters.m_Function = armnn::ActivationFunction::Linear;
320 std::unique_ptr<armnn::IWorkload> workload = workloadFactory.CreateActivation(data, info);
322 inputHandle->Allocate();
323 outputHandle->Allocate();
325 boost::multi_array<T, 4> input = MakeRandomTensor<T, 4>(inputTensorInfo, 7123561);
326 CopyDataToITensorHandle(inputHandle.get(), &input[0][0][0][0]);
330 CopyDataFromITensorHandle(&ret.output[0][0][0][0], outputHandle.get());
332 // Ensure output equals input.
333 ret.outputExpected = input;
338 LayerTestResult<float, 4> ConstantLinearActivationTest(armnn::IWorkloadFactory& workloadFactory)
340 return ConstantLinearActivationTestCommon<float>(workloadFactory);
343 LayerTestResult<uint8_t, 4> ConstantLinearActivationUint8Test(armnn::IWorkloadFactory& workloadFactory)
345 return ConstantLinearActivationTestCommon<uint8_t>(workloadFactory, 4.0f, 3);
349 LayerTestResult<T, 4> SimpleActivationTest(armnn::IWorkloadFactory& workloadFactory,
350 armnn::ActivationFunction activationFunction,
351 float activationParameterA,
352 float activationParameterB,
355 const std::vector<float>& inputData,
356 const std::vector<float>& outputExpectedData)
358 constexpr static unsigned int inputWidth = 16u;
359 constexpr static unsigned int inputHeight = 1u;
360 constexpr static unsigned int inputChannels = 1u;
361 constexpr static unsigned int inputBatchSize = 1u;
363 constexpr static unsigned int outputWidth = inputWidth;
364 constexpr static unsigned int outputHeight = inputHeight;
365 constexpr static unsigned int outputChannels = inputChannels;
366 constexpr static unsigned int outputBatchSize = inputBatchSize;
368 armnn::TensorInfo inputTensorInfo({ inputBatchSize, inputChannels, inputHeight, inputWidth },
369 armnn::GetDataType<T>());
370 armnn::TensorInfo outputTensorInfo({ outputBatchSize, outputChannels, outputHeight, outputWidth },
371 armnn::GetDataType<T>());
373 // Set quantization parameters if the requested type is a quantized type.
374 if(armnn::IsQuantizedType<T>())
376 inputTensorInfo.SetQuantizationScale(qScale);
377 inputTensorInfo.SetQuantizationOffset(qOffset);
378 outputTensorInfo.SetQuantizationScale(qScale);
379 outputTensorInfo.SetQuantizationOffset(qOffset);
382 LayerTestResult<T, 4> result(inputTensorInfo);
384 auto input = MakeTensor<T, 4>(inputTensorInfo, QuantizedVector<T>(qScale, qOffset, inputData));
386 std::unique_ptr<armnn::ITensorHandle> inputHandle = workloadFactory.CreateTensorHandle(inputTensorInfo);
387 std::unique_ptr<armnn::ITensorHandle> outputHandle = workloadFactory.CreateTensorHandle(outputTensorInfo);
389 // Setup bounded ReLu.
390 armnn::ActivationQueueDescriptor descriptor;
391 armnn::WorkloadInfo workloadInfo;
392 AddInputToWorkload(descriptor, workloadInfo, inputTensorInfo, inputHandle.get());
393 AddOutputToWorkload(descriptor, workloadInfo, outputTensorInfo, outputHandle.get());
395 descriptor.m_Parameters.m_Function = activationFunction;
396 descriptor.m_Parameters.m_A = activationParameterA;
397 descriptor.m_Parameters.m_B = activationParameterB;
399 std::unique_ptr<armnn::IWorkload> workload = workloadFactory.CreateActivation(descriptor, workloadInfo);
401 inputHandle->Allocate();
402 outputHandle->Allocate();
404 CopyDataToITensorHandle(inputHandle.get(), &input[0][0][0][0]);
408 CopyDataFromITensorHandle(&result.output[0][0][0][0], outputHandle.get());
410 // Calculated manually.
411 result.outputExpected = MakeTensor<T, 4>(outputTensorInfo, QuantizedVector<T>(qScale, qOffset, outputExpectedData));
417 LayerTestResult<T, 4> SimpleSigmoidTestCommon(armnn::IWorkloadFactory& workloadFactory, float qScale, int32_t qOffset)
419 std::vector<float> inputData = {
420 -0.1f, -0.2f, -0.3f, -0.4f,
421 0.1f, 0.2f, 0.3f, 0.4f,
422 -1.0f, -2.0f, -3.0f, -4.0f,
423 1.0f, 2.0f, 3.0f, 4.0f
426 // Calculate output values for input.
427 auto f = [](float value)
429 return 1.0f / (1.0f + std::exp(-value));
431 std::vector<float> outputExpectedData(inputData.size());
432 std::transform(inputData.begin(), inputData.end(), outputExpectedData.begin(), f);
434 return SimpleActivationTest<T>(workloadFactory,
435 armnn::ActivationFunction::Sigmoid,
444 LayerTestResult<float, 4> SimpleSigmoidTest(armnn::IWorkloadFactory& workloadFactory)
446 return SimpleSigmoidTestCommon<float>(workloadFactory, 0.0f, 0);
449 LayerTestResult<uint8_t, 4> SimpleSigmoidUint8Test(armnn::IWorkloadFactory& workloadFactory)
451 return SimpleSigmoidTestCommon<uint8_t>(workloadFactory, 0.1f, 50);
455 LayerTestResult<T,4> CompareActivationTestImpl(armnn::IWorkloadFactory& workloadFactory,
456 armnn::IWorkloadFactory& refWorkloadFactory,
457 armnn::ActivationFunction f,
458 unsigned int batchSize = 5,
462 unsigned int width = 17;
463 unsigned int height = 29;
464 unsigned int channels = 2;
469 armnn::TensorInfo inputTensorInfo;
470 armnn::TensorInfo outputTensorInfo;
472 unsigned int shape[] = {batchSize, channels, height, width};
474 inputTensorInfo = armnn::TensorInfo(4, shape, armnn::GetDataType<T>());
475 outputTensorInfo = armnn::TensorInfo(4, shape, armnn::GetDataType<T>());
477 // Set quantization parameters if the requested type is a quantized type.
478 if(armnn::IsQuantizedType<T>())
480 inputTensorInfo.SetQuantizationScale(qScale);
481 inputTensorInfo.SetQuantizationOffset(qOffset);
482 outputTensorInfo.SetQuantizationScale(qScale);
483 outputTensorInfo.SetQuantizationOffset(qOffset);
486 float minVal = -10.f;
487 if (f == armnn::ActivationFunction::Sqrt)
492 boost::multi_array<T, 4> input = MakeRandomTensor<T, 4>(inputTensorInfo, 21453, minVal, 10.f);
495 LayerTestResult<T,4> ret(outputTensorInfo);
496 auto boostArrayExtents = boost::extents
497 [boost::numeric_cast<boost::multi_array_types::extent_gen::index>(batchSize)]
498 [boost::numeric_cast<boost::multi_array_types::extent_gen::index>(channels)]
499 [boost::numeric_cast<boost::multi_array_types::extent_gen::index>(height)]
500 [boost::numeric_cast<boost::multi_array_types::extent_gen::index>(width)];
501 ret.output.resize(boostArrayExtents);
502 ret.outputExpected.resize(boostArrayExtents);
505 std::unique_ptr<armnn::ITensorHandle> inputHandle = workloadFactory.CreateTensorHandle(inputTensorInfo);
506 std::unique_ptr<armnn::ITensorHandle> outputHandle = workloadFactory.CreateTensorHandle(outputTensorInfo);
508 std::unique_ptr<armnn::ITensorHandle> inputHandleRef = refWorkloadFactory.CreateTensorHandle(inputTensorInfo);
509 std::unique_ptr<armnn::ITensorHandle> outputHandleRef = refWorkloadFactory.CreateTensorHandle(outputTensorInfo);
511 armnn::ActivationQueueDescriptor data;
512 armnn::WorkloadInfo info;
513 AddInputToWorkload(data, info, inputTensorInfo, inputHandle.get());
514 AddOutputToWorkload(data, info, outputTensorInfo, outputHandle.get());
515 data.m_Parameters.m_A = a;
516 data.m_Parameters.m_B = b;
517 data.m_Parameters.m_Function = f;
519 armnn::ActivationQueueDescriptor refData = data;
520 armnn::WorkloadInfo refInfo = info;
521 SetWorkloadInput(refData, refInfo, 0, inputTensorInfo, inputHandleRef.get());
522 SetWorkloadOutput(refData, refInfo, 0, outputTensorInfo, outputHandleRef.get());
524 std::unique_ptr<armnn::IWorkload> workload = workloadFactory.CreateActivation(data, info);
525 BOOST_ASSERT(workload != nullptr);
526 std::unique_ptr<armnn::IWorkload> workloadRef = refWorkloadFactory.CreateActivation(refData, refInfo);
527 BOOST_ASSERT(workloadRef != nullptr);
529 inputHandle->Allocate();
530 outputHandle->Allocate();
531 inputHandleRef->Allocate();
532 outputHandleRef->Allocate();
534 CopyDataToITensorHandle(inputHandle.get(), &input[0][0][0][0]);
535 CopyDataToITensorHandle(inputHandleRef.get(), &input[0][0][0][0]);
538 workloadRef->Execute();
540 CopyDataFromITensorHandle(&ret.output[0][0][0][0], outputHandle.get());
541 CopyDataFromITensorHandle(&ret.outputExpected[0][0][0][0], outputHandleRef.get());
546 LayerTestResult<float,4> CompareActivationTest(armnn::IWorkloadFactory& workloadFactory,
547 armnn::IWorkloadFactory& refWorkloadFactory,
548 armnn::ActivationFunction f,
549 unsigned int batchSize)
551 return CompareActivationTestImpl<float>(workloadFactory, refWorkloadFactory, f, batchSize);
554 LayerTestResult<uint8_t,4> CompareActivationUint8Test(armnn::IWorkloadFactory& workloadFactory,
555 armnn::IWorkloadFactory& refWorkloadFactory,
556 armnn::ActivationFunction f)
558 return CompareActivationTestImpl<uint8_t>(workloadFactory, refWorkloadFactory, f, 5, 0.1f, 50);