IVGCVSW-1946: Remove armnn/src from the include paths
[platform/upstream/armnn.git] / src / armnn / test / OptimizerTests.cpp
1 //
2 // Copyright © 2017 Arm Ltd. All rights reserved.
3 // SPDX-License-Identifier: MIT
4 //
5 #include <boost/test/unit_test.hpp>
6
7 #include <armnn/ArmNN.hpp>
8 #include <Graph.hpp>
9 #include <Optimizer.hpp>
10 #include <backendsCommon/CpuTensorHandle.hpp>
11 #include <FloatingPointConverter.hpp>
12
13 namespace
14 {
15 template <typename LayerT>
16 bool IsLayerOfType(const armnn::Layer* const layer)
17 {
18     return (layer->GetType() == armnn::LayerEnumOf<LayerT>());
19 }
20
21 bool CheckSequence(const armnn::Graph::ConstIterator first, const armnn::Graph::ConstIterator last)
22 {
23     return (first == last);
24 }
25
26 /// Checks each unary function in Us evaluates true for each correspondent layer in the sequence [first, last).
27 template <typename U, typename... Us>
28 bool CheckSequence(const armnn::Graph::ConstIterator first,
29                    const armnn::Graph::ConstIterator last,
30                    U&& u,
31                    Us&&... us)
32 {
33     return u(*first) && CheckSequence(std::next(first), last, us...);
34 }
35
36 template <typename LayerT>
37 bool CheckRelatedLayers(armnn::Graph& graph, const std::list<std::string>& testRelatedLayers)
38 {
39     for (auto& layer : graph)
40     {
41         if (layer->GetType() == armnn::LayerEnumOf<LayerT>())
42         {
43             auto& relatedLayers = layer->GetRelatedLayerNames();
44             if(!std::equal(relatedLayers.begin(), relatedLayers.end(),
45                            testRelatedLayers.begin(), testRelatedLayers.end()))
46             {
47                 return false;
48             }
49         }
50     }
51
52     return true;
53 }
54
55 // connects two layers
56 using namespace armnn;
57 void Connect(Layer* from, Layer* to, const TensorInfo& tensorInfo, unsigned int fromIndex = 0, unsigned int toIndex = 0)
58 {
59     from->GetOutputSlot(fromIndex).Connect(to->GetInputSlot(toIndex));
60     from->GetOutputHandler(fromIndex).SetTensorInfo(tensorInfo);
61 }
62
63 void CreateLSTMLayerHelper(Graph &graph, bool CifgEnabled)
64 {
65     LstmDescriptor layerDesc;
66     layerDesc.m_ActivationFunc = 4;
67     layerDesc.m_ClippingThresCell = 0.2f;
68     layerDesc.m_ClippingThresProj = 0.4f;
69     layerDesc.m_CifgEnabled = CifgEnabled;
70     layerDesc.m_PeepholeEnabled = false;
71     layerDesc.m_ProjectionEnabled = false;
72
73     LstmLayer* const layer = graph.AddLayer<LstmLayer>(layerDesc, "layer");
74     unsigned int batchSize = 3;
75     unsigned int inputSize = 2;
76     unsigned int numUnits = 4;
77     unsigned int outputSize = 4;
78
79     layer->m_BasicParameters.m_InputToForgetWeights = std::make_unique<ScopedCpuTensorHandle>
80             (TensorInfo({ numUnits, inputSize }, DataType::Float32));
81     layer->m_BasicParameters.m_InputToCellWeights = std::make_unique<ScopedCpuTensorHandle>
82             (TensorInfo({ numUnits, inputSize }, DataType::Float32));
83     layer->m_BasicParameters.m_InputToOutputWeights = std::make_unique<ScopedCpuTensorHandle>
84             (TensorInfo({ numUnits, inputSize }, DataType::Float32));
85     layer->m_BasicParameters.m_RecurrentToForgetWeights = std::make_unique<ScopedCpuTensorHandle>
86             (TensorInfo({ numUnits, outputSize }, DataType::Float32));
87     layer->m_BasicParameters.m_RecurrentToCellWeights = std::make_unique<ScopedCpuTensorHandle>
88             (TensorInfo({ numUnits, outputSize }, DataType::Float32));
89     layer->m_BasicParameters.m_RecurrentToOutputWeights = std::make_unique<ScopedCpuTensorHandle>
90             (TensorInfo({ numUnits, outputSize }, DataType::Float32));
91     layer->m_BasicParameters.m_ForgetGateBias = std::make_unique<ScopedCpuTensorHandle>
92             (TensorInfo({ numUnits }, DataType::Float32));
93     layer->m_BasicParameters.m_CellBias = std::make_unique<ScopedCpuTensorHandle>
94             (TensorInfo({ numUnits }, DataType::Float32));
95     layer->m_BasicParameters.m_OutputGateBias = std::make_unique<ScopedCpuTensorHandle>
96             (TensorInfo({ numUnits }, DataType::Float32));
97
98     layer->m_BasicParameters.m_InputToForgetWeights->Allocate();
99     layer->m_BasicParameters.m_InputToCellWeights->Allocate();
100     layer->m_BasicParameters.m_InputToOutputWeights->Allocate();
101     layer->m_BasicParameters.m_RecurrentToForgetWeights->Allocate();
102     layer->m_BasicParameters.m_RecurrentToCellWeights->Allocate();
103     layer->m_BasicParameters.m_RecurrentToOutputWeights->Allocate();
104     layer->m_BasicParameters.m_ForgetGateBias->Allocate();
105     layer->m_BasicParameters.m_CellBias->Allocate();
106     layer->m_BasicParameters.m_OutputGateBias->Allocate();
107
108     if (!layerDesc.m_CifgEnabled)
109     {
110         layer->m_CifgParameters.m_InputToInputWeights = std::make_unique<ScopedCpuTensorHandle>
111                 (TensorInfo({ numUnits, inputSize }, DataType::Float32));
112         layer->m_CifgParameters.m_RecurrentToInputWeights = std::make_unique<ScopedCpuTensorHandle>
113                 (TensorInfo({ numUnits, outputSize }, DataType::Float32));
114         layer->m_CifgParameters.m_CellToInputWeights = std::make_unique<ScopedCpuTensorHandle>
115                 (TensorInfo({ numUnits }, DataType::Float32));
116         layer->m_CifgParameters.m_InputGateBias = std::make_unique<ScopedCpuTensorHandle>
117                 (TensorInfo({ numUnits }, DataType::Float32));
118         layer->m_CifgParameters.m_InputToInputWeights->Allocate();
119         layer->m_CifgParameters.m_RecurrentToInputWeights->Allocate();
120         layer->m_CifgParameters.m_CellToInputWeights->Allocate();
121         layer->m_CifgParameters.m_InputGateBias->Allocate();
122     }
123
124     if (layerDesc.m_ProjectionEnabled)
125     {
126         layer->m_ProjectionParameters.m_ProjectionWeights = std::make_unique<ScopedCpuTensorHandle>
127                 (TensorInfo({ outputSize, numUnits }, DataType::Float32));
128         layer->m_ProjectionParameters.m_ProjectionBias = std::make_unique<ScopedCpuTensorHandle>
129                 (TensorInfo({ outputSize }, DataType::Float32));
130         layer->m_ProjectionParameters.m_ProjectionWeights->Allocate();
131         layer->m_ProjectionParameters.m_ProjectionBias->Allocate();
132     }
133
134     if (layerDesc.m_PeepholeEnabled)
135     {
136         layer->m_PeepholeParameters.m_CellToForgetWeights = std::make_unique<ScopedCpuTensorHandle>
137                 (TensorInfo({ numUnits }, DataType::Float32));
138         layer->m_PeepholeParameters.m_CellToOutputWeights = std::make_unique<ScopedCpuTensorHandle>
139                 (TensorInfo({ numUnits }, DataType::Float32));
140         layer->m_PeepholeParameters.m_CellToForgetWeights->Allocate();
141         layer->m_PeepholeParameters.m_CellToOutputWeights->Allocate();
142     }
143
144     // create input and output layers
145     Layer* const input = graph.AddLayer<InputLayer>(0, "input");
146     Layer* const outputStateIn = graph.AddLayer<InputLayer>(1, "outputStateIn");
147     Layer* const cellStateIn = graph.AddLayer<InputLayer>(2, "cellStateIn");
148     Layer* const scratchBuffer = graph.AddLayer<OutputLayer>(0, "scratchBuffer");
149     Layer* const outputStateOut = graph.AddLayer<OutputLayer>(1, "outputStateOut");
150     Layer* const cellStateOut = graph.AddLayer<OutputLayer>(2, "cellStateOut");
151     Layer* const output = graph.AddLayer<OutputLayer>(3, "output");
152
153     // connect up
154     armnn::TensorInfo lstmTensorInfo1({ batchSize, inputSize }, DataType::Float32);
155     armnn::TensorInfo lstmTensorInfo2({ batchSize, numUnits}, DataType::Float32);
156     armnn::TensorInfo lstmTensorInfo3({ batchSize, outputSize }, DataType::Float32);
157     armnn::TensorInfo lstmTensorInfoScratchBuff({ batchSize, numUnits*3 }, DataType::Float32);
158     if (layerDesc.m_CifgEnabled)
159     {
160         lstmTensorInfoScratchBuff.SetShape({ batchSize, numUnits*4 });
161     }
162
163     Connect(input, layer, lstmTensorInfo1, 0, 0);
164     Connect(cellStateIn, layer, lstmTensorInfo2, 0, 1);
165     Connect(outputStateIn, layer, lstmTensorInfo3, 0, 2);
166     Connect(layer, scratchBuffer, lstmTensorInfoScratchBuff, 0, 0);
167     Connect(layer, outputStateOut, lstmTensorInfo3, 1, 0);
168     Connect(layer, cellStateOut, lstmTensorInfo2, 2, 0);
169     Connect(layer, output, lstmTensorInfo3, 3, 0);
170 }
171
172 }
173
174 BOOST_AUTO_TEST_SUITE(Optimizer)
175 using namespace armnn::optimizations;
176
177 BOOST_AUTO_TEST_CASE(OptimizeInversePermutesTest)
178 {
179     armnn::Graph graph;
180
181     auto output = graph.AddLayer<armnn::OutputLayer>(0, "output");
182
183     graph.InsertNewLayer<armnn::InputLayer>(output->GetInputSlot(0), 0, "input");
184
185     // Inserts two permutes, one the inverse of the other.
186     graph.InsertNewLayer<armnn::PermuteLayer>(output->GetInputSlot(0),
187                                               armnn::PermuteDescriptor({0, 2, 3, 1}),
188                                               "perm0231");
189     graph.InsertNewLayer<armnn::PermuteLayer>(output->GetInputSlot(0),
190                                               armnn::PermuteDescriptor({0, 3, 1, 2}),
191                                               "perm0312");
192
193     BOOST_TEST(CheckSequence(graph.cbegin(),
194                              graph.cend(),
195                              &IsLayerOfType<armnn::InputLayer>,
196                              &IsLayerOfType<armnn::PermuteLayer>,
197                              &IsLayerOfType<armnn::PermuteLayer>,
198                              &IsLayerOfType<armnn::OutputLayer>));
199
200     armnn::Optimizer::Pass(graph, armnn::MakeOptimizations(OptimizeInversePermutes()));
201
202     // The permutes are removed.
203     BOOST_TEST(CheckSequence(graph.cbegin(),
204                              graph.cend(),
205                              &IsLayerOfType<armnn::InputLayer>,
206                              &IsLayerOfType<armnn::OutputLayer>));
207 }
208
209 BOOST_AUTO_TEST_CASE(LSTMValidateTensorShapesFromInputsCIFGDisabledTest)
210 {
211     Graph graph;
212
213     //Helper function creates graph containing LSTM layer with required input and output layers
214     CreateLSTMLayerHelper(graph, false);
215
216     //This function used to call ValidateShapesFromInputs();
217     BOOST_CHECK_NO_THROW(graph.InferTensorInfos());
218 }
219
220 BOOST_AUTO_TEST_CASE(LSTMValidateTensorShapesFromInputsCIFGEnabledTest)
221 {
222     Graph graph;
223
224     //Helper function creates graph containing LSTM layer with required input and output layers
225     CreateLSTMLayerHelper(graph, true);
226
227     //This function used to call ValidateShapesFromInputs();
228     BOOST_CHECK_NO_THROW(graph.InferTensorInfos());
229 }
230
231 BOOST_AUTO_TEST_CASE(MovePermuteUpTest)
232 {
233     const armnn::TensorInfo info({ 1, 5, 2, 3 }, armnn::DataType::Float32);
234     const armnn::TensorInfo permuted({ 1, 3, 5, 2 }, armnn::DataType::Float32);
235
236     armnn::Graph graph;
237
238     armnn::LayerBindingId inputId = 0;
239
240     armnn::Layer* head = graph.AddLayer<armnn::OutputLayer>(0, "output");
241
242     std::string permuteLayerName = "original_permute";
243
244     // Insert permute
245     head = graph.InsertNewLayer<armnn::PermuteLayer>(head->GetInputSlot(0),
246                                                      armnn::PermuteDescriptor({ 0, 2, 3, 1 }),
247                                                      permuteLayerName.c_str());
248
249     head->GetOutputHandler().SetTensorInfo(permuted);
250
251     // Inserts layers that don't care about data format.
252     head = graph.InsertNewLayer<armnn::ActivationLayer>(head->GetInputSlot(0),
253                                                         armnn::ActivationDescriptor{}, "");
254     head->GetOutputHandler().SetTensorInfo(info);
255
256     head = graph.InsertNewLayer<armnn::AdditionLayer>(head->GetInputSlot(0), "");
257     head->GetOutputHandler().SetTensorInfo(info);
258
259     // Inserts input for 2nd input of Addition.
260     graph.InsertNewLayer<armnn::InputLayer>(head->GetInputSlot(1), inputId++, "")
261         ->GetOutputHandler().SetTensorInfo(info);
262
263     head = graph.InsertNewLayer<armnn::FakeQuantizationLayer>(head->GetInputSlot(0),
264                                                               armnn::FakeQuantizationDescriptor{}, "");
265     head->GetOutputHandler().SetTensorInfo(info);
266
267     head = graph.InsertNewLayer<armnn::FloorLayer>(head->GetInputSlot(0), "");
268     head->GetOutputHandler().SetTensorInfo(info);
269
270     head = graph.InsertNewLayer<armnn::MemCopyLayer>(head->GetInputSlot(0), "");
271     head->GetOutputHandler().SetTensorInfo(info);
272
273     head = graph.InsertNewLayer<armnn::MultiplicationLayer>(head->GetInputSlot(0), "");
274     head->GetOutputHandler().SetTensorInfo(info);
275
276     // Inserts input for 2nd input of Multiplication.
277     graph.InsertNewLayer<armnn::InputLayer>(head->GetInputSlot(1), inputId++, "")
278         ->GetOutputHandler().SetTensorInfo(info);
279
280     // Inserts input.
281     graph.InsertNewLayer<armnn::InputLayer>(head->GetInputSlot(0), inputId++, "")
282         ->GetOutputHandler().SetTensorInfo(info);
283
284     BOOST_TEST(CheckSequence(graph.cbegin(),
285                              graph.cend(),
286                              &IsLayerOfType<armnn::InputLayer>,
287                              &IsLayerOfType<armnn::InputLayer>,
288                              &IsLayerOfType<armnn::InputLayer>,
289                              &IsLayerOfType<armnn::MultiplicationLayer>,
290                              &IsLayerOfType<armnn::MemCopyLayer>,
291                              &IsLayerOfType<armnn::FloorLayer>,
292                              &IsLayerOfType<armnn::FakeQuantizationLayer>,
293                              &IsLayerOfType<armnn::AdditionLayer>,
294                              &IsLayerOfType<armnn::ActivationLayer>,
295                              &IsLayerOfType<armnn::PermuteLayer>,
296                              &IsLayerOfType<armnn::OutputLayer>));
297
298     armnn::Optimizer::Pass(graph, armnn::MakeOptimizations(MovePermuteUp()));
299
300     // The permute is moved to the top. New permutes for layers with multiple inputs.
301     BOOST_TEST(CheckSequence(graph.cbegin(),
302                              graph.cend(),
303                              &IsLayerOfType<armnn::InputLayer>,
304                              &IsLayerOfType<armnn::InputLayer>,
305                              &IsLayerOfType<armnn::InputLayer>,
306                              &IsLayerOfType<armnn::PermuteLayer>,
307                              &IsLayerOfType<armnn::PermuteLayer>,
308                              &IsLayerOfType<armnn::PermuteLayer>,
309                              &IsLayerOfType<armnn::MultiplicationLayer>,
310                              &IsLayerOfType<armnn::MemCopyLayer>,
311                              &IsLayerOfType<armnn::FloorLayer>,
312                              &IsLayerOfType<armnn::FakeQuantizationLayer>,
313                              &IsLayerOfType<armnn::AdditionLayer>,
314                              &IsLayerOfType<armnn::ActivationLayer>,
315                              &IsLayerOfType<armnn::OutputLayer>));
316
317     std::list<std::string> testRelatedLayers = { permuteLayerName };
318
319     BOOST_TEST(CheckRelatedLayers<armnn::PermuteLayer>(graph, testRelatedLayers));
320 }
321
322 BOOST_AUTO_TEST_CASE(PermuteAsReshapeTest)
323 {
324     armnn::Graph graph;
325
326     std::string permuteLayerName = "permute";
327
328     const armnn::TensorInfo infoIn({ 1, 2, 3, 1 }, armnn::DataType::Float32);
329     const armnn::TensorInfo infoOut({ 1, 1, 2, 3 }, armnn::DataType::Float32);
330
331     auto output = graph.AddLayer<armnn::OutputLayer>(0, "output");
332
333     graph.InsertNewLayer<armnn::InputLayer>(output->GetInputSlot(0), 0, "input")
334         ->GetOutputHandler().SetTensorInfo(infoIn);
335
336     // Inserts permute.
337     graph.InsertNewLayer<armnn::PermuteLayer>(output->GetInputSlot(0),
338                                               armnn::PermuteDescriptor({ 0, 2, 3, 1 }), permuteLayerName.c_str())
339         ->GetOutputHandler().SetTensorInfo(infoOut);
340
341     BOOST_TEST(CheckSequence(graph.cbegin(),
342                              graph.cend(),
343                              &IsLayerOfType<armnn::InputLayer>,
344                              &IsLayerOfType<armnn::PermuteLayer>,
345                              &IsLayerOfType<armnn::OutputLayer>));
346
347     armnn::Optimizer::Pass(graph, armnn::MakeOptimizations(PermuteAsReshape()));
348
349     // The permute is replaced by an equivalent reshape.
350
351     auto checkReshape = [&infoOut](const armnn::Layer* const layer) -> bool
352         {
353             const auto reshapeLayer = static_cast<const armnn::ReshapeLayer*>(layer);
354             return IsLayerOfType<armnn::ReshapeLayer>(layer) &&
355                    (reshapeLayer->GetParameters().m_TargetShape == infoOut.GetShape()) &&
356                    (reshapeLayer->GetOutputHandler().GetTensorInfo().GetShape() == infoOut.GetShape());
357         };
358
359     BOOST_TEST(CheckSequence(graph.cbegin(),
360                              graph.cend(),
361                              &IsLayerOfType<armnn::InputLayer>,
362                              checkReshape,
363                              &IsLayerOfType<armnn::OutputLayer>));
364
365
366     std::list<std::string> testRelatedLayers = { permuteLayerName };
367     BOOST_TEST(CheckRelatedLayers<armnn::ReshapeLayer>(graph, testRelatedLayers));
368 }
369
370 BOOST_AUTO_TEST_CASE(OptimizeConsecutiveReshapesTest)
371 {
372     armnn::Graph graph;
373
374     const armnn::TensorInfo info0({ 1, 2, 3, 5 }, armnn::DataType::Float32);
375
376     auto output = graph.AddLayer<armnn::OutputLayer>(0, "output");
377     auto input = graph.InsertNewLayer<armnn::InputLayer>(output->GetInputSlot(0), 0, "input");
378
379     input->GetOutputHandler().SetTensorInfo(info0);
380
381     {
382         // Inserts two reshapes.
383         const armnn::TensorInfo info1({1, 30, 1, 1}, armnn::DataType::Float32);
384         const armnn::TensorInfo info2({1, 2, 1, 15}, armnn::DataType::Float32);
385
386         std::string reshape1Name = "reshape1";
387         std::string reshape2Name = "reshape2";
388
389         auto reshape1 = graph.InsertNewLayer<armnn::ReshapeLayer>(output->GetInputSlot(0),
390                                                                   armnn::ReshapeDescriptor{ info1.GetShape() },
391                                                                   reshape1Name.c_str());
392         auto reshape2 = graph.InsertNewLayer<armnn::ReshapeLayer>(output->GetInputSlot(0),
393                                                                   armnn::ReshapeDescriptor{ info2.GetShape() },
394                                                                   reshape2Name.c_str());
395
396         reshape1->GetOutputHandler().SetTensorInfo(info1);
397         reshape2->GetOutputHandler().SetTensorInfo(info2);
398
399         BOOST_TEST(CheckSequence(graph.cbegin(),
400                                  graph.cend(),
401                                  &IsLayerOfType<armnn::InputLayer>,
402                                  &IsLayerOfType<armnn::ReshapeLayer>,
403                                  &IsLayerOfType<armnn::ReshapeLayer>,
404                                  &IsLayerOfType<armnn::OutputLayer>));
405
406         armnn::Optimizer::Pass(graph, armnn::MakeOptimizations(OptimizeConsecutiveReshapes()));
407
408         auto checkReshape = [&info2](const armnn::Layer* const layer) -> bool
409             {
410                 const auto reshapeLayer = static_cast<const armnn::ReshapeLayer*>(layer);
411                 return IsLayerOfType<armnn::ReshapeLayer>(layer) &&
412                     (reshapeLayer->GetParameters().m_TargetShape == info2.GetShape()) &&
413                     (reshapeLayer->GetOutputHandler().GetTensorInfo().GetShape() == info2.GetShape());
414             };
415
416         // The two reshapes are replaced by a single equivalent reshape.
417         BOOST_TEST(CheckSequence(graph.cbegin(),
418                                  graph.cend(),
419                                  &IsLayerOfType<armnn::InputLayer>,
420                                  checkReshape,
421                                  &IsLayerOfType<armnn::OutputLayer>));
422
423         // Check the new reshape layer has the other two reshapes as related layers
424         std::list<std::string> testRelatedLayers = { reshape2Name, reshape1Name };
425
426         BOOST_TEST(CheckRelatedLayers<armnn::ReshapeLayer>(graph, testRelatedLayers));
427     }
428
429     {
430         // Inserts a reshape to the input shape.
431         auto reshapeToIn = graph.InsertNewLayer<armnn::ReshapeLayer>(output->GetInputSlot(0),
432                                                                      armnn::ReshapeDescriptor{ info0.GetShape() },
433                                                                      "reshapeToIn");
434
435         reshapeToIn->GetOutputHandler().SetTensorInfo(info0);
436
437         armnn::Optimizer::Pass(graph, armnn::MakeOptimizations(OptimizeConsecutiveReshapes()));
438
439         // The two reshapes are removed.
440         BOOST_TEST(CheckSequence(graph.cbegin(),
441                                  graph.cend(),
442                                  &IsLayerOfType<armnn::InputLayer>,
443                                  &IsLayerOfType<armnn::OutputLayer>));
444     }
445 }
446
447 BOOST_AUTO_TEST_CASE(SquashEqualSiblingsTest)
448 {
449     armnn::Graph graph;
450
451     armnn::LayerBindingId outputId = 0;
452
453     const armnn::TensorInfo info({ 1, 2, 3, 5 }, armnn::DataType::Float32);
454     const armnn::TensorInfo permuted({ 1, 5, 2, 3 }, armnn::DataType::Float32);
455
456     auto input = graph.AddLayer<armnn::InputLayer>(0, "input");
457     input->GetOutputSlot().SetTensorInfo(info);
458
459     // Inserts equal permutes, equal reshapes and something else.
460     const armnn::PermuteDescriptor permDesc({ 0, 2, 3, 1 });
461     const armnn::ReshapeDescriptor reshapeDesc{ { 1, 3, 1, 5 } };
462
463     armnn::Layer* layer;
464
465     layer = graph.AddLayer<armnn::PermuteLayer>(permDesc, "");
466     layer->GetOutputSlot().SetTensorInfo(permuted);
467     layer->GetOutputSlot().Connect(graph.AddLayer<armnn::OutputLayer>(outputId++, "")->GetInputSlot(0));
468     input->GetOutputSlot().Connect(layer->GetInputSlot(0));
469
470     layer = graph.AddLayer<armnn::ReshapeLayer>(reshapeDesc, "");
471     layer->GetOutputSlot().Connect(graph.AddLayer<armnn::OutputLayer>(outputId++, "")->GetInputSlot(0));
472     input->GetOutputSlot().Connect(layer->GetInputSlot(0));
473
474     layer = graph.AddLayer<armnn::FloorLayer>("");
475     layer->GetOutputSlot().Connect(graph.AddLayer<armnn::OutputLayer>(outputId++, "")->GetInputSlot(0));
476     input->GetOutputSlot().Connect(layer->GetInputSlot(0));
477
478     layer = graph.AddLayer<armnn::ReshapeLayer>(reshapeDesc, "");
479     layer->GetOutputSlot().Connect(graph.AddLayer<armnn::OutputLayer>(outputId++, "")->GetInputSlot(0));
480     input->GetOutputSlot().Connect(layer->GetInputSlot(0));
481
482     layer = graph.AddLayer<armnn::PermuteLayer>(permDesc, "");
483     layer->GetOutputSlot().SetTensorInfo(permuted);
484     layer->GetOutputSlot().Connect(graph.AddLayer<armnn::OutputLayer>(outputId++, "")->GetInputSlot(0));
485     input->GetOutputSlot().Connect(layer->GetInputSlot(0));
486
487     BOOST_TEST(CheckSequence(graph.cbegin(),
488                              graph.cend(),
489                              &IsLayerOfType<armnn::InputLayer>,
490                              &IsLayerOfType<armnn::PermuteLayer>,
491                              &IsLayerOfType<armnn::ReshapeLayer>,
492                              &IsLayerOfType<armnn::FloorLayer>,
493                              &IsLayerOfType<armnn::ReshapeLayer>,
494                              &IsLayerOfType<armnn::PermuteLayer>,
495                              &IsLayerOfType<armnn::OutputLayer>,
496                              &IsLayerOfType<armnn::OutputLayer>,
497                              &IsLayerOfType<armnn::OutputLayer>,
498                              &IsLayerOfType<armnn::OutputLayer>,
499                              &IsLayerOfType<armnn::OutputLayer>));
500
501     armnn::Optimizer::Pass(graph, armnn::MakeOptimizations(SquashEqualPermuteSiblings(),
502                                                             SquashEqualReshapeSiblings()));
503
504     // The permutes and reshapes are squashed.
505
506     BOOST_TEST(CheckSequence(graph.cbegin(),
507                              graph.cend(),
508                              &IsLayerOfType<armnn::InputLayer>,
509                              &IsLayerOfType<armnn::PermuteLayer>,
510                              &IsLayerOfType<armnn::ReshapeLayer>,
511                              &IsLayerOfType<armnn::FloorLayer>,
512                              &IsLayerOfType<armnn::OutputLayer>,
513                              &IsLayerOfType<armnn::OutputLayer>,
514                              &IsLayerOfType<armnn::OutputLayer>,
515                              &IsLayerOfType<armnn::OutputLayer>,
516                              &IsLayerOfType<armnn::OutputLayer>));
517 }
518
519 BOOST_AUTO_TEST_CASE(ConvertConstantsHalfToFloatTest)
520 {
521     armnn::Graph graph;
522
523     const armnn::TensorInfo info({ 1,1,1,2 }, armnn::DataType::Float32);
524
525     // Create the half precision input data
526     unsigned int dims[] = { 4,1,1,1 };
527     std::vector<float> convWeightsData{1.f, 2.f, 3.f, 4.f};
528     std::vector<uint16_t> halfWeights(4);
529     armnnUtils::FloatingPointConverter::ConvertFloat32To16(convWeightsData.data(),
530                                                            convWeightsData.size(),
531                                                            halfWeights.data());
532     armnn::ConstTensor weights(armnn::TensorInfo(4, dims, armnn::DataType::Float16), halfWeights);
533
534     //Create the simple test network
535     auto input = graph.AddLayer<armnn::InputLayer>(0, "input");
536     input->GetOutputSlot().SetTensorInfo(info);
537
538     auto fc = graph.AddLayer<armnn::FullyConnectedLayer>(armnn::FullyConnectedDescriptor(), "fc");
539     fc->m_Weight = std::make_unique<armnn::ScopedCpuTensorHandle>(weights);
540     fc->GetOutputSlot().SetTensorInfo(info);
541
542     auto output = graph.AddLayer<armnn::OutputLayer>(1, "output");
543
544     //Connect up the layers
545     input->GetOutputSlot().Connect(fc->GetInputSlot(0));
546     fc->GetOutputSlot().Connect(output->GetInputSlot(0));
547
548     //Test the tensor info is correct.
549     BOOST_CHECK(fc->m_Weight->GetTensorInfo().GetDataType() == armnn::DataType::Float16);
550
551     // Run the optimizer
552     armnn::Optimizer::Pass(graph, armnn::MakeOptimizations(ConvertConstantsHalfToFloat()));
553
554     //Test the tensor info is correct.
555     BOOST_CHECK(fc->m_Weight->GetTensorInfo().GetDataType() == armnn::DataType::Float32);
556
557     // Now test the data matches float32 data
558     float* data = fc->m_Weight->GetTensor<float>();
559     BOOST_CHECK(1.0f == data[0]);
560     BOOST_CHECK(2.0f == data[1]);
561     BOOST_CHECK(3.0f == data[2]);
562     BOOST_CHECK(4.0f == data[3]);
563 }
564
565 BOOST_AUTO_TEST_CASE(ConvertConstantsFloatToHalfTest)
566 {
567     armnn::Graph graph;
568
569     const armnn::TensorInfo info({ 1, 1, 1, 2 }, armnn::DataType::Float16);
570
571     // Create const tensor from fp32 data
572     unsigned int dims[] = { 4, 1, 1, 1 };
573     std::vector<float> floatWeights{ 1.0f, 2.0f, 3.0f, 4.0f };
574     armnn::ConstTensor weights(armnn::TensorInfo(4, dims, armnn::DataType::Float32), floatWeights);
575
576     // Create simple test network
577     auto input = graph.AddLayer<armnn::InputLayer>(0, "input");
578     input->GetOutputSlot().SetTensorInfo(info);
579
580     auto fc = graph.AddLayer<armnn::FullyConnectedLayer>(armnn::FullyConnectedDescriptor(), "fc");
581     fc->m_Weight = std::make_unique<armnn::ScopedCpuTensorHandle>(weights);
582     fc->GetOutputSlot().SetTensorInfo(info);
583
584     auto output = graph.AddLayer<armnn::OutputLayer>(1, "output");
585
586     // Connect up the layers
587     input->GetOutputSlot().Connect(fc->GetInputSlot(0));
588     fc->GetOutputSlot().Connect(output->GetInputSlot(0));
589
590     // Check tensor data type before conversion
591     BOOST_CHECK(fc->m_Weight->GetTensorInfo().GetDataType() == armnn::DataType::Float32);
592
593     // Run the optimizer
594     armnn::Optimizer::Pass(graph, armnn::MakeOptimizations(ConvertConstantsFloatToHalf()));
595
596     // Check tensor data type after conversion
597     BOOST_CHECK(fc->m_Weight->GetTensorInfo().GetDataType() == armnn::DataType::Float16);
598
599     // Check whether data matches expected fp16 data
600     Half* data = fc->m_Weight->GetTensor<Half>();
601     BOOST_CHECK(data[0] == Half(1.0f));
602     BOOST_CHECK(data[1] == Half(2.0f));
603     BOOST_CHECK(data[2] == Half(3.0f));
604     BOOST_CHECK(data[3] == Half(4.0f));
605 }
606
607 BOOST_AUTO_TEST_CASE(OptimizeInverseConversionsTest)
608 {
609     armnn::Graph graph;
610
611     auto output = graph.AddLayer<armnn::OutputLayer>(0, "output");
612
613     graph.InsertNewLayer<armnn::InputLayer>(output->GetInputSlot(0), 0, "input");
614
615     // Fp32ToFp16 conversion followed by an inverse Fp16ToFp32 conversion
616     graph.InsertNewLayer<armnn::ConvertFp32ToFp16Layer>(output->GetInputSlot(0), "convert1");
617     graph.InsertNewLayer<armnn::ConvertFp16ToFp32Layer>(output->GetInputSlot(0), "convert2");
618
619     graph.InsertNewLayer<armnn::Convolution2dLayer>(output->GetInputSlot(0), Convolution2dDescriptor(), "conv");
620
621     // Fp16ToFp32 conversion followed by an inverse Fp32ToFp16 conversion
622     graph.InsertNewLayer<armnn::ConvertFp16ToFp32Layer>(output->GetInputSlot(0), "convert3");
623     graph.InsertNewLayer<armnn::ConvertFp32ToFp16Layer>(output->GetInputSlot(0), "convert4");
624
625     BOOST_TEST(CheckSequence(graph.cbegin(),
626                              graph.cend(),
627                              &IsLayerOfType<armnn::InputLayer>,
628                              &IsLayerOfType<armnn::ConvertFp32ToFp16Layer>,
629                              &IsLayerOfType<armnn::ConvertFp16ToFp32Layer>,
630                              &IsLayerOfType<armnn::Convolution2dLayer>,
631                              &IsLayerOfType<armnn::ConvertFp16ToFp32Layer>,
632                              &IsLayerOfType<armnn::ConvertFp32ToFp16Layer>,
633                              &IsLayerOfType<armnn::OutputLayer>));
634
635     armnn::Optimizer::Pass(graph, armnn::MakeOptimizations(OptimizeInverseConversionsFp16(),
636                                                            OptimizeInverseConversionsFp32()));
637
638     // Check that all consecutive inverse conversions are removed
639     BOOST_TEST(CheckSequence(graph.cbegin(),
640                              graph.cend(),
641                              &IsLayerOfType<armnn::InputLayer>,
642                              &IsLayerOfType<armnn::Convolution2dLayer>,
643                              &IsLayerOfType<armnn::OutputLayer>));
644 }
645
646 BOOST_AUTO_TEST_CASE(InsertConvertersTest)
647 {
648     const armnn::TensorInfo info({ 1, 5, 2, 3 }, armnn::DataType::Float16);
649
650     armnn::Graph graph;
651
652     armnn::LayerBindingId inputId = 0;
653
654     armnn::Layer* head = graph.AddLayer<armnn::OutputLayer>(0, "output");
655
656     head = graph.InsertNewLayer<armnn::AdditionLayer>(head->GetInputSlot(0), "");
657     head->GetOutputHandler().SetTensorInfo(info);
658
659     graph.InsertNewLayer<armnn::InputLayer>(head->GetInputSlot(1), inputId++, "")
660         ->GetOutputHandler().SetTensorInfo(info);
661
662     head = graph.InsertNewLayer<armnn::FloorLayer>(head->GetInputSlot(0), "");
663     head->GetOutputHandler().SetTensorInfo(info);
664
665     head = graph.InsertNewLayer<armnn::MemCopyLayer>(head->GetInputSlot(0), "");
666     head->GetOutputHandler().SetTensorInfo(info);
667
668     graph.InsertNewLayer<armnn::InputLayer>(head->GetInputSlot(0), inputId++, "")
669         ->GetOutputHandler().SetTensorInfo(info);
670
671     // Check graph layer sequence before inserting convert layers
672     BOOST_TEST(CheckSequence(graph.cbegin(),
673                              graph.cend(),
674                              &IsLayerOfType<armnn::InputLayer>,
675                              &IsLayerOfType<armnn::InputLayer>,
676                              &IsLayerOfType<armnn::MemCopyLayer>,
677                              &IsLayerOfType<armnn::FloorLayer>,
678                              &IsLayerOfType<armnn::AdditionLayer>,
679                              &IsLayerOfType<armnn::OutputLayer>));
680
681     // Check layers have Float16 DataType
682     for (auto& layer : graph)
683     {
684         if(layer->GetType()==LayerType::Floor || layer->GetType() == LayerType::Addition)
685         {
686             BOOST_ASSERT(layer->GetOutputSlot(0).GetTensorInfo().GetDataType() == DataType::Float16);
687             BOOST_ASSERT(layer->GetDataType() == DataType::Float16);
688         }
689     }
690
691     // Insert convert layers either side of unsupported layer
692     for (auto& layer : graph)
693     {
694         if(layer->GetType()==LayerType::Floor || layer->GetType() == LayerType::Addition)
695         {
696             InsertConvertFp16ToFp32LayersBefore(graph, *layer);
697             InsertConvertFp32ToFp16LayersAfter(graph, *layer);
698         }
699     }
700
701     // Check layers have correct DataType after inserting convert layers
702     for (auto& layer : graph)
703     {
704         if (layer->GetType()==LayerType::Floor || layer->GetType() == LayerType::Addition)
705         {
706             BOOST_ASSERT(layer->GetOutputSlot(0).GetTensorInfo().GetDataType() == DataType::Float32);
707             BOOST_ASSERT(layer->GetDataType() == DataType::Float32);
708         }
709         else if (layer->GetType() == LayerType::ConvertFp16ToFp32)
710         {
711             BOOST_ASSERT(layer->GetOutputSlot(0).GetTensorInfo().GetDataType() == DataType::Float32);
712             BOOST_ASSERT(layer->GetDataType() == DataType::Float16);
713         }
714         else if (layer->GetType() == LayerType::ConvertFp32ToFp16)
715         {
716             BOOST_ASSERT(layer->GetOutputSlot(0).GetTensorInfo().GetDataType() == DataType::Float16);
717             BOOST_ASSERT(layer->GetDataType() == DataType::Float32);
718         }
719     }
720
721     // Check sequence of layers after inserting convert layers
722     BOOST_TEST(CheckSequence(graph.cbegin(),
723                              graph.cend(),
724                              &IsLayerOfType<armnn::InputLayer>,
725                              &IsLayerOfType<armnn::InputLayer>,
726                              &IsLayerOfType<armnn::ConvertFp16ToFp32Layer>,
727                              &IsLayerOfType<armnn::MemCopyLayer>,
728                              &IsLayerOfType<armnn::ConvertFp16ToFp32Layer>,
729                              &IsLayerOfType<armnn::FloorLayer>,
730                              &IsLayerOfType<armnn::ConvertFp32ToFp16Layer>,
731                              &IsLayerOfType<armnn::ConvertFp16ToFp32Layer>,
732                              &IsLayerOfType<armnn::AdditionLayer>,
733                              &IsLayerOfType<armnn::ConvertFp32ToFp16Layer>,
734                              &IsLayerOfType<armnn::OutputLayer>));
735 }
736
737 BOOST_AUTO_TEST_CASE(Fp32NetworkToFp16OptimizationTest)
738 {
739     armnn::Graph graph;
740
741     const armnn::TensorInfo infoFP32({ 2,2,1,3 }, armnn::DataType::Float32);
742
743     // Create the simple test network
744     auto input = graph.AddLayer<armnn::InputLayer>(0, "input");
745     input->GetOutputSlot().SetTensorInfo(infoFP32);
746
747     auto floor = graph.AddLayer<armnn::FloorLayer>("floor");
748     floor->GetOutputSlot().SetTensorInfo(infoFP32);
749
750     auto output = graph.AddLayer<armnn::OutputLayer>(1, "output");
751
752     // Connect up the layers
753     input->GetOutputSlot().Connect(floor->GetInputSlot(0));
754     floor->GetOutputSlot().Connect(output->GetInputSlot(0));
755
756     BOOST_TEST(CheckSequence(graph.cbegin(),
757                              graph.cend(),
758                              &IsLayerOfType<armnn::InputLayer>,
759                              &IsLayerOfType<armnn::FloorLayer>,
760                              &IsLayerOfType<armnn::OutputLayer>));
761
762     // Run the optimizer
763     armnn::Optimizer::Pass(graph, armnn::MakeOptimizations(Fp32NetworkToFp16Converter()));
764
765     BOOST_TEST(CheckSequence(graph.cbegin(),
766                              graph.cend(),
767                              &IsLayerOfType<armnn::InputLayer>,
768                              &IsLayerOfType<armnn::ConvertFp32ToFp16Layer>,
769                              &IsLayerOfType<armnn::FloorLayer>,
770                              &IsLayerOfType<armnn::ConvertFp16ToFp32Layer>,
771                              &IsLayerOfType<armnn::OutputLayer>));
772 }
773
774 void CreateConvolution2dGraph(Graph &graph, const unsigned int* inputShape,
775                               const unsigned int* weightsShape, const unsigned int* outputShape,
776                               DataLayout dataLayout = DataLayout::NCHW)
777 {
778     armnn::TensorInfo inputInfo(4, inputShape, DataType::Float32);
779     armnn::TensorInfo outputInfo(4, outputShape, DataType::Float32);
780
781     std::vector<float> weightsVector(90);
782     armnn::ConstTensor weights(armnn::TensorInfo(4, weightsShape, armnn::DataType::Float32), weightsVector);
783
784     Convolution2dDescriptor desc;
785     desc.m_BiasEnabled = false;
786     desc.m_StrideX = 1;
787     desc.m_StrideY = 1;
788     desc.m_DataLayout = dataLayout;
789
790     Layer* input = graph.AddLayer<InputLayer>(0, "input");
791     input->GetOutputSlot().SetTensorInfo(inputInfo);
792
793     Convolution2dLayer* layer = graph.AddLayer<Convolution2dLayer>(desc, "conv2d");
794     layer->m_Weight = std::make_unique<armnn::ScopedCpuTensorHandle>(weights);
795     layer->GetOutputSlot().SetTensorInfo(outputInfo);
796
797     Layer* output = graph.AddLayer<OutputLayer>(0, "output");
798     input->GetOutputSlot().Connect(layer->GetInputSlot(0));
799     layer->GetOutputSlot().Connect(output->GetInputSlot(0));
800 }
801
802 BOOST_AUTO_TEST_CASE(Conv2dValidateTensorShapesFromInputs)
803 {
804     Graph graph;
805     const unsigned int inputShape[] = { 1, 3, 8, 16 };
806     const unsigned int weightsShape[] = { 2, 3, 5, 3 };
807     const unsigned int outputShape[] = { 1, 2, 4, 14 };
808     CreateConvolution2dGraph(graph, inputShape, weightsShape, outputShape);
809
810     BOOST_CHECK_NO_THROW(graph.InferTensorInfos());
811 }
812
813 BOOST_AUTO_TEST_CASE(Conv2dValidateTensorShapesFromInputsNhwc)
814 {
815     Graph graph;
816     const unsigned int inputShape[] = { 1, 8, 16, 3 };
817     const unsigned int weightsShape[] = { 2, 5, 3, 3 };
818     const unsigned int outputShape[] = { 1, 4, 14, 2 };
819     CreateConvolution2dGraph(graph, inputShape, weightsShape, outputShape, DataLayout::NHWC);
820
821     BOOST_CHECK_NO_THROW(graph.InferTensorInfos());
822 }
823
824 void CreateDepthwiseConvolution2dGraph(Graph &graph, const unsigned int* inputShape,
825                               const unsigned int* weightsShape, const unsigned int* outputShape,
826                               DataLayout dataLayout = DataLayout::NCHW)
827 {
828     armnn::TensorInfo inputInfo(4, inputShape, DataType::Float32);
829     armnn::TensorInfo outputInfo(4, outputShape, DataType::Float32);
830
831     std::vector<float> weightsVector(18);
832     armnn::ConstTensor weights(armnn::TensorInfo(4, weightsShape, armnn::DataType::Float32), weightsVector);
833
834     DepthwiseConvolution2dDescriptor desc;
835     desc.m_BiasEnabled = false;
836     desc.m_StrideX = 1;
837     desc.m_StrideY = 1;
838     desc.m_DataLayout = dataLayout;
839
840     Layer* input = graph.AddLayer<InputLayer>(0, "input");
841     input->GetOutputSlot().SetTensorInfo(inputInfo);
842
843     DepthwiseConvolution2dLayer* layer = graph.AddLayer<DepthwiseConvolution2dLayer>(desc, "depthwiseConv2d");
844     layer->m_Weight = std::make_unique<armnn::ScopedCpuTensorHandle>(weights);
845     layer->GetOutputSlot().SetTensorInfo(outputInfo);
846
847     Layer* output = graph.AddLayer<OutputLayer>(0, "output");
848     input->GetOutputSlot().Connect(layer->GetInputSlot(0));
849     layer->GetOutputSlot().Connect(output->GetInputSlot(0));
850 }
851
852 BOOST_AUTO_TEST_CASE(DepthwiseConv2dValidateTensorShapesFromInputs)
853 {
854     Graph graph;
855     const unsigned int inputShape[] = { 1, 2, 3, 3 };
856     const unsigned int weightsShape[] = { 1, 2, 3, 3 };
857     const unsigned int outputShape[] = { 1, 2, 1, 1 };
858     CreateDepthwiseConvolution2dGraph(graph, inputShape, weightsShape, outputShape);
859
860     BOOST_CHECK_NO_THROW(graph.InferTensorInfos());
861 }
862
863 BOOST_AUTO_TEST_CASE(DepthwiseConv2dValidateTensorShapesFromInputsNhwc)
864 {
865     Graph graph;
866     const unsigned int inputShape[] = { 1, 3, 3, 2 };
867     const unsigned int weightsShape[] = { 1, 3, 3, 2 };
868     const unsigned int outputShape[] = { 1, 1, 1, 2 };
869     CreateDepthwiseConvolution2dGraph(graph, inputShape, weightsShape, outputShape, DataLayout::NHWC);
870
871     BOOST_CHECK_NO_THROW(graph.InferTensorInfos());
872 }
873
874 void CreatePooling2dGraph(Graph &graph, const unsigned int* inputShape,  const unsigned int* outputShape,
875                           DataLayout dataLayout = DataLayout::NCHW)
876 {
877     armnn::TensorInfo inputInfo(4, inputShape, DataType::Float32);
878     armnn::TensorInfo outputInfo(4, outputShape, DataType::Float32);
879
880     Pooling2dDescriptor desc;
881     desc.m_PoolType = armnn::PoolingAlgorithm::Average;
882     desc.m_PoolWidth = desc.m_PoolHeight = 100;
883     desc.m_StrideX = desc.m_StrideY = 5;
884     desc.m_PadLeft = 50;
885     desc.m_PadRight = 50;
886     desc.m_PadTop = 50;
887     desc.m_PadBottom = 50;
888     desc.m_PaddingMethod = armnn::PaddingMethod::Exclude;
889     desc.m_DataLayout = dataLayout;
890
891     Layer* input = graph.AddLayer<InputLayer>(0, "input");
892     input->GetOutputSlot().SetTensorInfo(inputInfo);
893
894     Pooling2dLayer* layer = graph.AddLayer<Pooling2dLayer>(desc, "pooling2d");
895     layer->GetOutputSlot().SetTensorInfo(outputInfo);
896
897     Layer* output = graph.AddLayer<OutputLayer>(0, "output");
898     input->GetOutputSlot().Connect(layer->GetInputSlot(0));
899     layer->GetOutputSlot().Connect(output->GetInputSlot(0));
900 }
901
902 BOOST_AUTO_TEST_CASE(Pooling2dValidateTensorShapesFromInputs)
903 {
904     Graph graph;
905     const unsigned int inputShape[] = { 5, 3, 52, 60 };
906     const unsigned int outputShape[] = { 5, 3, 11, 13 };
907     CreatePooling2dGraph(graph, inputShape, outputShape, DataLayout::NCHW);
908
909     BOOST_CHECK_NO_THROW(graph.InferTensorInfos());
910 }
911
912 BOOST_AUTO_TEST_CASE(Pooling2dValidateTensorShapesFromInputsNhwc)
913 {
914     Graph graph;
915     const unsigned int inputShape[] = { 5, 52, 60, 3 };
916     const unsigned int outputShape[] = { 5, 11, 13, 3 };
917     CreatePooling2dGraph(graph, inputShape, outputShape, DataLayout::NHWC);
918
919     BOOST_CHECK_NO_THROW(graph.InferTensorInfos());
920 }
921
922 void CreateResizeBilinearGraph(Graph &graph, const unsigned int* inputShape,  const unsigned int* outputShape,
923                                DataLayout dataLayout = DataLayout::NCHW)
924 {
925     armnn::TensorInfo inputInfo(4, inputShape, DataType::Float32);
926     armnn::TensorInfo outputInfo(4, outputShape, DataType::Float32);
927
928     ResizeBilinearDescriptor desc;
929     desc.m_TargetHeight = 3;
930     desc.m_TargetWidth = 4;
931     desc.m_DataLayout = dataLayout;
932
933     Layer* input = graph.AddLayer<InputLayer>(0, "input");
934     input->GetOutputSlot().SetTensorInfo(inputInfo);
935
936     ResizeBilinearLayer* layer = graph.AddLayer<ResizeBilinearLayer>(desc, "resizeBilinear");
937     layer->GetOutputSlot().SetTensorInfo(outputInfo);
938
939     Layer* output = graph.AddLayer<OutputLayer>(0, "output");
940     input->GetOutputSlot().Connect(layer->GetInputSlot(0));
941     layer->GetOutputSlot().Connect(output->GetInputSlot(0));
942 }
943
944 BOOST_AUTO_TEST_CASE(ResizeBilinearValidateTensorShapesFromInputs)
945 {
946     Graph graph;
947     const unsigned int inputShape[] = { 1, 2, 4, 5 };
948     const unsigned int outputShape[] = { 1, 2, 3, 4 };
949     CreateResizeBilinearGraph(graph, inputShape, outputShape);
950
951     BOOST_CHECK_NO_THROW(graph.InferTensorInfos());
952 }
953
954 BOOST_AUTO_TEST_CASE(ResizeBilinearValidateTensorShapesFromInputsNhwc)
955 {
956     Graph graph;
957     const unsigned int inputShape[] = { 1, 4, 5, 2 };
958     const unsigned int outputShape[] = { 1, 3, 4, 2 };
959     CreateResizeBilinearGraph(graph, inputShape, outputShape, DataLayout::NHWC);
960
961     BOOST_CHECK_NO_THROW(graph.InferTensorInfos());
962 }
963
964 BOOST_AUTO_TEST_SUITE_END()