IVGCVSW-1946: Remove armnn/src from the include paths
[platform/upstream/armnn.git] / src / backends / backendsCommon / test / NormTestImpl.hpp
1 //
2 // Copyright © 2017 Arm Ltd. All rights reserved.
3 // SPDX-License-Identifier: MIT
4 //
5
6 #include <armnn/Exceptions.hpp>
7 #include <armnn/LayerSupport.hpp>
8 #include "armnn/Types.hpp"
9
10 #include <backendsCommon/CpuTensorHandle.hpp>
11 #include <backendsCommon/WorkloadFactory.hpp>
12
13 LayerTestResult<float,4> SimpleNormalizationTestImpl(armnn::IWorkloadFactory& workloadFactory,
14                                                      armnn::NormalizationAlgorithmChannel normChannel,
15                                                      armnn::NormalizationAlgorithmMethod normMethod)
16 {
17     const unsigned int inputHeight = 2;
18     const unsigned int inputWidth = 2;
19     const unsigned int inputChannels = 1;
20     const unsigned int inputNum = 2;
21
22     unsigned int outputHeight = inputHeight;
23     unsigned int outputWidth = inputWidth;
24     unsigned int outputChannels = inputChannels;
25     unsigned int outputNum = inputNum;
26
27     unsigned int inputShape[] = { inputNum, inputChannels, inputHeight, inputWidth };
28     unsigned int outputShape[] = { outputNum, outputChannels, outputHeight, outputWidth };
29
30     auto inputTensorInfo = armnn::TensorInfo(4, inputShape, armnn::DataType::Float32);
31     auto outputTensorInfo = armnn::TensorInfo(4, outputShape, armnn::DataType::Float32);
32
33     LayerTestResult<float,4> ret(outputTensorInfo);
34
35     auto input = MakeTensor<float, 4>(inputTensorInfo, std::vector<float>({
36         // Batch #0
37         1.0f, 2.0f,
38         3.0f, 4.0f,
39         // Batch #1
40         5.0f, 6.0f,
41         7.0f, 8.0f
42     }));
43
44     float alpha = 1.f;
45     float beta = 1.f;
46     float kappa = 1.f;
47     uint32_t normSize = 3;
48
49     std::unique_ptr<armnn::ITensorHandle> inputHandle = workloadFactory.CreateTensorHandle(inputTensorInfo);
50     std::unique_ptr<armnn::ITensorHandle> outputHandle = workloadFactory.CreateTensorHandle(outputTensorInfo);
51
52     armnn::NormalizationQueueDescriptor data;
53     armnn::WorkloadInfo info;
54     AddInputToWorkload(data, info, inputTensorInfo, inputHandle.get());
55     AddOutputToWorkload(data, info, outputTensorInfo, outputHandle.get());
56     data.m_Parameters.m_NormChannelType = normChannel;
57     data.m_Parameters.m_NormMethodType = normMethod;
58     data.m_Parameters.m_NormSize = normSize;
59     data.m_Parameters.m_Alpha = alpha;
60     data.m_Parameters.m_Beta = beta;
61     data.m_Parameters.m_K = kappa;
62     data.m_Parameters.m_DataLayout = armnn::DataLayout::NCHW;
63
64     armnn::PassthroughCpuTensorHandle refHandle(outputTensorInfo, &ret.outputExpected[0][0][0][0]);
65     armnn::NormalizationQueueDescriptor refData = data;
66     armnn::WorkloadInfo refInfo = info;
67     SetWorkloadOutput(refData, refInfo, 0, outputTensorInfo, &refHandle);
68
69     std::unique_ptr<armnn::IWorkload> workload = workloadFactory.CreateNormalization(data, info);
70
71     inputHandle->Allocate();
72     outputHandle->Allocate();
73
74     CopyDataToITensorHandle(inputHandle.get(), &input[0][0][0][0]);
75
76     workloadFactory.Finalize();
77     workload->Execute();
78
79     CopyDataFromITensorHandle(&ret.output[0][0][0][0], outputHandle.get());
80
81     switch (normMethod)
82     {
83         case armnn::NormalizationAlgorithmMethod::LocalBrightness:
84         {
85             switch (normChannel)
86             {
87                 case armnn::NormalizationAlgorithmChannel::Within:
88                 {
89                     // When normalising within channels, the 3x3 kernel covers the entire 2x2 input at every index.
90                     // Therefore, all output values should equal the inputs, but divided by:
91                     // pow((kappa + (accumulatedScale * alpha)), beta)
92                     // ...where accumulatedScale is the sum of every element squared.
93                     float divisor[inputNum];
94                     for(int i = 0; i < boost::numeric_cast<int>(inputNum); i++)
95                     {
96                         float accumulatedScale = input[i][0][0][0]*input[i][0][0][0] +
97                                                  input[i][0][0][1]*input[i][0][0][1] +
98                                                  input[i][0][1][0]*input[i][0][1][0] +
99                                                  input[i][0][1][1]*input[i][0][1][1];
100                         divisor[i] = powf((kappa + accumulatedScale * alpha), beta);
101                     }
102                     ret.outputExpected = MakeTensor<float, 4>(outputTensorInfo,
103                                                               std::vector<float>({input[0][0][0][0]/divisor[0],
104                                                                                   input[0][0][0][1]/divisor[0],
105                                                                                   input[0][0][1][0]/divisor[0],
106                                                                                   input[0][0][1][1]/divisor[0],
107                                                                                   input[1][0][0][0]/divisor[1],
108                                                                                   input[1][0][0][1]/divisor[1],
109                                                                                   input[1][0][1][0]/divisor[1],
110                                                                                   input[1][0][1][1]/divisor[1]}));
111                     break;
112                 }
113                 case armnn::NormalizationAlgorithmChannel::Across:
114                 {
115                     // When normalising across channels, all output values should equal the inputs, but multiplied by:
116                     // pow((kappa + (accumulatedScale * alpha)), -beta)
117                     // ...where accumulatedScale is the sum of the inputs for adjacent channels for this element squared
118                     // ...where adjacent channels means within half the normSize for the channel
119                     // The test data has only one channel, so this is simplified below.
120                     std::vector<float> outputVector;
121                     for (int n = 0; n < boost::numeric_cast<int>(inputNum); ++n)
122                     {
123                         for (int h = 0; h < boost::numeric_cast<int>(inputHeight); ++h)
124                         {
125                             for (int w = 0; w < boost::numeric_cast<int>(inputWidth); ++w)
126                             {
127                                 float accumulatedScale = input[n][0][h][w]*input[n][0][h][w];
128                                 float scale = powf((kappa + accumulatedScale * alpha), -beta);
129                                 outputVector.push_back(input[n][0][h][w] * scale);
130                             }
131                         }
132                     }
133                     ret.outputExpected = MakeTensor<float, 4>(outputTensorInfo, outputVector);
134                     break;
135                 }
136                 default:
137                 {
138                     throw armnn::UnimplementedException("Unsupported normalisation channel type, "
139                                                         "only Across and Within are supported");
140                 }
141             }
142             break;
143         }
144         case armnn::NormalizationAlgorithmMethod::LocalContrast: // NOTE: intentional fallthrough.
145         default:
146         {
147             throw armnn::UnimplementedException("Unsupported normalisation method type, "
148                                                 "only LocalBrightness is supported");
149         }
150     }
151
152     return ret;
153 }
154
155 LayerTestResult<float,4> SimpleNormalizationNhwcTestImpl(armnn::IWorkloadFactory& workloadFactory,
156                                                          armnn::NormalizationAlgorithmChannel normChannel,
157                                                          armnn::NormalizationAlgorithmMethod normMethod)
158 {
159     const unsigned int inputHeight = 2;
160     const unsigned int inputWidth = 2;
161     const unsigned int inputChannels = 1;
162     const unsigned int inputNum = 2;
163
164     unsigned int outputHeight = inputHeight;
165     unsigned int outputWidth = inputWidth;
166     unsigned int outputChannels = inputChannels;
167     unsigned int outputNum = inputNum;
168
169     unsigned int inputShape[] = { inputNum, inputHeight, inputWidth, inputChannels };
170     unsigned int outputShape[] = { outputNum, outputHeight, outputWidth, outputChannels };
171
172     auto inputTensorInfo = armnn::TensorInfo(4, inputShape, armnn::DataType::Float32);
173     auto outputTensorInfo = armnn::TensorInfo(4, outputShape, armnn::DataType::Float32);
174
175     LayerTestResult<float,4> ret(outputTensorInfo);
176
177     auto input = MakeTensor<float, 4>(inputTensorInfo, std::vector<float>({
178         // Batch #0
179         1.0f, 2.0f,
180         3.0f, 4.0f,
181         // Batch #1
182         5.0f, 6.0f,
183         7.0f, 8.0f
184     }));
185
186     float alpha = 1.f;
187     float beta = 1.f;
188     float kappa = 1.f;
189     uint32_t normSize = 3;
190
191     std::unique_ptr<armnn::ITensorHandle> inputHandle = workloadFactory.CreateTensorHandle(inputTensorInfo);
192     std::unique_ptr<armnn::ITensorHandle> outputHandle = workloadFactory.CreateTensorHandle(outputTensorInfo);
193
194     armnn::NormalizationQueueDescriptor data;
195     armnn::WorkloadInfo info;
196     AddInputToWorkload(data, info, inputTensorInfo, inputHandle.get());
197     AddOutputToWorkload(data, info, outputTensorInfo, outputHandle.get());
198     data.m_Parameters.m_NormChannelType = normChannel;
199     data.m_Parameters.m_NormMethodType = normMethod;
200     data.m_Parameters.m_NormSize = normSize;
201     data.m_Parameters.m_Alpha = alpha;
202     data.m_Parameters.m_Beta = beta;
203     data.m_Parameters.m_K = kappa;
204     data.m_Parameters.m_DataLayout = armnn::DataLayout::NHWC;
205
206     armnn::PassthroughCpuTensorHandle refHandle(outputTensorInfo, &ret.outputExpected[0][0][0][0]);
207     armnn::NormalizationQueueDescriptor refData = data;
208     armnn::WorkloadInfo refInfo = info;
209     SetWorkloadOutput(refData, refInfo, 0, outputTensorInfo, &refHandle);
210
211     std::unique_ptr<armnn::IWorkload> workload = workloadFactory.CreateNormalization(data, info);
212
213     inputHandle->Allocate();
214     outputHandle->Allocate();
215
216     CopyDataToITensorHandle(inputHandle.get(), &input[0][0][0][0]);
217
218     workloadFactory.Finalize();
219     workload->Execute();
220
221     CopyDataFromITensorHandle(&ret.output[0][0][0][0], outputHandle.get());
222
223     switch (normMethod)
224     {
225         case armnn::NormalizationAlgorithmMethod::LocalBrightness:
226         {
227             switch (normChannel)
228             {
229                 case armnn::NormalizationAlgorithmChannel::Across:
230                 {
231                     std::vector<float> expectedOutput{ 0.5f, 0.400000006f, 0.300000012f, 0.235294119f,
232                                                        0.192307696f, 0.16216217f, 0.140000001f, 0.123076923f };
233                     ret.outputExpected = MakeTensor<float, 4>(outputTensorInfo, expectedOutput);
234                     break;
235                 }
236                 default:
237                 {
238                     throw armnn::UnimplementedException("Unsupported normalisation channel type, "
239                                                         "Only Cross-map is supported for NHWC layout");
240                 }
241             }
242             break;
243         }
244         case armnn::NormalizationAlgorithmMethod::LocalContrast: // NOTE: intentional fallthrough.
245         default:
246         {
247             throw armnn::UnimplementedException("Unsupported normalisation method type, "
248                                                 "only LocalBrightness is supported");
249         }
250     }
251
252     return ret;
253 }
254
255 LayerTestResult<float,4> CompareNormalizationTestImpl(armnn::IWorkloadFactory& workloadFactory,
256                                                       armnn::IWorkloadFactory& refWorkloadFactory,
257                                                       armnn::NormalizationAlgorithmChannel normChannel,
258                                                       armnn::NormalizationAlgorithmMethod normMethod)
259 {
260     constexpr unsigned int inputNum = 5;
261     constexpr unsigned int inputChannels = 3;
262     constexpr unsigned int inputHeight = 32;
263     constexpr unsigned int inputWidth = 24;
264
265     constexpr unsigned int outputNum = inputNum;
266     constexpr unsigned int outputChannels = inputChannels;
267     constexpr unsigned int outputHeight = inputHeight;
268     constexpr unsigned int outputWidth = inputWidth;
269
270     armnn::TensorInfo inputTensorInfo;
271     armnn::TensorInfo outputTensorInfo;
272
273     unsigned int inputShape[] = {inputNum, inputChannels, inputHeight, inputWidth};
274     unsigned int outputShape[] = {outputNum, outputChannels, outputHeight, outputWidth};
275
276     inputTensorInfo = armnn::TensorInfo(4, inputShape, armnn::DataType::Float32);
277     outputTensorInfo = armnn::TensorInfo(4, outputShape, armnn::DataType::Float32);
278
279     LayerTestResult<float,4> ret(outputTensorInfo);
280
281     auto input = MakeRandomTensor<float, 4>(inputTensorInfo, 111234);
282
283     constexpr float alpha = 1.f;
284     constexpr float beta = 1.f;
285     constexpr float kappa = 1.f;
286     constexpr uint32_t normSize = 5;
287
288     std::unique_ptr<armnn::ITensorHandle> inputHandle = workloadFactory.CreateTensorHandle(inputTensorInfo);
289     std::unique_ptr<armnn::ITensorHandle> outputHandle = workloadFactory.CreateTensorHandle(outputTensorInfo);
290
291     armnn::NormalizationQueueDescriptor data;
292     armnn::WorkloadInfo info;
293     AddInputToWorkload(data, info, inputTensorInfo, inputHandle.get());
294     AddOutputToWorkload(data, info, outputTensorInfo, outputHandle.get());
295     data.m_Parameters.m_NormChannelType = normChannel;
296     data.m_Parameters.m_NormMethodType  = normMethod;
297     data.m_Parameters.m_NormSize        = normSize;
298     data.m_Parameters.m_Alpha           = alpha;
299     data.m_Parameters.m_Beta            = beta;
300     data.m_Parameters.m_K               = kappa;
301
302     std::unique_ptr<armnn::ITensorHandle> outputHandleRef = refWorkloadFactory.CreateTensorHandle(outputTensorInfo);
303     std::unique_ptr<armnn::ITensorHandle> inputHandleRef = refWorkloadFactory.CreateTensorHandle(inputTensorInfo);
304
305     armnn::NormalizationQueueDescriptor refData = data;
306     armnn::WorkloadInfo refInfo = info;
307     SetWorkloadInput(refData, refInfo, 0, inputTensorInfo, inputHandleRef.get());
308     SetWorkloadOutput(refData, refInfo, 0, outputTensorInfo, outputHandleRef.get());
309
310     // Don't execute if Normalization is not supported for the method and channel types, as an exception will be raised.
311     armnn::BackendId backend = workloadFactory.GetBackendId();
312     const size_t reasonIfUnsupportedMaxLen = 255;
313     char reasonIfUnsupported[reasonIfUnsupportedMaxLen+1];
314     ret.supported = armnn::IsNormalizationSupported(backend, inputTensorInfo, outputTensorInfo, data.m_Parameters,
315                                                     reasonIfUnsupported, reasonIfUnsupportedMaxLen);
316     if (!ret.supported)
317     {
318         return ret;
319     }
320
321     std::unique_ptr<armnn::IWorkload> workload = workloadFactory.CreateNormalization(data, info);
322     std::unique_ptr<armnn::IWorkload> workloadRef = refWorkloadFactory.CreateNormalization(refData, refInfo);
323
324     outputHandleRef->Allocate();
325     inputHandleRef->Allocate();
326
327     inputHandle->Allocate();
328     outputHandle->Allocate();
329
330     CopyDataToITensorHandle(inputHandle.get(), &input[0][0][0][0]);
331     CopyDataToITensorHandle(inputHandleRef.get(), &input[0][0][0][0]);
332
333     workloadFactory.Finalize();
334     workload->Execute();
335     refWorkloadFactory.Finalize();
336     workloadRef->Execute();
337
338     CopyDataFromITensorHandle(&ret.output[0][0][0][0], outputHandle.get());
339     CopyDataFromITensorHandle(&ret.outputExpected[0][0][0][0], outputHandleRef.get());
340
341     return ret;
342 }
343