ArmNN
 21.08
ParserPrototxtFixture.hpp
Go to the documentation of this file.
1 //
2 // Copyright © 2017 Arm Ltd. All rights reserved.
3 // SPDX-License-Identifier: MIT
4 //
5 
6 #pragma once
7 
8 #include <armnn/IRuntime.hpp>
9 #include <test/TensorHelpers.hpp>
10 
11 #include <Network.hpp>
12 #include <VerificationHelpers.hpp>
13 
14 #include <doctest/doctest.h>
15 #include <fmt/format.h>
16 
17 #include <iomanip>
18 #include <string>
19 
20 namespace armnnUtils
21 {
22 
23 template<typename TParser>
25 {
27  : m_Parser(TParser::Create())
28  , m_Runtime(armnn::IRuntime::Create(armnn::IRuntime::CreationOptions()))
30  {
31  }
32 
33  /// Parses and loads the network defined by the m_Prototext string.
34  /// @{
35  void SetupSingleInputSingleOutput(const std::string& inputName, const std::string& outputName);
36  void SetupSingleInputSingleOutput(const armnn::TensorShape& inputTensorShape,
37  const std::string& inputName,
38  const std::string& outputName);
39  void SetupSingleInputSingleOutput(const armnn::TensorShape& inputTensorShape,
40  const armnn::TensorShape& outputTensorShape,
41  const std::string& inputName,
42  const std::string& outputName);
43  void Setup(const std::map<std::string, armnn::TensorShape>& inputShapes,
44  const std::vector<std::string>& requestedOutputs);
45  void Setup();
47  const std::map<std::string,armnn::TensorShape>& inputShapes,
48  const std::vector<std::string>& requestedOutputs);
49  /// @}
50 
51  /// Executes the network with the given input tensor and checks the result against the given output tensor.
52  /// This overload assumes that the network has a single input and a single output.
53  template <std::size_t NumOutputDimensions>
54  void RunTest(const std::vector<float>& inputData, const std::vector<float>& expectedOutputData);
55 
56  /// Executes the network with the given input tensor and checks the result against the given output tensor.
57  /// Calls RunTest with output type of uint8_t for checking comparison operators.
58  template <std::size_t NumOutputDimensions>
59  void RunComparisonTest(const std::map<std::string, std::vector<float>>& inputData,
60  const std::map<std::string, std::vector<uint8_t>>& expectedOutputData);
61 
62  /// Executes the network with the given input tensors and checks the results against the given output tensors.
63  /// This overload supports multiple inputs and multiple outputs, identified by name.
64  template <std::size_t NumOutputDimensions, typename T = float>
65  void RunTest(const std::map<std::string, std::vector<float>>& inputData,
66  const std::map<std::string, std::vector<T>>& expectedOutputData);
67 
68  std::string m_Prototext;
69  std::unique_ptr<TParser, void(*)(TParser* parser)> m_Parser;
72 
73  /// If the single-input-single-output overload of Setup() is called, these will store the input and output name
74  /// so they don't need to be passed to the single-input-single-output overload of RunTest().
75  /// @{
76  std::string m_SingleInputName;
77  std::string m_SingleOutputName;
78  /// @}
79 
80  /// This will store the output shape so it don't need to be passed to the single-input-single-output overload
81  /// of RunTest().
83 };
84 
85 template<typename TParser>
87  const std::string& outputName)
88 {
89  // Stores the input and output name so they don't need to be passed to the single-input-single-output RunTest().
90  m_SingleInputName = inputName;
91  m_SingleOutputName = outputName;
92  Setup({ }, { outputName });
93 }
94 
95 template<typename TParser>
97  const std::string& inputName,
98  const std::string& outputName)
99 {
100  // Stores the input and output name so they don't need to be passed to the single-input-single-output RunTest().
101  m_SingleInputName = inputName;
102  m_SingleOutputName = outputName;
103  Setup({ { inputName, inputTensorShape } }, { outputName });
104 }
105 
106 template<typename TParser>
108  const armnn::TensorShape& outputTensorShape,
109  const std::string& inputName,
110  const std::string& outputName)
111 {
112  // Stores the input name, the output name and the output tensor shape
113  // so they don't need to be passed to the single-input-single-output RunTest().
114  m_SingleInputName = inputName;
115  m_SingleOutputName = outputName;
116  m_SingleOutputShape = outputTensorShape;
117  Setup({ { inputName, inputTensorShape } }, { outputName });
118 }
119 
120 template<typename TParser>
121 void ParserPrototxtFixture<TParser>::Setup(const std::map<std::string, armnn::TensorShape>& inputShapes,
122  const std::vector<std::string>& requestedOutputs)
123 {
124  std::string errorMessage;
125 
126  armnn::INetworkPtr network =
127  m_Parser->CreateNetworkFromString(m_Prototext.c_str(), inputShapes, requestedOutputs);
128  auto optimized = Optimize(*network, { armnn::Compute::CpuRef }, m_Runtime->GetDeviceSpec());
129  armnn::Status ret = m_Runtime->LoadNetwork(m_NetworkIdentifier, move(optimized), errorMessage);
130  if (ret != armnn::Status::Success)
131  {
132  throw armnn::Exception(fmt::format("LoadNetwork failed with error: '{0}' {1}",
133  errorMessage,
134  CHECK_LOCATION().AsString()));
135  }
136 }
137 
138 template<typename TParser>
140 {
141  std::string errorMessage;
142 
143  armnn::INetworkPtr network =
144  m_Parser->CreateNetworkFromString(m_Prototext.c_str());
145  auto optimized = Optimize(*network, { armnn::Compute::CpuRef }, m_Runtime->GetDeviceSpec());
146  armnn::Status ret = m_Runtime->LoadNetwork(m_NetworkIdentifier, move(optimized), errorMessage);
147  if (ret != armnn::Status::Success)
148  {
149  throw armnn::Exception(fmt::format("LoadNetwork failed with error: '{0}' {1}",
150  errorMessage,
151  CHECK_LOCATION().AsString()));
152  }
153 }
154 
155 template<typename TParser>
157  const std::map<std::string,armnn::TensorShape>& inputShapes,
158  const std::vector<std::string>& requestedOutputs)
159 {
160  armnn::INetworkPtr network =
161  m_Parser->CreateNetworkFromString(m_Prototext.c_str(), inputShapes, requestedOutputs);
162  auto optimized = Optimize(*network, { armnn::Compute::CpuRef }, m_Runtime->GetDeviceSpec());
163  return optimized;
164 }
165 
166 template<typename TParser>
167 template <std::size_t NumOutputDimensions>
168 void ParserPrototxtFixture<TParser>::RunTest(const std::vector<float>& inputData,
169  const std::vector<float>& expectedOutputData)
170 {
171  RunTest<NumOutputDimensions>({ { m_SingleInputName, inputData } }, { { m_SingleOutputName, expectedOutputData } });
172 }
173 
174 template<typename TParser>
175 template <std::size_t NumOutputDimensions>
176 void ParserPrototxtFixture<TParser>::RunComparisonTest(const std::map<std::string, std::vector<float>>& inputData,
177  const std::map<std::string, std::vector<uint8_t>>&
178  expectedOutputData)
179 {
180  RunTest<NumOutputDimensions, uint8_t>(inputData, expectedOutputData);
181 }
182 
183 template<typename TParser>
184 template <std::size_t NumOutputDimensions, typename T>
185 void ParserPrototxtFixture<TParser>::RunTest(const std::map<std::string, std::vector<float>>& inputData,
186  const std::map<std::string, std::vector<T>>& expectedOutputData)
187 {
188  // Sets up the armnn input tensors from the given vectors.
189  armnn::InputTensors inputTensors;
190  for (auto&& it : inputData)
191  {
192  armnn::BindingPointInfo bindingInfo = m_Parser->GetNetworkInputBindingInfo(it.first);
193  inputTensors.push_back({ bindingInfo.first, armnn::ConstTensor(bindingInfo.second, it.second.data()) });
194  }
195 
196  // Allocates storage for the output tensors to be written to and sets up the armnn output tensors.
197  std::map<std::string, std::vector<T>> outputStorage;
198  armnn::OutputTensors outputTensors;
199  for (auto&& it : expectedOutputData)
200  {
201  armnn::BindingPointInfo bindingInfo = m_Parser->GetNetworkOutputBindingInfo(it.first);
202  outputStorage.emplace(it.first, std::vector<T>(bindingInfo.second.GetNumElements()));
203  outputTensors.push_back(
204  { bindingInfo.first, armnn::Tensor(bindingInfo.second, outputStorage.at(it.first).data()) });
205  }
206 
207  m_Runtime->EnqueueWorkload(m_NetworkIdentifier, inputTensors, outputTensors);
208 
209  // Compares each output tensor to the expected values.
210  for (auto&& it : expectedOutputData)
211  {
212  armnn::BindingPointInfo bindingInfo = m_Parser->GetNetworkOutputBindingInfo(it.first);
213  if (bindingInfo.second.GetNumElements() != it.second.size())
214  {
215  throw armnn::Exception(fmt::format("Output tensor {0} is expected to have {1} elements. "
216  "{2} elements supplied. {3}",
217  it.first,
218  bindingInfo.second.GetNumElements(),
219  it.second.size(),
220  CHECK_LOCATION().AsString()));
221  }
222 
223  // If the expected output shape is set, the output tensor checks will be carried out.
225  {
226 
227  if (bindingInfo.second.GetShape().GetNumDimensions() == NumOutputDimensions &&
228  bindingInfo.second.GetShape().GetNumDimensions() == m_SingleOutputShape.GetNumDimensions())
229  {
230  for (unsigned int i = 0; i < m_SingleOutputShape.GetNumDimensions(); ++i)
231  {
232  if (m_SingleOutputShape[i] != bindingInfo.second.GetShape()[i])
233  {
234  // This exception message could not be created by fmt:format because of an oddity in
235  // the operator << of TensorShape.
236  std::stringstream message;
237  message << "Output tensor " << it.first << " is expected to have "
238  << bindingInfo.second.GetShape() << "shape. "
239  << m_SingleOutputShape << " shape supplied. "
240  << CHECK_LOCATION().AsString();
241  throw armnn::Exception(message.str());
242  }
243  }
244  }
245  else
246  {
247  throw armnn::Exception(fmt::format("Output tensor {0} is expected to have {1} dimensions. "
248  "{2} dimensions supplied. {3}",
249  it.first,
250  bindingInfo.second.GetShape().GetNumDimensions(),
251  NumOutputDimensions,
252  CHECK_LOCATION().AsString()));
253  }
254  }
255 
256  auto outputExpected = it.second;
257  auto shape = bindingInfo.second.GetShape();
258  if (std::is_same<T, uint8_t>::value)
259  {
260  auto result = CompareTensors(outputExpected, outputStorage[it.first], shape, shape, true);
261  CHECK_MESSAGE(result.m_Result, result.m_Message.str());
262  }
263  else
264  {
265  auto result = CompareTensors(outputExpected, outputStorage[it.first], shape, shape);
266  CHECK_MESSAGE(result.m_Result, result.m_Message.str());
267  }
268  }
269 }
270 
271 } // namespace armnnUtils
CPU Execution: Reference C++ kernels.
armnn::TensorShape m_SingleOutputShape
This will store the output shape so it don&#39;t need to be passed to the single-input-single-output over...
std::unique_ptr< IRuntime, void(*)(IRuntime *runtime)> IRuntimePtr
Definition: IRuntime.hpp:30
void RunComparisonTest(const std::map< std::string, std::vector< float >> &inputData, const std::map< std::string, std::vector< uint8_t >> &expectedOutputData)
Executes the network with the given input tensor and checks the result against the given output tenso...
std::vector< std::pair< LayerBindingId, class ConstTensor > > InputTensors
Definition: Tensor.hpp:360
armnn::PredicateResult CompareTensors(const std::vector< T > &actualData, const std::vector< T > &expectedData, const armnn::TensorShape &actualShape, const armnn::TensorShape &expectedShape, bool compareBoolean=false, bool isDynamic=false)
Copyright (c) 2021 ARM Limited and Contributors.
A tensor defined by a TensorInfo (shape and data type) and a mutable backing store.
Definition: Tensor.hpp:319
std::unique_ptr< TParser, void(*)(TParser *parser)> m_Parser
armnn::IOptimizedNetworkPtr SetupOptimizedNetwork(const std::map< std::string, armnn::TensorShape > &inputShapes, const std::vector< std::string > &requestedOutputs)
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:1613
std::string m_SingleInputName
If the single-input-single-output overload of Setup() is called, these will store the input and outpu...
void RunTest(const std::vector< float > &inputData, const std::vector< float > &expectedOutputData)
Executes the network with the given input tensor and checks the result against the given output tenso...
int NetworkId
Definition: IRuntime.hpp:24
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:361
Status
enumeration
Definition: Types.hpp:29
std::unique_ptr< IOptimizedNetwork, void(*)(IOptimizedNetwork *network)> IOptimizedNetworkPtr
Definition: INetwork.hpp:173
#define CHECK_LOCATION()
Definition: Exceptions.hpp:197
std::pair< armnn::LayerBindingId, armnn::TensorInfo > BindingPointInfo
Definition: Tensor.hpp:274
void SetupSingleInputSingleOutput(const std::string &inputName, const std::string &outputName)
Parses and loads the network defined by the m_Prototext string.
Base class for all ArmNN exceptions so that users can filter to just those.
Definition: Exceptions.hpp:46
unsigned int GetNumDimensions() const
Function that returns the tensor rank.
Definition: Tensor.cpp:174
std::unique_ptr< INetwork, void(*)(INetwork *network)> INetworkPtr
Definition: INetwork.hpp:172