1 //
2 // Copyright © 2017, 2019-2023 Arm Ltd and Contributors. All rights reserved.
3 // SPDX-License-Identifier: MIT
4 //
5
6
7 #include <CommonTestUtils.hpp>
8
9 #include <Graph.hpp>
10 #include <Network.hpp>
11 #include <SubgraphViewSelector.hpp>
12
13 #include <armnn/backends/OptimizationViews.hpp>
14 #include <armnn/backends/SubgraphView.hpp>
15 #include <armnn/utility/PolymorphicDowncast.hpp>
16 #include <armnnTestUtils/MockBackend.hpp>
17
18 #include <doctest/doctest.h>
19
20 using namespace armnn;
21
CheckLayers(Graph & graph)22 void CheckLayers(Graph& graph)
23 {
24 unsigned int m_inputLayerCount = 0, m_outputLayerCount = 0, m_addLayerCount = 0;
25 for(auto layer : graph)
26 {
27 switch(layer->GetType())
28 {
29 case LayerType::Input:
30 ++m_inputLayerCount;
31 CHECK((layer->GetName() == std::string("inLayer0") ||
32 layer->GetName() == std::string("inLayer1")));
33 break;
34 // The Addition layer should become a PreCompiled Layer after Optimisation
35 case LayerType::PreCompiled:
36 ++m_addLayerCount;
37 CHECK(std::string(layer->GetName()) == "pre-compiled");
38 break;
39 case LayerType::Output:
40 ++m_outputLayerCount;
41 CHECK(std::string(layer->GetName()) == "outLayer");
42 break;
43 default:
44 //Fail for anything else
45 CHECK(false);
46 }
47 }
48 CHECK(m_inputLayerCount == 2);
49 CHECK(m_outputLayerCount == 1);
50 CHECK(m_addLayerCount == 1);
51 }
52
53 TEST_SUITE("OptimizationViewsTestSuite")
54 {
55 TEST_CASE("OptimizedViewsSubgraphLayerCount")
56 {
57 OptimizationViews view;
58 // Construct a graph with 3 layers
59 Graph baseGraph;
60
61 Layer* const inputLayer = baseGraph.AddLayer<InputLayer>(0, "input");
62
63 Convolution2dDescriptor convDescriptor;
64 PreCompiledDescriptor substitutionLayerDescriptor(2, 1);
65 Layer* const convLayer1 = baseGraph.AddLayer<Convolution2dLayer>(convDescriptor, "conv1");
66 Layer* const convLayer2 = baseGraph.AddLayer<Convolution2dLayer>(convDescriptor, "conv2");
67 Layer* const weightsLayer1 = baseGraph.AddLayer<ConstantLayer>("weights1");
68 Layer* const weightsLayer2 = baseGraph.AddLayer<ConstantLayer>("weights2");
69 Layer* const substitutableCompiledLayer =
70 baseGraph.AddLayer<PreCompiledLayer>(substitutionLayerDescriptor, "pre-compiled");
71
72 Layer* const outputLayer = baseGraph.AddLayer<OutputLayer>(0, "output");
73
74 inputLayer->GetOutputSlot(0).Connect(convLayer1->GetInputSlot(0));
75 weightsLayer1->GetOutputSlot(0).Connect(convLayer1->GetInputSlot(1));
76 convLayer1->GetOutputSlot(0).Connect(convLayer2->GetInputSlot(0));
77 weightsLayer2->GetOutputSlot(0).Connect(convLayer2->GetInputSlot(1));
78 convLayer2->GetOutputSlot(0).Connect(outputLayer->GetInputSlot(0));
79
80 // Subgraph for a failed layer
81 SubgraphView::SubgraphViewPtr failedSubgraph =
82 CreateSubgraphViewFrom(CreateInputsFrom(convLayer1),
83 CreateOutputsFrom({convLayer1}),
84 {convLayer1});
85 // Subgraph for an untouched layer
86 SubgraphView::SubgraphViewPtr untouchedSubgraph =
87 CreateSubgraphViewFrom(CreateInputsFrom(convLayer2),
88 CreateOutputsFrom({convLayer2}),
89 {convLayer2});
90 // Subgraph for a substitutable layer
91 SubgraphView::SubgraphViewPtr substitutableSubgraph =
92 CreateSubgraphViewFrom(CreateInputsFrom(convLayer1),
93 CreateOutputsFrom({convLayer2}),
94 {substitutableCompiledLayer});
95 // Create a Graph containing a layer to substitute in
96 Graph substitutableGraph;
97 Layer* const substitutionpreCompiledLayer =
98 substitutableGraph.AddLayer<PreCompiledLayer>(substitutionLayerDescriptor, "pre-compiled");
99
100 // Subgraph for a substitution layer
101 SubgraphView::SubgraphViewPtr substitutionSubgraph =
102 CreateSubgraphViewFrom(CreateInputsFrom(substitutionpreCompiledLayer),
103 CreateOutputsFrom({substitutionpreCompiledLayer}),
104 {substitutionpreCompiledLayer});
105
106 // Sub in the graph
107 baseGraph.SubstituteSubgraph(*substitutableSubgraph, *substitutionSubgraph);
108
109 view.AddFailedSubgraph(SubgraphView(*failedSubgraph));
110 view.AddUntouchedSubgraph(SubgraphView(*untouchedSubgraph));
111
112 SubgraphView::SubgraphViewPtr baseSubgraph =
113 CreateSubgraphViewFrom(CreateInputsFrom(convLayer1),
114 CreateOutputsFrom({convLayer2}),
115 {substitutionpreCompiledLayer});
116 view.AddSubstitution({*baseSubgraph, *substitutionSubgraph});
117
118 // Construct original subgraph to compare against
119 SubgraphView::SubgraphViewPtr originalSubgraph =
120 CreateSubgraphViewFrom(CreateInputsFrom(convLayer1),
121 CreateOutputsFrom({convLayer2}),
122 {convLayer1, convLayer2, substitutionpreCompiledLayer});
123
124 CHECK(view.Validate(*originalSubgraph));
125 }
126
127
128 TEST_CASE("OptimizedViewsSubgraphLayerCountUsingGetINetwork")
129 {
130 OptimizationViews view;
131
132 IConnectableLayer* const inputLayer = view.GetINetwork()->AddInputLayer(0, "input");
133
134 DepthwiseConvolution2dDescriptor convDescriptor;
135 PreCompiledDescriptor substitutionLayerDescriptor(2, 1);
136 CompiledBlobPtr blobPtr;
137 BackendId backend = Compute::CpuRef;
138
139 Layer* convLayer1 = PolymorphicDowncast<Layer*>(
140 view.GetINetwork()->AddDepthwiseConvolution2dLayer(convDescriptor,
141 "conv1"));
142
143 Layer* convLayer2 = PolymorphicDowncast<Layer*>(
144 view.GetINetwork()->AddDepthwiseConvolution2dLayer(convDescriptor,
145 "conv2"));
146
147 IConnectableLayer* const outputLayer = view.GetINetwork()->AddOutputLayer(0, "output");
148
149 inputLayer->GetOutputSlot(0).Connect(convLayer1->GetInputSlot(0));
150 convLayer1->GetOutputSlot(0).Connect(convLayer2->GetInputSlot(0));
151 convLayer2->GetOutputSlot(0).Connect(outputLayer->GetInputSlot(0));
152
153 // Subgraph for a failed layer
154 SubgraphView::SubgraphViewPtr failedSubgraph = CreateSubgraphViewFrom(CreateInputsFrom(convLayer1),
155 CreateOutputsFrom({convLayer1}),
156 {convLayer1});
157 // Subgraph for an untouched layer
158 SubgraphView::SubgraphViewPtr untouchedSubgraph = CreateSubgraphViewFrom(CreateInputsFrom(convLayer2),
159 CreateOutputsFrom({convLayer2}),
160 {convLayer2});
161
162 // Create a Network containing a layer to substitute in
163 NetworkImpl net;
164 Layer* substitutionpreCompiledLayer = PolymorphicDowncast<Layer*>(
165 net.AddPrecompiledLayer(substitutionLayerDescriptor, std::move(blobPtr), backend));
166
167 // Subgraph for a substitution layer
168 SubgraphView::SubgraphViewPtr substitutionSubgraph =
169 CreateSubgraphViewFrom(CreateInputsFrom(substitutionpreCompiledLayer),
170 CreateOutputsFrom({substitutionpreCompiledLayer}),
171 {substitutionpreCompiledLayer});
172
173 view.AddFailedSubgraph(SubgraphView(*failedSubgraph));
174 view.AddUntouchedSubgraph(SubgraphView(*untouchedSubgraph));
175
176 SubgraphView::SubgraphViewPtr baseSubgraph = CreateSubgraphViewFrom(CreateInputsFrom(convLayer1),
177 CreateOutputsFrom({convLayer2}),
178 {substitutionpreCompiledLayer});
179 view.AddSubstitution({*baseSubgraph, *substitutionSubgraph});
180
181 // Construct original subgraph to compare against
182 SubgraphView::SubgraphViewPtr originalSubgraph =
183 CreateSubgraphViewFrom(CreateInputsFrom(convLayer1),
184 CreateOutputsFrom({convLayer2}),
185 {convLayer1, convLayer2, substitutionpreCompiledLayer});
186
187 CHECK(view.Validate(*originalSubgraph));
188 }
189
190 TEST_CASE("OptimizedViewsSubgraphLayerCountFailValidate")
191 {
192 OptimizationViews view;
193 // Construct a graph with 3 layers
194 Graph baseGraph;
195
196 Layer* const inputLayer = baseGraph.AddLayer<InputLayer>(0, "input");
197
198 Convolution2dDescriptor convDescriptor;
199 PreCompiledDescriptor substitutionLayerDescriptor(2, 1);
200 Layer* const convLayer1 = baseGraph.AddLayer<Convolution2dLayer>(convDescriptor, "conv1");
201 Layer* const convLayer2 = baseGraph.AddLayer<Convolution2dLayer>(convDescriptor, "conv2");
202 Layer* const weightsLayer1 = baseGraph.AddLayer<ConstantLayer>("weights1");
203 Layer* const weightsLayer2 = baseGraph.AddLayer<ConstantLayer>("weights2");
204 Layer* const substitutableCompiledLayer =
205 baseGraph.AddLayer<PreCompiledLayer>(substitutionLayerDescriptor, "pre-compiled");
206
207 Layer* const outputLayer = baseGraph.AddLayer<OutputLayer>(0, "output");
208
209
210 inputLayer->GetOutputSlot(0).Connect(convLayer1->GetInputSlot(0));
211 weightsLayer1->GetOutputSlot(0).Connect(convLayer1->GetInputSlot(1));
212 convLayer1->GetOutputSlot(0).Connect(convLayer2->GetInputSlot(0));
213 weightsLayer2->GetOutputSlot(0).Connect(convLayer2->GetInputSlot(1));
214 convLayer2->GetOutputSlot(0).Connect(outputLayer->GetInputSlot(0));
215
216 // Subgraph for an untouched layer
217 SubgraphView::SubgraphViewPtr untouchedSubgraph =
218 CreateSubgraphViewFrom(CreateInputsFrom(convLayer2),
219 CreateOutputsFrom({convLayer2}),
220 {convLayer2});
221 // Subgraph for a substitutable layer
222 SubgraphView::SubgraphViewPtr substitutableSubgraph =
223 CreateSubgraphViewFrom(CreateInputsFrom(convLayer1),
224 CreateOutputsFrom({convLayer2}),
225 {substitutableCompiledLayer});
226 // Create a Graph containing a layer to substitute in
227 Graph substitutableGraph;
228 Layer* const substitutionpreCompiledLayer =
229 substitutableGraph.AddLayer<PreCompiledLayer>(substitutionLayerDescriptor, "pre-compiled");
230
231 // Subgraph for a substitution layer
232 SubgraphView::SubgraphViewPtr substitutionSubgraph =
233 CreateSubgraphViewFrom(CreateInputsFrom(substitutionpreCompiledLayer),
234 CreateOutputsFrom({substitutionpreCompiledLayer}),
235 {substitutionpreCompiledLayer});
236
237 // Sub in the graph
238 baseGraph.SubstituteSubgraph(*substitutableSubgraph, *substitutionSubgraph);
239
240 view.AddUntouchedSubgraph(SubgraphView(*untouchedSubgraph));
241
242 SubgraphView::SubgraphViewPtr baseSubgraph =
243 CreateSubgraphViewFrom(CreateInputsFrom(convLayer1),
244 CreateOutputsFrom({convLayer2}),
245 {substitutionpreCompiledLayer});
246 view.AddSubstitution({*baseSubgraph, *substitutionSubgraph});
247
248 // Construct original subgraph to compare against
249 SubgraphView::SubgraphViewPtr originalSubgraph =
250 CreateSubgraphViewFrom(CreateInputsFrom(convLayer1),
251 CreateOutputsFrom({convLayer2}),
252 {convLayer1, convLayer2, substitutionpreCompiledLayer});
253
254 // Validate should fail as convLayer1 is not counted
255 CHECK(!view.Validate(*originalSubgraph));
256 }
257
258 TEST_CASE("OptimizeViewsValidateDeviceMockBackend")
259 {
260 // build up the structure of the network
261 armnn::INetworkPtr net(armnn::INetwork::Create());
262
263 armnn::IConnectableLayer* input = net->AddInputLayer(0, "inLayer0");
264 armnn::IConnectableLayer* input1 = net->AddInputLayer(1, "inLayer1");
265
266 ARMNN_NO_DEPRECATE_WARN_BEGIN
267 armnn::IConnectableLayer* addition = net->AddAdditionLayer("addLayer");
268 ARMNN_NO_DEPRECATE_WARN_END
269
270 armnn::IConnectableLayer* output = net->AddOutputLayer(0, "outLayer");
271
272 input->GetOutputSlot(0).Connect(addition->GetInputSlot(0));
273 input1->GetOutputSlot(0).Connect(addition->GetInputSlot(1));
274 addition->GetOutputSlot(0).Connect(output->GetInputSlot(0));
275
276 input->GetOutputSlot(0).SetTensorInfo(armnn::TensorInfo({ 1, 1, 4, 4 }, armnn::DataType::Float32));
277 input1->GetOutputSlot(0).SetTensorInfo(armnn::TensorInfo({ 1, 1, 4, 4 }, armnn::DataType::Float32));
278 addition->GetOutputSlot(0).SetTensorInfo(armnn::TensorInfo({ 1, 1, 4, 4 }, armnn::DataType::Float32));
279
280 armnn::MockBackendInitialiser initialiser;
281 armnn::IRuntime::CreationOptions options;
282 armnn::IRuntimePtr runtime(armnn::IRuntime::Create(options));
283
284 std::vector<armnn::BackendId> backends = { MockBackend().GetIdStatic() };
285 armnn::IOptimizedNetworkPtr optNet = armnn::Optimize(*net, backends, runtime->GetDeviceSpec());
286 CHECK(optNet);
287
288 // Check the optimised graph
289 armnn::Graph& graph = GetGraphForTesting(optNet.get());
290 CheckLayers(graph);
291 }
292
293 TEST_CASE("OptimizedViewsReturnsINetworkReference")
294 {
295 OptimizationViews view;
296
297 auto layer = view.GetINetworkRef().AddInputLayer(0, "input");
298
299 // Check layer has been added to the referenced INetwork
300 CHECK(layer);
301 }
302
303
304 }
305