ArmNN
 22.05
PermuteAndBatchToSpaceAsDepthToSpaceTests.cpp File Reference
#include <TestUtils.hpp>
#include <Network.hpp>
#include <Optimizer.hpp>
#include <doctest/doctest.h>

Go to the source code of this file.

Functions

 TEST_SUITE ("Optimizer")
 

Function Documentation

◆ TEST_SUITE()

TEST_SUITE ( "Optimizer"  )

Shared function for the below tests, so that we test the same network in both cases.

Shared function for the below tests, so that we test the same network in both cases.

Tests that the optimization performed by PermuteAndBatchToSpaceAsDepthToSpace is as expected. Note this does not ensure the correctness of the optimization - that is done in the below test.

Tests that the optimization performed by PermuteAndBatchToSpaceAsDepthToSpace is as expected. Note this does not ensure the correctness of the optimization - that is done in the below test.

Definition at line 15 of file PermuteAndBatchToSpaceAsDepthToSpaceTests.cpp.

References Graph::cbegin(), Graph::cend(), CheckSequence(), armnn::CpuRef, IRuntime::Create(), INetwork::Create(), CreateTestNetwork(), armnn::Float32, armnn::GetGraphForTesting(), BatchToSpaceNdDescriptor::m_BlockShape, BatchToSpaceNdDescriptor::m_DataLayout, armnn::MakeOptimizations(), armnn::NHWC, armnn::Optimize(), and Optimizer::Pass().

