ArmNN
 21.08
ParserFlatbuffersSerializeFixture.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 "SchemaSerialize.hpp"
9 #include "test/TensorHelpers.hpp"
10 
11 #include "flatbuffers/idl.h"
12 #include "flatbuffers/util.h"
13 
14 #include <ArmnnSchema_generated.h>
15 #include <armnn/IRuntime.hpp>
17 #include <armnn/utility/Assert.hpp>
19 #include <ResolveType.hpp>
20 
21 #include <fmt/format.h>
22 #include <doctest/doctest.h>
23 
24 #include <vector>
25 
27 using TensorRawPtr = armnnSerializer::TensorInfo*;
28 
30 {
32  m_Parser(IDeserializer::Create()),
33  m_Runtime(armnn::IRuntime::Create(armnn::IRuntime::CreationOptions())),
35  {
36  }
37 
38  std::vector<uint8_t> m_GraphBinary;
39  std::string m_JsonString;
40  std::unique_ptr<IDeserializer, void (*)(IDeserializer* parser)> m_Parser;
43 
44  /// If the single-input-single-output overload of Setup() is called, these will store the input and output name
45  /// so they don't need to be passed to the single-input-single-output overload of RunTest().
46  std::string m_SingleInputName;
47  std::string m_SingleOutputName;
48 
49  void Setup()
50  {
51  bool ok = ReadStringToBinary();
52  if (!ok)
53  {
54  throw armnn::Exception("LoadNetwork failed while reading binary input");
55  }
56 
57  armnn::INetworkPtr network =
58  m_Parser->CreateNetworkFromBinary(m_GraphBinary);
59 
60  if (!network)
61  {
62  throw armnn::Exception("The parser failed to create an ArmNN network");
63  }
64 
65  auto optimized = Optimize(*network, {armnn::Compute::CpuRef},
66  m_Runtime->GetDeviceSpec());
67 
68  std::string errorMessage;
69  armnn::Status ret = m_Runtime->LoadNetwork(m_NetworkIdentifier, move(optimized), errorMessage);
70 
71  if (ret != armnn::Status::Success)
72  {
73  throw armnn::Exception(fmt::format("The runtime failed to load the network. "
74  "Error was: {0}. in {1} [{2}:{3}]",
75  errorMessage,
76  __func__,
77  __FILE__,
78  __LINE__));
79  }
80 
81  }
82 
83  void SetupSingleInputSingleOutput(const std::string& inputName, const std::string& outputName)
84  {
85  // Store the input and output name so they don't need to be passed to the single-input-single-output RunTest().
86  m_SingleInputName = inputName;
87  m_SingleOutputName = outputName;
88  Setup();
89  }
90 
92  {
93  std::string schemafile(&deserialize_schema_start, &deserialize_schema_end);
94 
95  // parse schema first, so we can use it to parse the data after
96  flatbuffers::Parser parser;
97 
98  bool ok = parser.Parse(schemafile.c_str());
99  ARMNN_ASSERT_MSG(ok, "Failed to parse schema file");
100 
101  ok &= parser.Parse(m_JsonString.c_str());
102  ARMNN_ASSERT_MSG(ok, "Failed to parse json input");
103 
104  if (!ok)
105  {
106  return false;
107  }
108 
109  {
110  const uint8_t* bufferPtr = parser.builder_.GetBufferPointer();
111  size_t size = static_cast<size_t>(parser.builder_.GetSize());
112  m_GraphBinary.assign(bufferPtr, bufferPtr+size);
113  }
114  return ok;
115  }
116 
117  /// Executes the network with the given input tensor and checks the result against the given output tensor.
118  /// This overload assumes the network has a single input and a single output.
119  template<std::size_t NumOutputDimensions,
120  armnn::DataType ArmnnType,
122  void RunTest(unsigned int layersId,
123  const std::vector<DataType>& inputData,
124  const std::vector<DataType>& expectedOutputData);
125 
126  template<std::size_t NumOutputDimensions,
127  armnn::DataType ArmnnInputType,
128  armnn::DataType ArmnnOutputType,
129  typename InputDataType = armnn::ResolveType<ArmnnInputType>,
130  typename OutputDataType = armnn::ResolveType<ArmnnOutputType>>
131  void RunTest(unsigned int layersId,
132  const std::vector<InputDataType>& inputData,
133  const std::vector<OutputDataType>& expectedOutputData);
134 
135  /// Executes the network with the given input tensors and checks the results against the given output tensors.
136  /// This overload supports multiple inputs and multiple outputs, identified by name.
137  template<std::size_t NumOutputDimensions,
138  armnn::DataType ArmnnType,
139  typename DataType = armnn::ResolveType<ArmnnType>>
140  void RunTest(unsigned int layersId,
141  const std::map<std::string, std::vector<DataType>>& inputData,
142  const std::map<std::string, std::vector<DataType>>& expectedOutputData);
143 
144  template<std::size_t NumOutputDimensions,
145  armnn::DataType ArmnnInputType,
146  armnn::DataType ArmnnOutputType,
147  typename InputDataType = armnn::ResolveType<ArmnnInputType>,
148  typename OutputDataType = armnn::ResolveType<ArmnnOutputType>>
149  void RunTest(unsigned int layersId,
150  const std::map<std::string, std::vector<InputDataType>>& inputData,
151  const std::map<std::string, std::vector<OutputDataType>>& expectedOutputData);
152 
153  void CheckTensors(const TensorRawPtr& tensors, size_t shapeSize, const std::vector<int32_t>& shape,
154  armnnSerializer::TensorInfo tensorType, const std::string& name,
155  const float scale, const int64_t zeroPoint)
156  {
157  armnn::IgnoreUnused(name);
158  CHECK_EQ(shapeSize, tensors->dimensions()->size());
159  CHECK(std::equal(shape.begin(), shape.end(),
160  tensors->dimensions()->begin(), tensors->dimensions()->end()));
161  CHECK_EQ(tensorType.dataType(), tensors->dataType());
162  CHECK_EQ(scale, tensors->quantizationScale());
163  CHECK_EQ(zeroPoint, tensors->quantizationOffset());
164  }
165 };
166 
167 template<std::size_t NumOutputDimensions, armnn::DataType ArmnnType, typename DataType>
169  const std::vector<DataType>& inputData,
170  const std::vector<DataType>& expectedOutputData)
171 {
172  RunTest<NumOutputDimensions, ArmnnType, ArmnnType, DataType, DataType>(layersId, inputData, expectedOutputData);
173 }
174 
175 template<std::size_t NumOutputDimensions,
176  armnn::DataType ArmnnInputType,
177  armnn::DataType ArmnnOutputType,
178  typename InputDataType,
179  typename OutputDataType>
181  const std::vector<InputDataType>& inputData,
182  const std::vector<OutputDataType>& expectedOutputData)
183 {
184  RunTest<NumOutputDimensions, ArmnnInputType, ArmnnOutputType>(layersId,
185  { { m_SingleInputName, inputData } },
186  { { m_SingleOutputName, expectedOutputData } });
187 }
188 
189 template<std::size_t NumOutputDimensions, armnn::DataType ArmnnType, typename DataType>
191  const std::map<std::string, std::vector<DataType>>& inputData,
192  const std::map<std::string, std::vector<DataType>>& expectedOutputData)
193 {
194  RunTest<NumOutputDimensions, ArmnnType, ArmnnType, DataType, DataType>(layersId, inputData, expectedOutputData);
195 }
196 
197 template<std::size_t NumOutputDimensions,
198  armnn::DataType ArmnnInputType,
199  armnn::DataType ArmnnOutputType,
200  typename InputDataType,
201  typename OutputDataType>
203  unsigned int layersId,
204  const std::map<std::string, std::vector<InputDataType>>& inputData,
205  const std::map<std::string, std::vector<OutputDataType>>& expectedOutputData)
206 {
207  auto ConvertBindingInfo = [](const armnnDeserializer::BindingPointInfo& bindingInfo)
208  {
209  return std::make_pair(bindingInfo.m_BindingId, bindingInfo.m_TensorInfo);
210  };
211 
212  // Setup the armnn input tensors from the given vectors.
213  armnn::InputTensors inputTensors;
214  for (auto&& it : inputData)
215  {
216  armnn::BindingPointInfo bindingInfo = ConvertBindingInfo(
217  m_Parser->GetNetworkInputBindingInfo(layersId, it.first));
218  armnn::VerifyTensorInfoDataType(bindingInfo.second, ArmnnInputType);
219  inputTensors.push_back({ bindingInfo.first, armnn::ConstTensor(bindingInfo.second, it.second.data()) });
220  }
221 
222  // Allocate storage for the output tensors to be written to and setup the armnn output tensors.
223  std::map<std::string, std::vector<OutputDataType>> outputStorage;
224  armnn::OutputTensors outputTensors;
225  for (auto&& it : expectedOutputData)
226  {
227  armnn::BindingPointInfo bindingInfo = ConvertBindingInfo(
228  m_Parser->GetNetworkOutputBindingInfo(layersId, it.first));
229  armnn::VerifyTensorInfoDataType(bindingInfo.second, ArmnnOutputType);
230  outputStorage.emplace(it.first, std::vector<OutputDataType>(bindingInfo.second.GetNumElements()));
231  outputTensors.push_back(
232  { bindingInfo.first, armnn::Tensor(bindingInfo.second, outputStorage.at(it.first).data()) });
233  }
234 
235  m_Runtime->EnqueueWorkload(m_NetworkIdentifier, inputTensors, outputTensors);
236 
237  // Compare each output tensor to the expected values
238  for (auto&& it : expectedOutputData)
239  {
240  armnn::BindingPointInfo bindingInfo = ConvertBindingInfo(
241  m_Parser->GetNetworkOutputBindingInfo(layersId, it.first));
242  auto outputExpected = it.second;
243  auto result = CompareTensors(outputExpected, outputStorage[it.first],
244  bindingInfo.second.GetShape(), bindingInfo.second.GetShape());
245  CHECK_MESSAGE(result.m_Result, result.m_Message.str());
246  }
247 }
void CheckTensors(const TensorRawPtr &tensors, size_t shapeSize, const std::vector< int32_t > &shape, armnnSerializer::TensorInfo tensorType, const std::string &name, const float scale, const int64_t zeroPoint)
CPU Execution: Reference C++ kernels.
void RunTest(unsigned int layersId, const std::vector< DataType > &inputData, const std::vector< DataType > &expectedOutputData)
Executes the network with the given input tensor and checks the result against the given output tenso...
void SetupSingleInputSingleOutput(const std::string &inputName, const std::string &outputName)
std::unique_ptr< IRuntime, void(*)(IRuntime *runtime)> IRuntimePtr
Definition: IRuntime.hpp:30
typename ResolveTypeImpl< DT >::Type ResolveType
Definition: ResolveType.hpp:79
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.
void IgnoreUnused(Ts &&...)
std::unique_ptr< IDeserializer, void(*)(IDeserializer *parser)> m_Parser
A tensor defined by a TensorInfo (shape and data type) and a mutable backing store.
Definition: Tensor.hpp:319
DataType
Definition: Types.hpp:35
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
#define ARMNN_ASSERT_MSG(COND, MSG)
Definition: Assert.hpp:15
armnnSerializer::TensorInfo * TensorRawPtr
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::pair< armnn::LayerBindingId, armnn::TensorInfo > BindingPointInfo
Definition: Tensor.hpp:274
const char deserialize_schema_start
Base class for all ArmNN exceptions so that users can filter to just those.
Definition: Exceptions.hpp:46
const char deserialize_schema_end
void VerifyTensorInfoDataType(const armnn::TensorInfo &info, armnn::DataType dataType)
Definition: TypesUtils.hpp:322
std::unique_ptr< INetwork, void(*)(INetwork *network)> INetworkPtr
Definition: INetwork.hpp:172
std::string m_SingleInputName
If the single-input-single-output overload of Setup() is called, these will store the input and outpu...