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