2 // Copyright © 2017 Arm Ltd. All rights reserved.
3 // See LICENSE file in the project root for full license information.
5 #include <boost/test/unit_test.hpp>
7 #include "armnn/ArmNN.hpp"
9 #include "Optimizer.hpp"
13 template <typename LayerT>
14 bool IsLayerOfType(const armnn::Layer* const layer)
16 return (layer->GetType() == armnn::LayerEnumOf<LayerT>());
19 bool CheckSequence(const armnn::Graph::ConstIterator first, const armnn::Graph::ConstIterator last)
21 return (first == last);
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,
31 return u(*first) && CheckSequence(std::next(first), last, us...);
35 BOOST_AUTO_TEST_SUITE(Optimizer)
37 BOOST_AUTO_TEST_CASE(OptimizeInversePermutes)
41 auto output = graph.AddLayer<armnn::OutputLayer>(0, "output");
43 graph.InsertNewLayer<armnn::InputLayer>(output->GetInputSlot(0), 0, "input");
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}),
49 graph.InsertNewLayer<armnn::PermuteLayer>(output->GetInputSlot(0),
50 armnn::PermuteDescriptor({0, 3, 1, 2}),
53 BOOST_TEST(CheckSequence(graph.cbegin(),
55 &IsLayerOfType<armnn::InputLayer>,
56 &IsLayerOfType<armnn::PermuteLayer>,
57 &IsLayerOfType<armnn::PermuteLayer>,
58 &IsLayerOfType<armnn::OutputLayer>));
60 armnn::Optimizer::Optimize(graph);
62 // The permutes are removed
63 BOOST_TEST(CheckSequence(graph.cbegin(),
65 &IsLayerOfType<armnn::InputLayer>,
66 &IsLayerOfType<armnn::OutputLayer>));
69 BOOST_AUTO_TEST_CASE(MovePermuteUp)
71 const armnn::TensorInfo info({ 1, 5, 2, 3 }, armnn::DataType::Float32);
72 const armnn::TensorInfo permuted({ 1, 3, 5, 2 }, armnn::DataType::Float32);
76 armnn::LayerBindingId inputId = 0;
78 armnn::Layer* head = graph.AddLayer<armnn::OutputLayer>(0, "output");
81 head = graph.InsertNewLayer<armnn::PermuteLayer>(head->GetInputSlot(0),
82 armnn::PermuteDescriptor({ 0, 2, 3, 1 }), "");
83 head->GetOutputHandler().SetTensorInfo(permuted);
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);
90 head = graph.InsertNewLayer<armnn::AdditionLayer>(head->GetInputSlot(0), "");
91 head->GetOutputHandler().SetTensorInfo(info);
93 // Insert input for 2nd input of Addition
94 graph.InsertNewLayer<armnn::InputLayer>(head->GetInputSlot(1), inputId++, "")
95 ->GetOutputHandler().SetTensorInfo(info);
97 head = graph.InsertNewLayer<armnn::FakeQuantizationLayer>(head->GetInputSlot(0),
98 armnn::FakeQuantizationDescriptor{}, "");
99 head->GetOutputHandler().SetTensorInfo(info);
101 head = graph.InsertNewLayer<armnn::FloorLayer>(head->GetInputSlot(0), "");
102 head->GetOutputHandler().SetTensorInfo(info);
104 head = graph.InsertNewLayer<armnn::MemCopyLayer>(head->GetInputSlot(0), "");
105 head->GetOutputHandler().SetTensorInfo(info);
107 head = graph.InsertNewLayer<armnn::MultiplicationLayer>(head->GetInputSlot(0), "");
108 head->GetOutputHandler().SetTensorInfo(info);
110 // Insert input for 2nd input of Multiplication
111 graph.InsertNewLayer<armnn::InputLayer>(head->GetInputSlot(1), inputId++, "")
112 ->GetOutputHandler().SetTensorInfo(info);
115 graph.InsertNewLayer<armnn::InputLayer>(head->GetInputSlot(0), inputId++, "")
116 ->GetOutputHandler().SetTensorInfo(info);
118 BOOST_TEST(CheckSequence(graph.cbegin(),
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>));
132 armnn::Optimizer::Optimize(graph);
134 // The permute is moved to the top. New permutes for layers with multiple inputs
135 BOOST_TEST(CheckSequence(graph.cbegin(),
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>));
152 BOOST_AUTO_TEST_CASE(PermuteAsReshape)
156 const armnn::TensorInfo infoIn({ 1, 2, 3, 1 }, armnn::DataType::Float32);
157 const armnn::TensorInfo infoOut({ 1, 1, 2, 3 }, armnn::DataType::Float32);
159 auto output = graph.AddLayer<armnn::OutputLayer>(0, "output");
161 graph.InsertNewLayer<armnn::InputLayer>(output->GetInputSlot(0), 0, "input")
162 ->GetOutputHandler().SetTensorInfo(infoIn);
165 graph.InsertNewLayer<armnn::PermuteLayer>(output->GetInputSlot(0),
166 armnn::PermuteDescriptor({ 0, 2, 3, 1 }), "")
167 ->GetOutputHandler().SetTensorInfo(infoOut);
169 BOOST_TEST(CheckSequence(graph.cbegin(),
171 &IsLayerOfType<armnn::InputLayer>,
172 &IsLayerOfType<armnn::PermuteLayer>,
173 &IsLayerOfType<armnn::OutputLayer>));
175 armnn::Optimizer::Optimize(graph);
177 // The permute is replaced by an equivalent reshape.
179 auto checkReshape = [&infoOut](const armnn::Layer* const layer) -> bool
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());
187 BOOST_TEST(CheckSequence(graph.cbegin(),
189 &IsLayerOfType<armnn::InputLayer>,
191 &IsLayerOfType<armnn::OutputLayer>));
194 BOOST_AUTO_TEST_CASE(OptimizeConsecutiveReshapes)
198 const armnn::TensorInfo info0({ 1, 2, 3, 5 }, armnn::DataType::Float32);
200 auto output = graph.AddLayer<armnn::OutputLayer>(0, "output");
201 auto input = graph.InsertNewLayer<armnn::InputLayer>(output->GetInputSlot(0), 0, "input");
203 input->GetOutputHandler().SetTensorInfo(info0);
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);
210 auto reshape1 = graph.InsertNewLayer<armnn::ReshapeLayer>(output->GetInputSlot(0),
211 armnn::ReshapeDescriptor{ info1.GetShape() },
213 auto reshape2 = graph.InsertNewLayer<armnn::ReshapeLayer>(output->GetInputSlot(0),
214 armnn::ReshapeDescriptor{ info2.GetShape() },
217 reshape1->GetOutputHandler().SetTensorInfo(info1);
218 reshape2->GetOutputHandler().SetTensorInfo(info2);
220 BOOST_TEST(CheckSequence(graph.cbegin(),
222 &IsLayerOfType<armnn::InputLayer>,
223 &IsLayerOfType<armnn::ReshapeLayer>,
224 &IsLayerOfType<armnn::ReshapeLayer>,
225 &IsLayerOfType<armnn::OutputLayer>));
227 armnn::Optimizer::Optimize(graph);
229 auto checkReshape = [&info2](const armnn::Layer* const layer) -> bool
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());
237 // The two reshapes are replaced by a single equivalent reshape
238 BOOST_TEST(CheckSequence(graph.cbegin(),
240 &IsLayerOfType<armnn::InputLayer>,
242 &IsLayerOfType<armnn::OutputLayer>));
246 // Insert a reshape to the input shape
247 auto reshapeToIn = graph.InsertNewLayer<armnn::ReshapeLayer>(output->GetInputSlot(0),
248 armnn::ReshapeDescriptor{ info0.GetShape() },
251 reshapeToIn->GetOutputHandler().SetTensorInfo(info0);
253 armnn::Optimizer::Optimize(graph);
255 // The two reshapes are removed
256 BOOST_TEST(CheckSequence(graph.cbegin(),
258 &IsLayerOfType<armnn::InputLayer>,
259 &IsLayerOfType<armnn::OutputLayer>));
263 BOOST_AUTO_TEST_CASE(SquashEqualSiblings)
267 armnn::LayerBindingId outputId = 0;
269 const armnn::TensorInfo info({ 1, 2, 3, 5 }, armnn::DataType::Float32);
270 const armnn::TensorInfo permuted({ 1, 5, 2, 3 }, armnn::DataType::Float32);
272 auto input = graph.AddLayer<armnn::InputLayer>(0, "input");
273 input->GetOutputSlot().SetTensorInfo(info);
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 } };
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));
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));
290 layer = graph.AddLayer<armnn::FloorLayer>("");
291 layer->GetOutputSlot().Connect(graph.AddLayer<armnn::OutputLayer>(outputId++, "")->GetInputSlot(0));
292 input->GetOutputSlot().Connect(layer->GetInputSlot(0));
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));
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));
303 BOOST_TEST(CheckSequence(graph.cbegin(),
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>));
317 armnn::Optimizer::Optimize(graph);
319 // The permutes and reshapes are squashed.
321 BOOST_TEST(CheckSequence(graph.cbegin(),
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>));
334 BOOST_AUTO_TEST_SUITE_END()