Release 18.05.01
[platform/upstream/armnn.git] / src / armnn / test / OptimizerTests.cpp
1 //
2 // Copyright © 2017 Arm Ltd. All rights reserved.
3 // See LICENSE file in the project root for full license information.
4 //
5 #include <boost/test/unit_test.hpp>
6
7 #include "armnn/ArmNN.hpp"
8 #include "Graph.hpp"
9 #include "Optimizer.hpp"
10
11 namespace
12 {
13 template <typename LayerT>
14 bool IsLayerOfType(const armnn::Layer* const layer)
15 {
16     return (layer->GetType() == armnn::LayerEnumOf<LayerT>());
17 }
18
19 bool CheckSequence(const armnn::Graph::ConstIterator first, const armnn::Graph::ConstIterator last)
20 {
21     return (first == last);
22 }
23
24 /// Check each unary function in Us evaluates true for each correspondent layer in the sequence [first, last)
25 template <typename U, typename... Us>
26 bool CheckSequence(const armnn::Graph::ConstIterator first,
27                    const armnn::Graph::ConstIterator last,
28                    U&& u,
29                    Us&&... us)
30 {
31     return u(*first) && CheckSequence(std::next(first), last, us...);
32 }
33 }
34
35 BOOST_AUTO_TEST_SUITE(Optimizer)
36
37 BOOST_AUTO_TEST_CASE(OptimizeInversePermutes)
38 {
39     armnn::Graph graph;
40
41     auto output = graph.AddLayer<armnn::OutputLayer>(0, "output");
42
43     graph.InsertNewLayer<armnn::InputLayer>(output->GetInputSlot(0), 0, "input");
44
45     // Insert two permutes, one the inverse of the other
46     graph.InsertNewLayer<armnn::PermuteLayer>(output->GetInputSlot(0),
47                                               armnn::PermuteDescriptor({0, 2, 3, 1}),
48                                               "perm0231");
49     graph.InsertNewLayer<armnn::PermuteLayer>(output->GetInputSlot(0),
50                                               armnn::PermuteDescriptor({0, 3, 1, 2}),
51                                               "perm0312");
52
53     BOOST_TEST(CheckSequence(graph.cbegin(),
54                              graph.cend(),
55                              &IsLayerOfType<armnn::InputLayer>,
56                              &IsLayerOfType<armnn::PermuteLayer>,
57                              &IsLayerOfType<armnn::PermuteLayer>,
58                              &IsLayerOfType<armnn::OutputLayer>));
59
60     armnn::Optimizer::Optimize(graph);
61
62     // The permutes are removed
63     BOOST_TEST(CheckSequence(graph.cbegin(),
64                              graph.cend(),
65                              &IsLayerOfType<armnn::InputLayer>,
66                              &IsLayerOfType<armnn::OutputLayer>));
67 }
68
69 BOOST_AUTO_TEST_CASE(MovePermuteUp)
70 {
71     const armnn::TensorInfo info({ 1, 5, 2, 3 }, armnn::DataType::Float32);
72     const armnn::TensorInfo permuted({ 1, 3, 5, 2 }, armnn::DataType::Float32);
73
74     armnn::Graph graph;
75
76     armnn::LayerBindingId inputId = 0;
77
78     armnn::Layer* head = graph.AddLayer<armnn::OutputLayer>(0, "output");
79
80     // Insert permute
81     head = graph.InsertNewLayer<armnn::PermuteLayer>(head->GetInputSlot(0),
82                                                      armnn::PermuteDescriptor({ 0, 2, 3, 1 }), "");
83     head->GetOutputHandler().SetTensorInfo(permuted);
84
85     // Insert layers that don't care about data format
86     head = graph.InsertNewLayer<armnn::ActivationLayer>(head->GetInputSlot(0),
87                                                         armnn::ActivationDescriptor{}, "");
88     head->GetOutputHandler().SetTensorInfo(info);
89
90     head = graph.InsertNewLayer<armnn::AdditionLayer>(head->GetInputSlot(0), "");
91     head->GetOutputHandler().SetTensorInfo(info);
92
93     // Insert input for 2nd input of Addition
94     graph.InsertNewLayer<armnn::InputLayer>(head->GetInputSlot(1), inputId++, "")
95         ->GetOutputHandler().SetTensorInfo(info);
96
97     head = graph.InsertNewLayer<armnn::FakeQuantizationLayer>(head->GetInputSlot(0),
98                                                               armnn::FakeQuantizationDescriptor{}, "");
99     head->GetOutputHandler().SetTensorInfo(info);
100
101     head = graph.InsertNewLayer<armnn::FloorLayer>(head->GetInputSlot(0), "");
102     head->GetOutputHandler().SetTensorInfo(info);
103
104     head = graph.InsertNewLayer<armnn::MemCopyLayer>(head->GetInputSlot(0), "");
105     head->GetOutputHandler().SetTensorInfo(info);
106
107     head = graph.InsertNewLayer<armnn::MultiplicationLayer>(head->GetInputSlot(0), "");
108     head->GetOutputHandler().SetTensorInfo(info);
109
110     // Insert input for 2nd input of Multiplication
111     graph.InsertNewLayer<armnn::InputLayer>(head->GetInputSlot(1), inputId++, "")
112         ->GetOutputHandler().SetTensorInfo(info);
113
114     // Insert input
115     graph.InsertNewLayer<armnn::InputLayer>(head->GetInputSlot(0), inputId++, "")
116         ->GetOutputHandler().SetTensorInfo(info);
117
118     BOOST_TEST(CheckSequence(graph.cbegin(),
119                              graph.cend(),
120                              &IsLayerOfType<armnn::InputLayer>,
121                              &IsLayerOfType<armnn::InputLayer>,
122                              &IsLayerOfType<armnn::InputLayer>,
123                              &IsLayerOfType<armnn::MultiplicationLayer>,
124                              &IsLayerOfType<armnn::MemCopyLayer>,
125                              &IsLayerOfType<armnn::FloorLayer>,
126                              &IsLayerOfType<armnn::FakeQuantizationLayer>,
127                              &IsLayerOfType<armnn::AdditionLayer>,
128                              &IsLayerOfType<armnn::ActivationLayer>,
129                              &IsLayerOfType<armnn::PermuteLayer>,
130                              &IsLayerOfType<armnn::OutputLayer>));
131
132     armnn::Optimizer::Optimize(graph);
133
134     // The permute is moved to the top. New permutes for layers with multiple inputs
135     BOOST_TEST(CheckSequence(graph.cbegin(),
136                              graph.cend(),
137                              &IsLayerOfType<armnn::InputLayer>,
138                              &IsLayerOfType<armnn::InputLayer>,
139                              &IsLayerOfType<armnn::InputLayer>,
140                              &IsLayerOfType<armnn::PermuteLayer>,
141                              &IsLayerOfType<armnn::PermuteLayer>,
142                              &IsLayerOfType<armnn::PermuteLayer>,
143                              &IsLayerOfType<armnn::MultiplicationLayer>,
144                              &IsLayerOfType<armnn::MemCopyLayer>,
145                              &IsLayerOfType<armnn::FloorLayer>,
146                              &IsLayerOfType<armnn::FakeQuantizationLayer>,
147                              &IsLayerOfType<armnn::AdditionLayer>,
148                              &IsLayerOfType<armnn::ActivationLayer>,
149                              &IsLayerOfType<armnn::OutputLayer>));
150 }
151
152 BOOST_AUTO_TEST_CASE(PermuteAsReshape)
153 {
154     armnn::Graph graph;
155
156     const armnn::TensorInfo infoIn({ 1, 2, 3, 1 }, armnn::DataType::Float32);
157     const armnn::TensorInfo infoOut({ 1, 1, 2, 3 }, armnn::DataType::Float32);
158
159     auto output = graph.AddLayer<armnn::OutputLayer>(0, "output");
160
161     graph.InsertNewLayer<armnn::InputLayer>(output->GetInputSlot(0), 0, "input")
162         ->GetOutputHandler().SetTensorInfo(infoIn);
163
164     // Insert permute
165     graph.InsertNewLayer<armnn::PermuteLayer>(output->GetInputSlot(0),
166                                               armnn::PermuteDescriptor({ 0, 2, 3, 1 }), "")
167         ->GetOutputHandler().SetTensorInfo(infoOut);
168
169     BOOST_TEST(CheckSequence(graph.cbegin(),
170                              graph.cend(),
171                              &IsLayerOfType<armnn::InputLayer>,
172                              &IsLayerOfType<armnn::PermuteLayer>,
173                              &IsLayerOfType<armnn::OutputLayer>));
174
175     armnn::Optimizer::Optimize(graph);
176
177     // The permute is replaced by an equivalent reshape.
178
179     auto checkReshape = [&infoOut](const armnn::Layer* const layer) -> bool
180         {
181             const auto reshapeLayer = static_cast<const armnn::ReshapeLayer*>(layer);
182             return IsLayerOfType<armnn::ReshapeLayer>(layer) &&
183                    (reshapeLayer->GetParameters().m_TargetShape == infoOut.GetShape()) &&
184                    (reshapeLayer->GetOutputHandler().GetTensorInfo().GetShape() == infoOut.GetShape());
185         };
186
187     BOOST_TEST(CheckSequence(graph.cbegin(),
188                              graph.cend(),
189                              &IsLayerOfType<armnn::InputLayer>,
190                              checkReshape,
191                              &IsLayerOfType<armnn::OutputLayer>));
192 }
193
194 BOOST_AUTO_TEST_CASE(OptimizeConsecutiveReshapes)
195 {
196     armnn::Graph graph;
197
198     const armnn::TensorInfo info0({ 1, 2, 3, 5 }, armnn::DataType::Float32);
199
200     auto output = graph.AddLayer<armnn::OutputLayer>(0, "output");
201     auto input = graph.InsertNewLayer<armnn::InputLayer>(output->GetInputSlot(0), 0, "input");
202
203     input->GetOutputHandler().SetTensorInfo(info0);
204
205     {
206         // Insert two reshapes
207         const armnn::TensorInfo info1({1, 30, 1, 1}, armnn::DataType::Float32);
208         const armnn::TensorInfo info2({1, 2, 1, 15}, armnn::DataType::Float32);
209
210         auto reshape1 = graph.InsertNewLayer<armnn::ReshapeLayer>(output->GetInputSlot(0),
211                                                                   armnn::ReshapeDescriptor{ info1.GetShape() },
212                                                                   "reshape1");
213         auto reshape2 = graph.InsertNewLayer<armnn::ReshapeLayer>(output->GetInputSlot(0),
214                                                                   armnn::ReshapeDescriptor{ info2.GetShape() },
215                                                                   "reshape2");
216
217         reshape1->GetOutputHandler().SetTensorInfo(info1);
218         reshape2->GetOutputHandler().SetTensorInfo(info2);
219
220         BOOST_TEST(CheckSequence(graph.cbegin(),
221                                  graph.cend(),
222                                  &IsLayerOfType<armnn::InputLayer>,
223                                  &IsLayerOfType<armnn::ReshapeLayer>,
224                                  &IsLayerOfType<armnn::ReshapeLayer>,
225                                  &IsLayerOfType<armnn::OutputLayer>));
226
227         armnn::Optimizer::Optimize(graph);
228
229         auto checkReshape = [&info2](const armnn::Layer* const layer) -> bool
230             {
231                 const auto reshapeLayer = static_cast<const armnn::ReshapeLayer*>(layer);
232                 return IsLayerOfType<armnn::ReshapeLayer>(layer) &&
233                     (reshapeLayer->GetParameters().m_TargetShape == info2.GetShape()) &&
234                     (reshapeLayer->GetOutputHandler().GetTensorInfo().GetShape() == info2.GetShape());
235             };
236
237         // The two reshapes are replaced by a single equivalent reshape
238         BOOST_TEST(CheckSequence(graph.cbegin(),
239                                  graph.cend(),
240                                  &IsLayerOfType<armnn::InputLayer>,
241                                  checkReshape,
242                                  &IsLayerOfType<armnn::OutputLayer>));
243     }
244
245     {
246         // Insert a reshape to the input shape
247         auto reshapeToIn = graph.InsertNewLayer<armnn::ReshapeLayer>(output->GetInputSlot(0),
248                                                                      armnn::ReshapeDescriptor{ info0.GetShape() },
249                                                                      "reshapeToIn");
250
251         reshapeToIn->GetOutputHandler().SetTensorInfo(info0);
252
253         armnn::Optimizer::Optimize(graph);
254
255         // The two reshapes are removed
256         BOOST_TEST(CheckSequence(graph.cbegin(),
257                                  graph.cend(),
258                                  &IsLayerOfType<armnn::InputLayer>,
259                                  &IsLayerOfType<armnn::OutputLayer>));
260     }
261 }
262
263 BOOST_AUTO_TEST_CASE(SquashEqualSiblings)
264 {
265     armnn::Graph graph;
266
267     armnn::LayerBindingId outputId = 0;
268
269     const armnn::TensorInfo info({ 1, 2, 3, 5 }, armnn::DataType::Float32);
270     const armnn::TensorInfo permuted({ 1, 5, 2, 3 }, armnn::DataType::Float32);
271
272     auto input = graph.AddLayer<armnn::InputLayer>(0, "input");
273     input->GetOutputSlot().SetTensorInfo(info);
274
275     // Insert equal permutes, equal reshapes and something else
276     const armnn::PermuteDescriptor permDesc({ 0, 2, 3, 1 });
277     const armnn::ReshapeDescriptor reshapeDesc{ { 1, 3, 1, 5 } };
278
279     armnn::Layer* layer;
280
281     layer = graph.AddLayer<armnn::PermuteLayer>(permDesc, "");
282     layer->GetOutputSlot().SetTensorInfo(permuted);
283     layer->GetOutputSlot().Connect(graph.AddLayer<armnn::OutputLayer>(outputId++, "")->GetInputSlot(0));
284     input->GetOutputSlot().Connect(layer->GetInputSlot(0));
285
286     layer = graph.AddLayer<armnn::ReshapeLayer>(reshapeDesc, "");
287     layer->GetOutputSlot().Connect(graph.AddLayer<armnn::OutputLayer>(outputId++, "")->GetInputSlot(0));
288     input->GetOutputSlot().Connect(layer->GetInputSlot(0));
289
290     layer = graph.AddLayer<armnn::FloorLayer>("");
291     layer->GetOutputSlot().Connect(graph.AddLayer<armnn::OutputLayer>(outputId++, "")->GetInputSlot(0));
292     input->GetOutputSlot().Connect(layer->GetInputSlot(0));
293
294     layer = graph.AddLayer<armnn::ReshapeLayer>(reshapeDesc, "");
295     layer->GetOutputSlot().Connect(graph.AddLayer<armnn::OutputLayer>(outputId++, "")->GetInputSlot(0));
296     input->GetOutputSlot().Connect(layer->GetInputSlot(0));
297
298     layer = graph.AddLayer<armnn::PermuteLayer>(permDesc, "");
299     layer->GetOutputSlot().SetTensorInfo(permuted);
300     layer->GetOutputSlot().Connect(graph.AddLayer<armnn::OutputLayer>(outputId++, "")->GetInputSlot(0));
301     input->GetOutputSlot().Connect(layer->GetInputSlot(0));
302
303     BOOST_TEST(CheckSequence(graph.cbegin(),
304                              graph.cend(),
305                              &IsLayerOfType<armnn::InputLayer>,
306                              &IsLayerOfType<armnn::PermuteLayer>,
307                              &IsLayerOfType<armnn::ReshapeLayer>,
308                              &IsLayerOfType<armnn::FloorLayer>,
309                              &IsLayerOfType<armnn::ReshapeLayer>,
310                              &IsLayerOfType<armnn::PermuteLayer>,
311                              &IsLayerOfType<armnn::OutputLayer>,
312                              &IsLayerOfType<armnn::OutputLayer>,
313                              &IsLayerOfType<armnn::OutputLayer>,
314                              &IsLayerOfType<armnn::OutputLayer>,
315                              &IsLayerOfType<armnn::OutputLayer>));
316
317     armnn::Optimizer::Optimize(graph);
318
319     // The permutes and reshapes are squashed.
320
321     BOOST_TEST(CheckSequence(graph.cbegin(),
322                              graph.cend(),
323                              &IsLayerOfType<armnn::InputLayer>,
324                              &IsLayerOfType<armnn::PermuteLayer>,
325                              &IsLayerOfType<armnn::ReshapeLayer>,
326                              &IsLayerOfType<armnn::FloorLayer>,
327                              &IsLayerOfType<armnn::OutputLayer>,
328                              &IsLayerOfType<armnn::OutputLayer>,
329                              &IsLayerOfType<armnn::OutputLayer>,
330                              &IsLayerOfType<armnn::OutputLayer>,
331                              &IsLayerOfType<armnn::OutputLayer>));
332 }
333
334 BOOST_AUTO_TEST_SUITE_END()