• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 //
2 // Copyright © 2020-2021,2023 Arm Ltd and Contributors. All rights reserved.
3 // SPDX-License-Identifier: MIT
4 //
5 
6 #include <GraphUtils.hpp>
7 #include <TestUtils.hpp>
8 
9 #include <Optimizer.hpp>
10 
11 #include <doctest/doctest.h>
12 
13 using namespace armnn;
14 
15 TEST_SUITE("Optimizer")
16 {
17 using namespace optimizations;
18 
AddBroadcastReshapeLayerOptimizerTest(const TensorInfo & info0,const TensorInfo & info1,const TensorInfo & outputInfo,const std::string & reshapeLayerName,const TensorShape & expectedReshapeShape,const DataType expectedDataType)19 void AddBroadcastReshapeLayerOptimizerTest(const TensorInfo& info0,
20                                            const TensorInfo& info1,
21                                            const TensorInfo& outputInfo,
22                                            const std::string& reshapeLayerName,
23                                            const TensorShape& expectedReshapeShape,
24                                            const DataType expectedDataType)
25 {
26     Graph graph;
27 
28     auto input0 = graph.AddLayer<InputLayer>(0, "input0");
29     auto input1 = graph.AddLayer<InputLayer>(1, "input1");
30     auto add = graph.AddLayer<ElementwiseBinaryLayer>(BinaryOperation::Add, "add");
31     auto output = graph.AddLayer<OutputLayer>(0, "output");
32     input0->GetOutputSlot().SetTensorInfo(info0);
33     input1->GetOutputSlot().SetTensorInfo(info1);
34     add->GetOutputSlot().SetTensorInfo(outputInfo);
35 
36     input0->GetOutputSlot().Connect(add->GetInputSlot(0));
37     input1->GetOutputSlot().Connect(add->GetInputSlot(1));
38     add->GetOutputSlot().Connect(output->GetInputSlot(0));
39 
40     CHECK(CheckSequence(graph.cbegin(), graph.cend(),
41                              &IsLayerOfType<InputLayer>,
42                              &IsLayerOfType<InputLayer>,
43                              &IsLayerOfType<ElementwiseBinaryLayer>,
44                              &IsLayerOfType<OutputLayer>));
45 
46     // Run optimizer
47     armnn::Optimizer::Pass(graph, MakeOptimizations(AddBroadcastReshapeLayer()));
48 
49     // Broadcast reshape layer has been added to the graph correctly
50     CHECK(CheckSequence(graph.cbegin(), graph.cend(),
51                         &IsLayerOfType<InputLayer>,
52                         &IsLayerOfType<InputLayer>,
53                         &IsLayerOfType<ReshapeLayer>,
54                         &IsLayerOfType<ElementwiseBinaryLayer>,
55                         &IsLayerOfType<OutputLayer>));
56 
57     Layer* const reshapeLayer = GetFirstLayerWithName(graph, reshapeLayerName);
58     CHECK(reshapeLayer);
59     auto addedReshapeTensorInfo = reshapeLayer->GetOutputSlot().GetTensorInfo();
60 
61     // Tensorshape and the data type are correct
62     CHECK_EQ(addedReshapeTensorInfo.GetShape(), expectedReshapeShape);
63     CHECK_EQ(addedReshapeTensorInfo.GetDataType(), expectedDataType);
64 }
65 
66 TEST_CASE("AddBroadcastReshapeLayerSimpleTest")
67 {
68     const TensorInfo info0({ 1, 2, 3, 5 }, DataType::Float32);
69     const TensorInfo info1({ 1 }, DataType::Float32);
70     AddBroadcastReshapeLayerOptimizerTest(info0, info1, info0, "Reshape_for:add-1",
71                                           TensorShape({ 1, 1, 1, 1 }),
72                                           DataType::Float32);
73 }
74 
75 TEST_CASE("AddBroadcastReshapeLayer1DTest")
76 {
77     const TensorInfo info0({ 1, 2, 3, 5 }, DataType::Float32);
78     const TensorInfo info1({ 5 }, DataType::Float32);
79     const TensorInfo outputInfo({ 1, 1, 1, 5 }, DataType::Float32);
80     AddBroadcastReshapeLayerOptimizerTest(info0, info1, outputInfo, "Reshape_for:add-1",
81                                           TensorShape({ 1, 1, 1, 5 }),
82                                           DataType::Float32);
83 }
84 
85 TEST_CASE("AddBroadcastReshapeLayer2DTest")
86 {
87     const TensorInfo info0({ 1, 2, 3, 5 }, DataType::Float32);
88     const TensorInfo info1({ 3, 5 }, DataType::Float32);
89     const TensorInfo outputInfo({ 1, 2, 3, 5 }, DataType::Float32);
90     AddBroadcastReshapeLayerOptimizerTest(info0, info1, outputInfo, "Reshape_for:add-1",
91                                           TensorShape({ 1, 1, 3, 5 }),
92                                           DataType::Float32);
93 }
94 
95 TEST_CASE("AddBroadcastReshapeLayer3DTest")
96 {
97     const TensorInfo info0({ 2, 1, 1, 1 }, DataType::Float32);
98     const TensorInfo info1({ 3, 4, 5 }, DataType::Float32);
99     const TensorInfo outputInfo({ 2, 3, 4, 5 }, DataType::Float32);
100     AddBroadcastReshapeLayerOptimizerTest(info0, info1, outputInfo, "Reshape_for:add-1",
101                                           TensorShape({ 1, 3, 4, 5 }),
102                                           DataType::Float32);
103 }
104 
105 TEST_CASE("AddBroadcastReshapeLayer3DMergedTest")
106 {
107     const TensorInfo info0({ 2, 3, 1, 1 }, DataType::Float32);
108     const TensorInfo info1({ 3, 4, 5 }, DataType::Float32);
109     const TensorInfo outputInfo({ 2, 3, 4, 5 }, DataType::Float32);
110     AddBroadcastReshapeLayerOptimizerTest(info0, info1, outputInfo, "Reshape_for:add-1",
111                                           TensorShape({ 1, 3, 4, 5 }),
112                                           DataType::Float32);
113 }
114 
115 TEST_CASE("AddBroadcastReshapeLayerSubtractionTest")
116 {
117     Graph graph;
118     const TensorInfo info0({ 5 }, DataType::Float32);
119     const TensorInfo info1({ 1, 2, 3, 5 }, DataType::Float32);
120     const TensorInfo outputInfo({ 1, 2, 3, 5 }, DataType::Float32);
121 
122     auto input0 = graph.AddLayer<InputLayer>(0, "input0");
123     auto input1 = graph.AddLayer<InputLayer>(1, "input1");
124     auto sub = graph.AddLayer<ElementwiseBinaryLayer>(BinaryOperation::Sub, "sub");
125     auto output = graph.AddLayer<OutputLayer>(0, "output");
126     input0->GetOutputSlot().SetTensorInfo(info0);
127     input1->GetOutputSlot().SetTensorInfo(info1);
128     sub->GetOutputSlot().SetTensorInfo(outputInfo);
129 
130     input0->GetOutputSlot().Connect(sub->GetInputSlot(0));
131     input1->GetOutputSlot().Connect(sub->GetInputSlot(1));
132     sub->GetOutputSlot().Connect(output->GetInputSlot(0));
133 
134     CHECK(CheckSequence(graph.cbegin(), graph.cend(),
135                              &IsLayerOfType<InputLayer>,
136                              &IsLayerOfType<InputLayer>,
137                              &IsLayerOfType<ElementwiseBinaryLayer>,
138                              &IsLayerOfType<OutputLayer>));
139 
140     // Run optimizer
141     armnn::Optimizer::Pass(graph, MakeOptimizations(AddBroadcastReshapeLayer()));
142 
143     // Broadcast reshape layer has been added to the graph correctly
144     CHECK(CheckSequence(graph.cbegin(), graph.cend(),
145                              &IsLayerOfType<InputLayer>,
146                              &IsLayerOfType<InputLayer>,
147                              &IsLayerOfType<ReshapeLayer>,
148                              &IsLayerOfType<ElementwiseBinaryLayer>,
149                              &IsLayerOfType<OutputLayer>));
150 
151     Layer* const reshapeLayer = GetFirstLayerWithName(graph, "Reshape_for:sub-0");
152     CHECK(reshapeLayer);
153     auto addedReshapeTensorInfo = reshapeLayer->GetOutputSlot().GetTensorInfo();
154 
155     // Tensorshape and the data type are correct
156     CHECK_EQ(addedReshapeTensorInfo.GetShape(), TensorShape({ 1, 1, 1, 5 }));
157     CHECK_EQ(addedReshapeTensorInfo.GetDataType(), DataType::Float32);
158 }
159 
160 TEST_CASE("AddBroadcastReshapeLayerDivisionTest")
161 {
162     Graph graph;
163     const TensorInfo info0({ 1, 4, 5 }, DataType::QAsymmS8);
164     const TensorInfo info1({ 1, 2, 4, 5 }, DataType::QAsymmS8);
165     const TensorInfo outputInfo({ 1, 2, 4, 5 }, DataType::QAsymmS8);
166 
167     auto input0 = graph.AddLayer<InputLayer>(0, "input0");
168     auto input1 = graph.AddLayer<InputLayer>(1, "input1");
169     auto div = graph.AddLayer<ElementwiseBinaryLayer>(BinaryOperation::Div, "div");
170     auto output = graph.AddLayer<OutputLayer>(0, "output");
171     input0->GetOutputSlot().SetTensorInfo(info0);
172     input1->GetOutputSlot().SetTensorInfo(info1);
173     div->GetOutputSlot().SetTensorInfo(outputInfo);
174 
175     input0->GetOutputSlot().Connect(div->GetInputSlot(0));
176     input1->GetOutputSlot().Connect(div->GetInputSlot(1));
177     div->GetOutputSlot().Connect(output->GetInputSlot(0));
178 
179     CHECK(CheckSequence(graph.cbegin(), graph.cend(),
180                              &IsLayerOfType<InputLayer>,
181                              &IsLayerOfType<InputLayer>,
182                              &IsLayerOfType<ElementwiseBinaryLayer>,
183                              &IsLayerOfType<OutputLayer>));
184 
185     // Run optimizer
186     armnn::Optimizer::Pass(graph, MakeOptimizations(AddBroadcastReshapeLayer()));
187 
188     // Broadcast reshape layer has been added to the graph correctly
189     CHECK(CheckSequence(graph.cbegin(), graph.cend(),
190                              &IsLayerOfType<InputLayer>,
191                              &IsLayerOfType<InputLayer>,
192                              &IsLayerOfType<ReshapeLayer>,
193                              &IsLayerOfType<ElementwiseBinaryLayer>,
194                              &IsLayerOfType<OutputLayer>));
195 
196     Layer* const reshapeLayer = GetFirstLayerWithName(graph, "Reshape_for:div-0");
197     CHECK(reshapeLayer);
198     auto addedReshapeTensorInfo = reshapeLayer->GetOutputSlot().GetTensorInfo();
199 
200     // Tensorshape and the data type are correct
201     CHECK_EQ(addedReshapeTensorInfo.GetShape(), TensorShape({ 1, 1, 4, 5 }));
202     CHECK_EQ(addedReshapeTensorInfo.GetDataType(), DataType::QAsymmS8);
203 }
204 
205 TEST_CASE("AddBroadcastReshapeLayerMultiplicationTest")
206 {
207     Graph graph;
208     const TensorInfo info0({ 3, 5 }, DataType::QAsymmU8);
209     const TensorInfo info1({ 1, 2, 3, 5 }, DataType::QAsymmU8);
210     const TensorInfo outputInfo({ 1, 2, 3, 5 }, DataType::QAsymmU8);
211 
212     auto input0 = graph.AddLayer<InputLayer>(0, "input0");
213     auto input1 = graph.AddLayer<InputLayer>(1, "input1");
214     auto mul = graph.AddLayer<ElementwiseBinaryLayer>(BinaryOperation::Mul, "mul");
215     auto output = graph.AddLayer<OutputLayer>(0, "output");
216     input0->GetOutputSlot().SetTensorInfo(info0);
217     input1->GetOutputSlot().SetTensorInfo(info1);
218     mul->GetOutputSlot().SetTensorInfo(outputInfo);
219 
220     input0->GetOutputSlot().Connect(mul->GetInputSlot(0));
221     input1->GetOutputSlot().Connect(mul->GetInputSlot(1));
222     mul->GetOutputSlot().Connect(output->GetInputSlot(0));
223 
224     CHECK(CheckSequence(graph.cbegin(), graph.cend(),
225                              &IsLayerOfType<InputLayer>,
226                              &IsLayerOfType<InputLayer>,
227                              &IsLayerOfType<ElementwiseBinaryLayer>,
228                              &IsLayerOfType<OutputLayer>));
229 
230     // Run optimizer
231     armnn::Optimizer::Pass(graph, MakeOptimizations(AddBroadcastReshapeLayer()));
232 
233     // Broadcast reshape layer has been added to the graph correctly
234     CHECK(CheckSequence(graph.cbegin(), graph.cend(),
235                              &IsLayerOfType<InputLayer>,
236                              &IsLayerOfType<InputLayer>,
237                              &IsLayerOfType<ReshapeLayer>,
238                              &IsLayerOfType<ElementwiseBinaryLayer>,
239                              &IsLayerOfType<OutputLayer>));
240 
241     Layer* const reshapeLayer = GetFirstLayerWithName(graph, "Reshape_for:mul-0");
242     CHECK(reshapeLayer);
243     auto addedReshapeTensorInfo = reshapeLayer->GetOutputSlot().GetTensorInfo();
244 
245     // Tensorshape and the data type are correct
246     CHECK_EQ(addedReshapeTensorInfo.GetShape(), TensorShape({ 1, 1, 3, 5 }));
247     CHECK_EQ(addedReshapeTensorInfo.GetDataType(), DataType::QAsymmU8);
248 }
249 
250 TEST_CASE("AddNoBroadcastReshapeLayerTest")
251 {
252     Graph graph;
253     const TensorInfo info0({ 1, 1, 1, 1 }, DataType::QAsymmU8);
254     const TensorInfo info1({ 1, 2, 3, 5 }, DataType::QAsymmU8);
255     const TensorInfo outputInfo({ 1, 2, 3, 5 }, DataType::QAsymmU8);
256 
257     auto input0 = graph.AddLayer<InputLayer>(0, "input0");
258     auto input1 = graph.AddLayer<InputLayer>(1, "input1");
259     auto mul = graph.AddLayer<ElementwiseBinaryLayer>(BinaryOperation::Mul, "mul");
260     auto output = graph.AddLayer<OutputLayer>(0, "output");
261     input0->GetOutputSlot().SetTensorInfo(info0);
262     input1->GetOutputSlot().SetTensorInfo(info1);
263     mul->GetOutputSlot().SetTensorInfo(outputInfo);
264 
265     input0->GetOutputSlot().Connect(mul->GetInputSlot(0));
266     input1->GetOutputSlot().Connect(mul->GetInputSlot(1));
267     mul->GetOutputSlot().Connect(output->GetInputSlot(0));
268 
269     CHECK(CheckSequence(graph.cbegin(), graph.cend(),
270                              &IsLayerOfType<InputLayer>,
271                              &IsLayerOfType<InputLayer>,
272                              &IsLayerOfType<ElementwiseBinaryLayer>,
273                              &IsLayerOfType<OutputLayer>));
274 
275     // Run optimizer
276     armnn::Optimizer::Pass(graph, MakeOptimizations(AddBroadcastReshapeLayer()));
277 
278     // Broadcast reshape layer has not been added to the graph
279     CHECK(CheckSequence(graph.cbegin(), graph.cend(),
280                              &IsLayerOfType<InputLayer>,
281                              &IsLayerOfType<InputLayer>,
282                              &IsLayerOfType<ElementwiseBinaryLayer>,
283                              &IsLayerOfType<OutputLayer>));
284 
285     Layer* const reshapeLayer = GetFirstLayerWithName(graph, "Reshape_for:mul-0");
286     CHECK(!reshapeLayer);
287 }
288 
289 TEST_CASE("ReshapeParentConstLayerTest")
290 {
291     Graph graph;
292     const TensorInfo info0({ 1, 2, 3, 5 }, DataType::QAsymmU8);
293     const TensorInfo info1({ 5 }, DataType::QAsymmU8, 0.0f, 0, true);
294     const TensorInfo outputInfo({ 1, 2, 3, 5 }, DataType::QAsymmU8);
295 
296     auto input = graph.AddLayer<InputLayer>(0, "input");
297     auto constant = graph.AddLayer<ConstantLayer>("constant");
298     auto mul = graph.AddLayer<ElementwiseBinaryLayer>(BinaryOperation::Mul, "mul");
299     auto output = graph.AddLayer<OutputLayer>(0, "output");
300 
301     uint8_t tensor[] = { 1, 1, 1, 1, 1 };
302 
303     constant->m_LayerOutput = std::make_unique<ScopedTensorHandle>(ConstTensor(info1, &tensor));
304 
305     input->GetOutputSlot().SetTensorInfo(info0);
306     constant->GetOutputSlot().SetTensorInfo(info1);
307     mul->GetOutputSlot().SetTensorInfo(outputInfo);
308 
309     input->GetOutputSlot().Connect(mul->GetInputSlot(0));
310     constant->GetOutputSlot().Connect(mul->GetInputSlot(1));
311     mul->GetOutputSlot().Connect(output->GetInputSlot(0));
312 
313     CHECK(CheckSequence(graph.cbegin(), graph.cend(),
314                              &IsLayerOfType<InputLayer>,
315                              &IsLayerOfType<ConstantLayer>,
316                              &IsLayerOfType<ElementwiseBinaryLayer>,
317                              &IsLayerOfType<OutputLayer>));
318 
319     // Run optimizer
320     armnn::Optimizer::Pass(graph, MakeOptimizations(AddBroadcastReshapeLayer()));
321 
322     // Broadcast reshape layer has not been added to the graph
323     CHECK(CheckSequence(graph.cbegin(), graph.cend(),
324                              &IsLayerOfType<InputLayer>,
325                              &IsLayerOfType<ConstantLayer>,
326                              &IsLayerOfType<ElementwiseBinaryLayer>,
327                              &IsLayerOfType<OutputLayer>));
328 
329     TensorShape expectedShape = TensorShape{ 1, 1, 1, 5 };
330     CHECK(constant->m_LayerOutput.get()->GetTensorInfo().GetShape() == expectedShape);
331 
332     CHECK(constant->m_LayerOutput.get()->GetTensorInfo().GetNumDimensions() == info0.GetNumDimensions());
333 
334     Layer* const reshapeLayer = GetFirstLayerWithName(graph, "Reshape_for:mul-0");
335     CHECK(!reshapeLayer);
336 }
337 
338 TEST_CASE("ReshapeParentConstAddLayerMultipleConnectionsTest")
339 {
340     // In this test case we recreate the situation where an Addition layer has
341     // a constant second term, e.g. [1,512] + [1]. The AddBroadcastReshapeLayer
342     // should modify the constant tensor info to match the number of dimensions.
343     // However, if this constant term is being reused elsewhere then we shouldn't
344     // modify it. Instead we insert a resize layer.
345 
346     // What we'll do is have two sequential add layers both using the same const tensor.
347     Graph graph;
348     const TensorInfo inputInfo({ 1, 512 }, DataType::Float32);
349     const TensorInfo constantTermInfo({ 1 }, DataType::Float32, 0.0f, 0, true);
350     const TensorInfo outputInfo({ 1, 512 }, DataType::Float32);
351 
352     auto input = graph.AddLayer<InputLayer>(0, "input");
353     auto constant = graph.AddLayer<ConstantLayer>("constant");
354     auto add1 = graph.AddLayer<ElementwiseBinaryLayer>(BinaryOperation::Add, "add1");
355     auto add2 = graph.AddLayer<ElementwiseBinaryLayer>(BinaryOperation::Add, "add2");
356     auto output = graph.AddLayer<OutputLayer>(0, "output");
357 
358     input->GetOutputSlot().SetTensorInfo(inputInfo);
359     constant->GetOutputSlot().SetTensorInfo(constantTermInfo);
360     float tensor[] = { 2.0f };
361     constant->m_LayerOutput = std::make_unique<ScopedTensorHandle>(ConstTensor(constantTermInfo, &tensor));
362     add1->GetOutputSlot().SetTensorInfo(outputInfo);
363 
364     input->GetOutputSlot().Connect(add1->GetInputSlot(0));
365     constant->GetOutputSlot().Connect(add1->GetInputSlot(1));
366     add1->GetOutputSlot().Connect(add2->GetInputSlot(0));
367     add2->GetOutputSlot().Connect(output->GetInputSlot(0));
368     // This second connection should prevent the modification of the const output tensor.
369     constant->GetOutputSlot().Connect(add2->GetInputSlot(1));
370 
371     CHECK(CheckSequence(graph.cbegin(), graph.cend(),
372                              &IsLayerOfType<InputLayer>,
373                              &IsLayerOfType<ConstantLayer>,
374                              &IsLayerOfType<ElementwiseBinaryLayer>,
375                              &IsLayerOfType<ElementwiseBinaryLayer>,
376                              &IsLayerOfType<OutputLayer>));
377 
378     // Run optimizer
379     armnn::Optimizer::Pass(graph, MakeOptimizations(AddBroadcastReshapeLayer()));
380 
381     // Broadcast reshape should have been added before each addition layer.
382     CHECK(CheckSequence(graph.cbegin(), graph.cend(),
383                              &IsLayerOfType<InputLayer>,
384                              &IsLayerOfType<ConstantLayer>,
385                              &IsLayerOfType<ReshapeLayer>,
386                              &IsLayerOfType<ReshapeLayer>,
387                              &IsLayerOfType<ElementwiseBinaryLayer>,
388                              &IsLayerOfType<ElementwiseBinaryLayer>,
389                              &IsLayerOfType<OutputLayer>));
390 
391     // Ensure the output shape of the constant hasn't changed.
392     CHECK(constant->m_LayerOutput.get()->GetTensorInfo().GetShape() == constantTermInfo.GetShape());
393     // There should be two extra reshape layers with appropriate names.
394     Layer* const reshapeLayer1 = GetFirstLayerWithName(graph, "Reshape_for:add1-1");
395     Layer* const reshapeLayer2 = GetFirstLayerWithName(graph, "Reshape_for:add2-1");
396     CHECK(reshapeLayer1);
397     CHECK(reshapeLayer2);
398 }
399 
400 
401 
402 }