1 //
2 // Copyright © 2021 Arm Ltd and Contributors. All rights reserved.
3 // SPDX-License-Identifier: MIT
4 //
5
6 #include <GraphUtils.hpp>
7 #include <TestUtils.hpp>
8
9 #include <armnn/INetwork.hpp>
10
11 #include <doctest/doctest.h>
12
13 using namespace armnn;
14
15 namespace
16 {
17 #if defined(ARMCOMPUTENEON_ENABLED)||defined(ARMCOMPUTECL_ENABLED)
CreateSimpleReduceNetwork(ReduceDescriptor reduceDescriptor,TensorShape & inputShape,TensorShape & outputShape)18 INetworkPtr CreateSimpleReduceNetwork(ReduceDescriptor reduceDescriptor,
19 TensorShape& inputShape,
20 TensorShape& outputShape)
21 {
22 // Create a network
23 INetworkPtr network = INetwork::Create();
24
25 const std::string layerName("reduce_layer");
26 const TensorInfo inputInfo(inputShape, DataType::Float32);
27 const TensorInfo outputInfo(outputShape, DataType::Float32);
28
29 IConnectableLayer* const inputLayer = network->AddInputLayer(0);
30 IConnectableLayer* const reduceLayer = network->AddReduceLayer(reduceDescriptor, layerName.c_str());
31 IConnectableLayer* const outputLayer1 = network->AddOutputLayer(0);
32 IConnectableLayer* const outputLayer2 = network->AddOutputLayer(1);
33
34 inputLayer->GetOutputSlot(0).SetTensorInfo(inputInfo);
35 reduceLayer->GetOutputSlot(0).SetTensorInfo(outputInfo);
36
37 inputLayer->GetOutputSlot(0).Connect(reduceLayer->GetInputSlot(0));
38 reduceLayer->GetOutputSlot(0).Connect(outputLayer1->GetInputSlot(0));
39 reduceLayer->GetOutputSlot(0).Connect(outputLayer2->GetInputSlot(0));
40
41 return network;
42 }
43
ReduceWithMultipleAxesTest(INetworkPtr & network,const TensorShape & outputShape,const std::vector<float> & inputData,const std::vector<float> & expectedOutput,const size_t numOfAxes,Compute backendId)44 void ReduceWithMultipleAxesTest(INetworkPtr& network,
45 const TensorShape& outputShape,
46 const std::vector<float>& inputData,
47 const std::vector<float>& expectedOutput,
48 const size_t numOfAxes,
49 Compute backendId)
50 {
51 // Create ArmNN runtime
52 IRuntimePtr run = IRuntime::Create(IRuntime::CreationOptions());
53
54 // Optimise ArmNN network
55 IOptimizedNetworkPtr optNet = Optimize(*network, {backendId}, run->GetDeviceSpec());
56
57 Graph& graph = GetGraphForTesting(optNet.get());
58 if (numOfAxes == 2)
59 {
60 CHECK(graph.GetNumLayers() == 5);
61 CHECK(CheckSequence(graph.cbegin(),
62 graph.cend(),
63 &IsLayerOfType<InputLayer>,
64 &IsLayerOfType<ReduceLayer>,
65 &IsLayerOfType<ReduceLayer>,
66 &IsLayerOfType<OutputLayer>,
67 &IsLayerOfType<OutputLayer>));
68 } else
69 {
70 CHECK(graph.GetNumLayers() == 6);
71 CHECK(CheckSequence(graph.cbegin(),
72 graph.cend(),
73 &IsLayerOfType<InputLayer>,
74 &IsLayerOfType<ReduceLayer>,
75 &IsLayerOfType<ReduceLayer>,
76 &IsLayerOfType<ReduceLayer>,
77 &IsLayerOfType<OutputLayer>,
78 &IsLayerOfType<OutputLayer>));
79 }
80
81 // Get last layer in new chain, layers name follow 0, 1, 2 pattern
82 std::string layerName = "reduce_layer_" + std::to_string(numOfAxes - 1);
83 Layer* const reduceLayer = GetFirstLayerWithName(graph, layerName);
84 CHECK(reduceLayer);
85 auto reduceTensorInfo = reduceLayer->GetOutputSlot().GetTensorInfo();
86
87 // Tensorshape and the data type are correct
88 CHECK((reduceTensorInfo.GetShape() == outputShape));
89 CHECK((reduceTensorInfo.GetDataType() == DataType::Float32));
90
91 // Load network into runtime
92 NetworkId networkIdentifier;
93 run->LoadNetwork(networkIdentifier, std::move(optNet));
94
95 // Create input and output tensors
96 std::vector<float> outputData(expectedOutput.size());
97 armnn::TensorInfo inputTensorInfo = run->GetInputTensorInfo(networkIdentifier, 0);
98 inputTensorInfo.SetConstant(true);
99 InputTensors inputTensors
100 {
101 {0, armnn::ConstTensor(inputTensorInfo, inputData.data())}
102 };
103 OutputTensors outputTensors
104 {
105 {0, armnn::Tensor(run->GetOutputTensorInfo(networkIdentifier, 0), outputData.data())},
106 {1, armnn::Tensor(run->GetOutputTensorInfo(networkIdentifier, 1), outputData.data())}
107 };
108
109 // Run inference
110 run->EnqueueWorkload(networkIdentifier, inputTensors, outputTensors);
111
112 // Checks the results
113 CHECK(outputData == expectedOutput);
114 }
115
ReduceSumWithTwoAxesKeepDimsTest(Compute backendId)116 void ReduceSumWithTwoAxesKeepDimsTest(Compute backendId)
117 {
118 armnn::ReduceDescriptor reduceDescriptor;
119 reduceDescriptor.m_vAxis = {1, 2};
120 reduceDescriptor.m_KeepDims = true;
121 reduceDescriptor.m_ReduceOperation = armnn::ReduceOperation::Sum;
122
123 TensorShape inputShape = {1, 3, 2, 4};
124 TensorShape outputShape = {1, 1, 1, 4};
125
126 // Construct ArmNN network
127 INetworkPtr network = CreateSimpleReduceNetwork(reduceDescriptor, inputShape, outputShape);
128
129 // Creates structures for input & output.
130 const std::vector<float> inputData({1.0f, 2.0f, 3.0f, 4.0f,
131 5.0f, 6.0f, 7.0f, 8.0f,
132
133 10.0f, 20.0f, 30.0f, 40.0f,
134 50.0f, 60.0f, 70.0f, 80.0f,
135
136 100.0f, 200.0f, 300.0f, 400.0f,
137 500.0f, 600.0f, 700.0f, 800.0f});
138 const std::vector<float> expectedOutput({666.0f, 888.0f, 1110.0f, 1332.0f});
139
140 ReduceWithMultipleAxesTest(network,
141 outputShape,
142 inputData,
143 expectedOutput,
144 reduceDescriptor.m_vAxis.size(),
145 backendId);
146 }
147
ReduceSumWithTwoAxesTest(Compute backendId)148 void ReduceSumWithTwoAxesTest(Compute backendId)
149 {
150 armnn::ReduceDescriptor reduceDescriptor;
151 reduceDescriptor.m_vAxis = {1, 2};
152 reduceDescriptor.m_KeepDims = false;
153 reduceDescriptor.m_ReduceOperation = armnn::ReduceOperation::Sum;
154
155 TensorShape inputShape = {1, 3, 2, 4};
156 TensorShape outputShape = {1, 4};
157
158 // Construct ArmNN network
159 INetworkPtr network = CreateSimpleReduceNetwork(reduceDescriptor, inputShape, outputShape);
160
161 // Creates structures for input & output.
162 const std::vector<float> inputData({1.0f, 2.0f, 3.0f, 4.0f,
163 5.0f, 6.0f, 7.0f, 8.0f,
164
165 10.0f, 20.0f, 30.0f, 40.0f,
166 50.0f, 60.0f, 70.0f, 80.0f,
167
168 100.0f, 200.0f, 300.0f, 400.0f,
169 500.0f, 600.0f, 700.0f, 800.0f});
170 const std::vector<float> expectedOutput({666.0f, 888.0f, 1110.0f, 1332.0f});
171
172 ReduceWithMultipleAxesTest(network,
173 outputShape,
174 inputData,
175 expectedOutput,
176 reduceDescriptor.m_vAxis.size(),
177 backendId);
178 }
179
ReduceSumWithThreeAxesKeepDimsTest(Compute backendId)180 void ReduceSumWithThreeAxesKeepDimsTest(Compute backendId)
181 {
182 armnn::ReduceDescriptor reduceDescriptor;
183 reduceDescriptor.m_vAxis = {0, 2, 3};
184 reduceDescriptor.m_KeepDims = true;
185 reduceDescriptor.m_ReduceOperation = armnn::ReduceOperation::Sum;
186
187 TensorShape inputShape = {2, 2, 2, 2};
188 TensorShape outputShape = {1, 2, 1, 1};
189
190 // Construct ArmNN network
191 INetworkPtr network = CreateSimpleReduceNetwork(reduceDescriptor, inputShape, outputShape);
192
193 // Creates structures for input & output.
194 const std::vector<float> inputData({1.0f, 2.0f,
195 3.0f, 4.0f,
196
197 5.0f, 6.0f,
198 7.0f, 8.0f,
199
200 10.0f, 20.0f,
201 30.0f, 40.0f,
202
203 50.0f, 60.0f,
204 70.0f, 80.0f});
205 const std::vector<float> expectedOutput({110.0f, 286.0f});
206
207 ReduceWithMultipleAxesTest(network,
208 outputShape,
209 inputData,
210 expectedOutput,
211 reduceDescriptor.m_vAxis.size(),
212 backendId);
213 }
214
ReduceSumWithThreeAxesTest(Compute backendId)215 void ReduceSumWithThreeAxesTest(Compute backendId)
216 {
217 armnn::ReduceDescriptor reduceDescriptor;
218 reduceDescriptor.m_vAxis = {0, 2, 3};
219 reduceDescriptor.m_KeepDims = false;
220 reduceDescriptor.m_ReduceOperation = armnn::ReduceOperation::Sum;
221
222 TensorShape inputShape = {2, 2, 2, 2};
223 TensorShape outputShape = {2};
224
225 // Construct ArmNN network
226 INetworkPtr network = CreateSimpleReduceNetwork(reduceDescriptor, inputShape, outputShape);
227
228 // Creates structures for input & output.
229 const std::vector<float> inputData({1.0f, 2.0f,
230 3.0f, 4.0f,
231
232 5.0f, 6.0f,
233 7.0f, 8.0f,
234
235 10.0f, 20.0f,
236 30.0f, 40.0f,
237
238 50.0f, 60.0f,
239 70.0f, 80.0f});
240 const std::vector<float> expectedOutput({110.0f, 286.0f});
241
242 ReduceWithMultipleAxesTest(network,
243 outputShape,
244 inputData,
245 expectedOutput,
246 reduceDescriptor.m_vAxis.size(),
247 backendId);
248 }
249 #endif
250 }
251
252 #if defined(ARMCOMPUTENEON_ENABLED)
253 TEST_SUITE("Optimizer_ReduceMultipleAxesCpu")
254 {
255 TEST_CASE("ReduceSumWithTwoAxesKeepDimsCpuAccTest")
256 {
257 ReduceSumWithTwoAxesKeepDimsTest(Compute::CpuAcc);
258 }
259
260 TEST_CASE("ReduceSumWithTwoAxesCpuAccTest")
261 {
262 ReduceSumWithTwoAxesTest(Compute::CpuAcc);
263 }
264
265 TEST_CASE("ReduceSumWithThreeAxesKeepDimsCpuAccTest")
266 {
267 ReduceSumWithThreeAxesKeepDimsTest(Compute::CpuAcc);
268 }
269
270 TEST_CASE("ReduceSumWithThreeAxesCpuAccTest")
271 {
272 ReduceSumWithThreeAxesTest(Compute::CpuAcc);
273 }
274 }
275 #endif
276
277 #if defined(ARMCOMPUTECL_ENABLED)
278 TEST_SUITE("Optimizer_ReduceMultipleAxesGpu")
279 {
280 TEST_CASE("ReduceSumWithTwoAxesKeepDimsGpuAccTest")
281 {
282 ReduceSumWithTwoAxesKeepDimsTest(Compute::GpuAcc);
283 }
284
285 TEST_CASE("ReduceSumWithTwoAxesGpuAccTest")
286 {
287 ReduceSumWithTwoAxesTest(Compute::GpuAcc);
288 }
289
290 TEST_CASE("ReduceSumWithThreeAxesKeepDimsGpuAccTest")
291 {
292 ReduceSumWithThreeAxesKeepDimsTest(Compute::GpuAcc);
293 }
294
295 TEST_CASE("ReduceSumWithThreeAxesGpuAccTest")
296 {
297 ReduceSumWithThreeAxesTest(Compute::GpuAcc);
298 }
299 }
300 #endif
301