IVGCVSW-1946: Remove armnn/src from the include paths
[platform/upstream/armnn.git] / src / armnnUtils / ParserPrototxtFixture.hpp
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 <armnnOnnxParser/IOnnxParser.hpp>
12
13 #include <VerificationHelpers.hpp>
14
15 #include <backendsCommon/BackendRegistry.hpp>
16
17 #include <boost/format.hpp>
18
19 #include <string>
20
21 namespace armnnUtils
22 {
23
24 template<typename TParser>
25 struct ParserPrototxtFixture
26 {
27     ParserPrototxtFixture()
28         : m_Parser(TParser::Create())
29         , m_NetworkIdentifier(-1)
30     {
31         armnn::IRuntime::CreationOptions options;
32
33         // Create runtimes for each available backend
34         const armnn::BackendIdSet availableBackendIds = armnn::BackendRegistryInstance().GetBackendIds();
35         for (auto& backendId : availableBackendIds)
36         {
37             m_Runtimes.push_back(std::make_pair(armnn::IRuntime::Create(options), backendId));
38         }
39     }
40
41     /// Parses and loads the network defined by the m_Prototext string.
42     /// @{
43     void SetupSingleInputSingleOutput(const std::string& inputName, const std::string& outputName);
44     void SetupSingleInputSingleOutput(const armnn::TensorShape& inputTensorShape,
45         const std::string& inputName,
46         const std::string& outputName);
47     void Setup(const std::map<std::string, armnn::TensorShape>& inputShapes,
48         const std::vector<std::string>& requestedOutputs);
49     void Setup();
50     /// @}
51
52     /// Executes the network with the given input tensor and checks the result against the given output tensor.
53     /// This overload assumes that the network has a single input and a single output.
54     template <std::size_t NumOutputDimensions>
55     void RunTest(const std::vector<float>& inputData, const std::vector<float>& expectedOutputData);
56
57     /// Executes the network with the given input tensors and checks the results against the given output tensors.
58     /// This overload supports multiple inputs and multiple outputs, identified by name.
59     template <std::size_t NumOutputDimensions>
60     void RunTest(const std::map<std::string, std::vector<float>>& inputData,
61         const std::map<std::string, std::vector<float>>& expectedOutputData);
62
63     std::string                                         m_Prototext;
64     std::unique_ptr<TParser, void(*)(TParser* parser)>  m_Parser;
65     std::vector<std::pair<armnn::IRuntimePtr, armnn::BackendId>> m_Runtimes;
66     armnn::NetworkId                                    m_NetworkIdentifier;
67
68     /// If the single-input-single-output overload of Setup() is called, these will store the input and output name
69     /// so they don't need to be passed to the single-input-single-output overload of RunTest().
70     /// @{
71     std::string m_SingleInputName;
72     std::string m_SingleOutputName;
73     /// @}
74 };
75
76 template<typename TParser>
77 void ParserPrototxtFixture<TParser>::SetupSingleInputSingleOutput(const std::string& inputName,
78     const std::string& outputName)
79 {
80     // Stores the input and output name so they don't need to be passed to the single-input-single-output RunTest().
81     m_SingleInputName = inputName;
82     m_SingleOutputName = outputName;
83     Setup({ }, { outputName });
84 }
85
86 template<typename TParser>
87 void ParserPrototxtFixture<TParser>::SetupSingleInputSingleOutput(const armnn::TensorShape& inputTensorShape,
88     const std::string& inputName,
89     const std::string& outputName)
90 {
91     // Stores the input and output name so they don't need to be passed to the single-input-single-output RunTest().
92     m_SingleInputName = inputName;
93     m_SingleOutputName = outputName;
94     Setup({ { inputName, inputTensorShape } }, { outputName });
95 }
96
97 template<typename TParser>
98 void ParserPrototxtFixture<TParser>::Setup(const std::map<std::string, armnn::TensorShape>& inputShapes,
99     const std::vector<std::string>& requestedOutputs)
100 {
101     for (auto&& runtime : m_Runtimes)
102     {
103         std::string errorMessage;
104
105         armnn::INetworkPtr network =
106             m_Parser->CreateNetworkFromString(m_Prototext.c_str(), inputShapes, requestedOutputs);
107         auto optimized = Optimize(*network,
108                 { runtime.second, armnn::Compute::CpuRef }, runtime.first->GetDeviceSpec());
109         armnn::Status ret = runtime.first->LoadNetwork(m_NetworkIdentifier, move(optimized), errorMessage);
110         if (ret != armnn::Status::Success)
111         {
112             throw armnn::Exception(boost::str(
113                 boost::format("LoadNetwork failed with error: '%1%' %2%")
114                               % errorMessage
115                               % CHECK_LOCATION().AsString()));
116         }
117     }
118 }
119
120 template<typename TParser>
121 void ParserPrototxtFixture<TParser>::Setup()
122 {
123     for (auto&& runtime : m_Runtimes)
124     {
125         std::string errorMessage;
126
127         armnn::INetworkPtr network =
128             m_Parser->CreateNetworkFromString(m_Prototext.c_str());
129         auto optimized = Optimize(*network,
130                 { runtime.second, armnn::Compute::CpuRef }, runtime.first->GetDeviceSpec());
131         armnn::Status ret = runtime.first->LoadNetwork(m_NetworkIdentifier, move(optimized), errorMessage);
132         if (ret != armnn::Status::Success)
133         {
134             throw armnn::Exception(boost::str(
135                 boost::format("LoadNetwork failed with error: '%1%' %2%")
136                               % errorMessage
137                               % CHECK_LOCATION().AsString()));
138         }
139     }
140 }
141
142 template<typename TParser>
143 template <std::size_t NumOutputDimensions>
144 void ParserPrototxtFixture<TParser>::RunTest(const std::vector<float>& inputData,
145     const std::vector<float>& expectedOutputData)
146 {
147     RunTest<NumOutputDimensions>({ { m_SingleInputName, inputData } }, { { m_SingleOutputName, expectedOutputData } });
148 }
149
150 template<typename TParser>
151 template <std::size_t NumOutputDimensions>
152 void ParserPrototxtFixture<TParser>::RunTest(const std::map<std::string, std::vector<float>>& inputData,
153     const std::map<std::string, std::vector<float>>& expectedOutputData)
154 {
155     for (auto&& runtime : m_Runtimes)
156     {
157         using BindingPointInfo = std::pair<armnn::LayerBindingId, armnn::TensorInfo>;
158
159         // Sets up the armnn input tensors from the given vectors.
160         armnn::InputTensors inputTensors;
161         for (auto&& it : inputData)
162         {
163             BindingPointInfo bindingInfo = m_Parser->GetNetworkInputBindingInfo(it.first);
164             inputTensors.push_back({ bindingInfo.first, armnn::ConstTensor(bindingInfo.second, it.second.data()) });
165         }
166
167         // Allocates storage for the output tensors to be written to and sets up the armnn output tensors.
168         std::map<std::string, boost::multi_array<float, NumOutputDimensions>> outputStorage;
169         armnn::OutputTensors outputTensors;
170         for (auto&& it : expectedOutputData)
171         {
172             BindingPointInfo bindingInfo = m_Parser->GetNetworkOutputBindingInfo(it.first);
173             outputStorage.emplace(it.first, MakeTensor<float, NumOutputDimensions>(bindingInfo.second));
174             outputTensors.push_back(
175                 { bindingInfo.first, armnn::Tensor(bindingInfo.second, outputStorage.at(it.first).data()) });
176         }
177
178         runtime.first->EnqueueWorkload(m_NetworkIdentifier, inputTensors, outputTensors);
179
180         // Compares each output tensor to the expected values.
181         for (auto&& it : expectedOutputData)
182         {
183             BindingPointInfo bindingInfo = m_Parser->GetNetworkOutputBindingInfo(it.first);
184             if (bindingInfo.second.GetNumElements() != it.second.size())
185             {
186                 throw armnn::Exception(
187                     boost::str(
188                         boost::format("Output tensor %1% is expected to have %2% elements. "
189                                       "%3% elements supplied. %4%") %
190                                       it.first %
191                                       bindingInfo.second.GetNumElements() %
192                                       it.second.size() %
193                                       CHECK_LOCATION().AsString()));
194             }
195             auto outputExpected = MakeTensor<float, NumOutputDimensions>(bindingInfo.second, it.second);
196             BOOST_TEST(CompareTensors(outputExpected, outputStorage[it.first]));
197         }
198     }
199 }
200
201 } // namespace armnnUtils