2 // Copyright © 2017 Arm Ltd. All rights reserved.
3 // See LICENSE file in the project root for full license information.
7 #include <armnn/ArmNN.hpp>
8 #include <armnn/Tensor.hpp>
9 #include <armnn/TypesUtils.hpp>
10 #include <backends/WorkloadInfo.hpp>
12 #include "test/TensorHelpers.hpp"
13 #include "QuantizeHelper.hpp"
15 #include "backends/CpuTensorHandle.hpp"
16 #include "backends/WorkloadFactory.hpp"
18 // Mapping from input type to bias type for fully connected layers.
19 // float => float, uint8_t => int32_t
21 struct FullyConnectedBiasTypeForInputType;
24 struct FullyConnectedBiasTypeForInputType<float>
30 struct FullyConnectedBiasTypeForInputType<uint8_t>
35 // Modifies a std::vector in-place using a specified bias.
36 template<typename T, typename B>
37 void ApplyBias(std::vector<T>& v, float vScale, int32_t vOffset,
38 const std::vector<B>& bias, float bScale, int32_t bOffset, uint32_t w, uint32_t h)
40 BOOST_ASSERT_MSG((armnn::IsQuantizedType<T>() && vScale != 0.0f) || (!armnn::IsQuantizedType<T>()),
41 "Invalid type and parameter combination.");
42 BOOST_ASSERT_MSG((armnn::IsQuantizedType<B>() && bScale != 0.0f) || (!armnn::IsQuantizedType<B>()),
43 "Invalid type and parameter combination.");
45 // Note we need to dequantize and re-quantize the image value and the bias.
46 for (uint32_t i = 0; i < bias.size(); ++i)
48 float dBias = SelectiveDequantize(bias[i], bScale, bOffset);
49 for (uint32_t y = 0; y < h; ++y)
51 for (uint32_t x = 0; x < w; ++x)
53 uint32_t offset = (i * h + y) * w + x;
54 BOOST_ASSERT(offset < v.size());
55 T& outRef = v[offset];
56 float dOutput = SelectiveDequantize(outRef, vScale, vOffset);
57 outRef = SelectiveQuantize<T>(dOutput + dBias, vScale, vOffset);
63 template<typename T, typename B>
64 LayerTestResult<T, 4> SimpleConvolution2dTestImpl(armnn::IWorkloadFactory& workloadFactory,
65 const boost::multi_array<T, 4>& input,
66 const boost::multi_array<T, 4>& kernel,
67 const boost::multi_array<B, 1>& bias,
68 const boost::multi_array<T, 4>& outputExpected,
73 uint32_t padRight = 0,
74 uint32_t padBottom = 0)
76 unsigned int inputHeight = boost::numeric_cast<unsigned int>(input.shape()[2]);
77 unsigned int inputWidth = boost::numeric_cast<unsigned int>(input.shape()[3]);
78 unsigned int inputChannels = boost::numeric_cast<unsigned int>(input.shape()[1]);
79 unsigned int inputNum = boost::numeric_cast<unsigned int>(input.shape()[0]);
81 unsigned int outputHeight = boost::numeric_cast<unsigned int>(outputExpected.shape()[2]);
82 unsigned int outputWidth = boost::numeric_cast<unsigned int>(outputExpected.shape()[3]);
83 unsigned int outputChannels = boost::numeric_cast<unsigned int>(outputExpected.shape()[1]);
84 unsigned int outputNum = boost::numeric_cast<unsigned int>(outputExpected.shape()[0]);
86 unsigned int kernelHeight = boost::numeric_cast<unsigned int>(kernel.shape()[2]);
87 unsigned int kernelWidth = boost::numeric_cast<unsigned int>(kernel.shape()[3]);
88 unsigned int kernelChannels = boost::numeric_cast<unsigned int>(kernel.shape()[1]);
89 unsigned int kernelDepthMul = boost::numeric_cast<unsigned int>(kernel.shape()[0]);
91 bool biasEnabled = bias.size() > 0;
93 // This function currently assumes 1 batch of input/output (and duplicates this into 2 batches).
94 BOOST_ASSERT(inputNum == 1);
95 BOOST_ASSERT(outputNum == 1);
97 // If a bias is used, its size must equal the number of output channels.
98 BOOST_ASSERT(!biasEnabled || bias.size() == outputChannels);
101 // Note these tensors will use two (identical) batches.
102 armnn::TensorInfo inputTensorInfo({2*inputNum, inputChannels, inputHeight, inputWidth}, armnn::GetDataType<T>());
103 armnn::TensorInfo outputTensorInfo({2*outputNum, outputChannels, outputHeight, outputWidth},
104 armnn::GetDataType<T>());
105 armnn::TensorInfo kernelDesc({kernelDepthMul, kernelChannels, kernelHeight, kernelWidth}, armnn::GetDataType<T>());
106 armnn::TensorInfo biasDesc({static_cast<unsigned int>(bias.size())}, armnn::GetDataType<B>());
108 // Set quantization parameters if the requested type is a quantized type.
109 if(armnn::IsQuantizedType<T>())
111 inputTensorInfo.SetQuantizationScale(qScale);
112 inputTensorInfo.SetQuantizationOffset(qOffset);
113 outputTensorInfo.SetQuantizationScale(qScale);
114 outputTensorInfo.SetQuantizationOffset(qOffset);
115 kernelDesc.SetQuantizationScale(qScale);
116 kernelDesc.SetQuantizationOffset(qOffset);
117 biasDesc.SetQuantizationScale(qScale*qScale);
118 biasDesc.SetQuantizationOffset(0);
121 LayerTestResult<T, 4> ret(outputTensorInfo);
123 // Construct input data - two batches of the same input image.
124 std::vector<T> inputImage;
125 inputImage.assign(input.data(), input.data() + 1*inputChannels*inputHeight*inputWidth);
126 std::vector<T> inputData;
127 inputData.insert(inputData.end(), inputImage.begin(), inputImage.end());
128 inputData.insert(inputData.end(), inputImage.begin(), inputImage.end());
129 auto batchedInput = MakeTensor<T, 4>(inputTensorInfo, inputData);
131 std::vector<T> outputImage;
132 outputImage.assign(outputExpected.data(), outputExpected.data() + outputChannels*outputHeight*outputWidth);
134 // Apply bias to output image if it is enabled.
137 std::vector<T> biasV;
138 biasV.assign(bias.data(), bias.data() + outputChannels);
139 ApplyBias(outputImage, outputTensorInfo.GetQuantizationScale(), outputTensorInfo.GetQuantizationOffset(),
140 biasV, biasDesc.GetQuantizationScale(), biasDesc.GetQuantizationOffset(),
141 outputWidth, outputHeight);
144 // Construct expected output data - two identical images.
145 std::vector<T> outputData;
146 outputData.insert(outputData.end(), outputImage.begin(), outputImage.end());
147 outputData.insert(outputData.end(), outputImage.begin(), outputImage.end());
149 ret.outputExpected = MakeTensor<T, 4>(outputTensorInfo, outputData);
151 // Todo: nontrivial padding and strides.
152 uint32_t strideX = 1;
153 uint32_t strideY = 1;
155 std::unique_ptr<armnn::ITensorHandle> inputHandle = workloadFactory.CreateTensorHandle(inputTensorInfo);
156 std::unique_ptr<armnn::ITensorHandle> outputHandle = workloadFactory.CreateTensorHandle(outputTensorInfo);
158 armnn::Convolution2dQueueDescriptor data;
159 armnn::WorkloadInfo info;
160 armnn::ScopedCpuTensorHandle weightsTensor(kernelDesc);
161 armnn::ScopedCpuTensorHandle biasTensor(biasDesc);
163 AllocateAndCopyDataToITensorHandle(&weightsTensor, &kernel[0][0][0][0]);
167 AllocateAndCopyDataToITensorHandle(&biasTensor, &bias[0]);
170 AddInputToWorkload(data, info, inputTensorInfo, inputHandle.get());
171 AddOutputToWorkload(data, info, outputTensorInfo, outputHandle.get());
173 data.m_Weight = &weightsTensor;
174 data.m_Bias = &biasTensor; // Still set this whether or not bias is enabled - can be a source of bugs.
175 data.m_Parameters.m_StrideX = strideX;
176 data.m_Parameters.m_StrideY = strideY;
177 data.m_Parameters.m_PadLeft = padLeft;
178 data.m_Parameters.m_PadRight = padRight;
179 data.m_Parameters.m_PadTop = padTop;
180 data.m_Parameters.m_PadBottom = padBottom;
181 data.m_Parameters.m_BiasEnabled = biasEnabled;
183 std::unique_ptr<armnn::IWorkload> workload = workloadFactory.CreateConvolution2d(data, info);
184 inputHandle->Allocate();
185 outputHandle->Allocate();
187 CopyDataToITensorHandle(inputHandle.get(), &batchedInput[0][0][0][0]);
189 workloadFactory.Finalize();
192 CopyDataFromITensorHandle(&ret.output[0][0][0][0], outputHandle.get());
197 template<typename T, typename B>
198 LayerTestResult<T, 4> DepthwiseConvolution2dAsymmetricTestImpl(armnn::IWorkloadFactory& workloadFactory,
199 const boost::multi_array<T, 4>& input,
200 const boost::multi_array<T, 4>& kernel,
201 const boost::multi_array<B, 1>& bias,
202 const boost::multi_array<T, 4>& outputExpected,
205 uint32_t padLeft = 0,
207 uint32_t padRight = 0,
208 uint32_t padBottom = 0,
209 uint32_t strideX = 1,
210 uint32_t strideY = 1)
212 unsigned int inputNum = boost::numeric_cast<unsigned int>(input.shape()[0]);
213 unsigned int inputChannels = boost::numeric_cast<unsigned int>(input.shape()[1]);
214 unsigned int inputHeight = boost::numeric_cast<unsigned int>(input.shape()[2]);
215 unsigned int inputWidth = boost::numeric_cast<unsigned int>(input.shape()[3]);
216 unsigned int kernelChanMul = boost::numeric_cast<unsigned int>(kernel.shape()[0]);
217 unsigned int kernelChannels = boost::numeric_cast<unsigned int>(kernel.shape()[1]);
218 unsigned int kernelHeight = boost::numeric_cast<unsigned int>(kernel.shape()[2]);
219 unsigned int kernelWidth = boost::numeric_cast<unsigned int>(kernel.shape()[3]);
220 unsigned int outputNum = boost::numeric_cast<unsigned int>(outputExpected.shape()[0]);
221 unsigned int outputChannels = boost::numeric_cast<unsigned int>(outputExpected.shape()[1]);
222 unsigned int outputHeight = boost::numeric_cast<unsigned int>(outputExpected.shape()[2]);
223 unsigned int outputWidth = boost::numeric_cast<unsigned int>(outputExpected.shape()[3]);
225 // If a bias is used, its size must equal the number of output channels.
226 bool biasEnabled = bias.size() > 0;
227 BOOST_ASSERT(!biasEnabled || bias.size() == outputChannels);
229 // Creates the tensors.
230 armnn::TensorInfo inputTensorInfo({inputNum, inputChannels, inputHeight, inputWidth}, armnn::GetDataType<T>());
231 armnn::TensorInfo outputTensorInfo({outputNum, outputChannels, outputHeight, outputWidth},
232 armnn::GetDataType<T>());
233 armnn::TensorInfo kernelDesc({kernelChanMul, kernelChannels, kernelHeight, kernelWidth}, armnn::GetDataType<T>());
234 armnn::TensorInfo biasDesc({static_cast<unsigned int>(bias.size())}, armnn::GetDataType<B>());
236 // Set quantization parameters if the requested type is a quantized type.
237 if (armnn::IsQuantizedType<T>())
239 inputTensorInfo.SetQuantizationScale(qScale);
240 inputTensorInfo.SetQuantizationOffset(qOffset);
241 outputTensorInfo.SetQuantizationScale(qScale);
242 outputTensorInfo.SetQuantizationOffset(qOffset);
243 kernelDesc.SetQuantizationScale(qScale);
244 kernelDesc.SetQuantizationOffset(qOffset);
245 biasDesc.SetQuantizationScale(qScale*qScale);
246 biasDesc.SetQuantizationOffset(0);
249 // Construct the input data.
250 std::vector<T> inputData;
251 inputData.assign(input.data(), input.data() + inputChannels*inputHeight*inputWidth);
252 auto batchedInput = MakeTensor<T, 4>(inputTensorInfo, inputData);
254 // Construct the output data, with bias applied, as appropriate.
255 std::vector<T> outputData;
256 outputData.assign(outputExpected.data(), outputExpected.data() + outputChannels*outputHeight*outputWidth);
259 std::vector<T> biasV;
260 biasV.assign(bias.data(), bias.data() + outputChannels);
261 ApplyBias(outputData, outputTensorInfo.GetQuantizationScale(), outputTensorInfo.GetQuantizationOffset(),
262 biasV, biasDesc.GetQuantizationScale(), biasDesc.GetQuantizationOffset(),
263 outputWidth, outputHeight);
266 LayerTestResult<T, 4> ret(outputTensorInfo);
267 ret.outputExpected = MakeTensor<T, 4>(outputTensorInfo, outputData);
269 std::unique_ptr<armnn::ITensorHandle> inputHandle = workloadFactory.CreateTensorHandle(inputTensorInfo);
270 std::unique_ptr<armnn::ITensorHandle> outputHandle = workloadFactory.CreateTensorHandle(outputTensorInfo);
272 armnn::ScopedCpuTensorHandle weightsTensor(kernelDesc);
273 AllocateAndCopyDataToITensorHandle(&weightsTensor, &kernel[0][0][0][0]);
275 armnn::ScopedCpuTensorHandle biasTensor(biasDesc);
278 AllocateAndCopyDataToITensorHandle(&biasTensor, &bias[0]);
281 armnn::DepthwiseConvolution2dQueueDescriptor data;
282 data.m_Weight = &weightsTensor;
283 data.m_Bias = &biasTensor; // Still set this whether or not bias is enabled - it can be a source of bugs.
284 data.m_Parameters.m_StrideX = strideX;
285 data.m_Parameters.m_StrideY = strideY;
286 data.m_Parameters.m_PadLeft = padLeft;
287 data.m_Parameters.m_PadRight = padRight;
288 data.m_Parameters.m_PadTop = padTop;
289 data.m_Parameters.m_PadBottom = padBottom;
290 data.m_Parameters.m_BiasEnabled = biasEnabled;
292 armnn::WorkloadInfo info;
293 AddInputToWorkload(data, info, inputTensorInfo, inputHandle.get());
294 AddOutputToWorkload(data, info, outputTensorInfo, outputHandle.get());
296 std::unique_ptr<armnn::IWorkload> workload = workloadFactory.CreateDepthwiseConvolution2d(data, info);
297 inputHandle->Allocate();
298 outputHandle->Allocate();
300 CopyDataToITensorHandle(inputHandle.get(), &batchedInput[0][0][0][0]);
302 workloadFactory.Finalize();
305 CopyDataFromITensorHandle(&ret.output[0][0][0][0], outputHandle.get());
310 template<typename T, typename B>
311 LayerTestResult<T, 4> DepthwiseConvolution2dDepthMul1TestImpl(armnn::IWorkloadFactory& workloadFactory,
316 unsigned int inputHeight = 3;
317 unsigned int inputWidth = 3;
318 unsigned int inputChannels = 2;
319 unsigned int inputNum = 1;
321 unsigned int kernelHeight = 3;
322 unsigned int kernelWidth = 3;
323 unsigned int kernelChannels = inputChannels;
325 unsigned int outputHeight = 1;
326 unsigned int outputWidth = 1;
327 unsigned int outputChannels = kernelChannels;
328 unsigned int outputNum = inputNum;
330 armnn::TensorInfo inputTensorInfo({ inputNum, inputChannels, inputHeight, inputWidth }, armnn::GetDataType<T>());
331 armnn::TensorInfo outputTensorInfo({ outputNum, outputChannels, outputHeight, outputWidth },
332 armnn::GetDataType<T>());
333 armnn::TensorInfo kernelDesc({ 1, outputChannels, kernelHeight, kernelWidth }, armnn::GetDataType<T>());
334 armnn::TensorInfo biasDesc({ outputChannels }, armnn::GetDataType<B>());
336 // Set quantization parameters if the requested type is a quantized type.
337 if(armnn::IsQuantizedType<T>())
339 inputTensorInfo.SetQuantizationScale(qScale);
340 inputTensorInfo.SetQuantizationOffset(qOffset);
341 outputTensorInfo.SetQuantizationScale(qScale);
342 outputTensorInfo.SetQuantizationOffset(qOffset);
343 kernelDesc.SetQuantizationScale(qScale);
344 kernelDesc.SetQuantizationOffset(qOffset);
345 biasDesc.SetQuantizationScale(qScale*qScale);
346 biasDesc.SetQuantizationOffset(0);
349 auto input = MakeTensor<T, 4>(inputTensorInfo, std::vector<T>(
350 QuantizedVector<T>(inputTensorInfo.GetQuantizationScale(), inputTensorInfo.GetQuantizationOffset(), {
360 std::vector<B> biasV(QuantizedVector<B>(biasDesc.GetQuantizationScale(), biasDesc.GetQuantizationOffset(),
362 auto bias = MakeTensor<B, 1>(biasDesc, biasV);
364 auto kernel = MakeTensor<T, 4>(kernelDesc, std::vector<T>(
365 QuantizedVector<T>(kernelDesc.GetQuantizationScale(), kernelDesc.GetQuantizationOffset(), {
375 // Manually calculated.
376 std::vector<T> outputImage(
377 QuantizedVector<T>(outputTensorInfo.GetQuantizationScale(),
378 outputTensorInfo.GetQuantizationOffset(),
382 // Optionally apply bias to output image.
385 ApplyBias(outputImage, outputTensorInfo.GetQuantizationScale(), outputTensorInfo.GetQuantizationOffset(),
386 biasV, biasDesc.GetQuantizationScale(), biasDesc.GetQuantizationOffset(),
387 outputWidth, outputHeight);
390 LayerTestResult<T, 4> ret(outputTensorInfo);
391 ret.outputExpected = MakeTensor<T, 4>(outputTensorInfo, outputImage);
393 std::unique_ptr<armnn::ITensorHandle> inputHandle = workloadFactory.CreateTensorHandle(inputTensorInfo);
394 std::unique_ptr<armnn::ITensorHandle> outputHandle = workloadFactory.CreateTensorHandle(outputTensorInfo);
396 armnn::DepthwiseConvolution2dQueueDescriptor data;
397 armnn::WorkloadInfo info;
398 armnn::ScopedCpuTensorHandle weightsTensor(kernelDesc);
399 armnn::ScopedCpuTensorHandle biasTensor(biasDesc);
401 AllocateAndCopyDataToITensorHandle(&weightsTensor, &kernel[0][0][0][0]);
402 AllocateAndCopyDataToITensorHandle(&biasTensor, &bias[0]);
404 AddInputToWorkload(data, info, inputTensorInfo, inputHandle.get());
405 AddOutputToWorkload(data, info, outputTensorInfo, outputHandle.get());
407 data.m_Weight = &weightsTensor;
408 data.m_Bias = &biasTensor; // Still set this whether or not bias is enabled.
409 data.m_Parameters.m_StrideX = 1;
410 data.m_Parameters.m_StrideY = 1;
411 data.m_Parameters.m_PadLeft = 0;
412 data.m_Parameters.m_PadRight = 0;
413 data.m_Parameters.m_PadTop = 0;
414 data.m_Parameters.m_PadBottom = 0;
415 data.m_Parameters.m_BiasEnabled = biasEnabled;
417 std::unique_ptr<armnn::IWorkload> workload = workloadFactory.CreateDepthwiseConvolution2d(data, info);
418 inputHandle->Allocate();
419 outputHandle->Allocate();
421 CopyDataToITensorHandle(inputHandle.get(), &input[0][0][0][0]);
423 workloadFactory.Finalize();
426 CopyDataFromITensorHandle(&ret.output[0][0][0][0], outputHandle.get());
431 template<typename T, typename B>
432 LayerTestResult<T, 4> DepthwiseConvolution2dTestImpl(armnn::IWorkloadFactory& workloadFactory,
437 unsigned int depthMultiplier = 2;
439 unsigned int inputHeight = 8;
440 unsigned int inputWidth = 16;
441 unsigned int inputChannels = 2;
442 unsigned int inputBatchSize = 1;
444 unsigned int kernelHeight = 5;
445 unsigned int kernelWidth = 3;
447 unsigned int outputHeight = inputHeight - kernelHeight + 1 + 2;
448 unsigned int outputWidth = (inputWidth - kernelWidth + 1)/2;
449 unsigned int outputChannels = inputChannels * depthMultiplier;
450 unsigned int outputBatchSize = inputBatchSize;
452 armnn::TensorInfo inputTensorInfo({inputBatchSize, inputChannels, inputHeight, inputWidth},
453 armnn::GetDataType<T>());
454 armnn::TensorInfo outputTensorInfo({outputBatchSize, outputChannels, outputHeight, outputWidth},
455 armnn::GetDataType<T>());
456 armnn::TensorInfo kernelDesc({depthMultiplier, inputChannels, kernelHeight, kernelWidth}, armnn::GetDataType<T>());
457 armnn::TensorInfo biasDesc({outputChannels}, armnn::GetDataType<B>());
459 // Set quantization parameters if the requested type is a quantized type.
460 if(armnn::IsQuantizedType<T>())
462 inputTensorInfo.SetQuantizationScale(qScale);
463 inputTensorInfo.SetQuantizationOffset(qOffset);
464 outputTensorInfo.SetQuantizationScale(qScale);
465 outputTensorInfo.SetQuantizationOffset(qOffset);
466 kernelDesc.SetQuantizationScale(qScale);
467 kernelDesc.SetQuantizationOffset(qOffset);
468 biasDesc.SetQuantizationScale(qScale*qScale);
469 biasDesc.SetQuantizationOffset(0);
472 auto input = MakeTensor<T, 4>(inputTensorInfo, std::vector<T>(
473 QuantizedVector<T>(inputTensorInfo.GetQuantizationScale(), inputTensorInfo.GetQuantizationOffset(), {
474 0.5f, 0.5f, 0.5f, 0.5f, 0.5f, 0.5f, 0.5f, 0.5f, 0.5f, 0.5f, 0.5f, 0.5f, 0.5f, 0.5f, 0.5f, 0.5f,
475 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f,
476 0.5f, 0.5f, 0.5f, 0.5f, 0.5f, 0.5f, 0.5f, 0.5f, 0.5f, 0.5f, 0.5f, 0.5f, 0.5f, 0.5f, 0.5f, 0.5f,
477 0.5f, 0.5f, 0.5f, 0.5f, 0.5f, 0.5f, 0.5f, 0.5f, 0.5f, 0.5f, 0.5f, 0.5f, 0.5f, 0.5f, 0.5f, 0.5f,
478 0.5f, 0.5f, 0.5f, 0.5f, 0.5f, 0.5f, 0.5f, 0.5f, 0.5f, 0.5f, 0.5f, 0.5f, 0.5f, 0.5f, 0.5f, 0.5f,
479 0.5f, 0.5f, 0.5f, 0.5f, 0.5f, 0.5f, 0.5f, 0.5f, 0.5f, 0.5f, 0.5f, 0.5f, 0.5f, 0.5f, 0.5f, 0.5f,
480 0.5f, 0.5f, 0.5f, 0.5f, 0.5f, 0.5f, 0.5f, 0.5f, 0.5f, 0.5f, 0.5f, 0.5f, 0.5f, 0.5f, 0.5f, 0.5f,
481 0.5f, 0.5f, 0.5f, 0.5f, 0.5f, 0.5f, 0.5f, 0.5f, 0.5f, 0.5f, 0.5f, 0.5f, 0.5f, 0.5f, 0.5f, 0.5f,
482 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
483 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
484 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
485 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
486 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
487 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
488 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
489 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
492 std::vector<B> biasV(QuantizedVector<B>(biasDesc.GetQuantizationScale(), biasDesc.GetQuantizationOffset(),
494 auto bias = MakeTensor<B, 1>(biasDesc, biasV);
496 auto kernel = MakeTensor<T, 4>(kernelDesc, std::vector<T>(
497 QuantizedVector<T>(kernelDesc.GetQuantizationScale(), kernelDesc.GetQuantizationOffset(), {
523 // Manually calculated.
524 std::vector<T> outputImage = std::vector<T>(
525 QuantizedVector<T>(outputTensorInfo.GetQuantizationScale(), outputTensorInfo.GetQuantizationOffset(), {
526 3.5f, 3.5f, 3.5f, 3.5f, 3.5f, 3.5f, 3.5f,
527 6.0f, 6.0f, 6.0f, 6.0f, 6.0f, 6.0f, 6.0f,
528 5.0f, 5.0f, 5.0f, 5.0f, 5.0f, 5.0f, 5.0f,
529 6.5f, 6.5f, 6.5f, 6.5f, 6.5f, 6.5f, 6.5f,
530 6.5f, 6.5f, 6.5f, 6.5f, 6.5f, 6.5f, 6.5f,
531 5.0f, 5.0f, 5.0f, 5.0f, 5.0f, 5.0f, 5.0f,
533 -0.5f, -0.5f, -0.5f, -0.5f, -0.5f, -0.5f, -0.5f,
534 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f,
535 -0.5f, -0.5f, -0.5f, -0.5f, -0.5f, -0.5f, -0.5f,
536 -0.5f, -0.5f, -0.5f, -0.5f, -0.5f, -0.5f, -0.5f,
537 -0.5f, -0.5f, -0.5f, -0.5f, -0.5f, -0.5f, -0.5f,
538 -0.5f, -0.5f, -0.5f, -0.5f, -0.5f, -0.5f, -0.5f,
540 8.0f, 8.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f,
541 10.0f, 10.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f,
542 10.0f, 10.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f,
543 10.0f, 10.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f,
544 10.0f, 10.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f,
545 8.0f, 8.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f,
547 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f,
548 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f,
549 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f,
550 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f,
551 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f,
552 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f
555 // Optionally apply bias to output image.
558 ApplyBias(outputImage, outputTensorInfo.GetQuantizationScale(), outputTensorInfo.GetQuantizationOffset(),
559 biasV, biasDesc.GetQuantizationScale(), biasDesc.GetQuantizationOffset(),
560 outputWidth, outputHeight);
563 LayerTestResult<T, 4> ret(outputTensorInfo);
564 ret.outputExpected = MakeTensor<T, 4>(outputTensorInfo, outputImage);
566 std::unique_ptr<armnn::ITensorHandle> inputHandle = workloadFactory.CreateTensorHandle(inputTensorInfo);
567 std::unique_ptr<armnn::ITensorHandle> outputHandle = workloadFactory.CreateTensorHandle(outputTensorInfo);
569 armnn::DepthwiseConvolution2dQueueDescriptor data;
570 armnn::WorkloadInfo info;
571 armnn::ScopedCpuTensorHandle weightsTensor(kernelDesc);
572 armnn::ScopedCpuTensorHandle biasTensor(biasDesc);
574 AllocateAndCopyDataToITensorHandle(&weightsTensor, &kernel[0][0][0][0]);
575 AllocateAndCopyDataToITensorHandle(&biasTensor, &bias[0]);
577 AddInputToWorkload(data, info, inputTensorInfo, inputHandle.get());
578 AddOutputToWorkload(data, info, outputTensorInfo, outputHandle.get());
580 data.m_Weight = &weightsTensor;
581 data.m_Bias = &biasTensor; // Still set this whether or not bias is enabled.
582 data.m_Parameters.m_StrideX = 2;
583 data.m_Parameters.m_StrideY = 1;
584 data.m_Parameters.m_PadLeft = 0;
585 data.m_Parameters.m_PadRight = 0;
586 data.m_Parameters.m_PadTop = 1;
587 data.m_Parameters.m_PadBottom = 1;
588 data.m_Parameters.m_BiasEnabled = biasEnabled;
590 std::unique_ptr<armnn::IWorkload> workload = workloadFactory.CreateDepthwiseConvolution2d(data, info);
591 inputHandle->Allocate();
592 outputHandle->Allocate();
594 CopyDataToITensorHandle(inputHandle.get(), &input[0][0][0][0]);
596 workloadFactory.Finalize();
599 CopyDataFromITensorHandle(&ret.output[0][0][0][0], outputHandle.get());
605 LayerTestResult<T,4> Convolution1dTestImpl(armnn::IWorkloadFactory& workloadFactory,
610 using B = typename FullyConnectedBiasTypeForInputType<T>::Type;
612 // Until we have a specialist 1D convolution layer, we can fake one using
613 // 2D convolution with the final dimension set to 1.
614 // I don't anticipate this being particularly slow, given that convolution is implemented
615 // as a matrix multiplication, at which point dimension doesn't matter.
617 unsigned int batchSize = 1;
618 unsigned int inputChannels = 2;
619 unsigned int outputChannels = 3;
620 unsigned int inputSize = 5; // The 1D size (could view as 'width' or 'height').
621 unsigned int kernelSize = 3;
622 unsigned int padSize = 2;
623 unsigned int stride = 1;
624 unsigned int outputSize = 7; // (inputSize + 2 * padSize - kernelSize + 1) / stride.
626 armnn::TensorInfo inputInfo({batchSize, inputChannels, inputSize, 1}, armnn::GetDataType<T>());
627 armnn::TensorInfo outputInfo({batchSize, outputChannels, outputSize, 1}, armnn::GetDataType<T>());
628 armnn::TensorInfo kernelInfo({outputChannels, inputChannels, kernelSize, 1}, armnn::GetDataType<T>());
629 armnn::TensorInfo biasInfo({outputChannels}, armnn::GetDataType<B>());
631 // Set quantization parameters if the requested type is a quantized type.
632 if(armnn::IsQuantizedType<T>())
634 inputInfo.SetQuantizationScale(qScale);
635 inputInfo.SetQuantizationOffset(qOffset);
636 outputInfo.SetQuantizationScale(qScale);
637 outputInfo.SetQuantizationOffset(qOffset);
638 kernelInfo.SetQuantizationScale(qScale);
639 kernelInfo.SetQuantizationOffset(qOffset);
640 biasInfo.SetQuantizationScale(inputInfo.GetQuantizationScale()*kernelInfo.GetQuantizationScale());
641 biasInfo.SetQuantizationOffset(0);
644 std::vector<T> inputData(
645 QuantizedVector<T>(inputInfo.GetQuantizationScale(), inputInfo.GetQuantizationOffset(), {
646 5.0f, -2.0f, 2.5f, 0.0f, 1.0f,
647 -3.0f, 3.2f, 5.0f, 2.0f, 3.0f,
650 std::vector<T> kernelData(
651 QuantizedVector<T>(kernelInfo.GetQuantizationScale(), kernelInfo.GetQuantizationOffset(), {
662 std::vector<B> biasData(
663 QuantizedVector<B>(biasInfo.GetQuantizationScale(), biasInfo.GetQuantizationOffset(), {
667 std::vector<T> outputData(
668 QuantizedVector<T>(outputInfo.GetQuantizationScale(), outputInfo.GetQuantizationOffset(), {
669 4.5f, -10.8f, 5.0f + 6.4f - 7.5f, -2.0f + 10.0f -3.0f, 2.5f + 4.0f - 4.5f, 6.0f, 1.0f,
670 -0.6f, -0.6f + 0.64f, -0.6f + 0.64f + 1.0f, 0.64f + 1.0f + 0.4f, 1.0f + 0.4f + 0.6f, 0.4f + 0.6f, 0.6f,
671 2.5f, -1.0f + 3.0f, 1.25f - 3.2f + 2.5f, -1.0f - 5.0f, 1.25f + 0.5f - 2.0f, -3.0f, 0.5f
674 // Optionally apply bias to output image.
677 ApplyBias(outputData, outputInfo.GetQuantizationScale(), outputInfo.GetQuantizationOffset(),
678 biasData, biasInfo.GetQuantizationScale(), biasInfo.GetQuantizationOffset(),
682 std::unique_ptr<armnn::ITensorHandle> inputHandle = workloadFactory.CreateTensorHandle(inputInfo);
683 std::unique_ptr<armnn::ITensorHandle> outputHandle = workloadFactory.CreateTensorHandle(outputInfo);
685 armnn::Convolution2dQueueDescriptor data;
686 armnn::WorkloadInfo info;
687 armnn::ScopedCpuTensorHandle weightsTensor(kernelInfo);
688 armnn::ScopedCpuTensorHandle biasTensor(biasInfo);
690 AllocateAndCopyDataToITensorHandle(&weightsTensor, kernelData.data());
691 AllocateAndCopyDataToITensorHandle(&biasTensor, biasData.data());
693 AddInputToWorkload(data, info, inputInfo, inputHandle.get());
694 AddOutputToWorkload(data, info, outputInfo, outputHandle.get());
696 data.m_Weight = &weightsTensor;
697 data.m_Bias = &biasTensor;
698 data.m_Parameters.m_StrideX = 1;
699 data.m_Parameters.m_StrideY = stride;
700 data.m_Parameters.m_PadLeft = 0;
701 data.m_Parameters.m_PadRight = 0;
702 data.m_Parameters.m_PadTop = padSize;
703 data.m_Parameters.m_PadBottom = padSize;
704 data.m_Parameters.m_BiasEnabled = biasEnabled;
706 std::unique_ptr<armnn::IWorkload> workload = workloadFactory.CreateConvolution2d(data, info);
707 inputHandle->Allocate();
708 outputHandle->Allocate();
710 CopyDataToITensorHandle(inputHandle.get(), inputData.data());
712 workloadFactory.Finalize();
716 LayerTestResult<T,4> ret(outputInfo);
717 CopyDataFromITensorHandle(&ret.output[0][0][0][0], outputHandle.get());
718 ret.outputExpected = MakeTensor<T, 4>(outputInfo, outputData);
725 LayerTestResult<T,4> CompareConvolution2dTestImpl(armnn::IWorkloadFactory& workloadFactory,
726 armnn::IWorkloadFactory& refWorkloadFactory)
728 unsigned int inputHeight = 8;
729 unsigned int inputWidth = 16;
730 unsigned int inputChannels = 3;
731 unsigned int inputNum = 5;
733 unsigned int kernelHeight = 3;
734 unsigned int kernelWidth = 3;
736 unsigned int strideX = 2;
737 unsigned int strideY = 3;
738 unsigned int padX = 1;
739 unsigned int padY = 1;
741 unsigned int outputNum = inputNum;
742 unsigned int outputChannels = 2;
743 unsigned int outputHeight = (inputHeight + 2 * padY - kernelHeight + strideY) / strideY;
744 unsigned int outputWidth = (inputWidth + 2 * padX - kernelWidth + strideX) / strideX;
746 armnn::TensorInfo inputTensorInfo;
747 armnn::TensorInfo outputTensorInfo;
748 armnn::TensorInfo kernelDesc;
749 armnn::TensorInfo biasDesc;
751 unsigned int inputShape[] = {inputNum, inputChannels, inputHeight, inputWidth};
752 unsigned int outputShape[] = {outputNum, outputChannels, outputHeight, outputWidth};
753 unsigned int kernelShape[] = {outputChannels, inputChannels, kernelHeight, kernelWidth};
754 unsigned int biasShape[] = {outputChannels};
756 inputTensorInfo = armnn::TensorInfo(4, inputShape, armnn::GetDataType<T>());
757 outputTensorInfo = armnn::TensorInfo(4, outputShape, armnn::GetDataType<T>());
758 kernelDesc = armnn::TensorInfo(4, kernelShape, armnn::GetDataType<T>());
759 biasDesc = armnn::TensorInfo(1, biasShape, armnn::GetDataType<T>());
761 LayerTestResult<T,4> ret(outputTensorInfo);
763 auto input = MakeRandomTensor<T, 4>(inputTensorInfo, 124908);
764 auto kernel = MakeRandomTensor<T, 4>(kernelDesc, 891234);
765 auto bias = MakeRandomTensor<T, 1>(biasDesc, 1028);
767 std::unique_ptr<armnn::ITensorHandle> inputHandle = workloadFactory.CreateTensorHandle(inputTensorInfo);
768 std::unique_ptr<armnn::ITensorHandle> outputHandle = workloadFactory.CreateTensorHandle(outputTensorInfo);
770 armnn::Convolution2dQueueDescriptor data;
771 armnn::WorkloadInfo info;
772 armnn::ScopedCpuTensorHandle weightsTensor(kernelDesc);
773 armnn::ScopedCpuTensorHandle biasTensor(biasDesc);
775 AllocateAndCopyDataToITensorHandle(&weightsTensor, &kernel[0][0][0][0]);
776 AllocateAndCopyDataToITensorHandle(&biasTensor, &bias[0]);
778 AddInputToWorkload(data, info, inputTensorInfo, inputHandle.get());
779 AddOutputToWorkload(data, info, outputTensorInfo, outputHandle.get());
780 data.m_Weight = &weightsTensor;
781 data.m_Bias = &biasTensor;
782 data.m_Parameters.m_StrideX = strideX;
783 data.m_Parameters.m_StrideY = strideY;
784 data.m_Parameters.m_PadLeft = padX;
785 data.m_Parameters.m_PadRight = padX;
786 data.m_Parameters.m_PadTop = padY;
787 data.m_Parameters.m_PadBottom = padY;
788 data.m_Parameters.m_BiasEnabled = true;
790 std::unique_ptr<armnn::ITensorHandle> outputHandleRef = refWorkloadFactory.CreateTensorHandle(outputTensorInfo);
791 std::unique_ptr<armnn::ITensorHandle> inputHandleRef = refWorkloadFactory.CreateTensorHandle(inputTensorInfo);
793 armnn::Convolution2dQueueDescriptor refData = data;
794 armnn::WorkloadInfo refInfo = info;
795 SetWorkloadInput(refData, refInfo, 0, inputTensorInfo, inputHandleRef.get());
796 SetWorkloadOutput(refData, refInfo, 0, outputTensorInfo, outputHandleRef.get());
798 std::unique_ptr<armnn::IWorkload> workload = workloadFactory.CreateConvolution2d(data, info);
799 std::unique_ptr<armnn::IWorkload> workloadRef = refWorkloadFactory.CreateConvolution2d(refData, refInfo);
801 outputHandleRef->Allocate();
802 inputHandleRef->Allocate();
804 inputHandle->Allocate();
805 outputHandle->Allocate();
807 CopyDataToITensorHandle(inputHandle.get(), &input[0][0][0][0]);
808 CopyDataToITensorHandle(inputHandleRef.get(), &input[0][0][0][0]);
810 workloadFactory.Finalize();
812 refWorkloadFactory.Finalize();
813 workloadRef->Execute();
815 CopyDataFromITensorHandle(&ret.output[0][0][0][0], outputHandle.get());
816 CopyDataFromITensorHandle(&ret.outputExpected[0][0][0][0], outputHandleRef.get());
822 LayerTestResult<T, 4> CompareDepthwiseConvolution2dTestImpl(armnn::IWorkloadFactory& workloadFactory,
823 armnn::IWorkloadFactory& refWorkloadFactory)
825 unsigned int inputHeight = 8;
826 unsigned int inputWidth = 16;
827 unsigned int inputChannels = 3;
828 unsigned int inputNum = 5;
830 unsigned int kernelHeight = 3;
831 unsigned int kernelWidth = 3;
832 unsigned int channelMultiplier = 1;
834 unsigned int strideX = 2;
835 unsigned int strideY = 3;
836 unsigned int padX = 1;
837 unsigned int padY = 1;
839 unsigned int outputNum = inputNum;
840 unsigned int outputChannels = inputChannels * channelMultiplier;
841 unsigned int outputHeight = (inputHeight + 2 * padY - kernelHeight + strideY) / strideY;
842 unsigned int outputWidth = (inputWidth + 2 * padX - kernelWidth + strideX) / strideX;
844 armnn::TensorInfo inputTensorInfo;
845 armnn::TensorInfo outputTensorInfo;
846 armnn::TensorInfo kernelDesc;
847 armnn::TensorInfo biasDesc;
849 unsigned int inputShape[] = { inputNum, inputChannels, inputHeight, inputWidth };
850 unsigned int outputShape[] = { outputNum, outputChannels, outputHeight, outputWidth };
851 unsigned int kernelShape[] = { channelMultiplier, inputChannels, kernelHeight, kernelWidth };
852 unsigned int biasShape[] = { outputChannels };
854 float inputsQScale = armnn::IsQuantizedType<T>() ? 1.0f : 0;
855 float outputQScale = armnn::IsQuantizedType<T>() ? 2.0f : 0;
858 inputTensorInfo = armnn::TensorInfo(4, inputShape, armnn::GetDataType<T>(), inputsQScale, qOffset);
859 outputTensorInfo = armnn::TensorInfo(4, outputShape, armnn::GetDataType<T>(), outputQScale, qOffset);
860 kernelDesc = armnn::TensorInfo(4, kernelShape, armnn::GetDataType<T>(), inputsQScale, qOffset);
861 biasDesc = armnn::TensorInfo(1, biasShape, armnn::GetBiasDataType(armnn::GetDataType<T>()), inputsQScale, qOffset);
863 LayerTestResult<T, 4> ret(outputTensorInfo);
865 auto input = MakeRandomTensor<T, 4>(inputTensorInfo, 124908, 0.0f, 255.0f);
866 auto kernel = MakeRandomTensor<T, 4>(kernelDesc, 891234, 0.0f, 255.0f);
867 auto bias = MakeRandomTensor<typename FullyConnectedBiasTypeForInputType<T>::Type, 1>(biasDesc, 1028, 0.0f, 255.0f);
869 std::unique_ptr<armnn::ITensorHandle> inputHandle = workloadFactory.CreateTensorHandle(inputTensorInfo);
870 std::unique_ptr<armnn::ITensorHandle> outputHandle = workloadFactory.CreateTensorHandle(outputTensorInfo);
872 armnn::DepthwiseConvolution2dQueueDescriptor data;
873 armnn::WorkloadInfo info;
874 armnn::ScopedCpuTensorHandle weightsTensor(kernelDesc);
875 armnn::ScopedCpuTensorHandle biasTensor(biasDesc);
877 AllocateAndCopyDataToITensorHandle(&weightsTensor, &kernel[0][0][0][0]);
878 AllocateAndCopyDataToITensorHandle(&biasTensor, &bias[0]);
880 AddInputToWorkload(data, info, inputTensorInfo, inputHandle.get());
881 AddOutputToWorkload(data, info, outputTensorInfo, outputHandle.get());
882 data.m_Weight = &weightsTensor;
883 data.m_Bias = &biasTensor;
884 data.m_Parameters.m_StrideX = strideX;
885 data.m_Parameters.m_StrideY = strideY;
886 data.m_Parameters.m_PadLeft = padX;
887 data.m_Parameters.m_PadRight = padX;
888 data.m_Parameters.m_PadTop = padY;
889 data.m_Parameters.m_PadBottom = padY;
890 data.m_Parameters.m_BiasEnabled = true;
892 std::unique_ptr<armnn::ITensorHandle> outputHandleRef = refWorkloadFactory.CreateTensorHandle(outputTensorInfo);
893 std::unique_ptr<armnn::ITensorHandle> inputHandleRef = refWorkloadFactory.CreateTensorHandle(inputTensorInfo);
895 armnn::DepthwiseConvolution2dQueueDescriptor refData = data;
896 armnn::WorkloadInfo refInfo = info;
897 SetWorkloadInput(refData, refInfo, 0, inputTensorInfo, inputHandleRef.get());
898 SetWorkloadOutput(refData, refInfo, 0, outputTensorInfo, outputHandleRef.get());
900 std::unique_ptr<armnn::IWorkload> workload = workloadFactory.CreateDepthwiseConvolution2d(data, info);
901 std::unique_ptr<armnn::IWorkload> workloadRef = refWorkloadFactory.CreateDepthwiseConvolution2d(refData, refInfo);
903 outputHandleRef->Allocate();
904 inputHandleRef->Allocate();
906 inputHandle->Allocate();
907 outputHandle->Allocate();
909 CopyDataToITensorHandle(inputHandle.get(), &input[0][0][0][0]);
910 CopyDataToITensorHandle(inputHandleRef.get(), &input[0][0][0][0]);
912 workloadFactory.Finalize();
914 refWorkloadFactory.Finalize();
915 workloadRef->Execute();
917 CopyDataFromITensorHandle(&ret.output[0][0][0][0], outputHandle.get());
918 CopyDataFromITensorHandle(&ret.outputExpected[0][0][0][0], outputHandleRef.get());