16 {
17 using namespace armnn::optimizations;
18 
19 namespace
20 {
21 
22 /// Shared function for the below tests, so that we test the same network in both cases.
23 std::unique_ptr<NetworkImpl> CreateTestNetworkImpl()
24 {
25  std::unique_ptr<NetworkImpl> network(new NetworkImpl());
26 
27  auto input = network->AddInputLayer(0, "input");
28  const TensorInfo inputInfo({ 1, 2, 3, 4 }, DataType::Float32);
29  input->GetOutputSlot(0).SetTensorInfo(inputInfo);
30 
31  // Insert Permute which swaps batches and channels dimensions
32  auto permute = network->AddPermuteLayer(PermuteDescriptor(PermutationVector{ 3, 1, 2, 0 }), "permute");
33  const TensorInfo permuteInfo({ 4, 2, 3, 1 }, DataType::Float32);
34  permute->GetOutputSlot(0).SetTensorInfo(permuteInfo);
35  input->GetOutputSlot(0).Connect(permute->GetInputSlot(0));
36 
37  // Insert BatchToSpace
38  BatchToSpaceNdDescriptor batchToSpaceDesc;
39  batchToSpaceDesc.m_BlockShape = { 2, 2 };
40  batchToSpaceDesc.m_DataLayout = DataLayout::NHWC;
41  auto batchToSpace = network->AddBatchToSpaceNdLayer(batchToSpaceDesc, "batchToSpace");
42  const TensorInfo batchToSpaceInfo({ 1, 4, 6, 1 }, DataType::Float32);
43  batchToSpace->GetOutputSlot(0).SetTensorInfo(batchToSpaceInfo);
44  permute->GetOutputSlot(0).Connect(batchToSpace->GetInputSlot(0));
45 
46  auto output = network->AddOutputLayer(0, "output");
47  batchToSpace->GetOutputSlot(0).Connect(output->GetInputSlot(0));
48 
49  return network;
50 }
51 
52 /// Shared function for the below tests, so that we test the same network in both cases.
53 std::unique_ptr<NetworkImpl> CreateTransposeTestNetworkImpl()
54 {
55  // Create a network
56  std::unique_ptr<NetworkImpl> network(new NetworkImpl());
57 
58  auto input = network->AddInputLayer(0, "input");
59  const TensorInfo inputInfo({ 1, 2, 3, 4 }, DataType::Float32);
60  input->GetOutputSlot(0).SetTensorInfo(inputInfo);
61 
62  // Insert Permute which swaps batches and channels dimensions
63  auto permute = network->AddTransposeLayer(TransposeDescriptor(PermutationVector{ 3, 1, 2, 0 }), "permute");
64  const TensorInfo permuteInfo({ 4, 2, 3, 1 }, DataType::Float32);
65  permute->GetOutputSlot(0).SetTensorInfo(permuteInfo);
66  input->GetOutputSlot(0).Connect(permute->GetInputSlot(0));
67 
68  // Insert BatchToSpace
69  BatchToSpaceNdDescriptor batchToSpaceDesc;
70  batchToSpaceDesc.m_BlockShape = { 2, 2 };
71  batchToSpaceDesc.m_DataLayout = DataLayout::NHWC;
72  auto batchToSpace = network->AddBatchToSpaceNdLayer(batchToSpaceDesc, "batchToSpace");
73  const TensorInfo batchToSpaceInfo({ 1, 4, 6, 1 }, DataType::Float32);
74  batchToSpace->GetOutputSlot(0).SetTensorInfo(batchToSpaceInfo);
75  permute->GetOutputSlot(0).Connect(batchToSpace->GetInputSlot(0));
76 
77  auto output = network->AddOutputLayer(0, "output");
78  batchToSpace->GetOutputSlot(0).Connect(output->GetInputSlot(0));
79 
80  return network;
81 }
82 
83 } // namespace
84 
85 /// Tests that the optimization performed by PermuteAndBatchToSpaceAsDepthToSpace is as expected.
86 /// Note this does not ensure the correctness of the optimization - that is done in the below test.
87 TEST_CASE("PermuteAndBatchToSpaceAsDepthToSpaceOptimizerTest")
88 {
89  std::unique_ptr<NetworkImpl> network = CreateTestNetworkImpl();
90  Graph graph = network.get()->GetGraph();
91 
92  // Confirm initial graph is as we expect
93  CHECK(CheckSequence(graph.cbegin(), graph.cend(), &IsLayerOfType<InputLayer>, &IsLayerOfType<PermuteLayer>,
94  &IsLayerOfType<BatchToSpaceNdLayer>, &IsLayerOfType<OutputLayer>));
95 
96  // Perform the optimization which should merge the two layers into a DepthToSpace
98 
99  // Check that the replacement has been made as expected
100  auto checkDepthToSpace = [](const Layer* const layer) -> bool {
101  return IsLayerOfType<DepthToSpaceLayer>(layer) &&
102  static_cast<const DepthToSpaceLayer*>(layer)->GetParameters().m_BlockSize == 2 &&
103  static_cast<const DepthToSpaceLayer*>(layer)->GetParameters().m_DataLayout == DataLayout::NHWC &&
104  layer->GetOutputHandler().GetTensorInfo() == TensorInfo({ 1, 4, 6, 1 }, DataType::Float32);
105  };
106 
107  CHECK(CheckSequence(graph.cbegin(), graph.cend(), &IsLayerOfType<InputLayer>, checkDepthToSpace,
108  &IsLayerOfType<OutputLayer>));
109 
110  // Check the new layer has the two merged layers listed as related layers
111  std::list<std::string> testRelatedLayers = { "batchToSpace", "permute" };
112  CHECK(CheckRelatedLayers<DepthToSpaceLayer>(graph, testRelatedLayers));
113 }
114 
115 /// Tests that the optimization performed by PermuteAndBatchToSpaceAsDepthToSpace is as expected.
116 /// Note this does not ensure the correctness of the optimization - that is done in the below test.
117 TEST_CASE("TransposeAndBatchToSpaceAsDepthToSpaceOptimizerTest")
118 {
119  std::unique_ptr<NetworkImpl> network = CreateTransposeTestNetworkImpl();
120  Graph graph = network.get()->GetGraph();
121 
122  // Confirm initial graph is as we expect
123  CHECK(CheckSequence(graph.cbegin(), graph.cend(), &IsLayerOfType<InputLayer>, &IsLayerOfType<TransposeLayer>,
124  &IsLayerOfType<BatchToSpaceNdLayer>, &IsLayerOfType<OutputLayer>));
125 
126  // Perform the optimization which should merge the two layers into a DepthToSpace
128 
129  // Check that the replacement has been made as expected
130  auto checkDepthToSpace = [](const Layer* const layer) -> bool {
131  return IsLayerOfType<DepthToSpaceLayer>(layer) &&
132  static_cast<const DepthToSpaceLayer*>(layer)->GetParameters().m_BlockSize == 2 &&
133  static_cast<const DepthToSpaceLayer*>(layer)->GetParameters().m_DataLayout == DataLayout::NHWC &&
134  layer->GetOutputHandler().GetTensorInfo() == TensorInfo({ 1, 4, 6, 1 }, DataType::Float32);
135  };
136 
137  CHECK(CheckSequence(graph.cbegin(), graph.cend(), &IsLayerOfType<InputLayer>, checkDepthToSpace,
138  &IsLayerOfType<OutputLayer>));
139 
140  // Check the new layer has the two merged layers listed as related layers
141  std::list<std::string> testRelatedLayers = { "batchToSpace", "permute" };
142  CHECK(CheckRelatedLayers<DepthToSpaceLayer>(graph, testRelatedLayers));
143 }
144 
145 // This unit test needs the reference backend, it's not available if the reference backend is not built
146 #if defined(ARMNNREF_ENABLED)
147 
148 /// Shared function for the below tests, so that we test the same network in both cases.
150 {
151  // Create a network
152  INetworkPtr network = INetwork::Create();
153 
154  auto input = network->AddInputLayer(0, "input");
155  const TensorInfo inputInfo({ 1, 2, 3, 4 }, DataType::Float32);
156  input->GetOutputSlot(0).SetTensorInfo(inputInfo);
157 
158  // Insert Permute which swaps batches and channels dimensions
159  auto permute = network->AddPermuteLayer(PermuteDescriptor(PermutationVector{ 3, 1, 2, 0 }), "permute");
160  const TensorInfo permuteInfo({ 4, 2, 3, 1 }, DataType::Float32);
161  permute->GetOutputSlot(0).SetTensorInfo(permuteInfo);
162  input->GetOutputSlot(0).Connect(permute->GetInputSlot(0));
163 
164  // Insert BatchToSpace
165  BatchToSpaceNdDescriptor batchToSpaceDesc;
166  batchToSpaceDesc.m_BlockShape = { 2, 2 };
167  batchToSpaceDesc.m_DataLayout = DataLayout::NHWC;
168  auto batchToSpace = network->AddBatchToSpaceNdLayer(batchToSpaceDesc, "batchToSpace");
169  const TensorInfo batchToSpaceInfo({ 1, 4, 6, 1 }, DataType::Float32);
170  batchToSpace->GetOutputSlot(0).SetTensorInfo(batchToSpaceInfo);
171  permute->GetOutputSlot(0).Connect(batchToSpace->GetInputSlot(0));
172 
173  auto output = network->AddOutputLayer(0, "output");
174  batchToSpace->GetOutputSlot(0).Connect(output->GetInputSlot(0));
175 
176  return network;
177 }
178 
179 /// Shared function for the below tests, so that we test the same network in both cases.
180 INetworkPtr CreateTransposeTestNetwork()
181 {
182  // Create a network
183  INetworkPtr network = INetwork::Create();
184 
185  auto input = network->AddInputLayer(0, "input");
186  const TensorInfo inputInfo({ 1, 2, 3, 4 }, DataType::Float32);
187  input->GetOutputSlot(0).SetTensorInfo(inputInfo);
188 
189  // Insert Permute which swaps batches and channels dimensions
190  auto permute = network->AddTransposeLayer(TransposeDescriptor(PermutationVector{ 3, 1, 2, 0 }), "permute");
191  const TensorInfo permuteInfo({ 4, 2, 3, 1 }, DataType::Float32);
192  permute->GetOutputSlot(0).SetTensorInfo(permuteInfo);
193  input->GetOutputSlot(0).Connect(permute->GetInputSlot(0));
194 
195  // Insert BatchToSpace
196  BatchToSpaceNdDescriptor batchToSpaceDesc;
197  batchToSpaceDesc.m_BlockShape = { 2, 2 };
198  batchToSpaceDesc.m_DataLayout = DataLayout::NHWC;
199  auto batchToSpace = network->AddBatchToSpaceNdLayer(batchToSpaceDesc, "batchToSpace");
200  const TensorInfo batchToSpaceInfo({ 1, 4, 6, 1 }, DataType::Float32);
201  batchToSpace->GetOutputSlot(0).SetTensorInfo(batchToSpaceInfo);
202  permute->GetOutputSlot(0).Connect(batchToSpace->GetInputSlot(0));
203 
204  auto output = network->AddOutputLayer(0, "output");
205  batchToSpace->GetOutputSlot(0).Connect(output->GetInputSlot(0));
206 
207  return network;
208 }
209 
210 /// Tests that a optimization performed by PermuteAndBatchToSpaceAsDepthToSpace does not change the behaviour
211 /// of the network (i.e. it still produces the correct output).
212 TEST_CASE("PermuteAndBatchToSpaceAsDepthToSpaceCorrectnessTest")
213 {
214  INetworkPtr network = CreateTestNetwork();
215 
217  IOptimizedNetworkPtr optimizedNetwork = Optimize(*network, { Compute::CpuRef }, runtime->GetDeviceSpec());
218 
219  // Confirm that the optimization has actually taken place
220  const Graph& optGraph = GetGraphForTesting(optimizedNetwork.get());
221  CHECK(CheckSequence(optGraph.cbegin(), optGraph.cend(), &IsLayerOfType<InputLayer>,
222  &IsLayerOfType<DepthToSpaceLayer>, &IsLayerOfType<OutputLayer>));
223 
224  // Load the graph into a runtime so we can check it produces the correct output
225  NetworkId netId;
226  runtime->LoadNetwork(netId, std::move(optimizedNetwork));
227 
228  std::vector<float> inputData{
229  // Each row here is a row of pixels where each pixel has 4 channels
230  // clang-format off
231  1.0f, 2.0f, 3.0f, 4.0f, 10.0f, 20.0f, 30.0f, 40.0f, 100.0f, 200.0f, 300.0f, 400.0f,
232  -1.0f, -2.0f, -3.0f, -4.0f, -10.0f, -20.0f, -30.0f, -40.0f, -100.0f, -200.0f, -300.0f, -400.0f,
233  // clang-format on
234  };
235  ConstTensor input(TensorInfo({ 1, 2, 3, 4 }, DataType::Float32, 0.0f, 0, true), inputData);
236  InputTensors inputs = { { 0, input } };
237  std::vector<float> outputData(4 * 6);
238  Tensor output(TensorInfo({ 1, 4, 6, 1 }, DataType::Float32), outputData.data());
239  OutputTensors outputs = { { 0, output } };
240  runtime->EnqueueWorkload(netId, inputs, outputs);
241 
242  // Check the output is as expected.
243  // Note this output has been generated by running the network *without* the optimization.
244  std::vector<float> expectedOutput = {
245  // Rows and columns here match exactly with the tensor, as there is only 1 channel.
246  // clang-format off
247  1.0f, 2.0f, 10.0f, 20.0f, 100.0f, 200.0f,
248  3.0f, 4.0f, 30.0f, 40.0f, 300.0f, 400.0f,
249 
250  -1.0f, -2.0f, -10.0f, -20.0f, -100.0f, -200.0f,
251  -3.0f, -4.0f, -30.0f, -40.0f, -300.0f, -400.0f,
252  // clang-format on
253  };
254  CHECK(outputData == expectedOutput);
255 }
256 
257 /// Tests that a optimization performed by PermuteAndBatchToSpaceAsDepthToSpace does not change the behaviour
258 /// of the network (i.e. it still produces the correct output).
259 TEST_CASE("TransposeAndBatchToSpaceAsDepthToSpaceCorrectnessTest")
260 {
261  INetworkPtr network = CreateTransposeTestNetwork();
262 
264  IOptimizedNetworkPtr optimizedNetwork = Optimize(*network, { Compute::CpuRef }, runtime->GetDeviceSpec());
265 
266  // Confirm that the optimization has actually taken place
267  const Graph& optGraph = GetGraphForTesting(optimizedNetwork.get());
268  CHECK(CheckSequence(optGraph.cbegin(), optGraph.cend(), &IsLayerOfType<InputLayer>,
269  &IsLayerOfType<DepthToSpaceLayer>, &IsLayerOfType<OutputLayer>));
270 
271  // Load the graph into a runtime so we can check it produces the correct output
272  NetworkId netId;
273  runtime->LoadNetwork(netId, std::move(optimizedNetwork));
274 
275  std::vector<float> inputData{
276  // Each row here is a row of pixels where each pixel has 4 channels
277  // clang-format off
278  1.0f, 2.0f, 3.0f, 4.0f, 10.0f, 20.0f, 30.0f, 40.0f, 100.0f, 200.0f, 300.0f, 400.0f,
279  -1.0f, -2.0f, -3.0f, -4.0f, -10.0f, -20.0f, -30.0f, -40.0f, -100.0f, -200.0f, -300.0f, -400.0f,
280  // clang-format on
281  };
282  ConstTensor input(TensorInfo({ 1, 2, 3, 4 }, DataType::Float32, 0.0f, 0, true), inputData);
283  InputTensors inputs = { { 0, input } };
284  std::vector<float> outputData(4 * 6);
285  Tensor output(TensorInfo({ 1, 4, 6, 1 }, DataType::Float32), outputData.data());
286  OutputTensors outputs = { { 0, output } };
287  runtime->EnqueueWorkload(netId, inputs, outputs);
288 
289  // Check the output is as expected.
290  // Note this output has been generated by running the network *without* the optimization.
291  std::vector<float> expectedOutput = {
292  // Rows and columns here match exactly with the tensor, as there is only 1 channel.
293  // clang-format off
294  1.0f, 2.0f, 10.0f, 20.0f, 100.0f, 200.0f,
295  3.0f, 4.0f, 30.0f, 40.0f, 300.0f, 400.0f,
296 
297  -1.0f, -2.0f, -10.0f, -20.0f, -100.0f, -200.0f,
298  -3.0f, -4.0f, -30.0f, -40.0f, -300.0f, -400.0f,
299  // clang-format on
300  };
301  CHECK(outputData == expectedOutput);
302 }
303 #endif
304 
305 }
static IRuntimePtr Create(const CreationOptions &options)
Definition: Runtime.cpp:49
CPU Execution: Reference C++ kernels.
Optimizer::Optimizations MakeOptimizations(Args &&... args)
Definition: Optimizer.hpp:43
bool CheckSequence(const armnn::Graph::ConstIterator first, const armnn::Graph::ConstIterator last)
Definition: TestUtils.hpp:21
OptimizeForConnection< TransposeLayer, BatchToSpaceNdLayer, PermuteAndBatchToSpaceAsDepthToSpaceImpl< TransposeLayer > > TransposeAndBatchToSpaceAsDepthToSpace
ConstIterator cbegin() const
Returns const iterator pointing to the beginning of the list. Lowercase for range-based for loops...
Definition: Graph.hpp:179
static void Pass(Graph &graph, const Optimizations &optimizations)
Definition: Optimizer.cpp:16
std::unique_ptr< IRuntime, void(*)(IRuntime *runtime)> IRuntimePtr
Definition: IRuntime.hpp:33
DataLayout m_DataLayout
The data layout to be used (NCHW, NHWC).
std::vector< std::pair< LayerBindingId, class ConstTensor > > InputTensors
Definition: Tensor.hpp:392
A BatchToSpaceNdDescriptor for the BatchToSpaceNdLayer.
Private implementation of INetwork.
Definition: Network.hpp:31
A tensor defined by a TensorInfo (shape and data type) and a mutable backing store.
Definition: Tensor.hpp:319
OptimizeForConnection< PermuteLayer, BatchToSpaceNdLayer, PermuteAndBatchToSpaceAsDepthToSpaceImpl< PermuteLayer > > PermuteAndBatchToSpaceAsDepthToSpace
IOptimizedNetworkPtr Optimize(const INetwork &network, const std::vector< BackendId > &backendPreferences, const IDeviceSpec &deviceSpec, const OptimizerOptions &options=OptimizerOptions(), Optional< std::vector< std::string > &> messages=EmptyOptional())
Create an optimized version of the network.
Definition: Network.cpp:1847
std::vector< unsigned int > m_BlockShape
Block shape values.
int NetworkId
Definition: IRuntime.hpp:27
A tensor defined by a TensorInfo (shape and data type) and an immutable backing store.
Definition: Tensor.hpp:327
std::vector< std::pair< LayerBindingId, class Tensor > > OutputTensors
Definition: Tensor.hpp:393
std::unique_ptr< IOptimizedNetwork, void(*)(IOptimizedNetwork *network)> IOptimizedNetworkPtr
Definition: INetwork.hpp:242
Graph & GetGraphForTesting(IOptimizedNetwork *optNet)
Definition: TestUtils.cpp:49
A TransposeDescriptor for the TransposeLayer.
ConstIterator cend() const
Returns const iterator pointing to the end of the list. Lowercase for range-based for loops...
Definition: Graph.hpp:181
armnn::INetworkPtr CreateTestNetwork(armnn::TensorInfo &inputTensorInfo)
std::unique_ptr< INetwork, void(*)(INetwork *network)> INetworkPtr
Definition: INetwork.hpp:241
static INetworkPtr Create(NetworkOptions networkOptions={})
Definition: Network.cpp:476
This layer represents a DepthToSpace operation.
A PermuteDescriptor for the PermuteLayer.