blob: 8845dae6c88c6495dd514cdef2e1029cdb7505c1 [file] [log] [blame]
Laurent Carlier749294b2020-06-01 09:03:17 +01001//
Teresa Charlin52664732020-06-29 16:27:03 +01002// Copyright © 2017 Arm Ltd and Contributors. All rights reserved.
David Beckecb56cd2018-09-05 12:52:57 +01003// SPDX-License-Identifier: MIT
surmeh01bceff2f2018-03-29 16:29:27 +01004//
Matteo Martincighbf0e7222019-06-20 17:17:45 +01005
6#include "TestUtils.hpp"
surmeh01bceff2f2018-03-29 16:29:27 +01007
Derek Lamberti4a9e24b2020-01-03 16:53:38 +00008#include <BackendSettings.hpp>
David Beckac42efd2018-09-26 17:41:13 +01009#include <Graph.hpp>
Derek Lamberti4a9e24b2020-01-03 16:53:38 +000010#include <Network.hpp>
David Beckac42efd2018-09-26 17:41:13 +010011#include <Optimizer.hpp>
Matteo Martincighe011d202019-11-28 11:35:47 +000012
Derek Lamberti4a9e24b2020-01-03 16:53:38 +000013#include <armnn/BackendRegistry.hpp>
14#include <armnn/INetwork.hpp>
15#include <armnn/LayerVisitorBase.hpp>
Matteo Martincighe011d202019-11-28 11:35:47 +000016
17#include <armnnUtils/FloatingPointConverter.hpp>
Jan Eilersbb446e52020-04-02 13:56:54 +010018#include <armnn/utility/PolymorphicDowncast.hpp>
Matteo Martincighe011d202019-11-28 11:35:47 +000019
Aron Virginas-Tarc9cc8042018-11-01 16:15:57 +000020#include <backendsCommon/CpuTensorHandle.hpp>
Derek Lamberti4a9e24b2020-01-03 16:53:38 +000021#include <backendsCommon/IBackendInternal.hpp>
22#include <backendsCommon/LayerSupportBase.hpp>
surmeh01bceff2f2018-03-29 16:29:27 +010023
Matteo Martincighbf0e7222019-06-20 17:17:45 +010024#include <boost/test/unit_test.hpp>
25
26using namespace armnn;
27
surmeh01bceff2f2018-03-29 16:29:27 +010028namespace
29{
telsoa01c577f2c2018-08-31 09:22:23 +010030
telsoa01c577f2c2018-08-31 09:22:23 +010031void CreateLSTMLayerHelper(Graph &graph, bool CifgEnabled)
32{
33 LstmDescriptor layerDesc;
34 layerDesc.m_ActivationFunc = 4;
35 layerDesc.m_ClippingThresCell = 0.2f;
36 layerDesc.m_ClippingThresProj = 0.4f;
37 layerDesc.m_CifgEnabled = CifgEnabled;
38 layerDesc.m_PeepholeEnabled = false;
39 layerDesc.m_ProjectionEnabled = false;
40
41 LstmLayer* const layer = graph.AddLayer<LstmLayer>(layerDesc, "layer");
42 unsigned int batchSize = 3;
43 unsigned int inputSize = 2;
44 unsigned int numUnits = 4;
45 unsigned int outputSize = 4;
46
47 layer->m_BasicParameters.m_InputToForgetWeights = std::make_unique<ScopedCpuTensorHandle>
48 (TensorInfo({ numUnits, inputSize }, DataType::Float32));
49 layer->m_BasicParameters.m_InputToCellWeights = std::make_unique<ScopedCpuTensorHandle>
50 (TensorInfo({ numUnits, inputSize }, DataType::Float32));
51 layer->m_BasicParameters.m_InputToOutputWeights = std::make_unique<ScopedCpuTensorHandle>
52 (TensorInfo({ numUnits, inputSize }, DataType::Float32));
53 layer->m_BasicParameters.m_RecurrentToForgetWeights = std::make_unique<ScopedCpuTensorHandle>
54 (TensorInfo({ numUnits, outputSize }, DataType::Float32));
55 layer->m_BasicParameters.m_RecurrentToCellWeights = std::make_unique<ScopedCpuTensorHandle>
56 (TensorInfo({ numUnits, outputSize }, DataType::Float32));
57 layer->m_BasicParameters.m_RecurrentToOutputWeights = std::make_unique<ScopedCpuTensorHandle>
58 (TensorInfo({ numUnits, outputSize }, DataType::Float32));
59 layer->m_BasicParameters.m_ForgetGateBias = std::make_unique<ScopedCpuTensorHandle>
60 (TensorInfo({ numUnits }, DataType::Float32));
61 layer->m_BasicParameters.m_CellBias = std::make_unique<ScopedCpuTensorHandle>
62 (TensorInfo({ numUnits }, DataType::Float32));
63 layer->m_BasicParameters.m_OutputGateBias = std::make_unique<ScopedCpuTensorHandle>
64 (TensorInfo({ numUnits }, DataType::Float32));
65
66 layer->m_BasicParameters.m_InputToForgetWeights->Allocate();
67 layer->m_BasicParameters.m_InputToCellWeights->Allocate();
68 layer->m_BasicParameters.m_InputToOutputWeights->Allocate();
69 layer->m_BasicParameters.m_RecurrentToForgetWeights->Allocate();
70 layer->m_BasicParameters.m_RecurrentToCellWeights->Allocate();
71 layer->m_BasicParameters.m_RecurrentToOutputWeights->Allocate();
72 layer->m_BasicParameters.m_ForgetGateBias->Allocate();
73 layer->m_BasicParameters.m_CellBias->Allocate();
74 layer->m_BasicParameters.m_OutputGateBias->Allocate();
75
76 if (!layerDesc.m_CifgEnabled)
77 {
78 layer->m_CifgParameters.m_InputToInputWeights = std::make_unique<ScopedCpuTensorHandle>
79 (TensorInfo({ numUnits, inputSize }, DataType::Float32));
80 layer->m_CifgParameters.m_RecurrentToInputWeights = std::make_unique<ScopedCpuTensorHandle>
81 (TensorInfo({ numUnits, outputSize }, DataType::Float32));
telsoa01c577f2c2018-08-31 09:22:23 +010082 layer->m_CifgParameters.m_InputGateBias = std::make_unique<ScopedCpuTensorHandle>
83 (TensorInfo({ numUnits }, DataType::Float32));
84 layer->m_CifgParameters.m_InputToInputWeights->Allocate();
85 layer->m_CifgParameters.m_RecurrentToInputWeights->Allocate();
telsoa01c577f2c2018-08-31 09:22:23 +010086 layer->m_CifgParameters.m_InputGateBias->Allocate();
87 }
88
89 if (layerDesc.m_ProjectionEnabled)
90 {
91 layer->m_ProjectionParameters.m_ProjectionWeights = std::make_unique<ScopedCpuTensorHandle>
92 (TensorInfo({ outputSize, numUnits }, DataType::Float32));
93 layer->m_ProjectionParameters.m_ProjectionBias = std::make_unique<ScopedCpuTensorHandle>
94 (TensorInfo({ outputSize }, DataType::Float32));
95 layer->m_ProjectionParameters.m_ProjectionWeights->Allocate();
96 layer->m_ProjectionParameters.m_ProjectionBias->Allocate();
97 }
98
99 if (layerDesc.m_PeepholeEnabled)
100 {
Jan Eilerse2062cd2020-03-30 15:07:45 +0100101 if (!layerDesc.m_CifgEnabled)
102 {
103 layer->m_PeepholeParameters.m_CellToInputWeights = std::make_unique<ScopedCpuTensorHandle>
104 (TensorInfo({ numUnits }, DataType::Float32));
105 layer->m_PeepholeParameters.m_CellToInputWeights->Allocate();
106 }
telsoa01c577f2c2018-08-31 09:22:23 +0100107 layer->m_PeepholeParameters.m_CellToForgetWeights = std::make_unique<ScopedCpuTensorHandle>
108 (TensorInfo({ numUnits }, DataType::Float32));
109 layer->m_PeepholeParameters.m_CellToOutputWeights = std::make_unique<ScopedCpuTensorHandle>
110 (TensorInfo({ numUnits }, DataType::Float32));
111 layer->m_PeepholeParameters.m_CellToForgetWeights->Allocate();
112 layer->m_PeepholeParameters.m_CellToOutputWeights->Allocate();
113 }
114
115 // create input and output layers
116 Layer* const input = graph.AddLayer<InputLayer>(0, "input");
117 Layer* const outputStateIn = graph.AddLayer<InputLayer>(1, "outputStateIn");
118 Layer* const cellStateIn = graph.AddLayer<InputLayer>(2, "cellStateIn");
119 Layer* const scratchBuffer = graph.AddLayer<OutputLayer>(0, "scratchBuffer");
120 Layer* const outputStateOut = graph.AddLayer<OutputLayer>(1, "outputStateOut");
121 Layer* const cellStateOut = graph.AddLayer<OutputLayer>(2, "cellStateOut");
122 Layer* const output = graph.AddLayer<OutputLayer>(3, "output");
123
124 // connect up
125 armnn::TensorInfo lstmTensorInfo1({ batchSize, inputSize }, DataType::Float32);
126 armnn::TensorInfo lstmTensorInfo2({ batchSize, numUnits}, DataType::Float32);
127 armnn::TensorInfo lstmTensorInfo3({ batchSize, outputSize }, DataType::Float32);
Matteo Martincigha65b7ae2018-11-14 12:39:55 +0000128 armnn::TensorInfo lstmTensorInfoScratchBuff({ batchSize, numUnits * (layerDesc.m_CifgEnabled ? 3 : 4) },
129 DataType::Float32);
telsoa01c577f2c2018-08-31 09:22:23 +0100130
131 Connect(input, layer, lstmTensorInfo1, 0, 0);
132 Connect(cellStateIn, layer, lstmTensorInfo2, 0, 1);
133 Connect(outputStateIn, layer, lstmTensorInfo3, 0, 2);
134 Connect(layer, scratchBuffer, lstmTensorInfoScratchBuff, 0, 0);
135 Connect(layer, outputStateOut, lstmTensorInfo3, 1, 0);
136 Connect(layer, cellStateOut, lstmTensorInfo2, 2, 0);
137 Connect(layer, output, lstmTensorInfo3, 3, 0);
138}
139
surmeh01bceff2f2018-03-29 16:29:27 +0100140}
141
142BOOST_AUTO_TEST_SUITE(Optimizer)
telsoa01c577f2c2018-08-31 09:22:23 +0100143using namespace armnn::optimizations;
surmeh01bceff2f2018-03-29 16:29:27 +0100144
telsoa01c577f2c2018-08-31 09:22:23 +0100145BOOST_AUTO_TEST_CASE(LSTMValidateTensorShapesFromInputsCIFGDisabledTest)
146{
147 Graph graph;
148
149 //Helper function creates graph containing LSTM layer with required input and output layers
150 CreateLSTMLayerHelper(graph, false);
151
152 //This function used to call ValidateShapesFromInputs();
153 BOOST_CHECK_NO_THROW(graph.InferTensorInfos());
154}
155
156BOOST_AUTO_TEST_CASE(LSTMValidateTensorShapesFromInputsCIFGEnabledTest)
157{
158 Graph graph;
159
160 //Helper function creates graph containing LSTM layer with required input and output layers
161 CreateLSTMLayerHelper(graph, true);
162
163 //This function used to call ValidateShapesFromInputs();
164 BOOST_CHECK_NO_THROW(graph.InferTensorInfos());
165}
166
telsoa01c577f2c2018-08-31 09:22:23 +0100167BOOST_AUTO_TEST_CASE(InsertConvertersTest)
168{
169 const armnn::TensorInfo info({ 1, 5, 2, 3 }, armnn::DataType::Float16);
170
171 armnn::Graph graph;
172
173 armnn::LayerBindingId inputId = 0;
174
175 armnn::Layer* head = graph.AddLayer<armnn::OutputLayer>(0, "output");
176
177 head = graph.InsertNewLayer<armnn::AdditionLayer>(head->GetInputSlot(0), "");
178 head->GetOutputHandler().SetTensorInfo(info);
179
180 graph.InsertNewLayer<armnn::InputLayer>(head->GetInputSlot(1), inputId++, "")
181 ->GetOutputHandler().SetTensorInfo(info);
182
183 head = graph.InsertNewLayer<armnn::FloorLayer>(head->GetInputSlot(0), "");
184 head->GetOutputHandler().SetTensorInfo(info);
185
186 head = graph.InsertNewLayer<armnn::MemCopyLayer>(head->GetInputSlot(0), "");
187 head->GetOutputHandler().SetTensorInfo(info);
188
189 graph.InsertNewLayer<armnn::InputLayer>(head->GetInputSlot(0), inputId++, "")
190 ->GetOutputHandler().SetTensorInfo(info);
191
192 // Check graph layer sequence before inserting convert layers
193 BOOST_TEST(CheckSequence(graph.cbegin(),
194 graph.cend(),
195 &IsLayerOfType<armnn::InputLayer>,
196 &IsLayerOfType<armnn::InputLayer>,
197 &IsLayerOfType<armnn::MemCopyLayer>,
198 &IsLayerOfType<armnn::FloorLayer>,
199 &IsLayerOfType<armnn::AdditionLayer>,
200 &IsLayerOfType<armnn::OutputLayer>));
201
202 // Check layers have Float16 DataType
203 for (auto& layer : graph)
204 {
205 if(layer->GetType()==LayerType::Floor || layer->GetType() == LayerType::Addition)
206 {
Narumol Prangnawaratac2770a2020-04-01 16:51:23 +0100207 ARMNN_ASSERT(layer->GetOutputSlot(0).GetTensorInfo().GetDataType() == DataType::Float16);
208 ARMNN_ASSERT(layer->GetDataType() == DataType::Float16);
telsoa01c577f2c2018-08-31 09:22:23 +0100209 }
210 }
211
212 // Insert convert layers either side of unsupported layer
213 for (auto& layer : graph)
214 {
215 if(layer->GetType()==LayerType::Floor || layer->GetType() == LayerType::Addition)
216 {
217 InsertConvertFp16ToFp32LayersBefore(graph, *layer);
218 InsertConvertFp32ToFp16LayersAfter(graph, *layer);
219 }
220 }
221
222 // Check layers have correct DataType after inserting convert layers
223 for (auto& layer : graph)
224 {
225 if (layer->GetType()==LayerType::Floor || layer->GetType() == LayerType::Addition)
226 {
Narumol Prangnawaratac2770a2020-04-01 16:51:23 +0100227 ARMNN_ASSERT(layer->GetOutputSlot(0).GetTensorInfo().GetDataType() == DataType::Float32);
228 ARMNN_ASSERT(layer->GetDataType() == DataType::Float32);
telsoa01c577f2c2018-08-31 09:22:23 +0100229 }
230 else if (layer->GetType() == LayerType::ConvertFp16ToFp32)
231 {
Narumol Prangnawaratac2770a2020-04-01 16:51:23 +0100232 ARMNN_ASSERT(layer->GetOutputSlot(0).GetTensorInfo().GetDataType() == DataType::Float32);
233 ARMNN_ASSERT(layer->GetDataType() == DataType::Float16);
telsoa01c577f2c2018-08-31 09:22:23 +0100234 }
235 else if (layer->GetType() == LayerType::ConvertFp32ToFp16)
236 {
Narumol Prangnawaratac2770a2020-04-01 16:51:23 +0100237 ARMNN_ASSERT(layer->GetOutputSlot(0).GetTensorInfo().GetDataType() == DataType::Float16);
238 ARMNN_ASSERT(layer->GetDataType() == DataType::Float32);
telsoa01c577f2c2018-08-31 09:22:23 +0100239 }
240 }
241
242 // Check sequence of layers after inserting convert layers
243 BOOST_TEST(CheckSequence(graph.cbegin(),
244 graph.cend(),
245 &IsLayerOfType<armnn::InputLayer>,
246 &IsLayerOfType<armnn::InputLayer>,
247 &IsLayerOfType<armnn::ConvertFp16ToFp32Layer>,
248 &IsLayerOfType<armnn::MemCopyLayer>,
249 &IsLayerOfType<armnn::ConvertFp16ToFp32Layer>,
250 &IsLayerOfType<armnn::FloorLayer>,
251 &IsLayerOfType<armnn::ConvertFp32ToFp16Layer>,
252 &IsLayerOfType<armnn::ConvertFp16ToFp32Layer>,
253 &IsLayerOfType<armnn::AdditionLayer>,
254 &IsLayerOfType<armnn::ConvertFp32ToFp16Layer>,
255 &IsLayerOfType<armnn::OutputLayer>));
256}
257
telsoa01c577f2c2018-08-31 09:22:23 +0100258
keidav01738c2e62018-12-11 16:14:20 +0000259
narpra017af76882018-10-26 17:36:32 +0100260void CreateConvolution2dGraph(Graph &graph, const unsigned int* inputShape,
261 const unsigned int* weightsShape, const unsigned int* outputShape,
262 DataLayout dataLayout = DataLayout::NCHW)
263{
264 armnn::TensorInfo inputInfo(4, inputShape, DataType::Float32);
265 armnn::TensorInfo outputInfo(4, outputShape, DataType::Float32);
266
267 std::vector<float> weightsVector(90);
268 armnn::ConstTensor weights(armnn::TensorInfo(4, weightsShape, armnn::DataType::Float32), weightsVector);
269
270 Convolution2dDescriptor desc;
271 desc.m_BiasEnabled = false;
272 desc.m_StrideX = 1;
273 desc.m_StrideY = 1;
274 desc.m_DataLayout = dataLayout;
275
276 Layer* input = graph.AddLayer<InputLayer>(0, "input");
277 input->GetOutputSlot().SetTensorInfo(inputInfo);
278
279 Convolution2dLayer* layer = graph.AddLayer<Convolution2dLayer>(desc, "conv2d");
280 layer->m_Weight = std::make_unique<armnn::ScopedCpuTensorHandle>(weights);
281 layer->GetOutputSlot().SetTensorInfo(outputInfo);
282
283 Layer* output = graph.AddLayer<OutputLayer>(0, "output");
284 input->GetOutputSlot().Connect(layer->GetInputSlot(0));
285 layer->GetOutputSlot().Connect(output->GetInputSlot(0));
286}
287
288BOOST_AUTO_TEST_CASE(Conv2dValidateTensorShapesFromInputs)
289{
290 Graph graph;
291 const unsigned int inputShape[] = { 1, 3, 8, 16 };
292 const unsigned int weightsShape[] = { 2, 3, 5, 3 };
293 const unsigned int outputShape[] = { 1, 2, 4, 14 };
294 CreateConvolution2dGraph(graph, inputShape, weightsShape, outputShape);
295
296 BOOST_CHECK_NO_THROW(graph.InferTensorInfos());
297}
298
299BOOST_AUTO_TEST_CASE(Conv2dValidateTensorShapesFromInputsNhwc)
300{
301 Graph graph;
302 const unsigned int inputShape[] = { 1, 8, 16, 3 };
303 const unsigned int weightsShape[] = { 2, 5, 3, 3 };
304 const unsigned int outputShape[] = { 1, 4, 14, 2 };
305 CreateConvolution2dGraph(graph, inputShape, weightsShape, outputShape, DataLayout::NHWC);
306
307 BOOST_CHECK_NO_THROW(graph.InferTensorInfos());
308}
309
310void CreateDepthwiseConvolution2dGraph(Graph &graph, const unsigned int* inputShape,
311 const unsigned int* weightsShape, const unsigned int* outputShape,
312 DataLayout dataLayout = DataLayout::NCHW)
313{
314 armnn::TensorInfo inputInfo(4, inputShape, DataType::Float32);
315 armnn::TensorInfo outputInfo(4, outputShape, DataType::Float32);
316
317 std::vector<float> weightsVector(18);
318 armnn::ConstTensor weights(armnn::TensorInfo(4, weightsShape, armnn::DataType::Float32), weightsVector);
319
320 DepthwiseConvolution2dDescriptor desc;
321 desc.m_BiasEnabled = false;
322 desc.m_StrideX = 1;
323 desc.m_StrideY = 1;
324 desc.m_DataLayout = dataLayout;
325
326 Layer* input = graph.AddLayer<InputLayer>(0, "input");
327 input->GetOutputSlot().SetTensorInfo(inputInfo);
328
329 DepthwiseConvolution2dLayer* layer = graph.AddLayer<DepthwiseConvolution2dLayer>(desc, "depthwiseConv2d");
330 layer->m_Weight = std::make_unique<armnn::ScopedCpuTensorHandle>(weights);
331 layer->GetOutputSlot().SetTensorInfo(outputInfo);
332
333 Layer* output = graph.AddLayer<OutputLayer>(0, "output");
334 input->GetOutputSlot().Connect(layer->GetInputSlot(0));
335 layer->GetOutputSlot().Connect(output->GetInputSlot(0));
336}
337
338BOOST_AUTO_TEST_CASE(DepthwiseConv2dValidateTensorShapesFromInputs)
339{
340 Graph graph;
341 const unsigned int inputShape[] = { 1, 2, 3, 3 };
342 const unsigned int weightsShape[] = { 1, 2, 3, 3 };
343 const unsigned int outputShape[] = { 1, 2, 1, 1 };
344 CreateDepthwiseConvolution2dGraph(graph, inputShape, weightsShape, outputShape);
345
346 BOOST_CHECK_NO_THROW(graph.InferTensorInfos());
347}
348
349BOOST_AUTO_TEST_CASE(DepthwiseConv2dValidateTensorShapesFromInputsNhwc)
350{
351 Graph graph;
352 const unsigned int inputShape[] = { 1, 3, 3, 2 };
Matteo Martincigh747ef822018-12-18 09:26:39 +0000353 const unsigned int weightsShape[] = { 1, 2, 3, 3 };
narpra017af76882018-10-26 17:36:32 +0100354 const unsigned int outputShape[] = { 1, 1, 1, 2 };
355 CreateDepthwiseConvolution2dGraph(graph, inputShape, weightsShape, outputShape, DataLayout::NHWC);
356
357 BOOST_CHECK_NO_THROW(graph.InferTensorInfos());
358}
359
360void CreatePooling2dGraph(Graph &graph, const unsigned int* inputShape, const unsigned int* outputShape,
361 DataLayout dataLayout = DataLayout::NCHW)
362{
363 armnn::TensorInfo inputInfo(4, inputShape, DataType::Float32);
364 armnn::TensorInfo outputInfo(4, outputShape, DataType::Float32);
365
366 Pooling2dDescriptor desc;
367 desc.m_PoolType = armnn::PoolingAlgorithm::Average;
368 desc.m_PoolWidth = desc.m_PoolHeight = 100;
369 desc.m_StrideX = desc.m_StrideY = 5;
370 desc.m_PadLeft = 50;
371 desc.m_PadRight = 50;
372 desc.m_PadTop = 50;
373 desc.m_PadBottom = 50;
374 desc.m_PaddingMethod = armnn::PaddingMethod::Exclude;
375 desc.m_DataLayout = dataLayout;
376
377 Layer* input = graph.AddLayer<InputLayer>(0, "input");
378 input->GetOutputSlot().SetTensorInfo(inputInfo);
379
380 Pooling2dLayer* layer = graph.AddLayer<Pooling2dLayer>(desc, "pooling2d");
381 layer->GetOutputSlot().SetTensorInfo(outputInfo);
382
383 Layer* output = graph.AddLayer<OutputLayer>(0, "output");
384 input->GetOutputSlot().Connect(layer->GetInputSlot(0));
385 layer->GetOutputSlot().Connect(output->GetInputSlot(0));
386}
387
388BOOST_AUTO_TEST_CASE(Pooling2dValidateTensorShapesFromInputs)
389{
390 Graph graph;
391 const unsigned int inputShape[] = { 5, 3, 52, 60 };
392 const unsigned int outputShape[] = { 5, 3, 11, 13 };
393 CreatePooling2dGraph(graph, inputShape, outputShape, DataLayout::NCHW);
394
395 BOOST_CHECK_NO_THROW(graph.InferTensorInfos());
396}
397
398BOOST_AUTO_TEST_CASE(Pooling2dValidateTensorShapesFromInputsNhwc)
399{
400 Graph graph;
401 const unsigned int inputShape[] = { 5, 52, 60, 3 };
402 const unsigned int outputShape[] = { 5, 11, 13, 3 };
403 CreatePooling2dGraph(graph, inputShape, outputShape, DataLayout::NHWC);
404
405 BOOST_CHECK_NO_THROW(graph.InferTensorInfos());
406}
407
408void CreateResizeBilinearGraph(Graph &graph, const unsigned int* inputShape, const unsigned int* outputShape,
409 DataLayout dataLayout = DataLayout::NCHW)
410{
Aron Virginas-Tar169d2f12019-07-01 19:01:44 +0100411 TensorInfo inputInfo(4, inputShape, DataType::Float32);
412 TensorInfo outputInfo(4, outputShape, DataType::Float32);
narpra017af76882018-10-26 17:36:32 +0100413
Aron Virginas-Tar169d2f12019-07-01 19:01:44 +0100414 ResizeDescriptor desc;
415 desc.m_Method = ResizeMethod::Bilinear;
narpra017af76882018-10-26 17:36:32 +0100416 desc.m_TargetHeight = 3;
Aron Virginas-Tar169d2f12019-07-01 19:01:44 +0100417 desc.m_TargetWidth = 4;
418 desc.m_DataLayout = dataLayout;
narpra017af76882018-10-26 17:36:32 +0100419
420 Layer* input = graph.AddLayer<InputLayer>(0, "input");
421 input->GetOutputSlot().SetTensorInfo(inputInfo);
422
Aron Virginas-Tar169d2f12019-07-01 19:01:44 +0100423 ResizeLayer* layer = graph.AddLayer<ResizeLayer>(desc, "resizeBilinear");
narpra017af76882018-10-26 17:36:32 +0100424 layer->GetOutputSlot().SetTensorInfo(outputInfo);
425
426 Layer* output = graph.AddLayer<OutputLayer>(0, "output");
427 input->GetOutputSlot().Connect(layer->GetInputSlot(0));
428 layer->GetOutputSlot().Connect(output->GetInputSlot(0));
429}
430
431BOOST_AUTO_TEST_CASE(ResizeBilinearValidateTensorShapesFromInputs)
432{
433 Graph graph;
434 const unsigned int inputShape[] = { 1, 2, 4, 5 };
435 const unsigned int outputShape[] = { 1, 2, 3, 4 };
436 CreateResizeBilinearGraph(graph, inputShape, outputShape);
437
438 BOOST_CHECK_NO_THROW(graph.InferTensorInfos());
439}
440
441BOOST_AUTO_TEST_CASE(ResizeBilinearValidateTensorShapesFromInputsNhwc)
442{
443 Graph graph;
444 const unsigned int inputShape[] = { 1, 4, 5, 2 };
445 const unsigned int outputShape[] = { 1, 3, 4, 2 };
446 CreateResizeBilinearGraph(graph, inputShape, outputShape, DataLayout::NHWC);
447
448 BOOST_CHECK_NO_THROW(graph.InferTensorInfos());
449}
450
narpra0133f8e3b2019-01-16 17:22:19 +0000451
452void CreateGatherGraph(Graph& graph, const armnn::TensorInfo& paramsInfo, const armnn::TensorInfo& indicesInfo,
453 const armnn::TensorInfo& outputInfo)
454{
455 Layer* input0 = graph.AddLayer<InputLayer>(0, "params");
456 input0->GetOutputSlot().SetTensorInfo(paramsInfo);
457
458 Layer* input1 = graph.AddLayer<InputLayer>(1, "indices");
459 input1->GetOutputSlot().SetTensorInfo(indicesInfo);
460
Teresa Charlin52664732020-06-29 16:27:03 +0100461 GatherDescriptor descriptor;
462 GatherLayer* layer = graph.AddLayer<GatherLayer>(descriptor, "gather");
narpra0133f8e3b2019-01-16 17:22:19 +0000463 layer->GetOutputSlot().SetTensorInfo(outputInfo);
464
465 Layer* output = graph.AddLayer<OutputLayer>(0, "output");
466 input0->GetOutputSlot().Connect(layer->GetInputSlot(0));
467 input1->GetOutputSlot().Connect(layer->GetInputSlot(1));
468 layer->GetOutputSlot().Connect(output->GetInputSlot(0));
469}
470
471BOOST_AUTO_TEST_CASE(GatherValidateTensorShapesFromInputs)
472{
473 Graph graph;
474 armnn::TensorInfo paramsInfo({10, 5}, DataType::Float32);
475 armnn::TensorInfo indicesInfo({3}, DataType::Signed32);
476 armnn::TensorInfo outputInfo({3, 5}, DataType::Float32);
477
478 CreateGatherGraph(graph, paramsInfo, indicesInfo, outputInfo);
479
480 BOOST_CHECK_NO_THROW(graph.InferTensorInfos());
481}
482
483BOOST_AUTO_TEST_CASE(GatherValidateTensorShapesFromInputs1DParams)
484{
485 Graph graph;
486 armnn::TensorInfo paramsInfo({8}, DataType::Float32);
487 armnn::TensorInfo indicesInfo({5}, DataType::Signed32);
488 armnn::TensorInfo outputInfo( {5}, DataType::Float32);
489
490 CreateGatherGraph(graph, paramsInfo, indicesInfo, outputInfo);
491
492 BOOST_CHECK_NO_THROW(graph.InferTensorInfos());
493}
494
495BOOST_AUTO_TEST_CASE(GatherValidateTensorShapesFromInputsMultiDimIndices)
496{
497 Graph graph;
498 armnn::TensorInfo paramsInfo({3, 2, 5}, DataType::Float32);
499 armnn::TensorInfo indicesInfo({2, 2}, DataType::Signed32);
500 armnn::TensorInfo outputInfo({2, 2, 2, 5}, DataType::Float32);
501
502 CreateGatherGraph(graph, paramsInfo, indicesInfo, outputInfo);
503
504 BOOST_CHECK_NO_THROW(graph.InferTensorInfos());
505}
506
Narumol Prangnawarata0d56c72019-01-25 10:46:40 +0000507BOOST_AUTO_TEST_CASE(DetectionPostProcessValidateTensorShapes)
508{
509 Graph graph;
Derek Lambertif90c56d2020-01-10 17:14:08 +0000510 armnn::TensorInfo boxEncodingsInfo({1, 10, 4}, DataType::QAsymmU8);
511 armnn::TensorInfo scoresInfo({1, 10, 4}, DataType::QAsymmU8);
Narumol Prangnawarata0d56c72019-01-25 10:46:40 +0000512 std::vector<uint8_t> anchorsVector(40);
Derek Lambertif90c56d2020-01-10 17:14:08 +0000513 armnn::ConstTensor anchors(armnn::TensorInfo({10, 4}, armnn::DataType::QAsymmU8), anchorsVector);
Narumol Prangnawarata0d56c72019-01-25 10:46:40 +0000514
Derek Lambertif90c56d2020-01-10 17:14:08 +0000515 armnn::TensorInfo detectionBoxesInfo({1, 3, 4}, DataType::QAsymmU8);
516 armnn::TensorInfo detectionScoresInfo({1, 3}, DataType::QAsymmU8);
517 armnn::TensorInfo detectionClassesInfo({1, 3}, DataType::QAsymmU8);
518 armnn::TensorInfo numDetectionInfo({1}, DataType::QAsymmU8);
Narumol Prangnawarata0d56c72019-01-25 10:46:40 +0000519
520 Layer* input0 = graph.AddLayer<InputLayer>(0, "boxEncodings");
521 input0->GetOutputSlot().SetTensorInfo(boxEncodingsInfo);
522
523 Layer* input1 = graph.AddLayer<InputLayer>(1, "score");
524 input1->GetOutputSlot().SetTensorInfo(scoresInfo);
525
526 DetectionPostProcessDescriptor descriptor;
527 descriptor.m_MaxDetections = 3;
528
529 DetectionPostProcessLayer* layer = graph.AddLayer<DetectionPostProcessLayer>(descriptor, "detectionPostProcess");
530 layer->m_Anchors = std::make_unique<armnn::ScopedCpuTensorHandle>(anchors);
531 layer->GetOutputSlot(0).SetTensorInfo(detectionBoxesInfo);
532 layer->GetOutputSlot(1).SetTensorInfo(detectionScoresInfo);
533 layer->GetOutputSlot(2).SetTensorInfo(detectionClassesInfo);
534 layer->GetOutputSlot(3).SetTensorInfo(numDetectionInfo);
535
536 input0->GetOutputSlot().Connect(layer->GetInputSlot(0));
537 input1->GetOutputSlot().Connect(layer->GetInputSlot(1));
538
539 BOOST_CHECK_NO_THROW(graph.InferTensorInfos());
540}
541
Nina Drozd861985f2019-04-18 14:48:51 +0100542BOOST_AUTO_TEST_CASE(FoldPadLayerIntoConvolution2dLayer)
543{
544 Graph graph;
545 const unsigned int inputShape[] = { 1, 2, 2, 3 };
546 const unsigned int paddedShape[] = { 1, 6, 6, 3 };
547 const unsigned int weightsShape[] = { 1, 2, 3, 3 };
548 const unsigned int outputShape[] = { 1, 2, 1, 1 };
549
550
551 armnn::TensorInfo inputInfo(4, inputShape, DataType::Float32);
552 armnn::TensorInfo paddedInfo(4, paddedShape, DataType::Float32);
553 armnn::TensorInfo outputInfo(4, outputShape, DataType::Float32);
554
555 Layer* input = graph.AddLayer<InputLayer>(0, "input");
556 input->GetOutputSlot().SetTensorInfo(inputInfo);
557
558 PadDescriptor padDescriptor({{ 0, 0 }, { 2, 2 }, { 2, 2 }, { 0, 0 }});
559
560 PadLayer* padLayer = graph.AddLayer<PadLayer>(padDescriptor, "pad");
561 padLayer->GetOutputSlot().SetTensorInfo(paddedInfo);
562
563 Convolution2dDescriptor convolution2dDescriptor;
564 convolution2dDescriptor.m_BiasEnabled = false;
565 convolution2dDescriptor.m_StrideX = 1;
566 convolution2dDescriptor.m_StrideY = 1;
567 convolution2dDescriptor.m_DataLayout = DataLayout::NHWC;
568
569 std::vector<float> weightsVector(18);
570 armnn::ConstTensor weights(armnn::TensorInfo(4, weightsShape, armnn::DataType::Float32), weightsVector);
571
572 Convolution2dLayer* conv2dLayer = graph.AddLayer<Convolution2dLayer>(convolution2dDescriptor,"conv2d");
573 conv2dLayer->m_Weight = std::make_unique<armnn::ScopedCpuTensorHandle>(weights);
574 conv2dLayer->GetOutputSlot().SetTensorInfo(outputInfo);
575
576 Layer* output = graph.AddLayer<OutputLayer>(0, "output");
577
578 // Connect up layers - input -> pad -> conv2d -> output
579 input->GetOutputSlot().Connect(padLayer->GetInputSlot(0));
580 padLayer->GetOutputSlot().Connect(conv2dLayer->GetInputSlot(0));
581 conv2dLayer->GetOutputSlot().Connect(output->GetInputSlot(0));
582
583 auto checkSimpleConv2d = [ ](const armnn::Layer* const layer) -> bool
584 {
585 const auto conv2dLayer = static_cast<const armnn::Convolution2dLayer*>(layer);
586 const auto conv2dLayerParams = conv2dLayer->GetParameters();
587 return IsLayerOfType<armnn::Convolution2dLayer>(layer) &&
588 (layer->GetNameStr() == "conv2d") &&
589 (conv2dLayerParams.m_PadLeft == 0) &&
590 (conv2dLayerParams.m_PadRight == 0) &&
591 (conv2dLayerParams.m_PadTop == 0) &&
592 (conv2dLayerParams.m_PadBottom == 0) &&
593 (conv2dLayerParams.m_BiasEnabled == false) &&
594 (conv2dLayerParams.m_StrideX == 1) &&
595 (conv2dLayerParams.m_StrideY == 1) &&
596 (conv2dLayerParams.m_DataLayout == DataLayout::NHWC);
597 };
598
599 BOOST_TEST(CheckSequence(graph.cbegin(),
Teresa Charlin06e03002020-10-15 13:16:07 +0100600 graph.cend(),
601 &IsLayerOfType<armnn::InputLayer>,
602 &IsLayerOfType<armnn::PadLayer>,
603 checkSimpleConv2d,
604 &IsLayerOfType<armnn::OutputLayer>));
Nina Drozd861985f2019-04-18 14:48:51 +0100605
606 armnn::Optimizer::Pass(graph, armnn::MakeOptimizations(FoldPadIntoConvolution2d()));
607
608 auto checkPadFoldedIntoConv2d = [ ](const armnn::Layer* const layer) -> bool
609 {
610 const auto conv2dLayer = static_cast<const armnn::Convolution2dLayer*>(layer);
611 const auto conv2dLayerParams = conv2dLayer->GetParameters();
612 return IsLayerOfType<armnn::Convolution2dLayer>(layer) &&
613 (layer->GetNameStr() == "folded-pad-into-conv2d") &&
614 (conv2dLayerParams.m_PadLeft == 2) &&
615 (conv2dLayerParams.m_PadRight == 2) &&
616 (conv2dLayerParams.m_PadTop == 2) &&
617 (conv2dLayerParams.m_PadBottom == 2) &&
618 (conv2dLayerParams.m_BiasEnabled == false) &&
619 (conv2dLayerParams.m_StrideX == 1) &&
620 (conv2dLayerParams.m_StrideY == 1) &&
621 (conv2dLayerParams.m_DataLayout == DataLayout::NHWC);
622 };
623
624 BOOST_TEST(CheckSequence(graph.cbegin(),
Teresa Charlin06e03002020-10-15 13:16:07 +0100625 graph.cend(),
626 &IsLayerOfType<armnn::InputLayer>,
627 checkPadFoldedIntoConv2d,
628 &IsLayerOfType<armnn::OutputLayer>));
Nina Drozd861985f2019-04-18 14:48:51 +0100629}
630
Derek Lamberti4a9e24b2020-01-03 16:53:38 +0000631
632
633
634class MockLayerSupport : public LayerSupportBase {
635public:
636 bool IsInputSupported(const TensorInfo& /*input*/,
637 Optional<std::string&> /*reasonIfUnsupported = EmptyOptional()*/) const override
638 {
639 return true;
640 }
641
642 bool IsOutputSupported(const TensorInfo& /*input*/,
643 Optional<std::string&> /*reasonIfUnsupported = EmptyOptional()*/) const override
644 {
645 return true;
646 }
647
648 bool IsActivationSupported(const TensorInfo& /*input0*/,
649 const TensorInfo& /*output*/,
650 const ActivationDescriptor& /*descriptor*/,
651 Optional<std::string&> /*reasonIfUnsupported = EmptyOptional()*/) const override
652 {
653 return true;
654 }
655};
656
657template<typename NamePolicy>
658class MockBackend : public IBackendInternal
659{
660public:
661 MockBackend() = default;
662 ~MockBackend() = default;
663
664 static const BackendId& GetIdStatic() { return NamePolicy::GetIdStatic(); }
665 const BackendId& GetId() const override { return GetIdStatic(); }
666
667 IBackendInternal::IMemoryManagerUniquePtr CreateMemoryManager() const override { return nullptr; };
668
669 IBackendInternal::IWorkloadFactoryPtr CreateWorkloadFactory(
670 const IBackendInternal::IMemoryManagerSharedPtr&) const override { return nullptr; }
671
672 IBackendInternal::IBackendContextPtr CreateBackendContext(const IRuntime::CreationOptions&) const override
673 {
674 return nullptr;
675 }
676
677 IBackendInternal::Optimizations GetOptimizations() const override { return {}; }
678 IBackendInternal::ILayerSupportSharedPtr GetLayerSupport() const override
679 {
680 return std::make_shared<MockLayerSupport>();
681 }
682
683 OptimizationViews OptimizeSubgraphView(const SubgraphView&) const override
684 {
685 return {};
686 };
687};
688
689
690BOOST_AUTO_TEST_CASE(BackendHintTest)
691{
692 class TestBackendAssignment : public LayerVisitorBase<VisitorNoThrowPolicy>
693 {
694 public:
695 void VisitInputLayer(const IConnectableLayer* layer,
696 LayerBindingId id,
697 const char* name = nullptr) override
698 {
Jan Eilers8eb25602020-03-09 12:13:48 +0000699 IgnoreUnused(id, name);
Jan Eilersbb446e52020-04-02 13:56:54 +0100700 auto inputLayer = PolymorphicDowncast<const InputLayer*>(layer);
Derek Lamberti4a9e24b2020-01-03 16:53:38 +0000701 BOOST_TEST((inputLayer->GetBackendId() == "MockBackend"));
702 }
703
704 void VisitOutputLayer(const IConnectableLayer* layer,
705 LayerBindingId id,
706 const char* name = nullptr) override
707 {
Jan Eilers8eb25602020-03-09 12:13:48 +0000708 IgnoreUnused(id, name);
Jan Eilersbb446e52020-04-02 13:56:54 +0100709 auto outputLayer = PolymorphicDowncast<const OutputLayer*>(layer);
Derek Lamberti4a9e24b2020-01-03 16:53:38 +0000710 BOOST_TEST((outputLayer->GetBackendId() == "MockBackend"));
711 }
712
713 void VisitActivationLayer(const IConnectableLayer* layer,
714 const ActivationDescriptor& activationDescriptor,
715 const char* name = nullptr) override
716 {
Jan Eilers8eb25602020-03-09 12:13:48 +0000717 IgnoreUnused(activationDescriptor, name);
Jan Eilersbb446e52020-04-02 13:56:54 +0100718 auto activation = PolymorphicDowncast<const ActivationLayer*>(layer);
Derek Lamberti4a9e24b2020-01-03 16:53:38 +0000719 BOOST_TEST((activation->GetBackendId() == "CustomBackend"));
720 }
721 };
722
723 struct CustomPolicy
724 {
725 static const BackendId& GetIdStatic()
726 {
727 static BackendId id="CustomBackend";
728 return id;
729 }
730 };
731
732 struct MockPolicy
733 {
734 static const BackendId& GetIdStatic()
735 {
736 static BackendId id="MockBackend";
737 return id;
738 }
739 };
740
741 auto& backendRegistry = BackendRegistryInstance();
742
743 backendRegistry.Register("MockBackend", [](){
744 return std::make_unique<MockBackend<MockPolicy>>();
745 });
746
747 backendRegistry.Register("CustomBackend", [](){
748 return std::make_unique<MockBackend<CustomPolicy>>();
749 });
750
751 // Define the network
752 auto network = INetwork::Create();
753 ActivationDescriptor desc;
754 desc.m_Function = ActivationFunction::Linear;
755
756 std::unique_ptr<Graph> graph = std::make_unique<Graph>();
757 auto input = graph->AddLayer<InputLayer>(0, "input");
758 auto act = graph->AddLayer<ActivationLayer>(desc, "activation");
759 auto output = graph->AddLayer<OutputLayer>(0, "output");
760
761 BackendId customBackendId("CustomBackend");
762 act->BackendSelectionHint(customBackendId);
763
764 input->GetOutputSlot(0).Connect(act->GetInputSlot(0));
765 act->GetOutputSlot(0).Connect(output->GetInputSlot(0));
766
767
768 auto optNet = IOptimizedNetworkPtr(new OptimizedNetwork(std::move(graph)), &IOptimizedNetwork::Destroy);
769
Jan Eilersbb446e52020-04-02 13:56:54 +0100770 OptimizedNetwork* optNetObjPtr = PolymorphicDowncast<OptimizedNetwork*>(optNet.get());
Derek Lamberti4a9e24b2020-01-03 16:53:38 +0000771
772 // Get the optimized graph
773 Graph& optGraph = optNetObjPtr->GetGraph();
774
775
776 std::vector<BackendId> prefs{"MockBackend", "CustomBackend"};
777
778 BackendIdSet availableBackends = {"CustomBackend", "MockBackend"};
779 DeviceSpec spec(availableBackends);
780
781 BackendSettings backendSettings(prefs, spec);
782
783 // Assign an available backend to each layer
784 Graph::Iterator firstLayer = optGraph.begin();
785 Graph::Iterator lastLayer = optGraph.end();
786 OptimizationResult res = AssignBackends(optNetObjPtr,
787 backendSettings,
788 firstLayer,
789 lastLayer,
790 EmptyOptional());
791
792 BOOST_TEST(res.IsOk());
793
794 TestBackendAssignment visitor;
795 for (auto it =firstLayer; it != lastLayer; ++it)
796 {
797 (*it)->Accept(visitor);
798 }
799}
800
Teresa Charlin6fff4f42020-10-31 13:21:01 +0000801// Tests that OptimizeForExclusiveConnections works, fusing when needed, using BatchNorm fusing as example
Teresa Charlin06e03002020-10-15 13:16:07 +0100802BOOST_AUTO_TEST_CASE(OptimizeForExclusiveConnections_fuse_Test)
803{
804 using namespace armnn;
805 // Define layers information
806 Convolution2dDescriptor convolution2dDescriptor;
807 convolution2dDescriptor.m_BiasEnabled = false;
808 convolution2dDescriptor.m_DataLayout = DataLayout::NHWC;
809 BatchNormalizationDescriptor batchNormDescriptor;
810 batchNormDescriptor.m_DataLayout = DataLayout::NHWC;
811
812 const unsigned int inputDimensionSizes[] = {1, 4, 4, 3}; // NHWCin
813 const unsigned int weightsDimensionSizes[] = {1, 2, 2, 3}; // CoutHWCin
814 const unsigned int outputDimensionSizes[] = {1, 3, 3, 1}; // NHWCout
815 const unsigned int outputChannelSize[] = {outputDimensionSizes[3]}; // Cout
816
817 TensorInfo inputInfo (4, inputDimensionSizes, DataType::Float32);
818 TensorInfo outputInfo(4, outputDimensionSizes, DataType::Float32);
819
820 std::vector<float> weightsVector = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12};
821 ConstTensor weights (TensorInfo(4, weightsDimensionSizes, DataType::Float32), weightsVector);
822
823
824 std::vector<float> betaVector = {0.1f};
825 std::vector<float> gammaVector = {0.5f};
826 std::vector<float> meanVector = {0};
827 std::vector<float> varianceVector = {1};
828 ConstTensor beta (TensorInfo(1, outputChannelSize, DataType::Float32), betaVector);
829 ConstTensor gamma (TensorInfo(1, outputChannelSize, DataType::Float32), gammaVector);
830 ConstTensor mean (TensorInfo(1, outputChannelSize, DataType::Float32), meanVector);
831 ConstTensor variance(TensorInfo(1, outputChannelSize, DataType::Float32), varianceVector);
832
833 // Define the network
834 Graph graph;
835 auto input = graph.AddLayer<InputLayer>(0, "input");
836 auto conv = graph.AddLayer<Convolution2dLayer>(convolution2dDescriptor, "convolution");
837 auto batchNorm = graph.AddLayer<BatchNormalizationLayer>(batchNormDescriptor, "batchNorm");
838 auto output = graph.AddLayer<OutputLayer>(0, "output");
839
840 // Set layer information
841 input ->GetOutputSlot().SetTensorInfo(inputInfo);
842 conv ->GetOutputSlot().SetTensorInfo(outputInfo);
843 batchNorm->GetOutputSlot().SetTensorInfo(outputInfo);
844 conv ->m_Weight = std::make_unique<ScopedCpuTensorHandle>(weights);
845 batchNorm->m_Beta = std::make_unique<ScopedCpuTensorHandle>(beta);
846 batchNorm->m_Gamma = std::make_unique<ScopedCpuTensorHandle>(gamma);
847 batchNorm->m_Mean = std::make_unique<ScopedCpuTensorHandle>(mean);
848 batchNorm->m_Variance = std::make_unique<ScopedCpuTensorHandle>(variance);
849 if (convolution2dDescriptor.m_BiasEnabled)
850 {
851 std::vector<float> biasVector = {11};
852 ConstTensor bias (TensorInfo(1, outputChannelSize, DataType::Float32), biasVector);
853 conv->m_Bias = std::make_unique<ScopedCpuTensorHandle>(bias);
854 }
855
856 // Connect layers
857 input ->GetOutputSlot(0).Connect(conv ->GetInputSlot(0));
858 conv ->GetOutputSlot(0).Connect(batchNorm->GetInputSlot(0));
859 batchNorm ->GetOutputSlot(0).Connect(output ->GetInputSlot(0));
860
861 BOOST_CHECK(4 == graph.GetNumLayers());
862 BOOST_TEST(CheckSequence(graph.cbegin(),
863 graph.cend(),
864 &IsLayerOfType<InputLayer>,
865 &IsLayerOfType<Convolution2dLayer>,
866 &IsLayerOfType<BatchNormalizationLayer>,
867 &IsLayerOfType<OutputLayer>));
868
869 // Optimize graph
870 armnn::Optimizer::Pass(graph, MakeOptimizations(FuseBatchNormIntoConvolution2D()));
871
872 auto checkFusedConv2d = [ ](const armnn::Layer* const layer) -> bool
873 {
874 return IsLayerOfType<armnn::Convolution2dLayer>(layer) &&
875 (layer->GetNameStr() == "fused-batchNorm-into-convolution");
876 };
877
878 BOOST_CHECK(3 == graph.GetNumLayers());
879 BOOST_TEST(CheckSequence(graph.cbegin(),
880 graph.cend(),
881 &IsLayerOfType<InputLayer>,
882 checkFusedConv2d,
883 &IsLayerOfType<OutputLayer>));
884}
885
Teresa Charlin6fff4f42020-10-31 13:21:01 +0000886// Tests that OptimizeForExclusiveConnections works, not fusing when not needed, using BatchNorm fusing as example
Teresa Charlin06e03002020-10-15 13:16:07 +0100887BOOST_AUTO_TEST_CASE(OptimizeForExclusiveConnections_notFuse_Test)
888{
889 // Define the network
890 Graph graph;
891 Convolution2dDescriptor convolution2dDescriptor;
892 BatchNormalizationDescriptor batchNormDescriptor;
893
894 auto input = graph.AddLayer<InputLayer>(0, "input");
895 auto conv = graph.AddLayer<Convolution2dLayer>(convolution2dDescriptor, "convolution");
896 auto batchNorm = graph.AddLayer<BatchNormalizationLayer>(batchNormDescriptor, "batchNorm");
897 auto output = graph.AddLayer<OutputLayer>(0, "output");
898 auto output2 = graph.AddLayer<OutputLayer>(1, "output2");
899
900 // Connect layers
901 input ->GetOutputSlot(0).Connect(conv ->GetInputSlot(0));
902 conv ->GetOutputSlot(0).Connect(batchNorm->GetInputSlot(0));
903 batchNorm ->GetOutputSlot(0).Connect(output ->GetInputSlot(0));
904 conv ->GetOutputSlot(0).Connect(output2 ->GetInputSlot(0));
905
906 BOOST_CHECK(5 == graph.GetNumLayers());
907 BOOST_TEST(CheckSequence(graph.cbegin(),
908 graph.cend(),
909 &IsLayerOfType<armnn::InputLayer>,
910 &IsLayerOfType<armnn::Convolution2dLayer>,
911 &IsLayerOfType<armnn::BatchNormalizationLayer>,
912 &IsLayerOfType<armnn::OutputLayer>,
913 &IsLayerOfType<armnn::OutputLayer>));
914 // Optimize graph
915 armnn::Optimizer::Pass(graph, armnn::MakeOptimizations(FuseBatchNormIntoConvolution2D()));
916
917 BOOST_CHECK(5 == graph.GetNumLayers());
918 BOOST_TEST(CheckSequence(graph.cbegin(),
919 graph.cend(),
920 &IsLayerOfType<armnn::InputLayer>,
921 &IsLayerOfType<armnn::Convolution2dLayer>,
922 &IsLayerOfType<armnn::BatchNormalizationLayer>,
923 &IsLayerOfType<armnn::OutputLayer>,
924 &IsLayerOfType<armnn::OutputLayer>));
925}
surmeh01bceff2f2018-03-29 16:29:27 +0100926BOOST_AUTO_TEST_SUITE_END()