blob: 58ccfb781305da1d205fbe19951a5d54195c1991 [file] [log] [blame]
telsoa014fcda012018-03-09 14:13:49 +00001//
2// Copyright © 2017 Arm Ltd. All rights reserved.
David Beckecb56cd2018-09-05 12:52:57 +01003// SPDX-License-Identifier: MIT
telsoa014fcda012018-03-09 14:13:49 +00004//
Matteo Martincigh49124022019-01-11 13:25:59 +00005
telsoa014fcda012018-03-09 14:13:49 +00006#include "Network.hpp"
7#include "Graph.hpp"
8#include "Layer.hpp"
telsoa01c577f2c2018-08-31 09:22:23 +01009#include "DeviceSpec.hpp"
telsoa014fcda012018-03-09 14:13:49 +000010#include "Optimizer.hpp"
Derek Lambertiff05cc52019-04-26 13:05:17 +010011#include "SubgraphViewSelector.hpp"
Matteo Martincigh49124022019-01-11 13:25:59 +000012#include "BackendSettings.hpp"
David Beckac42efd2018-09-26 17:41:13 +010013#include "optimizations/All.hpp"
telsoa014fcda012018-03-09 14:13:49 +000014
Aron Virginas-Tarc9cc8042018-11-01 16:15:57 +000015#include <backendsCommon/CpuTensorHandle.hpp>
16#include <backendsCommon/WorkloadFactory.hpp>
David Beck263e3492018-11-09 14:46:40 +000017#include <backendsCommon/BackendRegistry.hpp>
18#include <backendsCommon/IBackendInternal.hpp>
Derek Lamberti84da38b2019-06-13 11:40:08 +010019#include <backendsCommon/TensorHandleFactoryRegistry.hpp>
David Beckac42efd2018-09-26 17:41:13 +010020
21#include <armnn/Exceptions.hpp>
telsoa014fcda012018-03-09 14:13:49 +000022#include <armnn/Utils.hpp>
telsoa01c577f2c2018-08-31 09:22:23 +010023#include <armnn/TypesUtils.hpp>
telsoa014fcda012018-03-09 14:13:49 +000024
25#include <fcntl.h>
26#include <algorithm>
27#include <fstream>
28#include <memory>
telsoa01c577f2c2018-08-31 09:22:23 +010029#include <vector>
30#include <algorithm>
telsoa014fcda012018-03-09 14:13:49 +000031
32#include <boost/assert.hpp>
33#include <boost/format.hpp>
34#include <boost/log/trivial.hpp>
35#include <boost/numeric/conversion/converter_policies.hpp>
36#include <boost/cast.hpp>
37
38namespace armnn
39{
40
41armnn::INetwork* INetwork::CreateRaw()
42{
43 return new Network();
44}
45
46armnn::INetworkPtr INetwork::Create()
47{
48 return INetworkPtr(CreateRaw(), &INetwork::Destroy);
49}
50
51void INetwork::Destroy(INetwork* network)
52{
53 delete boost::polymorphic_downcast<Network*>(network);
54}
55
56Status Network::PrintGraph()
57{
58 m_Graph->Print();
59 return Status::Success;
60}
61
62void IOptimizedNetwork::Destroy(IOptimizedNetwork* network)
63{
64 delete boost::polymorphic_downcast<OptimizedNetwork*>(network);
65}
66
67Status OptimizedNetwork::PrintGraph()
68{
69 m_Graph->Print();
70 return Status::Success;
71}
72
surmeh01bceff2f2018-03-29 16:29:27 +010073Status OptimizedNetwork::SerializeToDot(std::ostream& stream) const
74{
75 return m_Graph->SerializeToDot(stream);
76}
77
Matteo Martincigh49124022019-01-11 13:25:59 +000078
Matteo Martincigh49124022019-01-11 13:25:59 +000079
80void ReportError(const std::string& errorMessage,
81 Optional<std::vector<std::string>&> errorMessages)
82{
83 std::stringstream fullErrorMessage;
84 fullErrorMessage << "ERROR: " << errorMessage;
85 BOOST_LOG_TRIVIAL(warning) << fullErrorMessage.str();
86 if (errorMessages)
87 {
88 errorMessages.value().push_back(fullErrorMessage.str());
89 }
90}
91
92void ReportWarning(const std::string& warningMessage,
93 Optional<std::vector<std::string>&> warningMessages)
94{
95 std::stringstream fullWarningMessage;
96 fullWarningMessage << "WARNING: " << warningMessage;
97 BOOST_LOG_TRIVIAL(warning) << fullWarningMessage.str();
98 if (warningMessages)
99 {
100 warningMessages.value().push_back(fullWarningMessage.str());
101 }
102}
103
jimfly016b0b53d2018-10-08 14:43:01 +0100104bool CheckScaleSetOnQuantizedType(Layer* layer, Optional<std::vector<std::string>&> errMessages)
105{
106 bool noErrors = true;
107 unsigned int numOutputs = layer->GetNumOutputSlots();
108 for (unsigned int i = 0; i < numOutputs; i++) {
David Monahanb8554702019-04-25 16:03:38 +0100109 OutputSlot& outputSlot = layer->GetOutputSlot(i);
110 TensorInfo info = outputSlot.GetTensorInfo();
jimfly016b0b53d2018-10-08 14:43:01 +0100111 if (DataType::QuantisedAsymm8 == info.GetDataType()) {
112 if (0.f == info.GetQuantizationScale()) {
113 noErrors = false;
114 std::stringstream ss;
Matteo Martincigh49124022019-01-11 13:25:59 +0000115 ss << "output " << i << " of layer " << GetLayerTypeAsCString(layer->GetType())
jimfly016b0b53d2018-10-08 14:43:01 +0100116 << " (" << layer->GetNameStr() << ") is of type"
117 << " Quantized 8 bit but its scale parameter has not been set";
Matteo Martincigh49124022019-01-11 13:25:59 +0000118 ReportError(ss.str(), errMessages);
jimfly016b0b53d2018-10-08 14:43:01 +0100119 }
David Monahanb8554702019-04-25 16:03:38 +0100120 // Softmax under QuantisedAsymm8 must always be scale (1.0f/256.0f) and offset 0
121 if ((info.GetQuantizationScale() != (1.0f / 256.0f) ||
122 info.GetQuantizationOffset() != 0) &&
123 layer->GetType() == armnn::LayerType::Softmax)
124 {
125 std::stringstream ss;
126 ss << "Quantization parameters for Softmax layer (Scale: " <<
127 info.GetQuantizationScale() << " and Offset: " << info.GetQuantizationOffset() <<
128 ") are incorrect and have been updated to Scale: 0.00390625 and Offset: 0";
129 BOOST_LOG_TRIVIAL(warning) << ss.str();
130 info.SetQuantizationScale((1.0f /256.0f));
131 info.SetQuantizationOffset(0);
132 outputSlot.SetTensorInfo(info);
133 }
jimfly016b0b53d2018-10-08 14:43:01 +0100134 }
135 }
136 return noErrors;
137}
138
Matteo Martincigh49124022019-01-11 13:25:59 +0000139OptimizationResult AssignBackends(OptimizedNetwork* optNetObjPtr,
140 BackendSettings& backendSettings,
141 Graph::Iterator& firstLayer,
142 Graph::Iterator& lastLayer,
143 Optional<std::vector<std::string>&> errMessages)
telsoa014fcda012018-03-09 14:13:49 +0000144{
Matteo Martincigh49124022019-01-11 13:25:59 +0000145 OptimizationResult result;
telsoa014fcda012018-03-09 14:13:49 +0000146
Matteo Martincigh49124022019-01-11 13:25:59 +0000147 // Helper lambda to compose meaningful error message before returning with error
148 auto ReturnWithError = [&](const Layer* layer)
telsoa01c577f2c2018-08-31 09:22:23 +0100149 {
jimfly016b0b53d2018-10-08 14:43:01 +0100150 std::stringstream failureMsg;
Matteo Martincigh49124022019-01-11 13:25:59 +0000151 failureMsg << "Layer of type " << GetLayerTypeAsCString(layer->GetType())
152 << " is not supported on any preferred backend " << backendSettings.m_PreferredBackends;
153 ReportError(failureMsg.str(), errMessages);
154
155 result.m_Error = true;
156 return result;
telsoa01c577f2c2018-08-31 09:22:23 +0100157 };
158
Matteo Martincigh49124022019-01-11 13:25:59 +0000159 auto availablePreferredBackends = backendSettings.GetAvailablePreferredBackends();
160 if (availablePreferredBackends.empty())
telsoa01c577f2c2018-08-31 09:22:23 +0100161 {
Matteo Martincigh49124022019-01-11 13:25:59 +0000162 std::stringstream failureMsg;
163 failureMsg << "No preferred backends are available";
164 ReportError(failureMsg.str(), errMessages);
165
166 result.m_Error = true;
167 return result;
168 }
169
170 for (auto it = firstLayer; it != lastLayer; ++it)
171 {
172 auto layer = *it;
telsoa01c577f2c2018-08-31 09:22:23 +0100173 DataType dataType = layer->GetDataType();
174 std::string reasonIfUnsupported;
175 bool found = false;
jimfly016b0b53d2018-10-08 14:43:01 +0100176 if (!CheckScaleSetOnQuantizedType(layer, errMessages))
177 {
178 // don't bomb immediately, find all the quantized outputs
179 // which haven't had a scale set and report them all back.
Matteo Martincigh49124022019-01-11 13:25:59 +0000180 result.m_Error = true;
jimfly016b0b53d2018-10-08 14:43:01 +0100181 }
Matteo Martincigh49124022019-01-11 13:25:59 +0000182
David Beckf0b48452018-10-19 15:20:56 +0100183 for (const auto& backend : availablePreferredBackends)
telsoa01c577f2c2018-08-31 09:22:23 +0100184 {
185 // need to set the compute device on the layer
186 // before we can check if it is supported
David Beck33f0ae02018-10-18 15:13:56 +0100187 layer->SetBackendId(backend);
telsoa01c577f2c2018-08-31 09:22:23 +0100188 if (!IWorkloadFactory::IsLayerSupported(*layer, dataType, reasonIfUnsupported))
189 {
190 if (dataType == DataType::Float16)
191 {
192 if (IWorkloadFactory::IsLayerSupported(*layer, DataType::Float32, reasonIfUnsupported)
193 && layer->GetType() != LayerType::ConvertFp32ToFp16
194 && layer->GetType() != LayerType::ConvertFp16ToFp32)
195 {
196 // Insert FP16 -> FP32 conversion layer before current layer
197 std::vector<ConvertFp16ToFp32Layer*> convertFp16ToFp32Layers =
198 InsertConvertFp16ToFp32LayersBefore(optNetObjPtr->GetGraph(), *layer);
199
200 // Insert FP32 -> FP16 conversion layer after current layer
201 std::vector<ConvertFp32ToFp16Layer*> convertFp32ToFp16Layers =
202 InsertConvertFp32ToFp16LayersAfter(optNetObjPtr->GetGraph(), *layer);
203
204 // Assign a supported backend to the newly introduced conversion layers
David Beckf0b48452018-10-19 15:20:56 +0100205 auto AssignFirstSupportedBackend = [&](Layer* layer, BackendId preferredBackend)
telsoa01c577f2c2018-08-31 09:22:23 +0100206 {
207 bool supportedBackendFound = false;
208 std::string reasonIfUnsupported;
209
210 // Try preferred backend first
David Beck33f0ae02018-10-18 15:13:56 +0100211 layer->SetBackendId(preferredBackend);
David Beck29c75de2018-10-23 13:35:58 +0100212 if (IWorkloadFactory::IsLayerSupported(*layer,
213 EmptyOptional(),
214 reasonIfUnsupported))
telsoa01c577f2c2018-08-31 09:22:23 +0100215 {
216 supportedBackendFound = true;
217 }
218 else
219 {
David Beckf0b48452018-10-19 15:20:56 +0100220 for (const auto& backend : availablePreferredBackends)
telsoa01c577f2c2018-08-31 09:22:23 +0100221 {
222 // Skip preferred backend (we already determined that it is not supported)
223 if (backend == preferredBackend)
224 {
225 continue;
226 }
227
David Beck33f0ae02018-10-18 15:13:56 +0100228 layer->SetBackendId(backend);
David Beck29c75de2018-10-23 13:35:58 +0100229 if (IWorkloadFactory::IsLayerSupported(*layer,
230 EmptyOptional(),
231 reasonIfUnsupported))
telsoa01c577f2c2018-08-31 09:22:23 +0100232 {
233 supportedBackendFound = true;
234 break;
235 }
236 }
237 }
238
239 return supportedBackendFound;
240 };
241
242 for (ConvertFp16ToFp32Layer* convertLayer : convertFp16ToFp32Layers)
243 {
244 if (!AssignFirstSupportedBackend(convertLayer, backend))
245 {
246 return ReturnWithError(convertLayer);
247 }
248 }
249
250 for (ConvertFp32ToFp16Layer* convertLayer : convertFp32ToFp16Layers)
251 {
252 if (!AssignFirstSupportedBackend(convertLayer, backend))
253 {
254 return ReturnWithError(convertLayer);
255 }
256 }
257
258 found = true;
259 break;
260 }
261 }
jimfly016b0b53d2018-10-08 14:43:01 +0100262 std::stringstream warningMsg;
Matteo Martincigh49124022019-01-11 13:25:59 +0000263 warningMsg << "Layer of type " << GetLayerTypeAsCString(layer->GetType())
David Beck33f0ae02018-10-18 15:13:56 +0100264 << " is not supported on requested backend " << layer->GetBackendId().Get()
jimfly016b0b53d2018-10-08 14:43:01 +0100265 << " for data type " << GetDataTypeName(dataType)
266 << " (reason: " << reasonIfUnsupported
267 << "), falling back to the next backend.";
Matteo Martincigh49124022019-01-11 13:25:59 +0000268 ReportWarning(warningMsg.str(), errMessages);
telsoa01c577f2c2018-08-31 09:22:23 +0100269 }
270 else
271 {
272 found = true;
Matteo Martincigh49124022019-01-11 13:25:59 +0000273 backendSettings.m_SelectedBackends.insert(backend);
telsoa01c577f2c2018-08-31 09:22:23 +0100274 break;
275 }
276 }
277
278 // If the layer is unsupported by any devices, log and return a null network.
Matteo Martincigh49124022019-01-11 13:25:59 +0000279 if (!found)
280 {
telsoa01c577f2c2018-08-31 09:22:23 +0100281 // NOTE: if the layer is not an operation queue type AND we have not got CpuRef as a
282 // fallback we should set the compute device on the layer to CpuRef (these are not
283 // available as accelerated operations, or are only available under certain
284 // conditions, currently they comprise MemCopy, Constant, Permute)
285 armnn::LayerType layerType = layer->GetType();
Matteo Martincigh49124022019-01-11 13:25:59 +0000286 if (!backendSettings.IsCpuRefUsed() && (layerType == armnn::LayerType::MemCopy ||
287 layerType == armnn::LayerType::Constant ||
288 layerType == armnn::LayerType::Permute))
telsoa01c577f2c2018-08-31 09:22:23 +0100289 {
Matteo Martincigh49124022019-01-11 13:25:59 +0000290 BackendId cpuBackendId(armnn::Compute::CpuRef);
291 layer->SetBackendId(cpuBackendId);
292 backendSettings.m_SelectedBackends.insert(cpuBackendId);
telsoa01c577f2c2018-08-31 09:22:23 +0100293 }
294 else
295 {
296 return ReturnWithError(layer);
297 }
298 }
299 }
Matteo Martincigh49124022019-01-11 13:25:59 +0000300
301 return result;
302}
303
Matteo Martincighadddddb2019-01-24 14:06:23 +0000304OptimizationResult AssignBackends(OptimizedNetwork* optNetObjPtr,
305 BackendSettings& backendSettings,
Derek Lambertiff05cc52019-04-26 13:05:17 +0100306 SubgraphView& subgraph,
Matteo Martincighadddddb2019-01-24 14:06:23 +0000307 Optional<std::vector<std::string>&> errMessages)
Matteo Martincigh49124022019-01-11 13:25:59 +0000308{
Derek Lambertiff05cc52019-04-26 13:05:17 +0100309 Graph::Iterator firstLayer = subgraph.begin();
310 Graph::Iterator lastLayer = subgraph.end();
Matteo Martincighadddddb2019-01-24 14:06:23 +0000311 return AssignBackends(optNetObjPtr,
312 backendSettings,
313 firstLayer,
314 lastLayer,
315 errMessages);
316}
317
Derek Lamberti84da38b2019-06-13 11:40:08 +0100318BackendsMap CreateSupportedBackends(TensorHandleFactoryRegistry& handleFactoryRegistry,
319 BackendSettings& backendSettings)
320{
321 BackendsMap backends;
322 auto const& backendRegistry = BackendRegistryInstance();
323 for (auto&& selectedBackend : backendSettings.m_SupportedBackends)
324 {
325 auto backendFactory = backendRegistry.GetFactory(selectedBackend);
326 auto backendObjPtr = backendFactory();
327 BOOST_ASSERT(backendObjPtr);
328
329 backendObjPtr->RegisterTensorHandleFactories(handleFactoryRegistry);
330
331 backends[backendObjPtr->GetId()] = std::move(backendObjPtr);
332 }
333
334 return backends;
335}
336
Matteo Martincighadddddb2019-01-24 14:06:23 +0000337OptimizationResult ApplyBackendOptimizations(OptimizedNetwork* optNetObjPtr,
338 BackendSettings& backendSettings,
Derek Lamberti84da38b2019-06-13 11:40:08 +0100339 BackendsMap& backends,
Matteo Martincighadddddb2019-01-24 14:06:23 +0000340 Optional<std::vector<std::string>&> errMessages)
341{
342 BOOST_ASSERT(optNetObjPtr);
Matteo Martincigh49124022019-01-11 13:25:59 +0000343
344 OptimizationResult result;
345
Matteo Martincighadddddb2019-01-24 14:06:23 +0000346 // Get the optimized graph
347 Graph& optGraph = optNetObjPtr->GetGraph();
Matteo Martincigh49124022019-01-11 13:25:59 +0000348
Matteo Martincighadddddb2019-01-24 14:06:23 +0000349 // Get the entire graph as a sub-graph
Derek Lambertiff05cc52019-04-26 13:05:17 +0100350 SubgraphView mainSubgraph(optGraph);
Matteo Martincigh49124022019-01-11 13:25:59 +0000351
Matteo Martincighadddddb2019-01-24 14:06:23 +0000352 // Run backend specific optimizations
Matteo Martincighadddddb2019-01-24 14:06:23 +0000353 for (auto&& selectedBackend : backendSettings.m_SelectedBackends)
Matteo Martincigh49124022019-01-11 13:25:59 +0000354 {
Derek Lamberti84da38b2019-06-13 11:40:08 +0100355 auto backendObjPtr = backends.find(selectedBackend)->second.get();
Matteo Martincighadddddb2019-01-24 14:06:23 +0000356 BOOST_ASSERT(backendObjPtr);
357
358 // Select sub-graphs based on backend
Derek Lambertiff05cc52019-04-26 13:05:17 +0100359 SubgraphViewSelector::Subgraphs subgraphs =
360 SubgraphViewSelector::SelectSubgraphs(mainSubgraph,
Matteo Martincigh602af092019-05-01 10:31:27 +0100361 // Select layers assigned to the requested backend
362 [&backendObjPtr](const Layer& layer)
363 {
364 return layer.GetType() != LayerType::Input &&
365 layer.GetType() != LayerType::Output &&
366 layer.GetBackendId() == backendObjPtr->GetId();
367 });
Derek Lambertiff05cc52019-04-26 13:05:17 +0100368 if (subgraphs.empty())
Matteo Martincigh49124022019-01-11 13:25:59 +0000369 {
Matteo Martincighadddddb2019-01-24 14:06:23 +0000370 // No sub-graphs found, try with next selected backend
371 continue;
Matteo Martincigh49124022019-01-11 13:25:59 +0000372 }
Matteo Martincighadddddb2019-01-24 14:06:23 +0000373
374 // Try to optimize each sub-graph
Derek Lambertiff05cc52019-04-26 13:05:17 +0100375 for (auto& subgraph : subgraphs)
Matteo Martincigh49124022019-01-11 13:25:59 +0000376 {
Matteo Martincighadddddb2019-01-24 14:06:23 +0000377 // Try to optimize the current sub-graph
Matteo Martincigh84924332019-05-09 12:46:16 +0100378 OptimizationViews optimizationViews = backendObjPtr->OptimizeSubgraphView(*subgraph);
379 BOOST_ASSERT(optimizationViews.Validate(*subgraph));
Matteo Martincighadddddb2019-01-24 14:06:23 +0000380
381 // Optimization attempted, check the resulting optimized sub-graph
Matteo Martincigh84924332019-05-09 12:46:16 +0100382 for (auto& substitution : optimizationViews.GetSubstitutions())
Matteo Martincighadddddb2019-01-24 14:06:23 +0000383 {
384 // Sub-graph optimized, substitute the sub-graph with the new optimized one in the main optimized graph
Matteo Martincigh84924332019-05-09 12:46:16 +0100385 SubgraphView& replacementSubgraph = substitution.m_ReplacementSubgraph;
386 SubgraphView& substitutableSubgraph = substitution.m_SubstitutableSubgraph;
387 optGraph.SubstituteSubgraph(substitutableSubgraph, replacementSubgraph);
Matteo Martincighadddddb2019-01-24 14:06:23 +0000388
389 // Assign the current backend to the optimized sub-graph
Matteo Martincigh84924332019-05-09 12:46:16 +0100390 std::for_each(replacementSubgraph.begin(), replacementSubgraph.end(), [&selectedBackend](Layer* l)
Derek Lambertic2fe5fb2019-05-08 10:23:08 +0100391 {
392 BOOST_ASSERT(l);
393 l->SetBackendId(selectedBackend);
394 });
Matteo Martincighadddddb2019-01-24 14:06:23 +0000395 }
Derek Lambertic2fe5fb2019-05-08 10:23:08 +0100396
Matteo Martincigh84924332019-05-09 12:46:16 +0100397 if (!optimizationViews.GetFailedSubgraphs().empty())
Matteo Martincighadddddb2019-01-24 14:06:23 +0000398 {
Matteo Martincighadddddb2019-01-24 14:06:23 +0000399 std::stringstream warningMsg;
Derek Lambertic2fe5fb2019-05-08 10:23:08 +0100400 warningMsg << "Some sub-graph(s) failed to optimized on " << backendObjPtr->GetId() << " backend.";
Matteo Martincighadddddb2019-01-24 14:06:23 +0000401 ReportWarning(warningMsg.str(), errMessages);
402
403 // Failed to optimize the given sub-graph, re-assign the sub-graph layers to other available backends
Derek Lambertic2fe5fb2019-05-08 10:23:08 +0100404 BackendSettings settingsCopy(backendSettings);
Matteo Martincighadddddb2019-01-24 14:06:23 +0000405 if (!backendObjPtr->GetId().IsCpuRef())
406 {
407 // Add the current backend to the list of backends to ignore
Derek Lambertic2fe5fb2019-05-08 10:23:08 +0100408 settingsCopy.m_IgnoredBackends.insert(backendObjPtr->GetId());
Matteo Martincighadddddb2019-01-24 14:06:23 +0000409 }
Derek Lambertic2fe5fb2019-05-08 10:23:08 +0100410
411 int count=0;
Matteo Martincigh84924332019-05-09 12:46:16 +0100412 for (auto& failedSubgraph : optimizationViews.GetFailedSubgraphs())
Matteo Martincighadddddb2019-01-24 14:06:23 +0000413 {
Derek Lambertic2fe5fb2019-05-08 10:23:08 +0100414 // An error occurred: the optimization was attempted but not performed, try different backends
415 std::stringstream subgraphMsg;
416 subgraphMsg << "Re-assigning backends to " << failedSubgraph.GetLayers().size()
417 << " layers inside sub-graph " << count++;
418 ReportWarning(warningMsg.str(), errMessages);
419
420 OptimizationResult reassignmentResult = AssignBackends(optNetObjPtr,
421 settingsCopy,
422 *subgraph,
423 errMessages);
424 if (reassignmentResult.m_Error)
425 {
426 // Failed to re-assign one of the remaining backends to each layer of the sub-graph
427 result.m_Error = true;
428 return result;
429 }
Matteo Martincighadddddb2019-01-24 14:06:23 +0000430 }
Matteo Martincigh49124022019-01-11 13:25:59 +0000431 }
432 }
433 }
434
435 return result;
436}
437
Derek Lamberti84da38b2019-06-13 11:40:08 +0100438bool RequiresCopy(ITensorHandleFactory::FactoryId src,
439 ITensorHandleFactory::FactoryId dst,
440 TensorHandleFactoryRegistry& registry)
441{
442 if (src != dst)
443 {
444 ITensorHandleFactory* srcFactory = registry.GetFactory(src);
445 ITensorHandleFactory* dstFactory = registry.GetFactory(dst);
446
447 if (srcFactory->SupportsExport() && dstFactory->SupportsImport())
448 {
449 return false;
450 }
451 return true;
452 }
453 return false;
454}
455
456// Find the handle factory for the input layer which results in fewest required copies.
457ITensorHandleFactory::FactoryId CalculateSlotOptionForInput(BackendsMap& backends,
458 OutputSlot& slot,
459 TensorHandleFactoryRegistry& registry)
460{
461 Layer& layer = slot.GetOwningLayer();
462 BOOST_ASSERT(layer.GetType() == LayerType::Input);
463
464 // Explicitly select the tensorhandle factory for InputLayer because the rules for it are slightly different. It
465 // doesn't matter which backend it is assigned to because they all use the same implementation, which
466 // requires Map/Unmap support. This means that, so long as the handle type supports map/unmap semantics, we can
467 // select a factory with maximum compatibility with the layers connected to the InputLayer.
468
469 // First ensure the from backends can support the TensorHandeAPI
470 auto frmBackend = backends.find(layer.GetBackendId());
471 if (frmBackend == backends.end() ||
472 !frmBackend->second->SupportsTensorAllocatorAPI())
473 {
474 return ITensorHandleFactory::LegacyFactoryId;
475 }
476
477 // Go through all connections to the output slot and determine the TensorHandleFactory which results in the
478 // fewest copies.
479 std::map<ITensorHandleFactory::FactoryId, int> factoryScores;
480 int topScore = 0;
481 ITensorHandleFactory::FactoryId topChoice = ITensorHandleFactory::LegacyFactoryId;
482
483 for (auto&& connection : slot.GetConnections())
484 {
485 const Layer& connectedLayer = connection->GetOwningLayer();
486
487 auto toBackend = backends.find(connectedLayer.GetBackendId());
488 BOOST_ASSERT_MSG(toBackend != backends.end(), "Backend id not found for the connected layer");
489
490 if (!toBackend->second.get()->SupportsTensorAllocatorAPI())
491 {
492 // The destination backend does not support the tensor allocator API, move to the next one
493 continue;
494 }
495
496 auto dstPrefs = toBackend->second.get()->GetHandleFactoryPreferences();
497 for (auto&& dst : dstPrefs)
498 {
499 // Input layers use the mem copy workload, so the selected factory must support map/unmap API
500 ITensorHandleFactory* factory = registry.GetFactory(dst);
501 if (!factory->SupportsMapUnmap())
502 {
503 // The current tensor handle factory does not support the map/unmap strategy, move to the next one
504 continue;
505 }
506
507 auto it = factoryScores.find(dst);
508 if (it == factoryScores.end())
509 {
510 // Add new score to the table
511 factoryScores[dst] = 0;
512 if (topChoice == ITensorHandleFactory::LegacyFactoryId)
513 {
514 topChoice = dst;
515 }
516 }
517 else
518 {
519 // Increase the score
520 factoryScores[dst]++;
521
522 // Track the best option
523 if (factoryScores[dst] > topScore)
524 {
525 topScore = factoryScores[dst];
526 topChoice = dst;
527 }
528 }
529 }
530 }
531
532 return topChoice;
533}
534
535// Find the handle factory for the output layer which results in fewest required copies.
536ITensorHandleFactory::FactoryId CalculateSlotOptionForOutput(BackendsMap& backends,
537 OutputSlot& slot,
538 TensorHandleFactoryRegistry& registry)
539{
540 return ITensorHandleFactory::DeferredFactoryId;
541}
542
543// For all handle factories supported on the source backend, we wish to find the one which requires the fewest copies
544// when considering all connections.
545ITensorHandleFactory::FactoryId CalculateSlotOption(BackendsMap& backends,
546 OutputSlot& outputSlot,
547 TensorHandleFactoryRegistry& registry)
548{
549 // First ensure the from backends can support the TensorHandeAPI
550 Layer& layer = outputSlot.GetOwningLayer();
551 auto frmBackend = backends.find(layer.GetBackendId());
552 if (frmBackend == backends.end() ||
553 !frmBackend->second->SupportsTensorAllocatorAPI())
554 {
555 return ITensorHandleFactory::LegacyFactoryId;
556 }
557
558 // Connections to Output Layers requires support for map/unmap on the TensorHandle.
559 bool requiresMapUnmap = false;
560 for (auto&& connection : outputSlot.GetConnections())
561 {
562 const Layer& connectedLayer = connection->GetOwningLayer();
563 if (connectedLayer.GetType() == LayerType::Output)
564 {
565 requiresMapUnmap = true;
566 }
567 }
568
569 IBackendInternal* srcBackend = frmBackend->second.get();
570 auto srcPrefs = srcBackend->GetHandleFactoryPreferences();
571
572 // Initialize the scores
573 std::map<ITensorHandleFactory::FactoryId, int> factoryScores;
574 for (auto&& pref : srcPrefs)
575 {
576 if (requiresMapUnmap) // Only consider factories that support map/unmap if required
577 {
578 ITensorHandleFactory* factory = registry.GetFactory(pref);
579 if (!factory->SupportsMapUnmap())
580 {
581 // The current tensor handle factory does not support the map/unmap strategy, move to the next one
582 continue;
583 }
584 }
585
586 auto it = factoryScores.find(pref);
587 if (it == factoryScores.end())
588 {
589 // Add new score to the table
590 factoryScores[pref] = 0;
591 }
592 }
593
594 // Score each handle factory based on how many times it requires copies on the slot connections
595 for (auto&& connection : outputSlot.GetConnections())
596 {
597 const Layer& connectedLayer = connection->GetOwningLayer();
598
599 auto toBackend = backends.find(connectedLayer.GetBackendId());
600 BOOST_ASSERT_MSG(toBackend != backends.end(), "Backend id not found for the connected layer");
601
602 auto dstPrefs = toBackend->second.get()->GetHandleFactoryPreferences();
603 for (auto&& src : srcPrefs)
604 {
605 if (factoryScores.find(src) == factoryScores.end()) // Don't consider excluded factories
606 {
607 continue;
608 }
609
610 for (auto&& dst : dstPrefs)
611 {
612 if (RequiresCopy(src, dst, registry))
613 {
614 // Copy avoided, increase the score
615 factoryScores[src]++;
616 break;
617 }
618 }
619 }
620 }
621
622 // Find the lowest score
623 int minScore = std::numeric_limits<int>::max();
624 for (auto it : factoryScores)
625 {
626 minScore = std::min(minScore, it.second);
627 }
628
629 // Collect factories matching the best(lowest) score
630 std::vector<ITensorHandleFactory::FactoryId> optimalFactories;
631 for (auto it : factoryScores)
632 {
633 if (it.second == minScore)
634 {
635 optimalFactories.push_back(it.first);
636 }
637 }
638
639 // For all compatible Factories matching the best score, find the preferred one for the current layer.
640 for (auto&& srcPref : srcPrefs)
641 {
642 for (auto&& comp : optimalFactories)
643 {
644 if (comp == srcPref)
645 {
646 return comp;
647 }
648 }
649 }
650
651 return ITensorHandleFactory::LegacyFactoryId;
652}
653
654MemoryStrategy CalculateStrategy(BackendsMap& backends,
655 ITensorHandleFactory::FactoryId srcFactoryId,
656 const Layer& layer,
657 const Layer& connectedLayer,
658 TensorHandleFactoryRegistry& registry)
659{
660 auto toBackend = backends.find(connectedLayer.GetBackendId());
661 BOOST_ASSERT_MSG(toBackend != backends.end(), "Backend id not found for the connected layer");
662
663 auto dstPrefs = toBackend->second.get()->GetHandleFactoryPreferences();
664
665 // Legacy API check for backward compatibility
666 if (srcFactoryId == ITensorHandleFactory::LegacyFactoryId || dstPrefs.empty())
667 {
668 if (layer.GetBackendId() != connectedLayer.GetBackendId())
669 {
670 return MemoryStrategy::CopyToTarget;
671 }
672 else
673 {
674 return MemoryStrategy::DirectCompatibility;
675 }
676 }
677
678 // TensorHandleFactory API present, so perform more sophisticated strategies.
679 // Dst Output layers don't require copy because they use map/unmap
680 if (connectedLayer.GetType() == LayerType::Output)
681 {
682 return MemoryStrategy::DirectCompatibility;
683 }
684
685 // Search for direct match in prefs
686 for (auto&& pref : dstPrefs)
687 {
688 if (pref == srcFactoryId)
689 {
690 return MemoryStrategy::DirectCompatibility;
691 }
692 }
693
694 // Search for export/import options
695 ITensorHandleFactory* srcFactory = registry.GetFactory(srcFactoryId);
696 if (srcFactory->SupportsExport())
697 {
698 for (auto&& pref : dstPrefs)
699 {
700 ITensorHandleFactory* dstFactory = registry.GetFactory(pref);
701 if (dstFactory->SupportsImport())
702 {
703 return MemoryStrategy::ExportToTarget;
704 }
705 }
706 }
707
708 // Search for copy options via map/unmap
709 if (srcFactory->SupportsMapUnmap())
710 {
711 for (auto&& pref : dstPrefs)
712 {
713 ITensorHandleFactory* dstFactory = registry.GetFactory(pref);
714 if (dstFactory->SupportsMapUnmap())
715 {
716 return MemoryStrategy::CopyToTarget;
717 }
718 }
719 }
720
721 return MemoryStrategy::Undefined;
722}
723
724// Select the TensorHandleFactories and the corresponding memory strategy
725OptimizationResult SelectTensorHandleStrategy(Graph& optGraph,
726 BackendsMap& backends,
727 TensorHandleFactoryRegistry& registry,
728 Optional<std::vector<std::string>&> errMessages)
729{
730 OptimizationResult result;
731
732 optGraph.ForEachLayer([&backends, &registry, &result, &errMessages](Layer* layer)
733 {
734 BOOST_ASSERT(layer);
735
736 // Lets make sure the backend is in our list of supported backends. Something went wrong during backend
737 // assignment if this check fails
738 BOOST_ASSERT(backends.find(layer->GetBackendId()) != backends.end());
739
740 // Check each output separately
741 for (unsigned int slotIdx = 0; slotIdx < layer->GetNumOutputSlots(); slotIdx++)
742 {
743 OutputSlot& outputSlot = layer->GetOutputSlot(slotIdx);
744
745 ITensorHandleFactory::FactoryId slotOption = ITensorHandleFactory::LegacyFactoryId;
746
747 // Calculate the factory to use which results in the fewest copies being made.
748 switch(layer->GetType())
749 {
750 case LayerType::Input:
751 slotOption = CalculateSlotOptionForInput(backends, outputSlot, registry);
752 break;
753 case LayerType::Output:
754 slotOption = CalculateSlotOptionForOutput(backends, outputSlot, registry);
755 break;
756 default:
757 slotOption = CalculateSlotOption(backends, outputSlot, registry);
758 break;
759 }
760 outputSlot.SetTensorHandleFactory(slotOption);
761
762 // Now determine the "best" memory strategy for each connection given the slotOption.
763 unsigned int connectionIdx = 0;
764 for (auto&& connection : outputSlot.GetConnections())
765 {
766 const Layer& connectedLayer = connection->GetOwningLayer();
767
768 MemoryStrategy strategy = CalculateStrategy(backends, slotOption, *layer, connectedLayer, registry);
769
770 if (strategy == MemoryStrategy::Undefined)
771 {
772 result.m_Error = true;
773 if (errMessages)
774 {
775 errMessages.value().emplace_back("Could not find valid strategy required for compatibility"
776 " between backends.");
777 }
778 return;
779 }
780
781 outputSlot.SetMemoryStrategy(connectionIdx, strategy);
782
783 connectionIdx++;
784 }
785 }
786 });
787
788 return result;
789}
790
Matteo Martincigh49124022019-01-11 13:25:59 +0000791IOptimizedNetworkPtr Optimize(const INetwork& inNetwork,
792 const std::vector<BackendId>& backendPreferences,
793 const IDeviceSpec& deviceSpec,
794 const OptimizerOptions& options,
795 Optional<std::vector<std::string>&> errMessages)
796{
797 if (backendPreferences.empty())
798 {
799 throw armnn::InvalidArgumentException("Invoked Optimize with no backends specified");
800 }
801
802 const Network& network = *boost::polymorphic_downcast<const Network*>(&inNetwork);
803 std::unique_ptr<Graph> graph = std::make_unique<Graph>(network.GetGraph());
804
805 auto optNet = IOptimizedNetworkPtr(new OptimizedNetwork(std::move(graph)), &IOptimizedNetwork::Destroy);
806
807 OptimizedNetwork* optNetObjPtr = boost::polymorphic_downcast<OptimizedNetwork*>(optNet.get());
808
Matteo Martincighadddddb2019-01-24 14:06:23 +0000809 // Get the optimized graph
810 Graph& optGraph = optNetObjPtr->GetGraph();
811
Matteo Martincigh49124022019-01-11 13:25:59 +0000812 // Perform optimisation passes
813 using namespace optimizations;
Matteo Martincighadddddb2019-01-24 14:06:23 +0000814 Optimizer::Pass(optGraph, MakeOptimizations(SquashEqualPermuteSiblings(),
815 SquashEqualReshapeSiblings(),
816 OptimizeInversePermutes(),
817 MovePermuteUp(),
818 PermuteAsReshape(),
Nina Drozd861985f2019-04-18 14:48:51 +0100819 OptimizeConsecutiveReshapes(),
820 FoldPadIntoConvolution2d()));
Matteo Martincigh49124022019-01-11 13:25:59 +0000821
Matteo Martincighadddddb2019-01-24 14:06:23 +0000822 // Infer the tensor infos for all output slots. Throws an exception on failure
823 optGraph.InferTensorInfos();
Matteo Martincigh49124022019-01-11 13:25:59 +0000824
825 // If Fp32 to Fp16 optimization is set convert Fp32 network to Fp16
826 if (options.m_ReduceFp32ToFp16)
827 {
Matteo Martincighadddddb2019-01-24 14:06:23 +0000828 Optimizer::Pass(optGraph, MakeOptimizations(Fp32NetworkToFp16Converter()));
Matteo Martincigh49124022019-01-11 13:25:59 +0000829 }
830
831 // Initialize backend settings
832 BackendSettings backendSettings(backendPreferences, deviceSpec);
833 if (backendSettings.GetAvailablePreferredBackends().empty())
834 {
835 std::stringstream failureMsg;
836 failureMsg << "None of the preferred backends " << backendPreferences
837 << " are supported. Current platform provides " << backendSettings.m_SupportedBackends;
838 ReportError(failureMsg.str(), errMessages);
839 return IOptimizedNetworkPtr(nullptr, &IOptimizedNetwork::Destroy);
840 }
841
Derek Lamberti84da38b2019-06-13 11:40:08 +0100842 // Create a map to temporarily hold initialized backend objects
843 TensorHandleFactoryRegistry tensorHandleFactoryRegistry;
844 BackendsMap backends = CreateSupportedBackends(tensorHandleFactoryRegistry, backendSettings);
845
Matteo Martincigh49124022019-01-11 13:25:59 +0000846 // Assign an available backend to each layer
Matteo Martincighadddddb2019-01-24 14:06:23 +0000847 Graph::Iterator firstLayer = optGraph.begin();
848 Graph::Iterator lastLayer = optGraph.end();
Derek Lamberti84da38b2019-06-13 11:40:08 +0100849 OptimizationResult assignBackendsResult = AssignBackends(optNetObjPtr,
850 backendSettings,
851 firstLayer,
852 lastLayer,
853 errMessages);
854 if (assignBackendsResult.m_Error)
Matteo Martincigh49124022019-01-11 13:25:59 +0000855 {
856 // Failed to assign a backend to each layer
jimfly016b0b53d2018-10-08 14:43:01 +0100857 return IOptimizedNetworkPtr(nullptr, &IOptimizedNetwork::Destroy);
858 }
telsoa01c577f2c2018-08-31 09:22:23 +0100859
Matteo Martincighadddddb2019-01-24 14:06:23 +0000860 Optimizer::Pass(optGraph, MakeOptimizations(OptimizeInverseConversionsFp16(),
861 OptimizeInverseConversionsFp32()));
telsoa01c577f2c2018-08-31 09:22:23 +0100862
Matteo Martincighadddddb2019-01-24 14:06:23 +0000863 // Apply the backend-specific optimizations
864 OptimizationResult backendOptimizationResult = ApplyBackendOptimizations(optNetObjPtr,
865 backendSettings,
Derek Lamberti84da38b2019-06-13 11:40:08 +0100866 backends,
Matteo Martincighadddddb2019-01-24 14:06:23 +0000867 errMessages);
868 if (backendOptimizationResult.m_Error)
Matteo Martincigh49124022019-01-11 13:25:59 +0000869 {
Matteo Martincighadddddb2019-01-24 14:06:23 +0000870 // Failed to apply the backend-specific optimizations
871 return IOptimizedNetworkPtr(nullptr, &IOptimizedNetwork::Destroy);
Matteo Martincigh49124022019-01-11 13:25:59 +0000872 }
873
Matteo Martincighadddddb2019-01-24 14:06:23 +0000874 // If the debug flag is set, then insert a DebugLayer after each layer
875 // Doing this after applying the backend optimizations as they might have changed some layers
876 if (options.m_Debug)
877 {
878 Optimizer::Pass(optGraph, MakeOptimizations(InsertDebugLayer()));
879 }
880
Derek Lamberti84da38b2019-06-13 11:40:08 +0100881 // Calculate the compatibility strategies for tensor handles
882 OptimizationResult strategyResult = SelectTensorHandleStrategy(optGraph,
883 backends,
884 tensorHandleFactoryRegistry,
885 errMessages);
886 if (strategyResult.m_Error)
887 {
888 // Failed to apply the backend-specific optimizations
889 return IOptimizedNetworkPtr(nullptr, &IOptimizedNetwork::Destroy);
890 }
891
892 // Based on the tensor handle strategy determined above, insert copy layers where required.
893 optGraph.AddCopyLayers(backends, tensorHandleFactoryRegistry);
telsoa01c577f2c2018-08-31 09:22:23 +0100894
895 // Convert constants
Matteo Martincighadddddb2019-01-24 14:06:23 +0000896 Optimizer::Pass(optGraph, MakeOptimizations(ConvertConstantsFloatToHalf()));
897 Optimizer::Pass(optGraph, MakeOptimizations(ConvertConstantsHalfToFloat()));
telsoa01c577f2c2018-08-31 09:22:23 +0100898
Derek Lamberti84da38b2019-06-13 11:40:08 +0100899 // Run backend specific optimizations (deprecated)
Matteo Martincigh49124022019-01-11 13:25:59 +0000900 for (auto&& chosenBackend : backendSettings.m_SelectedBackends)
David Beck263e3492018-11-09 14:46:40 +0000901 {
902 auto factoryFun = BackendRegistryInstance().GetFactory(chosenBackend);
903 auto backendPtr = factoryFun();
904 BOOST_ASSERT(backendPtr.get() != nullptr);
905
Matteo Martincighed735042019-05-22 09:42:43 +0100906 ARMNN_NO_DEPRECATE_WARN_BEGIN
David Beck263e3492018-11-09 14:46:40 +0000907 auto backendSpecificOptimizations = backendPtr->GetOptimizations();
Matteo Martincighed735042019-05-22 09:42:43 +0100908 ARMNN_NO_DEPRECATE_WARN_END
909
David Beck263e3492018-11-09 14:46:40 +0000910 if (!backendSpecificOptimizations.empty())
911 {
912 Optimizer::Pass(optNetObjPtr->GetGraph(), backendSpecificOptimizations);
913 }
914 }
915
telsoa01c577f2c2018-08-31 09:22:23 +0100916 return optNet;
telsoa014fcda012018-03-09 14:13:49 +0000917}
918
919Network::Network()
920: m_Graph(std::make_unique<Graph>())
921{
922}
923
924Network::~Network()
925{
926}
927
928IConnectableLayer* Network::AddInputLayer(LayerBindingId id, const char* name)
929{
930 return m_Graph->AddLayer<InputLayer>(id, name);
931}
932
Éanna Ó Catháin4e1e1362018-11-12 11:36:34 +0000933IConnectableLayer* Network::AddBatchToSpaceNdLayer(const BatchToSpaceNdDescriptor& batchToSpaceNdDescriptor,
934 const char* name)
935{
936 return m_Graph->AddLayer<BatchToSpaceNdLayer>(batchToSpaceNdDescriptor, name);
937}
938
telsoa014fcda012018-03-09 14:13:49 +0000939IConnectableLayer* Network::AddFullyConnectedLayerImpl(const FullyConnectedDescriptor& fullyConnectedDescriptor,
telsoa01c577f2c2018-08-31 09:22:23 +0100940 const ConstTensor& weights,
Aron Virginas-Tarad402702019-02-22 17:03:44 +0000941 const Optional<ConstTensor>& biases,
telsoa01c577f2c2018-08-31 09:22:23 +0100942 const char* name)
telsoa014fcda012018-03-09 14:13:49 +0000943{
Aron Virginas-Tarad402702019-02-22 17:03:44 +0000944 if (fullyConnectedDescriptor.m_BiasEnabled && !biases.has_value())
telsoa014fcda012018-03-09 14:13:49 +0000945 {
Aron Virginas-Tarad402702019-02-22 17:03:44 +0000946 throw InvalidArgumentException("AddFullyConnectedLayer: biases cannot be empty");
telsoa014fcda012018-03-09 14:13:49 +0000947 }
948
949 const auto layer = m_Graph->AddLayer<FullyConnectedLayer>(fullyConnectedDescriptor, name);
950
951 layer->m_Weight = std::make_unique<ScopedCpuTensorHandle>(weights);
952
953 if (fullyConnectedDescriptor.m_BiasEnabled)
954 {
Aron Virginas-Tarad402702019-02-22 17:03:44 +0000955 layer->m_Bias = std::make_unique<ScopedCpuTensorHandle>(biases.value());
telsoa014fcda012018-03-09 14:13:49 +0000956 }
957
958 return layer;
959}
960
961IConnectableLayer* Network::AddFullyConnectedLayer(const FullyConnectedDescriptor& fullyConnectedDescriptor,
telsoa01c577f2c2018-08-31 09:22:23 +0100962 const ConstTensor& weights,
Aron Virginas-Tarad402702019-02-22 17:03:44 +0000963 const Optional<ConstTensor>& biases,
telsoa01c577f2c2018-08-31 09:22:23 +0100964 const char* name)
telsoa014fcda012018-03-09 14:13:49 +0000965{
Aron Virginas-Tarad402702019-02-22 17:03:44 +0000966 return AddFullyConnectedLayerImpl(fullyConnectedDescriptor, weights, biases, name);
telsoa014fcda012018-03-09 14:13:49 +0000967}
968
Aron Virginas-Tarad402702019-02-22 17:03:44 +0000969IConnectableLayer* Network::AddFullyConnectedLayer(const FullyConnectedDescriptor& fullyConnectedDescriptor,
970 const ConstTensor& weights,
971 const char* name)
972{
Matteo Martincighfc598e12019-05-14 10:36:13 +0100973 Optional<ConstTensor> biases;
Aron Virginas-Tarad402702019-02-22 17:03:44 +0000974 return AddFullyConnectedLayerImpl(fullyConnectedDescriptor, weights, biases, name);
975}
976
telsoa014fcda012018-03-09 14:13:49 +0000977IConnectableLayer* Network::AddFullyConnectedLayer(const FullyConnectedDescriptor& fullyConnectedDescriptor,
telsoa01c577f2c2018-08-31 09:22:23 +0100978 const ConstTensor& weights,
979 const ConstTensor& biases,
980 const char* name)
telsoa014fcda012018-03-09 14:13:49 +0000981{
Aron Virginas-Tarad402702019-02-22 17:03:44 +0000982 Optional<ConstTensor> optionalBiases(biases);
983 return AddFullyConnectedLayerImpl(fullyConnectedDescriptor, weights, optionalBiases, name);
telsoa014fcda012018-03-09 14:13:49 +0000984}
985
Jim Flynne242f2d2019-05-22 14:24:13 +0100986IConnectableLayer* Network::AddConcatLayer(const ConcatDescriptor& concatDescriptor,
Jim Flynn906f9462019-05-10 13:55:21 +0100987 const char* name)
988{
Jim Flynne242f2d2019-05-22 14:24:13 +0100989 return m_Graph->AddLayer<ConcatLayer>(concatDescriptor, name);
Jim Flynn906f9462019-05-10 13:55:21 +0100990}
991
telsoa014fcda012018-03-09 14:13:49 +0000992IConnectableLayer* Network::AddConvolution2dLayerImpl(const Convolution2dDescriptor& convolution2dDescriptor,
telsoa01c577f2c2018-08-31 09:22:23 +0100993 const ConstTensor& weights,
Aron Virginas-Tarad402702019-02-22 17:03:44 +0000994 const Optional<ConstTensor>& biases,
telsoa01c577f2c2018-08-31 09:22:23 +0100995 const char* name)
telsoa014fcda012018-03-09 14:13:49 +0000996{
Aron Virginas-Tarad402702019-02-22 17:03:44 +0000997 if (convolution2dDescriptor.m_BiasEnabled && !biases.has_value())
telsoa014fcda012018-03-09 14:13:49 +0000998 {
Aron Virginas-Tarad402702019-02-22 17:03:44 +0000999 throw InvalidArgumentException("AddConvolution2dLayer: biases cannot be empty");
telsoa014fcda012018-03-09 14:13:49 +00001000 }
1001
1002 const auto layer = m_Graph->AddLayer<Convolution2dLayer>(convolution2dDescriptor, name);
1003
1004 layer->m_Weight = std::make_unique<ScopedCpuTensorHandle>(weights);
1005
1006 if (convolution2dDescriptor.m_BiasEnabled)
1007 {
Aron Virginas-Tarad402702019-02-22 17:03:44 +00001008 layer->m_Bias = std::make_unique<ScopedCpuTensorHandle>(biases.value());
telsoa014fcda012018-03-09 14:13:49 +00001009 }
1010
1011 return layer;
1012}
1013
1014IConnectableLayer* Network::AddConvolution2dLayer(const Convolution2dDescriptor& convolution2dDescriptor,
telsoa01c577f2c2018-08-31 09:22:23 +01001015 const ConstTensor& weights,
Aron Virginas-Tarad402702019-02-22 17:03:44 +00001016 const Optional<ConstTensor>& biases,
telsoa01c577f2c2018-08-31 09:22:23 +01001017 const char* name)
telsoa014fcda012018-03-09 14:13:49 +00001018{
Aron Virginas-Tarad402702019-02-22 17:03:44 +00001019 return AddConvolution2dLayerImpl(convolution2dDescriptor, weights, biases, name);
telsoa014fcda012018-03-09 14:13:49 +00001020}
Aron Virginas-Tarad402702019-02-22 17:03:44 +00001021
Aron Virginas-Tarad402702019-02-22 17:03:44 +00001022IConnectableLayer* Network::AddConvolution2dLayer(const Convolution2dDescriptor& convolution2dDescriptor,
1023 const ConstTensor& weights,
1024 const char* name)
1025{
Matteo Martincighfc598e12019-05-14 10:36:13 +01001026 Optional<ConstTensor> biases;
Aron Virginas-Tarad402702019-02-22 17:03:44 +00001027 return AddConvolution2dLayerImpl(convolution2dDescriptor, weights, biases, name);
1028}
1029
telsoa014fcda012018-03-09 14:13:49 +00001030IConnectableLayer* Network::AddConvolution2dLayer(const Convolution2dDescriptor& convolution2dDescriptor,
telsoa01c577f2c2018-08-31 09:22:23 +01001031 const ConstTensor& weights,
1032 const ConstTensor& biases,
1033 const char* name)
telsoa014fcda012018-03-09 14:13:49 +00001034{
Aron Virginas-Tarad402702019-02-22 17:03:44 +00001035 Optional<ConstTensor> optionalBiases(biases);
1036 return AddConvolution2dLayerImpl(convolution2dDescriptor, weights, optionalBiases, name);
telsoa014fcda012018-03-09 14:13:49 +00001037}
1038
1039IConnectableLayer* Network::AddDepthwiseConvolution2dLayerImpl(
1040 const DepthwiseConvolution2dDescriptor& convolution2dDescriptor,
1041 const ConstTensor& weights,
Aron Virginas-Tarad402702019-02-22 17:03:44 +00001042 const Optional<ConstTensor>& biases,
telsoa014fcda012018-03-09 14:13:49 +00001043 const char* name)
1044{
Aron Virginas-Tarad402702019-02-22 17:03:44 +00001045 if (convolution2dDescriptor.m_BiasEnabled && !biases.has_value())
telsoa014fcda012018-03-09 14:13:49 +00001046 {
Aron Virginas-Tarad402702019-02-22 17:03:44 +00001047 throw InvalidArgumentException("AddDepthwiseConvolution2dLayer: biases cannot be empty");
telsoa014fcda012018-03-09 14:13:49 +00001048 }
1049
Matteo Martincigh3d6898c2019-01-15 16:11:44 +00001050 const auto layer = m_Graph->AddLayer<DepthwiseConvolution2dLayer>(convolution2dDescriptor, name);
telsoa014fcda012018-03-09 14:13:49 +00001051
1052 layer->m_Weight = std::make_unique<ScopedCpuTensorHandle>(weights);
1053
1054 if (convolution2dDescriptor.m_BiasEnabled)
1055 {
Aron Virginas-Tarad402702019-02-22 17:03:44 +00001056 layer->m_Bias = std::make_unique<ScopedCpuTensorHandle>(biases.value());
telsoa014fcda012018-03-09 14:13:49 +00001057 }
1058
1059 return layer;
1060}
1061
1062IConnectableLayer* Network::AddDepthwiseConvolution2dLayer(
Aron Virginas-Tarad402702019-02-22 17:03:44 +00001063 const DepthwiseConvolution2dDescriptor& convolution2dDescriptor,
1064 const ConstTensor& weights,
1065 const Optional<ConstTensor>& biases,
1066 const char* name)
1067{
1068 return AddDepthwiseConvolution2dLayerImpl(convolution2dDescriptor, weights, biases, name);
1069}
1070
Aron Virginas-Tarad402702019-02-22 17:03:44 +00001071IConnectableLayer* Network::AddDepthwiseConvolution2dLayer(
telsoa014fcda012018-03-09 14:13:49 +00001072 const DepthwiseConvolution2dDescriptor& convolution2dDescriptor,
1073 const ConstTensor& weights,
1074 const char* name)
1075{
Matteo Martincighfc598e12019-05-14 10:36:13 +01001076 Optional<ConstTensor> biases;
Aron Virginas-Tarad402702019-02-22 17:03:44 +00001077 return AddDepthwiseConvolution2dLayerImpl(convolution2dDescriptor, weights, biases, name);
telsoa014fcda012018-03-09 14:13:49 +00001078}
Aron Virginas-Tarad402702019-02-22 17:03:44 +00001079
telsoa014fcda012018-03-09 14:13:49 +00001080IConnectableLayer* Network::AddDepthwiseConvolution2dLayer(
1081 const DepthwiseConvolution2dDescriptor& convolution2dDescriptor,
1082 const ConstTensor& weights,
1083 const ConstTensor& biases,
1084 const char* name)
1085{
Aron Virginas-Tarad402702019-02-22 17:03:44 +00001086 Optional<ConstTensor> optionalBiases(biases);
1087 return AddDepthwiseConvolution2dLayerImpl(convolution2dDescriptor, weights, optionalBiases, name);
telsoa014fcda012018-03-09 14:13:49 +00001088}
1089
Narumol Prangnawarat94dd5d82019-01-23 18:06:26 +00001090IConnectableLayer* Network::AddDetectionPostProcessLayer(const armnn::DetectionPostProcessDescriptor& descriptor,
Narumol Prangnawarat6d302bf2019-02-04 11:46:26 +00001091 const ConstTensor& anchors, const char* name)
Narumol Prangnawarat94dd5d82019-01-23 18:06:26 +00001092{
Narumol Prangnawarat6d302bf2019-02-04 11:46:26 +00001093 const auto layer = m_Graph->AddLayer<DetectionPostProcessLayer>(descriptor, name);
1094
1095 layer->m_Anchors = std::make_unique<ScopedCpuTensorHandle>(anchors);
1096
1097 return layer;
Narumol Prangnawarat94dd5d82019-01-23 18:06:26 +00001098}
1099
telsoa014fcda012018-03-09 14:13:49 +00001100IConnectableLayer* Network::AddPermuteLayer(const PermuteDescriptor& permuteDescriptor,
1101 const char* name)
1102{
1103 return m_Graph->AddLayer<PermuteLayer>(permuteDescriptor, name);
1104}
1105
1106IConnectableLayer* Network::AddPooling2dLayer(const Pooling2dDescriptor& pooling2dDescriptor,
1107 const char* name)
1108{
1109 return m_Graph->AddLayer<Pooling2dLayer>(pooling2dDescriptor, name);
1110}
1111
1112IConnectableLayer* Network::AddActivationLayer(const ActivationDescriptor& activationDescriptor,
1113 const char* name)
1114{
1115 return m_Graph->AddLayer<ActivationLayer>(activationDescriptor, name);
1116}
1117
telsoa01c577f2c2018-08-31 09:22:23 +01001118IConnectableLayer* Network::AddNormalizationLayer(const NormalizationDescriptor&
1119normalizationDescriptor,
telsoa014fcda012018-03-09 14:13:49 +00001120 const char* name)
1121{
1122 return m_Graph->AddLayer<NormalizationLayer>(normalizationDescriptor, name);
1123}
1124
1125IConnectableLayer* Network::AddSoftmaxLayer(const SoftmaxDescriptor& softmaxDescriptor,
1126 const char* name)
1127{
1128 return m_Graph->AddLayer<SoftmaxLayer>(softmaxDescriptor, name);
1129}
1130
1131IConnectableLayer* Network::AddSplitterLayer(const ViewsDescriptor& splitterDescriptor,
1132 const char* name)
1133{
1134 return m_Graph->AddLayer<SplitterLayer>(splitterDescriptor, name);
1135}
1136
Nattapat Chaimanowong5a4304a2018-11-28 10:44:37 +00001137IConnectableLayer* Network::AddMaximumLayer(const char* name)
1138{
1139 return m_Graph->AddLayer<MaximumLayer>(name);
1140}
1141
Éanna Ó Catháin20e58802018-12-04 10:29:06 +00001142IConnectableLayer* Network::AddMinimumLayer(const char* name)
1143{
1144 return m_Graph->AddLayer<MinimumLayer>(name);
1145}
1146
Jim Flynne242f2d2019-05-22 14:24:13 +01001147IConnectableLayer* Network::AddMergerLayer(const MergerDescriptor& mergerDescriptor,
Jim Flynn906f9462019-05-10 13:55:21 +01001148 const char* name)
telsoa014fcda012018-03-09 14:13:49 +00001149{
Jim Flynne242f2d2019-05-22 14:24:13 +01001150 return AddConcatLayer(mergerDescriptor, name);
telsoa014fcda012018-03-09 14:13:49 +00001151}
1152
1153IConnectableLayer* Network::AddAdditionLayer(const char* name)
1154{
1155 return m_Graph->AddLayer<AdditionLayer>(name);
1156}
1157
1158IConnectableLayer* Network::AddMultiplicationLayer(const char* name)
1159{
1160 return m_Graph->AddLayer<MultiplicationLayer>(name);
1161}
1162
1163IConnectableLayer* Network::AddOutputLayer(LayerBindingId id, const char* name)
1164{
1165 return m_Graph->AddLayer<OutputLayer>(id, name);
1166}
1167
1168IConnectableLayer* Network::AddBatchNormalizationLayer(const BatchNormalizationDescriptor& desc,
1169 const ConstTensor& mean,
1170 const ConstTensor& variance,
1171 const ConstTensor& beta,
1172 const ConstTensor& gamma,
1173 const char* name)
1174{
1175 const auto layer = m_Graph->AddLayer<BatchNormalizationLayer>(desc, name);
1176
1177 layer->m_Mean = std::make_unique<ScopedCpuTensorHandle>(mean);
1178 layer->m_Variance = std::make_unique<ScopedCpuTensorHandle>(variance);
1179 layer->m_Beta = std::make_unique<ScopedCpuTensorHandle>(beta);
1180 layer->m_Gamma = std::make_unique<ScopedCpuTensorHandle>(gamma);
1181
1182 return layer;
1183}
1184
telsoa01c577f2c2018-08-31 09:22:23 +01001185IConnectableLayer* Network::AddResizeBilinearLayer(const ResizeBilinearDescriptor&
1186resizeDescriptor, const char* name)
telsoa014fcda012018-03-09 14:13:49 +00001187{
1188 return m_Graph->AddLayer<ResizeBilinearLayer>(resizeDescriptor,name);
1189}
1190
Matteo Martincighbcd3c852018-09-28 14:14:12 +01001191IConnectableLayer* Network::AddL2NormalizationLayer(const L2NormalizationDescriptor& desc,
1192 const char* name)
telsoa014fcda012018-03-09 14:13:49 +00001193{
Matteo Martincighbcd3c852018-09-28 14:14:12 +01001194 return m_Graph->AddLayer<L2NormalizationLayer>(desc, name);
telsoa014fcda012018-03-09 14:13:49 +00001195}
1196
1197IConnectableLayer* Network::AddConstantLayer(const ConstTensor& input, const char* name)
1198{
telsoa01c577f2c2018-08-31 09:22:23 +01001199 auto layer = m_Graph->AddLayer<ConstantLayer>(name);
1200
1201 layer->m_LayerOutput = std::make_unique<ScopedCpuTensorHandle>(input);
1202
1203 return layer;
telsoa014fcda012018-03-09 14:13:49 +00001204}
1205
telsoa01c577f2c2018-08-31 09:22:23 +01001206IConnectableLayer* Network::AddReshapeLayer(const ReshapeDescriptor& reshapeDescriptor,
1207 const char* name)
telsoa014fcda012018-03-09 14:13:49 +00001208{
1209 return m_Graph->AddLayer<ReshapeLayer>(reshapeDescriptor, name);
1210}
1211
Nattapat Chaimanowong207ef9a2018-11-02 10:57:25 +00001212IConnectableLayer* Network::AddSpaceToBatchNdLayer(const SpaceToBatchNdDescriptor& spaceToBatchNdDescriptor,
1213 const char* name)
1214{
1215 return m_Graph->AddLayer<SpaceToBatchNdLayer>(spaceToBatchNdDescriptor, name);
1216}
1217
Aron Virginas-Tar972af152019-06-11 14:14:03 +01001218IConnectableLayer* Network::AddSpaceToDepthLayer(const SpaceToDepthDescriptor& spaceToDepthDescriptor,
1219 const char* name)
1220{
1221 return m_Graph->AddLayer<SpaceToDepthLayer>(spaceToDepthDescriptor, name);
1222}
1223
telsoa014fcda012018-03-09 14:13:49 +00001224IConnectableLayer* Network::AddFloorLayer(const char* name)
1225{
1226 return m_Graph->AddLayer<FloorLayer>(name);
1227}
1228
telsoa01c577f2c2018-08-31 09:22:23 +01001229IConnectableLayer* Network::AddLstmLayer(const LstmDescriptor& descriptor,
1230 const LstmInputParams& params,
1231 const char* name)
1232{
1233 const auto layer = m_Graph->AddLayer<LstmLayer>(descriptor, name);
1234
1235 //Lstm Basic Parameters
1236 layer->m_BasicParameters.m_InputToForgetWeights =
1237 std::make_unique<ScopedCpuTensorHandle>(*(params.m_InputToForgetWeights));
1238 layer->m_BasicParameters.m_InputToCellWeights =
1239 std::make_unique<ScopedCpuTensorHandle>(*(params.m_InputToCellWeights));
1240 layer->m_BasicParameters.m_InputToOutputWeights =
1241 std::make_unique<ScopedCpuTensorHandle>(*(params.m_InputToOutputWeights));
1242 layer->m_BasicParameters.m_RecurrentToForgetWeights =
1243 std::make_unique<ScopedCpuTensorHandle>(*(params.m_RecurrentToForgetWeights));
1244 layer->m_BasicParameters.m_RecurrentToCellWeights =
1245 std::make_unique<ScopedCpuTensorHandle>(*(params.m_RecurrentToCellWeights));
1246 layer->m_BasicParameters.m_RecurrentToOutputWeights =
1247 std::make_unique<ScopedCpuTensorHandle>(*(params.m_RecurrentToOutputWeights));
1248 layer->m_BasicParameters.m_ForgetGateBias =
1249 std::make_unique<ScopedCpuTensorHandle>(*(params.m_ForgetGateBias));
1250 layer->m_BasicParameters.m_CellBias =
1251 std::make_unique<ScopedCpuTensorHandle>(*(params.m_CellBias));
1252 layer->m_BasicParameters.m_OutputGateBias =
1253 std::make_unique<ScopedCpuTensorHandle>(*(params.m_OutputGateBias));
1254
1255 //Lstm Cifg parameters
1256 if(!descriptor.m_CifgEnabled)
1257 {
1258 if(params.m_InputToInputWeights == nullptr)
1259 {
1260 throw InvalidArgumentException("AddLstmLayer: Input To Input Weights cannot be NULL");
1261 }
1262 if(params.m_RecurrentToInputWeights == nullptr)
1263 {
1264 throw InvalidArgumentException(
1265 "AddLstmLayer: Recurrent To Input Weights cannot be NULL");
1266 }
1267 if(params.m_InputGateBias == nullptr)
1268 {
1269 throw InvalidArgumentException("AddLstmLayer: Input Gate Bias cannot be NULL");
1270 }
1271 layer->m_CifgParameters.m_InputToInputWeights =
1272 std::make_unique<ScopedCpuTensorHandle>(*(params.m_InputToInputWeights));
1273 layer->m_CifgParameters.m_RecurrentToInputWeights =
1274 std::make_unique<ScopedCpuTensorHandle>(*(params.m_RecurrentToInputWeights));
1275 // In the VTS tests, cell-to-input weights may be null, even if the other CIFG params are not.
1276 if(params.m_CellToInputWeights != nullptr)
1277 {
1278 layer->m_CifgParameters.m_CellToInputWeights =
1279 std::make_unique<ScopedCpuTensorHandle>(*(params.m_CellToInputWeights));
1280 }
1281 layer->m_CifgParameters.m_InputGateBias =
1282 std::make_unique<ScopedCpuTensorHandle>(*(params.m_InputGateBias));
1283 }
1284
1285 //Lstm projection parameters
1286 if(descriptor.m_ProjectionEnabled)
1287 {
1288 if(params.m_ProjectionWeights == nullptr)
1289 {
1290 throw InvalidArgumentException("AddLstmLayer: Projection Weights cannot be NULL");
1291 }
1292 layer->m_ProjectionParameters.m_ProjectionWeights =
1293 std::make_unique<ScopedCpuTensorHandle>(*(params.m_ProjectionWeights));
1294 if(params.m_ProjectionBias != nullptr)
1295 {
1296 layer->m_ProjectionParameters.m_ProjectionBias =
1297 std::make_unique<ScopedCpuTensorHandle>(*(params.m_ProjectionBias));
1298 }
1299 }
1300
1301 //Lstm Peephole params
1302 if(descriptor.m_PeepholeEnabled)
1303 {
1304 if(params.m_CellToForgetWeights == nullptr)
1305 {
1306 throw InvalidArgumentException("AddLstmLayer: Cell To Forget Weights cannot be NULL");
1307 }
1308 if(params.m_CellToOutputWeights == nullptr)
1309 {
1310 throw InvalidArgumentException("AddLstmLayer: Cell To Output Weights cannot be NULL");
1311 }
1312 layer->m_PeepholeParameters.m_CellToForgetWeights =
1313 std::make_unique<ScopedCpuTensorHandle>(*(params.m_CellToForgetWeights));
1314 layer->m_PeepholeParameters.m_CellToOutputWeights =
1315 std::make_unique<ScopedCpuTensorHandle>(*(params.m_CellToOutputWeights));
1316 }
1317 return layer;
1318}
1319
Francis Murtaghe7a86a42018-08-29 12:42:10 +01001320IConnectableLayer* Network::AddDivisionLayer(const char* name)
1321{
1322 return m_Graph->AddLayer<DivisionLayer>(name);
1323}
1324
David Beck19526222018-09-12 16:00:08 +01001325IConnectableLayer* Network::AddSubtractionLayer(const char* name)
1326{
1327 return m_Graph->AddLayer<SubtractionLayer>(name);
1328}
1329
narpra0132b90462018-09-13 11:07:48 +01001330IConnectableLayer* Network::AddMeanLayer(const MeanDescriptor& meanDescriptor, const char* name)
1331{
1332 return m_Graph->AddLayer<MeanLayer>(meanDescriptor,name);
1333}
1334
Mohamed Nour Abouelseoud5662c202018-09-24 13:30:09 +01001335IConnectableLayer* Network::AddPadLayer(const PadDescriptor& padDescriptor, const char* name)
1336{
1337 return m_Graph->AddLayer<PadLayer>(padDescriptor,name);
1338}
1339
Derek Lambertia9cca6a2019-03-25 15:41:58 +00001340IConnectableLayer *Network::AddQuantizeLayer(const char *name)
1341{
1342 return m_Graph->AddLayer<QuantizeLayer>(name);
1343}
1344
Nattapat Chaimanowonge4294fd2019-03-28 09:56:53 +00001345IConnectableLayer* Network::AddDequantizeLayer(const char* name)
1346{
1347 return m_Graph->AddLayer<DequantizeLayer>(name);
1348}
1349
Conor Kennedy430b5d82018-11-14 15:28:28 +00001350IConnectableLayer* Network::AddStridedSliceLayer(const StridedSliceDescriptor& stridedSliceDescriptor,
1351 const char* name)
1352{
1353 return m_Graph->AddLayer<StridedSliceLayer>(stridedSliceDescriptor, name);
1354}
1355
Matteo Martincigh59a950c2018-12-13 12:48:25 +00001356IConnectableLayer* Network::AddGreaterLayer(const char* name)
1357{
1358 return m_Graph->AddLayer<GreaterLayer>(name);
1359}
1360
FrancisMurtagh20995952018-12-17 12:11:36 +00001361IConnectableLayer* Network::AddEqualLayer(const char* name)
1362{
jimfly0184c70e62018-12-19 13:14:46 +00001363 return m_Graph->AddLayer<EqualLayer>(name);
FrancisMurtagh20995952018-12-17 12:11:36 +00001364}
1365
Mohamed Nour Abouelseouda1d3c6a2018-12-27 12:39:16 +00001366IConnectableLayer* Network::AddRsqrtLayer(const char * name)
1367{
1368 return m_Graph->AddLayer<RsqrtLayer>(name);
1369}
1370
narpra01b89b05f2019-01-16 09:53:09 +00001371IConnectableLayer* Network::AddGatherLayer(const char* name)
1372{
1373 return m_Graph->AddLayer<GatherLayer>(name);
1374}
1375
Nattapat Chaimanowong1f886302019-04-05 13:37:19 +01001376IConnectableLayer* Network::AddMergeLayer(const char* name)
1377{
1378 return m_Graph->AddLayer<MergeLayer>(name);
1379}
1380
Sadik Armaganeff363d2019-04-05 15:25:46 +01001381IConnectableLayer* Network::AddSwitchLayer(const char* name)
1382{
1383 return m_Graph->AddLayer<SwitchLayer>(name);
1384}
1385
Matteo Martincigh0e406ee2019-06-12 15:42:18 +01001386IConnectableLayer* Network::AddPreluLayer(const char* name)
1387{
1388 return m_Graph->AddLayer<PreluLayer>(name);
1389}
1390
Aron Virginas-Tar639fb042019-06-20 14:28:19 +01001391IConnectableLayer* Network::AddTransposeConvolution2dLayer(const TransposeConvolution2dDescriptor& descriptor,
1392 const ConstTensor& weights,
1393 const Optional<ConstTensor>& biases,
1394 const char* name)
1395{
1396 if (descriptor.m_BiasEnabled && !biases.has_value())
1397 {
1398 throw InvalidArgumentException("AddTransposeConvolution2dLayer: Biases cannot be empty");
1399 }
1400
1401 const auto layer = m_Graph->AddLayer<TransposeConvolution2dLayer>(descriptor, name);
1402
1403 layer->m_Weight = std::make_unique<ScopedCpuTensorHandle>(weights);
1404
1405 if (descriptor.m_BiasEnabled)
1406 {
1407 layer->m_Bias = std::make_unique<ScopedCpuTensorHandle>(biases.value());
1408 }
1409
1410 return layer;
1411}
1412
Mike Kelly8c1701a2019-02-11 17:01:27 +00001413void Network::Accept(ILayerVisitor& visitor) const
1414{
1415 for (auto layer : GetGraph())
1416 {
1417 layer->Accept(visitor);
1418 };
1419}
1420
telsoa014fcda012018-03-09 14:13:49 +00001421OptimizedNetwork::OptimizedNetwork(std::unique_ptr<Graph> graph)
1422 : m_Graph(std::move(graph))
1423{
1424}
1425
1426OptimizedNetwork::~OptimizedNetwork()
1427{
1428}
1429
1430} // namespace armnn