1 //
2 // Copyright © 2022-2023 Arm Ltd and Contributors. All rights reserved.
3 // SPDX-License-Identifier: MIT
4 //
5
6 #include "LayersFwd.hpp"
7 #include <Network.hpp>
8 #include <TestUtils.hpp>
9 #include <doctest/doctest.h>
10 #include <armnn/backends/TensorHandle.hpp>
11 #include <Optimizer.hpp>
12
13 TEST_SUITE("Optimizer")
14 {
15 using namespace armnn;
16 using namespace armnn::optimizations;
17
18 // Helpers for testing
19 auto checkConstantFloat32 = [](const armnn::Layer *const layer)
__anon9f0260d60102(const armnn::Layer *const layer) 20 {
21 return IsLayerOfType<ConstantLayer>(layer) && (layer->GetDataType() == DataType::Float32);
22 };
23
24 auto checkConstantFloat16 = [](const armnn::Layer *const layer)
__anon9f0260d60202(const armnn::Layer *const layer) 25 {
26 return IsLayerOfType<ConstantLayer>(layer) && (layer->GetDataType() == DataType::Float16);
27 };
28
29 auto checkConstantQAsymmS8 = [](const armnn::Layer *const layer)
__anon9f0260d60302(const armnn::Layer *const layer) 30 {
31 return IsLayerOfType<ConstantLayer>(layer) && (layer->GetDataType() == DataType::QAsymmS8);
32 };
33
34 auto checkPadFoldedIntoConv2d = [](const Layer* const layer)
__anon9f0260d60402(const Layer* const layer) 35 {
36 const auto conv2dLayer = static_cast<const Convolution2dLayer*>(layer);
37 const auto conv2dLayerParams = conv2dLayer->GetParameters();
38
39 return IsLayerOfType<Convolution2dLayer>(layer) &&
40 (layer->GetNameStr() == "folded-pad-into-conv2d") &&
41 (conv2dLayerParams.m_PadLeft == 2) &&
42 (conv2dLayerParams.m_PadRight == 2) &&
43 (conv2dLayerParams.m_PadTop == 2) &&
44 (conv2dLayerParams.m_PadBottom == 2) &&
45 (conv2dLayerParams.m_StrideX == 1) &&
46 (conv2dLayerParams.m_StrideY == 1) &&
47 (conv2dLayerParams.m_BiasEnabled == false) &&
48 (conv2dLayerParams.m_DataLayout == DataLayout::NHWC);
49 };
50
51 TEST_CASE("ConvertConstFloat16DequantizeToConstFloat32")
52 {
53 Graph graph;
54 const unsigned int shape[] = {1, 2, 2, 3};
55
56 const TensorInfo constTensorInfo(4, shape, DataType::Float16, 1.0, 0, true);
57 const TensorInfo outputDequantizeInfo(4, shape, DataType::Float32, 1.0, 0, true);
58
59 auto constantLayer = graph.AddLayer<ConstantLayer>("constant");
60 std::vector<float> constantValues(constTensorInfo.GetNumElements(), 4.5f);
61 ConstTensor constTensor(constTensorInfo, constantValues.data());
62 constantLayer->m_LayerOutput = std::make_shared<ScopedTensorHandle>(constTensor);
63 constantLayer->GetOutputSlot().SetTensorInfo(constTensorInfo);
64
65 auto dequantizeLayer = graph.AddLayer<DequantizeLayer>("dequantize");
66 dequantizeLayer->GetOutputSlot().SetTensorInfo(outputDequantizeInfo);
67
68 auto output = graph.AddLayer<OutputLayer>(0, "output");
69
70 // Connect up constant -> dequantize -> output
71 constantLayer->GetOutputSlot().Connect(dequantizeLayer->GetInputSlot(0));
72 dequantizeLayer->GetOutputSlot().Connect(output->GetInputSlot(0));
73
74
75 CHECK(CheckSequence(graph.cbegin(), graph.cend(),
76 checkConstantFloat16,
77 &IsLayerOfType<DequantizeLayer>,
78 &IsLayerOfType<OutputLayer>));
79
80 armnn::Optimizer::Pass(graph, MakeOptimizations(ConvertConstDequantisationLayersToConstLayers()));
81
82 CHECK(CheckSequence(graph.cbegin(), graph.cend(),
83 checkConstantFloat32,
84 &IsLayerOfType<OutputLayer>));
85 }
86
87 TEST_CASE("ConvertConstFloat16DequantizeToConstFloat32PlusFusePadWithConv2d")
88 {
89 Graph graph;
90 const unsigned int shape[] = {1, 2, 2, 3};
91
92 const TensorInfo constTensorInfo(4, shape, DataType::Float16, 1.0, 0, true);
93 const TensorInfo outputDequantizeInfo(4, shape, DataType::Float32, 1.0, 0, true);
94
95 auto constantLayer = graph.AddLayer<ConstantLayer>("constant");
96 std::vector<float> constantValues(constTensorInfo.GetNumElements(), 4.5f);
97 ConstTensor constTensor(constTensorInfo, constantValues.data());
98 constantLayer->m_LayerOutput = std::make_shared<ScopedTensorHandle>(constTensor);
99 constantLayer->GetOutputSlot().SetTensorInfo(constTensorInfo);
100
101 auto dequantizeLayer = graph.AddLayer<DequantizeLayer>("dequantize");
102 dequantizeLayer->GetOutputSlot().SetTensorInfo(outputDequantizeInfo);
103
104 auto output = graph.AddLayer<OutputLayer>(0, "output");
105
106 Convolution2dDescriptor convolution2dDescriptor;
107 convolution2dDescriptor.m_BiasEnabled = false;
108 convolution2dDescriptor.m_StrideX = 1;
109 convolution2dDescriptor.m_StrideY = 1;
110 convolution2dDescriptor.m_DataLayout = DataLayout::NHWC;
111 auto conv2d = graph.AddLayer<Convolution2dLayer>(convolution2dDescriptor, "conv2d");
112
113
114 auto inputLayer = graph.AddLayer<InputLayer>(0, "input");
115
116 PadDescriptor padDescriptor({{0, 0},
117 {2, 2},
118 {2, 2},
119 {0, 0}});
120
121 const unsigned int paddedShape[] = {1, 6, 6, 3};
122
123 TensorInfo paddedInfo(4, paddedShape, DataType::Float32);
124
125 auto padLayer = graph.AddLayer<PadLayer>(padDescriptor, "pad");
126 padLayer->GetOutputSlot().SetTensorInfo(paddedInfo);
127
128 // Connect up:
129 // input -> pad -> conv2d -> output
130 // constant -> dequantize ->
131 constantLayer->GetOutputSlot().Connect(dequantizeLayer->GetInputSlot(0));
132 dequantizeLayer->GetOutputSlot().Connect(conv2d->GetInputSlot(1));
133 inputLayer->GetOutputSlot().Connect(padLayer->GetInputSlot(0));
134 padLayer->GetOutputSlot().Connect(conv2d->GetInputSlot(0));
135 conv2d->GetOutputSlot().Connect(output->GetInputSlot(0));
136
137 CHECK(CheckSequence(graph.cbegin(), graph.cend(),
138 &IsLayerOfType<InputLayer>,
139 checkConstantFloat16,
140 &IsLayerOfType<DequantizeLayer>,
141 &IsLayerOfType<Convolution2dLayer>,
142 &IsLayerOfType<PadLayer>,
143 &IsLayerOfType<OutputLayer>));
144
145 armnn::Optimizer::Pass(graph, MakeOptimizations(ConvertConstDequantisationLayersToConstLayers()));
146 armnn::Optimizer::Pass(graph, MakeOptimizations(FoldPadIntoConvolution2d()));
147
148 // Ensure that the const and dequantize are now constant of type fp32
149 // Ensure pad and conv2d are now just convolution
150 CHECK(CheckSequence(graph.cbegin(), graph.cend(),
151 &IsLayerOfType<InputLayer>,
152 checkConstantFloat32,
153 checkPadFoldedIntoConv2d,
154 &IsLayerOfType<OutputLayer>));
155 }
156
157 TEST_CASE("ConvertConstInt8DequantizeToConstFloat32")
158 {
159 Graph graph;
160 const unsigned int shape[] = {1, 2, 2, 3};
161
162 const TensorInfo constTensorInfo(4, shape, DataType::QAsymmS8, 1.0, 0, true);
163 const TensorInfo outputDequantizeInfo(4, shape, DataType::Float32, 1.0, 0, true);
164
165 auto constantLayer = graph.AddLayer<ConstantLayer>("constant");
166 std::vector<int8_t> constantValues(constTensorInfo.GetNumElements(), 5);
167 ConstTensor constTensor(constTensorInfo, constantValues.data());
168 constantLayer->m_LayerOutput = std::make_shared<ScopedTensorHandle>(constTensor);
169 constantLayer->GetOutputSlot().SetTensorInfo(constTensorInfo);
170
171 auto dequantizeLayer = graph.AddLayer<DequantizeLayer>("dequantize");
172 dequantizeLayer->GetOutputSlot().SetTensorInfo(outputDequantizeInfo);
173
174 auto output = graph.AddLayer<OutputLayer>(0, "output");
175
176 // Connect up constant -> dequantize -> output
177 constantLayer->GetOutputSlot().Connect(dequantizeLayer->GetInputSlot(0));
178 dequantizeLayer->GetOutputSlot().Connect(output->GetInputSlot(0));
179
180 CHECK(CheckSequence(graph.cbegin(), graph.cend(),
181 checkConstantQAsymmS8,
182 &IsLayerOfType<DequantizeLayer>,
183 &IsLayerOfType<OutputLayer>));
184
185 armnn::Optimizer::Pass(graph, MakeOptimizations(ConvertConstDequantisationLayersToConstLayers()));
186
187 CHECK(CheckSequence(graph.cbegin(), graph.cend(),
188 checkConstantFloat32,
189 &IsLayerOfType<OutputLayer>));
190 }
191 }
192