IVGCVSW-2093 Add SpaceToBatchNd layer and corresponding no-op factory implementations
[platform/upstream/armnn.git] / src / backends / test / OptimizedNetworkTests.cpp
1 //
2 // Copyright © 2017 Arm Ltd. All rights reserved.
3 // SPDX-License-Identifier: MIT
4 //
5
6 #include <armnn/ArmNN.hpp>
7 #include <armnn/Graph.hpp>
8 #include <armnn/Network.hpp>
9
10 #include <backends/reference/RefWorkloadFactory.hpp>
11
12 #include <boost/test/unit_test.hpp>
13
14 BOOST_AUTO_TEST_SUITE(OptimizedNetwork)
15
16 BOOST_AUTO_TEST_CASE(SerializeToDot)
17 {
18     armnn::Network net;
19
20     //Defines layers.
21     auto input = net.AddInputLayer(0);
22     auto add = net.AddAdditionLayer();
23     auto output = net.AddOutputLayer(0);
24
25     // Connects layers.
26     input->GetOutputSlot(0).Connect(add->GetInputSlot(0));
27     input->GetOutputSlot(0).Connect(add->GetInputSlot(1));
28     add->GetOutputSlot(0).Connect(output->GetInputSlot(0));
29
30     armnn::TensorShape shape({4});
31     armnn::TensorInfo info(shape, armnn::DataType::Float32);
32     input->GetOutputSlot(0).SetTensorInfo(info);
33     add->GetOutputSlot(0).SetTensorInfo(info);
34
35     armnn::IRuntime::CreationOptions options;
36     armnn::IRuntimePtr runtime(armnn::IRuntime::Create(options));
37
38     std::vector<armnn::BackendId> backends = {armnn::Compute::CpuRef};
39     armnn::IOptimizedNetworkPtr optimizedNet = armnn::Optimize(net, backends, runtime->GetDeviceSpec());
40
41     std::ostringstream ss;
42     optimizedNet->SerializeToDot(ss);
43
44     auto inputId = input->GetGuid();
45     auto addId = add->GetGuid();
46     auto outputId = output->GetGuid();
47
48     std::stringstream expected;
49     expected <<
50         "digraph Optimized {\n"
51         "    node [shape=\"record\"];\n"
52         "    edge [fontsize=8 fontcolor=\"blue\" fontname=\"arial-bold\"];\n"
53         "    " << inputId << " [label=\"{Input}\"];\n"
54         "    " << addId << " [label=\"{Addition}\"];\n"
55         "    " << outputId << " [label=\"{Output}\"];\n"
56         "    " << inputId << " -> " << addId << " [label=< [4] >];\n"
57         "    " << inputId << " -> " << addId << " [label=< [4] >];\n"
58         "    " << addId << " -> " << outputId << " [label=< [4] >];\n"
59         "}\n";
60
61     BOOST_TEST(ss.str() == expected.str());
62 }
63
64 BOOST_AUTO_TEST_CASE(OptimizeValidateDeviceNonSupportLayerNoFallback)
65 {
66     // build up the structure of the network
67     armnn::INetworkPtr net(armnn::INetwork::Create());
68
69     armnn::IConnectableLayer* input = net->AddInputLayer(0);
70
71     // This layer configuration isn't supported by CpuAcc and isn't allowed to fall back, so Optimize will return null.
72     armnn::NormalizationDescriptor descriptor;
73     armnn::IConnectableLayer* normalize = net->AddNormalizationLayer(descriptor);
74
75     armnn::IConnectableLayer* output = net->AddOutputLayer(0);
76
77     input->GetOutputSlot(0).Connect(normalize->GetInputSlot(0));
78     normalize->GetOutputSlot(0).Connect(output->GetInputSlot(0));
79
80     input->GetOutputSlot(0).SetTensorInfo(armnn::TensorInfo({ 1, 1, 4, 4 }, armnn::DataType::Float32));
81     normalize->GetOutputSlot(0).SetTensorInfo(armnn::TensorInfo({ 1, 1, 4, 4 }, armnn::DataType::Float32));
82
83     armnn::IRuntime::CreationOptions options;
84     armnn::IRuntimePtr runtime(armnn::IRuntime::Create(options));
85
86     std::vector<armnn::BackendId> backends = { armnn::Compute::CpuAcc };
87     armnn::IOptimizedNetworkPtr optNet = armnn::Optimize(*net, backends, runtime->GetDeviceSpec());
88     BOOST_CHECK(!optNet);
89 }
90
91 BOOST_AUTO_TEST_CASE(OptimizeValidateDeviceNonSupportLayerWithFallback)
92 {
93     // build up the structure of the network
94     armnn::INetworkPtr net(armnn::INetwork::Create());
95
96     armnn::IConnectableLayer* input = net->AddInputLayer(0);
97
98     // This layer configuration isn't supported by CpuAcc but it allows to fallback to CpuRef.
99     armnn::NormalizationDescriptor descriptor;
100     armnn::IConnectableLayer* normalize = net->AddNormalizationLayer(descriptor);
101
102     armnn::IConnectableLayer* output = net->AddOutputLayer(0);
103
104     input->GetOutputSlot(0).Connect(normalize->GetInputSlot(0));
105     normalize->GetOutputSlot(0).Connect(output->GetInputSlot(0));
106
107     input->GetOutputSlot(0).SetTensorInfo(armnn::TensorInfo({ 1, 1, 4, 4 }, armnn::DataType::Float32));
108     normalize->GetOutputSlot(0).SetTensorInfo(armnn::TensorInfo({ 1, 1, 4, 4 }, armnn::DataType::Float32));
109
110     armnn::IRuntime::CreationOptions options;
111     armnn::IRuntimePtr runtime(armnn::IRuntime::Create(options));
112
113     std::vector<armnn::BackendId> backends = { armnn::Compute::CpuAcc, armnn::Compute::CpuRef };
114     armnn::IOptimizedNetworkPtr optNet = armnn::Optimize(*net, backends, runtime->GetDeviceSpec());
115     BOOST_REQUIRE(optNet);
116
117     for (auto&& layer : static_cast<armnn::OptimizedNetwork*>(optNet.get())->GetGraph())
118     {
119         // If NEON is enabled, Input and Output layers are supported by CpuAcc,
120         // the other layers are supported by CpuRef.
121         // If NEON is not enabled, all layers are supported by CpuRef.
122 #if ARMCOMPUTENEON_ENABLED
123         if (layer->GetType() == armnn::LayerType::Input || layer->GetType() == armnn::LayerType::Output)
124         {
125             BOOST_CHECK(layer->GetBackendId() == armnn::Compute::CpuAcc);
126         }
127         else if (layer->GetType() == armnn::LayerType::Normalization)
128         {
129             BOOST_CHECK(layer->GetBackendId() == armnn::Compute::CpuRef);
130         }
131 #else
132         BOOST_CHECK(layer->GetBackendId() == armnn::Compute::CpuRef);
133 #endif
134     }
135 }
136
137 BOOST_AUTO_TEST_CASE(OptimizeValidateWorkloadsUndefinedComputeDevice)
138 {
139     const armnn::TensorInfo desc({3, 5}, armnn::DataType::Float32);
140
141     armnn::Network  net;
142
143     armnn::NormalizationDescriptor nmDesc;
144     armnn::ActivationDescriptor acDesc;
145
146     //    in
147     //     |
148     //    nm
149     //   /  |
150     //  ac  |
151     //   \  |
152     //    ml
153     //     |
154     //    sm
155     //     |
156     //    ot
157     armnn::IConnectableLayer* layer = net.AddInputLayer(0, "in");
158     layer->GetOutputSlot(0).SetTensorInfo(desc);
159
160     armnn::IConnectableLayer* const normLayer = net.AddNormalizationLayer(nmDesc, "nm");
161
162     layer->GetOutputSlot(0).Connect(normLayer->GetInputSlot(0));
163     normLayer->GetOutputSlot(0).SetTensorInfo(desc);
164
165     layer = net.AddActivationLayer(acDesc, "ac");
166
167     normLayer->GetOutputSlot(0).Connect(layer->GetInputSlot(0));
168     layer->GetOutputSlot(0).SetTensorInfo(desc);
169
170     armnn::IConnectableLayer* prevLayer = layer;
171     layer = net.AddMultiplicationLayer("ml");
172
173     prevLayer->GetOutputSlot(0).Connect(layer->GetInputSlot(0));
174     normLayer->GetOutputSlot(0).Connect(layer->GetInputSlot(1));
175     layer->GetOutputSlot(0).SetTensorInfo(desc);
176
177     prevLayer = layer;
178     armnn::SoftmaxDescriptor softmaxDescriptor;
179     layer = net.AddSoftmaxLayer(softmaxDescriptor, "sm");
180
181     prevLayer->GetOutputSlot(0).Connect(layer->GetInputSlot(0));
182     layer->GetOutputSlot(0).SetTensorInfo(desc);
183
184     prevLayer = layer;
185     layer = net.AddOutputLayer(0, "ot");
186
187     prevLayer->GetOutputSlot(0).Connect(layer->GetInputSlot(0));
188
189     armnn::IRuntime::CreationOptions options;
190     armnn::IRuntimePtr runtime(armnn::IRuntime::Create(options));
191
192     std::vector<armnn::BackendId> backends = { armnn::Compute::Undefined };
193
194     armnn::IOptimizedNetworkPtr optNet = armnn::Optimize(net, backends, runtime->GetDeviceSpec());
195     BOOST_CHECK(!optNet);
196
197 }
198
199 BOOST_AUTO_TEST_CASE(OptimizeValidateWorkloadsUndefinedComputeDeviceWithFallback)
200 {
201     const armnn::TensorInfo desc({3, 5}, armnn::DataType::Float32);
202
203     armnn::Network  net;
204
205     armnn::NormalizationDescriptor nmDesc;
206     armnn::ActivationDescriptor acDesc;
207
208     //    in
209     //     |
210     //    nm
211     //   /  |
212     //  ac  |
213     //   \  |
214     //    ml
215     //     |
216     //    sm
217     //     |
218     //    ot
219     armnn::IConnectableLayer* layer = net.AddInputLayer(0, "in");
220     layer->GetOutputSlot(0).SetTensorInfo(desc);
221
222     armnn::IConnectableLayer* const normLayer = net.AddNormalizationLayer(nmDesc, "nm");
223
224     layer->GetOutputSlot(0).Connect(normLayer->GetInputSlot(0));
225     normLayer->GetOutputSlot(0).SetTensorInfo(desc);
226
227     layer = net.AddActivationLayer(acDesc, "ac");
228
229     normLayer->GetOutputSlot(0).Connect(layer->GetInputSlot(0));
230     layer->GetOutputSlot(0).SetTensorInfo(desc);
231
232     armnn::IConnectableLayer* prevLayer = layer;
233     layer = net.AddMultiplicationLayer("ml");
234
235     prevLayer->GetOutputSlot(0).Connect(layer->GetInputSlot(0));
236     normLayer->GetOutputSlot(0).Connect(layer->GetInputSlot(1));
237     layer->GetOutputSlot(0).SetTensorInfo(desc);
238
239     prevLayer = layer;
240     armnn::SoftmaxDescriptor softmaxDescriptor;
241     layer = net.AddSoftmaxLayer(softmaxDescriptor, "sm");
242
243     prevLayer->GetOutputSlot(0).Connect(layer->GetInputSlot(0));
244     layer->GetOutputSlot(0).SetTensorInfo(desc);
245
246     prevLayer = layer;
247     layer = net.AddOutputLayer(0, "ot");
248
249     prevLayer->GetOutputSlot(0).Connect(layer->GetInputSlot(0));
250
251     armnn::IRuntime::CreationOptions options;
252     armnn::IRuntimePtr runtime(armnn::IRuntime::Create(options));
253
254     std::vector<armnn::BackendId> backends = { armnn::Compute::Undefined, armnn::Compute::CpuRef };
255
256     armnn::IOptimizedNetworkPtr optNet = armnn::Optimize(net, backends, runtime->GetDeviceSpec());
257     BOOST_CHECK(optNet);
258
259     // validate workloads
260     armnn::RefWorkloadFactory fact;
261     for (auto&& layer : static_cast<armnn::OptimizedNetwork*>(optNet.get())->GetGraph())
262     {
263         BOOST_CHECK(layer->GetBackendId() == armnn::Compute::CpuRef);
264         BOOST_CHECK_NO_THROW(
265             layer->CreateWorkload(static_cast<armnn::OptimizedNetwork*>(optNet.get())->GetGraph(), fact));
266     }
267 }
268
269 BOOST_AUTO_TEST_CASE(OptimizeValidateWorkloadsDuplicateComputeDeviceWithFallback)
270 {
271     // build up the structure of the network
272     armnn::INetworkPtr net(armnn::INetwork::Create());
273
274     armnn::IConnectableLayer* input = net->AddInputLayer(0);
275
276     // This layer configuration isn't supported by CpuAcc but it allows to fallback to CpuRef.
277     armnn::NormalizationDescriptor descriptor;
278     armnn::IConnectableLayer* normalize = net->AddNormalizationLayer(descriptor);
279
280     armnn::IConnectableLayer* output = net->AddOutputLayer(0);
281
282     input->GetOutputSlot(0).Connect(normalize->GetInputSlot(0));
283     normalize->GetOutputSlot(0).Connect(output->GetInputSlot(0));
284
285     input->GetOutputSlot(0).SetTensorInfo(armnn::TensorInfo({ 1, 1, 4, 4 }, armnn::DataType::Float32));
286     normalize->GetOutputSlot(0).SetTensorInfo(armnn::TensorInfo({ 1, 1, 4, 4 }, armnn::DataType::Float32));
287
288     armnn::IRuntime::CreationOptions options;
289     armnn::IRuntimePtr runtime(armnn::IRuntime::Create(options));
290
291     std::vector<armnn::BackendId> backends = { armnn::Compute::CpuAcc,
292                                              armnn::Compute::GpuAcc,
293                                              armnn::Compute::CpuRef };
294
295     armnn::IOptimizedNetworkPtr optNet = armnn::Optimize(*net, backends, runtime->GetDeviceSpec());
296     BOOST_REQUIRE(optNet);
297
298     for (auto&& layer : static_cast<armnn::OptimizedNetwork*>(optNet.get())->GetGraph())
299     {
300         // If NEON is enabled, Input and Output layers are supported by CpuAcc,
301         // the other layers are supported by CpuRef.
302         // If only CL is enabled, Input and Output layers are supported by GpuAcc,
303         // the other layers are supported by CpuRef.
304         // If neither NEON, nor CL is enabled, all layers are supported by CpuRef.
305 #if ARMCOMPUTENEON_ENABLED
306         if (layer->GetType() == armnn::LayerType::Input || layer->GetType() == armnn::LayerType::Output)
307         {
308             BOOST_CHECK(layer->GetBackendId() == armnn::Compute::CpuAcc);
309         }
310         else if (layer->GetType() == armnn::LayerType::Normalization)
311         {
312             BOOST_CHECK(layer->GetBackendId() == armnn::Compute::CpuRef);
313         }
314 #elif ARMCOMPUTECL_ENABLED
315         if (layer->GetType() == armnn::LayerType::Input || layer->GetType() == armnn::LayerType::Output)
316         {
317             BOOST_CHECK(layer->GetBackendId() == armnn::Compute::GpuAcc);
318         }
319         else if (layer->GetType() == armnn::LayerType::Normalization)
320         {
321             BOOST_CHECK(layer->GetBackendId() == armnn::Compute::CpuRef);
322         }
323 #else
324         BOOST_CHECK(layer->GetBackendId() == armnn::Compute::CpuRef);
325 #endif
326     }
327 }
328
329 BOOST_AUTO_TEST_SUITE_END()