blob: c764e2a059e8e0aade8d60ebccffd461ef9c31a2 [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/IBackendInternal.hpp>
Derek Lamberti84da38b2019-06-13 11:40:08 +010018#include <backendsCommon/TensorHandleFactoryRegistry.hpp>
David Beckac42efd2018-09-26 17:41:13 +010019
20#include <armnn/Exceptions.hpp>
telsoa014fcda012018-03-09 14:13:49 +000021#include <armnn/Utils.hpp>
telsoa01c577f2c2018-08-31 09:22:23 +010022#include <armnn/TypesUtils.hpp>
Matteo Martincighc601aa62019-10-29 15:03:22 +000023#include <armnn/BackendRegistry.hpp>
telsoa014fcda012018-03-09 14:13:49 +000024
Jan Eilers99d9d4a2019-11-06 10:02:16 +000025#include <ProfilingService.hpp>
26
telsoa014fcda012018-03-09 14:13:49 +000027#include <fcntl.h>
28#include <algorithm>
29#include <fstream>
30#include <memory>
telsoa01c577f2c2018-08-31 09:22:23 +010031#include <vector>
32#include <algorithm>
telsoa014fcda012018-03-09 14:13:49 +000033
34#include <boost/assert.hpp>
35#include <boost/format.hpp>
telsoa014fcda012018-03-09 14:13:49 +000036#include <boost/numeric/conversion/converter_policies.hpp>
37#include <boost/cast.hpp>
38
39namespace armnn
40{
41
42armnn::INetwork* INetwork::CreateRaw()
43{
44 return new Network();
45}
46
47armnn::INetworkPtr INetwork::Create()
48{
49 return INetworkPtr(CreateRaw(), &INetwork::Destroy);
50}
51
52void INetwork::Destroy(INetwork* network)
53{
54 delete boost::polymorphic_downcast<Network*>(network);
55}
56
telsoa014fcda012018-03-09 14:13:49 +000057void IOptimizedNetwork::Destroy(IOptimizedNetwork* network)
58{
59 delete boost::polymorphic_downcast<OptimizedNetwork*>(network);
60}
61
62Status OptimizedNetwork::PrintGraph()
63{
64 m_Graph->Print();
65 return Status::Success;
66}
67
surmeh01bceff2f2018-03-29 16:29:27 +010068Status OptimizedNetwork::SerializeToDot(std::ostream& stream) const
69{
70 return m_Graph->SerializeToDot(stream);
71}
72
Matteo Martincigh49124022019-01-11 13:25:59 +000073void ReportError(const std::string& errorMessage,
74 Optional<std::vector<std::string>&> errorMessages)
75{
76 std::stringstream fullErrorMessage;
77 fullErrorMessage << "ERROR: " << errorMessage;
Derek Lamberti08446972019-11-26 16:38:31 +000078 ARMNN_LOG(warning) << fullErrorMessage.str();
Matteo Martincigh49124022019-01-11 13:25:59 +000079 if (errorMessages)
80 {
81 errorMessages.value().push_back(fullErrorMessage.str());
82 }
83}
84
85void ReportWarning(const std::string& warningMessage,
86 Optional<std::vector<std::string>&> warningMessages)
87{
88 std::stringstream fullWarningMessage;
89 fullWarningMessage << "WARNING: " << warningMessage;
Derek Lamberti08446972019-11-26 16:38:31 +000090 ARMNN_LOG(warning) << fullWarningMessage.str();
Matteo Martincigh49124022019-01-11 13:25:59 +000091 if (warningMessages)
92 {
93 warningMessages.value().push_back(fullWarningMessage.str());
94 }
95}
96
jimfly016b0b53d2018-10-08 14:43:01 +010097bool CheckScaleSetOnQuantizedType(Layer* layer, Optional<std::vector<std::string>&> errMessages)
98{
99 bool noErrors = true;
100 unsigned int numOutputs = layer->GetNumOutputSlots();
101 for (unsigned int i = 0; i < numOutputs; i++) {
David Monahanb8554702019-04-25 16:03:38 +0100102 OutputSlot& outputSlot = layer->GetOutputSlot(i);
103 TensorInfo info = outputSlot.GetTensorInfo();
jimfly016b0b53d2018-10-08 14:43:01 +0100104 if (DataType::QuantisedAsymm8 == info.GetDataType()) {
105 if (0.f == info.GetQuantizationScale()) {
106 noErrors = false;
107 std::stringstream ss;
Matteo Martincigh49124022019-01-11 13:25:59 +0000108 ss << "output " << i << " of layer " << GetLayerTypeAsCString(layer->GetType())
jimfly016b0b53d2018-10-08 14:43:01 +0100109 << " (" << layer->GetNameStr() << ") is of type"
110 << " Quantized 8 bit but its scale parameter has not been set";
Matteo Martincigh49124022019-01-11 13:25:59 +0000111 ReportError(ss.str(), errMessages);
jimfly016b0b53d2018-10-08 14:43:01 +0100112 }
David Monahanb8554702019-04-25 16:03:38 +0100113 // Softmax under QuantisedAsymm8 must always be scale (1.0f/256.0f) and offset 0
114 if ((info.GetQuantizationScale() != (1.0f / 256.0f) ||
115 info.GetQuantizationOffset() != 0) &&
116 layer->GetType() == armnn::LayerType::Softmax)
117 {
118 std::stringstream ss;
119 ss << "Quantization parameters for Softmax layer (Scale: " <<
120 info.GetQuantizationScale() << " and Offset: " << info.GetQuantizationOffset() <<
121 ") are incorrect and have been updated to Scale: 0.00390625 and Offset: 0";
Derek Lamberti08446972019-11-26 16:38:31 +0000122 ARMNN_LOG(warning) << ss.str();
David Monahanb8554702019-04-25 16:03:38 +0100123 info.SetQuantizationScale((1.0f /256.0f));
124 info.SetQuantizationOffset(0);
125 outputSlot.SetTensorInfo(info);
126 }
jimfly016b0b53d2018-10-08 14:43:01 +0100127 }
128 }
129 return noErrors;
130}
131
Matteo Martincigh49124022019-01-11 13:25:59 +0000132OptimizationResult AssignBackends(OptimizedNetwork* optNetObjPtr,
133 BackendSettings& backendSettings,
134 Graph::Iterator& firstLayer,
135 Graph::Iterator& lastLayer,
136 Optional<std::vector<std::string>&> errMessages)
telsoa014fcda012018-03-09 14:13:49 +0000137{
Matteo Martincigh49124022019-01-11 13:25:59 +0000138 OptimizationResult result;
telsoa014fcda012018-03-09 14:13:49 +0000139
Matteo Martincigh49124022019-01-11 13:25:59 +0000140 // Helper lambda to compose meaningful error message before returning with error
141 auto ReturnWithError = [&](const Layer* layer)
telsoa01c577f2c2018-08-31 09:22:23 +0100142 {
jimfly016b0b53d2018-10-08 14:43:01 +0100143 std::stringstream failureMsg;
Matteo Martincigh49124022019-01-11 13:25:59 +0000144 failureMsg << "Layer of type " << GetLayerTypeAsCString(layer->GetType())
145 << " is not supported on any preferred backend " << backendSettings.m_PreferredBackends;
146 ReportError(failureMsg.str(), errMessages);
147
148 result.m_Error = true;
149 return result;
telsoa01c577f2c2018-08-31 09:22:23 +0100150 };
151
Matteo Martincigh49124022019-01-11 13:25:59 +0000152 auto availablePreferredBackends = backendSettings.GetAvailablePreferredBackends();
153 if (availablePreferredBackends.empty())
telsoa01c577f2c2018-08-31 09:22:23 +0100154 {
Matteo Martincigh49124022019-01-11 13:25:59 +0000155 std::stringstream failureMsg;
156 failureMsg << "No preferred backends are available";
157 ReportError(failureMsg.str(), errMessages);
158
159 result.m_Error = true;
160 return result;
161 }
162
163 for (auto it = firstLayer; it != lastLayer; ++it)
164 {
165 auto layer = *it;
Aron Virginas-Tar87972be2019-11-13 15:16:28 +0000166
167 DataType dataTypeIn = layer->GetNumInputSlots() == 0 ? DataType::Float32 :
168 layer->GetInputSlot(0).GetConnectedOutputSlot()->GetTensorInfo().GetDataType();
169 DataType dataTypeOut = layer->GetNumOutputSlots() == 0 ? DataType::Float32 :
170 layer->GetOutputSlot(0).GetTensorInfo().GetDataType();
171
telsoa01c577f2c2018-08-31 09:22:23 +0100172 std::string reasonIfUnsupported;
173 bool found = false;
jimfly016b0b53d2018-10-08 14:43:01 +0100174 if (!CheckScaleSetOnQuantizedType(layer, errMessages))
175 {
176 // don't bomb immediately, find all the quantized outputs
177 // which haven't had a scale set and report them all back.
Matteo Martincigh49124022019-01-11 13:25:59 +0000178 result.m_Error = true;
jimfly016b0b53d2018-10-08 14:43:01 +0100179 }
Matteo Martincigh49124022019-01-11 13:25:59 +0000180
David Beckf0b48452018-10-19 15:20:56 +0100181 for (const auto& backend : availablePreferredBackends)
telsoa01c577f2c2018-08-31 09:22:23 +0100182 {
183 // need to set the compute device on the layer
184 // before we can check if it is supported
David Beck33f0ae02018-10-18 15:13:56 +0100185 layer->SetBackendId(backend);
Aron Virginas-Tar87972be2019-11-13 15:16:28 +0000186 if (!IWorkloadFactory::IsLayerSupported(*layer, EmptyOptional(), reasonIfUnsupported))
telsoa01c577f2c2018-08-31 09:22:23 +0100187 {
Aron Virginas-Tar87972be2019-11-13 15:16:28 +0000188 if (dataTypeIn == DataType::Float16 || dataTypeOut == DataType::Float16)
telsoa01c577f2c2018-08-31 09:22:23 +0100189 {
190 if (IWorkloadFactory::IsLayerSupported(*layer, DataType::Float32, reasonIfUnsupported)
191 && layer->GetType() != LayerType::ConvertFp32ToFp16
192 && layer->GetType() != LayerType::ConvertFp16ToFp32)
193 {
194 // Insert FP16 -> FP32 conversion layer before current layer
Aron Virginas-Tar87972be2019-11-13 15:16:28 +0000195 std::vector<ConvertFp16ToFp32Layer*> convertFp16ToFp32Layers;
196 if (dataTypeIn == DataType::Float16)
197 {
198 convertFp16ToFp32Layers =
199 InsertConvertFp16ToFp32LayersBefore(optNetObjPtr->GetGraph(), *layer);
200 }
telsoa01c577f2c2018-08-31 09:22:23 +0100201
202 // Insert FP32 -> FP16 conversion layer after current layer
Aron Virginas-Tar87972be2019-11-13 15:16:28 +0000203 std::vector<ConvertFp32ToFp16Layer*> convertFp32ToFp16Layers;
204 if (dataTypeOut == DataType::Float16)
205 {
206 convertFp32ToFp16Layers =
207 InsertConvertFp32ToFp16LayersAfter(optNetObjPtr->GetGraph(), *layer);
208 }
telsoa01c577f2c2018-08-31 09:22:23 +0100209
210 // Assign a supported backend to the newly introduced conversion layers
David Beckf0b48452018-10-19 15:20:56 +0100211 auto AssignFirstSupportedBackend = [&](Layer* layer, BackendId preferredBackend)
telsoa01c577f2c2018-08-31 09:22:23 +0100212 {
213 bool supportedBackendFound = false;
214 std::string reasonIfUnsupported;
215
216 // Try preferred backend first
David Beck33f0ae02018-10-18 15:13:56 +0100217 layer->SetBackendId(preferredBackend);
David Beck29c75de2018-10-23 13:35:58 +0100218 if (IWorkloadFactory::IsLayerSupported(*layer,
219 EmptyOptional(),
220 reasonIfUnsupported))
telsoa01c577f2c2018-08-31 09:22:23 +0100221 {
222 supportedBackendFound = true;
223 }
224 else
225 {
David Beckf0b48452018-10-19 15:20:56 +0100226 for (const auto& backend : availablePreferredBackends)
telsoa01c577f2c2018-08-31 09:22:23 +0100227 {
228 // Skip preferred backend (we already determined that it is not supported)
229 if (backend == preferredBackend)
230 {
231 continue;
232 }
233
David Beck33f0ae02018-10-18 15:13:56 +0100234 layer->SetBackendId(backend);
David Beck29c75de2018-10-23 13:35:58 +0100235 if (IWorkloadFactory::IsLayerSupported(*layer,
236 EmptyOptional(),
237 reasonIfUnsupported))
telsoa01c577f2c2018-08-31 09:22:23 +0100238 {
239 supportedBackendFound = true;
240 break;
241 }
242 }
243 }
244
245 return supportedBackendFound;
246 };
247
248 for (ConvertFp16ToFp32Layer* convertLayer : convertFp16ToFp32Layers)
249 {
250 if (!AssignFirstSupportedBackend(convertLayer, backend))
251 {
252 return ReturnWithError(convertLayer);
253 }
254 }
255
256 for (ConvertFp32ToFp16Layer* convertLayer : convertFp32ToFp16Layers)
257 {
258 if (!AssignFirstSupportedBackend(convertLayer, backend))
259 {
260 return ReturnWithError(convertLayer);
261 }
262 }
263
264 found = true;
265 break;
266 }
267 }
jimfly016b0b53d2018-10-08 14:43:01 +0100268 std::stringstream warningMsg;
Matteo Martincigh49124022019-01-11 13:25:59 +0000269 warningMsg << "Layer of type " << GetLayerTypeAsCString(layer->GetType())
David Beck33f0ae02018-10-18 15:13:56 +0100270 << " is not supported on requested backend " << layer->GetBackendId().Get()
Aron Virginas-Tar87972be2019-11-13 15:16:28 +0000271 << " for input data type " << GetDataTypeName(dataTypeIn)
272 << " and output data type " << GetDataTypeName(dataTypeOut)
jimfly016b0b53d2018-10-08 14:43:01 +0100273 << " (reason: " << reasonIfUnsupported
274 << "), falling back to the next backend.";
Matteo Martincigh49124022019-01-11 13:25:59 +0000275 ReportWarning(warningMsg.str(), errMessages);
telsoa01c577f2c2018-08-31 09:22:23 +0100276 }
277 else
278 {
279 found = true;
Matteo Martincigh49124022019-01-11 13:25:59 +0000280 backendSettings.m_SelectedBackends.insert(backend);
telsoa01c577f2c2018-08-31 09:22:23 +0100281 break;
282 }
283 }
284
285 // If the layer is unsupported by any devices, log and return a null network.
Matteo Martincigh49124022019-01-11 13:25:59 +0000286 if (!found)
287 {
telsoa01c577f2c2018-08-31 09:22:23 +0100288 // NOTE: if the layer is not an operation queue type AND we have not got CpuRef as a
289 // fallback we should set the compute device on the layer to CpuRef (these are not
290 // available as accelerated operations, or are only available under certain
291 // conditions, currently they comprise MemCopy, Constant, Permute)
292 armnn::LayerType layerType = layer->GetType();
Matteo Martincigh49124022019-01-11 13:25:59 +0000293 if (!backendSettings.IsCpuRefUsed() && (layerType == armnn::LayerType::MemCopy ||
294 layerType == armnn::LayerType::Constant ||
295 layerType == armnn::LayerType::Permute))
telsoa01c577f2c2018-08-31 09:22:23 +0100296 {
Matteo Martincigh49124022019-01-11 13:25:59 +0000297 BackendId cpuBackendId(armnn::Compute::CpuRef);
298 layer->SetBackendId(cpuBackendId);
299 backendSettings.m_SelectedBackends.insert(cpuBackendId);
telsoa01c577f2c2018-08-31 09:22:23 +0100300 }
301 else
302 {
303 return ReturnWithError(layer);
304 }
305 }
306 }
Matteo Martincigh49124022019-01-11 13:25:59 +0000307
308 return result;
309}
310
Matteo Martincighadddddb2019-01-24 14:06:23 +0000311OptimizationResult AssignBackends(OptimizedNetwork* optNetObjPtr,
312 BackendSettings& backendSettings,
Derek Lambertiff05cc52019-04-26 13:05:17 +0100313 SubgraphView& subgraph,
Matteo Martincighadddddb2019-01-24 14:06:23 +0000314 Optional<std::vector<std::string>&> errMessages)
Matteo Martincigh49124022019-01-11 13:25:59 +0000315{
Derek Lambertiff05cc52019-04-26 13:05:17 +0100316 Graph::Iterator firstLayer = subgraph.begin();
317 Graph::Iterator lastLayer = subgraph.end();
Matteo Martincighadddddb2019-01-24 14:06:23 +0000318 return AssignBackends(optNetObjPtr,
319 backendSettings,
320 firstLayer,
321 lastLayer,
322 errMessages);
323}
324
Derek Lamberti84da38b2019-06-13 11:40:08 +0100325BackendsMap CreateSupportedBackends(TensorHandleFactoryRegistry& handleFactoryRegistry,
326 BackendSettings& backendSettings)
327{
328 BackendsMap backends;
329 auto const& backendRegistry = BackendRegistryInstance();
330 for (auto&& selectedBackend : backendSettings.m_SupportedBackends)
331 {
332 auto backendFactory = backendRegistry.GetFactory(selectedBackend);
333 auto backendObjPtr = backendFactory();
334 BOOST_ASSERT(backendObjPtr);
335
336 backendObjPtr->RegisterTensorHandleFactories(handleFactoryRegistry);
337
338 backends[backendObjPtr->GetId()] = std::move(backendObjPtr);
339 }
340
341 return backends;
342}
343
Matteo Martincighadddddb2019-01-24 14:06:23 +0000344OptimizationResult ApplyBackendOptimizations(OptimizedNetwork* optNetObjPtr,
345 BackendSettings& backendSettings,
Derek Lamberti84da38b2019-06-13 11:40:08 +0100346 BackendsMap& backends,
Matteo Martincighadddddb2019-01-24 14:06:23 +0000347 Optional<std::vector<std::string>&> errMessages)
348{
349 BOOST_ASSERT(optNetObjPtr);
Matteo Martincigh49124022019-01-11 13:25:59 +0000350
351 OptimizationResult result;
352
Matteo Martincighadddddb2019-01-24 14:06:23 +0000353 // Get the optimized graph
354 Graph& optGraph = optNetObjPtr->GetGraph();
Matteo Martincigh49124022019-01-11 13:25:59 +0000355
Matteo Martincighadddddb2019-01-24 14:06:23 +0000356 // Run backend specific optimizations
Matteo Martincighadddddb2019-01-24 14:06:23 +0000357 for (auto&& selectedBackend : backendSettings.m_SelectedBackends)
Matteo Martincigh49124022019-01-11 13:25:59 +0000358 {
Derek Lamberti84da38b2019-06-13 11:40:08 +0100359 auto backendObjPtr = backends.find(selectedBackend)->second.get();
Matteo Martincighadddddb2019-01-24 14:06:23 +0000360 BOOST_ASSERT(backendObjPtr);
361
362 // Select sub-graphs based on backend
Derek Lambertiff05cc52019-04-26 13:05:17 +0100363 SubgraphViewSelector::Subgraphs subgraphs =
Rob Hughes65c32262019-07-23 15:33:39 +0100364 SubgraphViewSelector::SelectSubgraphs(optGraph,
Matteo Martincigh602af092019-05-01 10:31:27 +0100365 // Select layers assigned to the requested backend
366 [&backendObjPtr](const Layer& layer)
367 {
368 return layer.GetType() != LayerType::Input &&
369 layer.GetType() != LayerType::Output &&
370 layer.GetBackendId() == backendObjPtr->GetId();
371 });
Derek Lambertiff05cc52019-04-26 13:05:17 +0100372 if (subgraphs.empty())
Matteo Martincigh49124022019-01-11 13:25:59 +0000373 {
Matteo Martincighadddddb2019-01-24 14:06:23 +0000374 // No sub-graphs found, try with next selected backend
375 continue;
Matteo Martincigh49124022019-01-11 13:25:59 +0000376 }
Matteo Martincighadddddb2019-01-24 14:06:23 +0000377
378 // Try to optimize each sub-graph
Derek Lambertiff05cc52019-04-26 13:05:17 +0100379 for (auto& subgraph : subgraphs)
Matteo Martincigh49124022019-01-11 13:25:59 +0000380 {
Matteo Martincighadddddb2019-01-24 14:06:23 +0000381 // Try to optimize the current sub-graph
Matteo Martincigh84924332019-05-09 12:46:16 +0100382 OptimizationViews optimizationViews = backendObjPtr->OptimizeSubgraphView(*subgraph);
383 BOOST_ASSERT(optimizationViews.Validate(*subgraph));
Matteo Martincighadddddb2019-01-24 14:06:23 +0000384
385 // Optimization attempted, check the resulting optimized sub-graph
Matteo Martincigh84924332019-05-09 12:46:16 +0100386 for (auto& substitution : optimizationViews.GetSubstitutions())
Matteo Martincighadddddb2019-01-24 14:06:23 +0000387 {
388 // Sub-graph optimized, substitute the sub-graph with the new optimized one in the main optimized graph
Matteo Martincigh84924332019-05-09 12:46:16 +0100389 SubgraphView& replacementSubgraph = substitution.m_ReplacementSubgraph;
390 SubgraphView& substitutableSubgraph = substitution.m_SubstitutableSubgraph;
391 optGraph.SubstituteSubgraph(substitutableSubgraph, replacementSubgraph);
Matteo Martincighadddddb2019-01-24 14:06:23 +0000392
393 // Assign the current backend to the optimized sub-graph
Matteo Martincigh84924332019-05-09 12:46:16 +0100394 std::for_each(replacementSubgraph.begin(), replacementSubgraph.end(), [&selectedBackend](Layer* l)
Derek Lambertic2fe5fb2019-05-08 10:23:08 +0100395 {
396 BOOST_ASSERT(l);
397 l->SetBackendId(selectedBackend);
398 });
Matteo Martincighadddddb2019-01-24 14:06:23 +0000399 }
Derek Lambertic2fe5fb2019-05-08 10:23:08 +0100400
Matteo Martincigh84924332019-05-09 12:46:16 +0100401 if (!optimizationViews.GetFailedSubgraphs().empty())
Matteo Martincighadddddb2019-01-24 14:06:23 +0000402 {
Matteo Martincighadddddb2019-01-24 14:06:23 +0000403 std::stringstream warningMsg;
Derek Lambertic2fe5fb2019-05-08 10:23:08 +0100404 warningMsg << "Some sub-graph(s) failed to optimized on " << backendObjPtr->GetId() << " backend.";
Matteo Martincighadddddb2019-01-24 14:06:23 +0000405 ReportWarning(warningMsg.str(), errMessages);
406
407 // Failed to optimize the given sub-graph, re-assign the sub-graph layers to other available backends
Derek Lambertic2fe5fb2019-05-08 10:23:08 +0100408 BackendSettings settingsCopy(backendSettings);
Matteo Martincighadddddb2019-01-24 14:06:23 +0000409 if (!backendObjPtr->GetId().IsCpuRef())
410 {
411 // Add the current backend to the list of backends to ignore
Derek Lambertic2fe5fb2019-05-08 10:23:08 +0100412 settingsCopy.m_IgnoredBackends.insert(backendObjPtr->GetId());
Matteo Martincighadddddb2019-01-24 14:06:23 +0000413 }
Derek Lambertic2fe5fb2019-05-08 10:23:08 +0100414
415 int count=0;
Matteo Martincigh84924332019-05-09 12:46:16 +0100416 for (auto& failedSubgraph : optimizationViews.GetFailedSubgraphs())
Matteo Martincighadddddb2019-01-24 14:06:23 +0000417 {
Derek Lambertic2fe5fb2019-05-08 10:23:08 +0100418 // An error occurred: the optimization was attempted but not performed, try different backends
419 std::stringstream subgraphMsg;
420 subgraphMsg << "Re-assigning backends to " << failedSubgraph.GetLayers().size()
421 << " layers inside sub-graph " << count++;
Matteo Martincigh328d92b2019-07-04 17:52:55 +0100422 ReportWarning(subgraphMsg.str(), errMessages);
Derek Lambertic2fe5fb2019-05-08 10:23:08 +0100423
424 OptimizationResult reassignmentResult = AssignBackends(optNetObjPtr,
425 settingsCopy,
426 *subgraph,
427 errMessages);
428 if (reassignmentResult.m_Error)
429 {
430 // Failed to re-assign one of the remaining backends to each layer of the sub-graph
431 result.m_Error = true;
432 return result;
433 }
Matteo Martincighadddddb2019-01-24 14:06:23 +0000434 }
Matteo Martincigh49124022019-01-11 13:25:59 +0000435 }
436 }
437 }
438
439 return result;
440}
441
Derek Lamberti84da38b2019-06-13 11:40:08 +0100442bool RequiresCopy(ITensorHandleFactory::FactoryId src,
443 ITensorHandleFactory::FactoryId dst,
444 TensorHandleFactoryRegistry& registry)
445{
446 if (src != dst)
447 {
448 ITensorHandleFactory* srcFactory = registry.GetFactory(src);
449 ITensorHandleFactory* dstFactory = registry.GetFactory(dst);
450
Matteo Martincigha6539ed2019-08-27 13:43:32 +0100451 if (srcFactory && dstFactory &&
452 (srcFactory->GetExportFlags() & dstFactory->GetImportFlags()) != 0)
Derek Lamberti84da38b2019-06-13 11:40:08 +0100453 {
454 return false;
455 }
456 return true;
457 }
458 return false;
459}
460
461// Find the handle factory for the input layer which results in fewest required copies.
462ITensorHandleFactory::FactoryId CalculateSlotOptionForInput(BackendsMap& backends,
463 OutputSlot& slot,
464 TensorHandleFactoryRegistry& registry)
465{
466 Layer& layer = slot.GetOwningLayer();
467 BOOST_ASSERT(layer.GetType() == LayerType::Input);
468
469 // Explicitly select the tensorhandle factory for InputLayer because the rules for it are slightly different. It
470 // doesn't matter which backend it is assigned to because they all use the same implementation, which
471 // requires Map/Unmap support. This means that, so long as the handle type supports map/unmap semantics, we can
472 // select a factory with maximum compatibility with the layers connected to the InputLayer.
473
474 // First ensure the from backends can support the TensorHandeAPI
475 auto frmBackend = backends.find(layer.GetBackendId());
476 if (frmBackend == backends.end() ||
477 !frmBackend->second->SupportsTensorAllocatorAPI())
478 {
479 return ITensorHandleFactory::LegacyFactoryId;
480 }
481
482 // Go through all connections to the output slot and determine the TensorHandleFactory which results in the
483 // fewest copies.
484 std::map<ITensorHandleFactory::FactoryId, int> factoryScores;
485 int topScore = 0;
486 ITensorHandleFactory::FactoryId topChoice = ITensorHandleFactory::LegacyFactoryId;
487
488 for (auto&& connection : slot.GetConnections())
489 {
490 const Layer& connectedLayer = connection->GetOwningLayer();
491
492 auto toBackend = backends.find(connectedLayer.GetBackendId());
493 BOOST_ASSERT_MSG(toBackend != backends.end(), "Backend id not found for the connected layer");
494
495 if (!toBackend->second.get()->SupportsTensorAllocatorAPI())
496 {
497 // The destination backend does not support the tensor allocator API, move to the next one
498 continue;
499 }
500
501 auto dstPrefs = toBackend->second.get()->GetHandleFactoryPreferences();
502 for (auto&& dst : dstPrefs)
503 {
Derek Lambertif674aa02019-08-01 15:56:25 +0100504 // Input layers use the mem copy workload or import, so the selected factory must
505 // support either the map/unmap API or Import API
Derek Lamberti84da38b2019-06-13 11:40:08 +0100506 ITensorHandleFactory* factory = registry.GetFactory(dst);
Derek Lambertif674aa02019-08-01 15:56:25 +0100507 if (!factory->SupportsMapUnmap() &&
508 !CheckFlag(factory->GetImportFlags(), MemorySource::Malloc)) // Just support cpu mem imports for now
Derek Lamberti84da38b2019-06-13 11:40:08 +0100509 {
Derek Lambertif674aa02019-08-01 15:56:25 +0100510 // The current tensor handle factory does not support the map/unmap or import
511 // strategy, move to the next one
Derek Lamberti84da38b2019-06-13 11:40:08 +0100512 continue;
513 }
514
515 auto it = factoryScores.find(dst);
516 if (it == factoryScores.end())
517 {
518 // Add new score to the table
519 factoryScores[dst] = 0;
520 if (topChoice == ITensorHandleFactory::LegacyFactoryId)
521 {
522 topChoice = dst;
523 }
524 }
525 else
526 {
527 // Increase the score
528 factoryScores[dst]++;
529
530 // Track the best option
531 if (factoryScores[dst] > topScore)
532 {
533 topScore = factoryScores[dst];
534 topChoice = dst;
535 }
536 }
537 }
538 }
539
540 return topChoice;
541}
542
543// Find the handle factory for the output layer which results in fewest required copies.
544ITensorHandleFactory::FactoryId CalculateSlotOptionForOutput(BackendsMap& backends,
545 OutputSlot& slot,
546 TensorHandleFactoryRegistry& registry)
547{
548 return ITensorHandleFactory::DeferredFactoryId;
549}
550
551// For all handle factories supported on the source backend, we wish to find the one which requires the fewest copies
552// when considering all connections.
553ITensorHandleFactory::FactoryId CalculateSlotOption(BackendsMap& backends,
554 OutputSlot& outputSlot,
555 TensorHandleFactoryRegistry& registry)
556{
557 // First ensure the from backends can support the TensorHandeAPI
558 Layer& layer = outputSlot.GetOwningLayer();
559 auto frmBackend = backends.find(layer.GetBackendId());
560 if (frmBackend == backends.end() ||
561 !frmBackend->second->SupportsTensorAllocatorAPI())
562 {
563 return ITensorHandleFactory::LegacyFactoryId;
564 }
565
566 // Connections to Output Layers requires support for map/unmap on the TensorHandle.
567 bool requiresMapUnmap = false;
568 for (auto&& connection : outputSlot.GetConnections())
569 {
570 const Layer& connectedLayer = connection->GetOwningLayer();
571 if (connectedLayer.GetType() == LayerType::Output)
572 {
573 requiresMapUnmap = true;
574 }
575 }
576
577 IBackendInternal* srcBackend = frmBackend->second.get();
578 auto srcPrefs = srcBackend->GetHandleFactoryPreferences();
579
580 // Initialize the scores
581 std::map<ITensorHandleFactory::FactoryId, int> factoryScores;
582 for (auto&& pref : srcPrefs)
583 {
584 if (requiresMapUnmap) // Only consider factories that support map/unmap if required
585 {
586 ITensorHandleFactory* factory = registry.GetFactory(pref);
587 if (!factory->SupportsMapUnmap())
588 {
589 // The current tensor handle factory does not support the map/unmap strategy, move to the next one
590 continue;
591 }
592 }
593
594 auto it = factoryScores.find(pref);
595 if (it == factoryScores.end())
596 {
597 // Add new score to the table
598 factoryScores[pref] = 0;
599 }
600 }
601
602 // Score each handle factory based on how many times it requires copies on the slot connections
603 for (auto&& connection : outputSlot.GetConnections())
604 {
605 const Layer& connectedLayer = connection->GetOwningLayer();
606
607 auto toBackend = backends.find(connectedLayer.GetBackendId());
608 BOOST_ASSERT_MSG(toBackend != backends.end(), "Backend id not found for the connected layer");
609
610 auto dstPrefs = toBackend->second.get()->GetHandleFactoryPreferences();
611 for (auto&& src : srcPrefs)
612 {
613 if (factoryScores.find(src) == factoryScores.end()) // Don't consider excluded factories
614 {
615 continue;
616 }
617
618 for (auto&& dst : dstPrefs)
619 {
620 if (RequiresCopy(src, dst, registry))
621 {
622 // Copy avoided, increase the score
623 factoryScores[src]++;
624 break;
625 }
626 }
627 }
628 }
629
630 // Find the lowest score
631 int minScore = std::numeric_limits<int>::max();
632 for (auto it : factoryScores)
633 {
634 minScore = std::min(minScore, it.second);
635 }
636
637 // Collect factories matching the best(lowest) score
638 std::vector<ITensorHandleFactory::FactoryId> optimalFactories;
639 for (auto it : factoryScores)
640 {
641 if (it.second == minScore)
642 {
643 optimalFactories.push_back(it.first);
644 }
645 }
646
647 // For all compatible Factories matching the best score, find the preferred one for the current layer.
648 for (auto&& srcPref : srcPrefs)
649 {
650 for (auto&& comp : optimalFactories)
651 {
652 if (comp == srcPref)
653 {
654 return comp;
655 }
656 }
657 }
658
659 return ITensorHandleFactory::LegacyFactoryId;
660}
661
Derek Lambertif674aa02019-08-01 15:56:25 +0100662EdgeStrategy CalculateEdgeStrategy(BackendsMap& backends,
663 ITensorHandleFactory::FactoryId srcFactoryId,
664 const Layer& layer,
665 const Layer& connectedLayer,
666 TensorHandleFactoryRegistry& registry)
Derek Lamberti84da38b2019-06-13 11:40:08 +0100667{
668 auto toBackend = backends.find(connectedLayer.GetBackendId());
669 BOOST_ASSERT_MSG(toBackend != backends.end(), "Backend id not found for the connected layer");
670
671 auto dstPrefs = toBackend->second.get()->GetHandleFactoryPreferences();
672
673 // Legacy API check for backward compatibility
674 if (srcFactoryId == ITensorHandleFactory::LegacyFactoryId || dstPrefs.empty())
675 {
676 if (layer.GetBackendId() != connectedLayer.GetBackendId())
677 {
Derek Lambertif674aa02019-08-01 15:56:25 +0100678 return EdgeStrategy::CopyToTarget;
Derek Lamberti84da38b2019-06-13 11:40:08 +0100679 }
680 else
681 {
Derek Lambertif674aa02019-08-01 15:56:25 +0100682 return EdgeStrategy::DirectCompatibility;
Derek Lamberti84da38b2019-06-13 11:40:08 +0100683 }
684 }
685
686 // TensorHandleFactory API present, so perform more sophisticated strategies.
Derek Lambertif674aa02019-08-01 15:56:25 +0100687 // Dst Output layers don't require copy because they use import or map/unmap
Derek Lamberti84da38b2019-06-13 11:40:08 +0100688 if (connectedLayer.GetType() == LayerType::Output)
689 {
Derek Lambertif674aa02019-08-01 15:56:25 +0100690 return EdgeStrategy::DirectCompatibility;
Derek Lamberti84da38b2019-06-13 11:40:08 +0100691 }
692
693 // Search for direct match in prefs
694 for (auto&& pref : dstPrefs)
695 {
696 if (pref == srcFactoryId)
697 {
Derek Lambertif674aa02019-08-01 15:56:25 +0100698 return EdgeStrategy::DirectCompatibility;
Derek Lamberti84da38b2019-06-13 11:40:08 +0100699 }
700 }
701
702 // Search for export/import options
703 ITensorHandleFactory* srcFactory = registry.GetFactory(srcFactoryId);
Derek Lambertif674aa02019-08-01 15:56:25 +0100704 if (srcFactory->GetExportFlags() != 0)
Derek Lamberti84da38b2019-06-13 11:40:08 +0100705 {
706 for (auto&& pref : dstPrefs)
707 {
708 ITensorHandleFactory* dstFactory = registry.GetFactory(pref);
James Conroyffab16f2019-11-07 14:37:09 +0000709
James Conroy47e863d2019-11-18 17:07:43 +0000710 // Handles cases when a destPref is not listed in TensorHandleFactoryRegistry
James Conroyffab16f2019-11-07 14:37:09 +0000711 if (!dstFactory) {
James Conroy47e863d2019-11-18 17:07:43 +0000712 continue;
James Conroyffab16f2019-11-07 14:37:09 +0000713 }
714
Derek Lambertif674aa02019-08-01 15:56:25 +0100715 if ((dstFactory->GetImportFlags() & srcFactory->GetExportFlags()) != 0)
Derek Lamberti84da38b2019-06-13 11:40:08 +0100716 {
Derek Lambertif674aa02019-08-01 15:56:25 +0100717 return EdgeStrategy::ExportToTarget;
Derek Lamberti84da38b2019-06-13 11:40:08 +0100718 }
719 }
720 }
721
722 // Search for copy options via map/unmap
723 if (srcFactory->SupportsMapUnmap())
724 {
725 for (auto&& pref : dstPrefs)
726 {
727 ITensorHandleFactory* dstFactory = registry.GetFactory(pref);
James Conroy47e863d2019-11-18 17:07:43 +0000728 if (dstFactory && dstFactory->SupportsMapUnmap())
Derek Lamberti84da38b2019-06-13 11:40:08 +0100729 {
Derek Lambertif674aa02019-08-01 15:56:25 +0100730 return EdgeStrategy::CopyToTarget;
Derek Lamberti84da38b2019-06-13 11:40:08 +0100731 }
732 }
733 }
734
Derek Lambertif674aa02019-08-01 15:56:25 +0100735 return EdgeStrategy::Undefined;
Derek Lamberti84da38b2019-06-13 11:40:08 +0100736}
737
738// Select the TensorHandleFactories and the corresponding memory strategy
739OptimizationResult SelectTensorHandleStrategy(Graph& optGraph,
740 BackendsMap& backends,
741 TensorHandleFactoryRegistry& registry,
742 Optional<std::vector<std::string>&> errMessages)
743{
744 OptimizationResult result;
745
746 optGraph.ForEachLayer([&backends, &registry, &result, &errMessages](Layer* layer)
747 {
748 BOOST_ASSERT(layer);
749
750 // Lets make sure the backend is in our list of supported backends. Something went wrong during backend
751 // assignment if this check fails
752 BOOST_ASSERT(backends.find(layer->GetBackendId()) != backends.end());
753
754 // Check each output separately
755 for (unsigned int slotIdx = 0; slotIdx < layer->GetNumOutputSlots(); slotIdx++)
756 {
757 OutputSlot& outputSlot = layer->GetOutputSlot(slotIdx);
758
759 ITensorHandleFactory::FactoryId slotOption = ITensorHandleFactory::LegacyFactoryId;
760
761 // Calculate the factory to use which results in the fewest copies being made.
762 switch(layer->GetType())
763 {
764 case LayerType::Input:
765 slotOption = CalculateSlotOptionForInput(backends, outputSlot, registry);
766 break;
767 case LayerType::Output:
768 slotOption = CalculateSlotOptionForOutput(backends, outputSlot, registry);
769 break;
770 default:
771 slotOption = CalculateSlotOption(backends, outputSlot, registry);
772 break;
773 }
774 outputSlot.SetTensorHandleFactory(slotOption);
775
Derek Lambertif674aa02019-08-01 15:56:25 +0100776 // Now determine the "best" edge strategy for each connection given the slotOption.
Derek Lamberti84da38b2019-06-13 11:40:08 +0100777 unsigned int connectionIdx = 0;
778 for (auto&& connection : outputSlot.GetConnections())
779 {
780 const Layer& connectedLayer = connection->GetOwningLayer();
781
Derek Lambertif674aa02019-08-01 15:56:25 +0100782 EdgeStrategy strategy = CalculateEdgeStrategy(backends, slotOption, *layer, connectedLayer, registry);
Derek Lamberti84da38b2019-06-13 11:40:08 +0100783
Derek Lambertif674aa02019-08-01 15:56:25 +0100784 if (strategy == EdgeStrategy::Undefined)
Derek Lamberti84da38b2019-06-13 11:40:08 +0100785 {
786 result.m_Error = true;
787 if (errMessages)
788 {
789 errMessages.value().emplace_back("Could not find valid strategy required for compatibility"
790 " between backends.");
791 }
792 return;
793 }
794
Derek Lambertif674aa02019-08-01 15:56:25 +0100795 outputSlot.SetEdgeStrategy(connectionIdx, strategy);
Derek Lamberti84da38b2019-06-13 11:40:08 +0100796
797 connectionIdx++;
798 }
799 }
800 });
801
802 return result;
803}
804
Matteo Martincigh49124022019-01-11 13:25:59 +0000805IOptimizedNetworkPtr Optimize(const INetwork& inNetwork,
806 const std::vector<BackendId>& backendPreferences,
807 const IDeviceSpec& deviceSpec,
808 const OptimizerOptions& options,
Rob Hughes23214432019-11-05 11:27:36 +0000809 Optional<std::vector<std::string>&> messages)
Matteo Martincigh49124022019-01-11 13:25:59 +0000810{
811 if (backendPreferences.empty())
812 {
813 throw armnn::InvalidArgumentException("Invoked Optimize with no backends specified");
814 }
815
816 const Network& network = *boost::polymorphic_downcast<const Network*>(&inNetwork);
817 std::unique_ptr<Graph> graph = std::make_unique<Graph>(network.GetGraph());
818
819 auto optNet = IOptimizedNetworkPtr(new OptimizedNetwork(std::move(graph)), &IOptimizedNetwork::Destroy);
820
821 OptimizedNetwork* optNetObjPtr = boost::polymorphic_downcast<OptimizedNetwork*>(optNet.get());
822
Matteo Martincighadddddb2019-01-24 14:06:23 +0000823 // Get the optimized graph
824 Graph& optGraph = optNetObjPtr->GetGraph();
825
Matteo Martincigh49124022019-01-11 13:25:59 +0000826 // Perform optimisation passes
827 using namespace optimizations;
Matteo Martincighadddddb2019-01-24 14:06:23 +0000828 Optimizer::Pass(optGraph, MakeOptimizations(SquashEqualPermuteSiblings(),
829 SquashEqualReshapeSiblings(),
830 OptimizeInversePermutes(),
831 MovePermuteUp(),
832 PermuteAsReshape(),
Nina Drozd861985f2019-04-18 14:48:51 +0100833 OptimizeConsecutiveReshapes(),
Rob Hughes3a7d3a72019-09-24 16:59:56 +0100834 FoldPadIntoConvolution2d(),
835 PermuteAndBatchToSpaceAsDepthToSpace()));
Matteo Martincigh49124022019-01-11 13:25:59 +0000836
Matteo Martincighadddddb2019-01-24 14:06:23 +0000837 // Infer the tensor infos for all output slots. Throws an exception on failure
838 optGraph.InferTensorInfos();
Matteo Martincigh49124022019-01-11 13:25:59 +0000839
840 // If Fp32 to Fp16 optimization is set convert Fp32 network to Fp16
841 if (options.m_ReduceFp32ToFp16)
842 {
Matteo Martincighadddddb2019-01-24 14:06:23 +0000843 Optimizer::Pass(optGraph, MakeOptimizations(Fp32NetworkToFp16Converter()));
Derek Lambertidd6804b2019-11-27 09:29:57 +0000844 Optimizer::Pass(optGraph, MakeOptimizations(ConvertConstantsFloatToHalf()));
Matteo Martincigh49124022019-01-11 13:25:59 +0000845 }
846
847 // Initialize backend settings
848 BackendSettings backendSettings(backendPreferences, deviceSpec);
849 if (backendSettings.GetAvailablePreferredBackends().empty())
850 {
851 std::stringstream failureMsg;
852 failureMsg << "None of the preferred backends " << backendPreferences
853 << " are supported. Current platform provides " << backendSettings.m_SupportedBackends;
Rob Hughes23214432019-11-05 11:27:36 +0000854 ReportError(failureMsg.str(), messages);
Matteo Martincigh49124022019-01-11 13:25:59 +0000855 return IOptimizedNetworkPtr(nullptr, &IOptimizedNetwork::Destroy);
856 }
857
Derek Lamberti84da38b2019-06-13 11:40:08 +0100858 // Create a map to temporarily hold initialized backend objects
859 TensorHandleFactoryRegistry tensorHandleFactoryRegistry;
860 BackendsMap backends = CreateSupportedBackends(tensorHandleFactoryRegistry, backendSettings);
861
Matteo Martincigh49124022019-01-11 13:25:59 +0000862 // Assign an available backend to each layer
Matteo Martincighadddddb2019-01-24 14:06:23 +0000863 Graph::Iterator firstLayer = optGraph.begin();
864 Graph::Iterator lastLayer = optGraph.end();
Derek Lamberti84da38b2019-06-13 11:40:08 +0100865 OptimizationResult assignBackendsResult = AssignBackends(optNetObjPtr,
866 backendSettings,
867 firstLayer,
868 lastLayer,
Rob Hughes23214432019-11-05 11:27:36 +0000869 messages);
Derek Lamberti84da38b2019-06-13 11:40:08 +0100870 if (assignBackendsResult.m_Error)
Matteo Martincigh49124022019-01-11 13:25:59 +0000871 {
872 // Failed to assign a backend to each layer
jimfly016b0b53d2018-10-08 14:43:01 +0100873 return IOptimizedNetworkPtr(nullptr, &IOptimizedNetwork::Destroy);
874 }
telsoa01c577f2c2018-08-31 09:22:23 +0100875
Matteo Martincighadddddb2019-01-24 14:06:23 +0000876 Optimizer::Pass(optGraph, MakeOptimizations(OptimizeInverseConversionsFp16(),
877 OptimizeInverseConversionsFp32()));
telsoa01c577f2c2018-08-31 09:22:23 +0100878
Matteo Martincighadddddb2019-01-24 14:06:23 +0000879 // Apply the backend-specific optimizations
880 OptimizationResult backendOptimizationResult = ApplyBackendOptimizations(optNetObjPtr,
881 backendSettings,
Derek Lamberti84da38b2019-06-13 11:40:08 +0100882 backends,
Rob Hughes23214432019-11-05 11:27:36 +0000883 messages);
Matteo Martincighadddddb2019-01-24 14:06:23 +0000884 if (backendOptimizationResult.m_Error)
Matteo Martincigh49124022019-01-11 13:25:59 +0000885 {
Matteo Martincighadddddb2019-01-24 14:06:23 +0000886 // Failed to apply the backend-specific optimizations
887 return IOptimizedNetworkPtr(nullptr, &IOptimizedNetwork::Destroy);
Matteo Martincigh49124022019-01-11 13:25:59 +0000888 }
889
Matteo Martincighadddddb2019-01-24 14:06:23 +0000890 // If the debug flag is set, then insert a DebugLayer after each layer
891 // Doing this after applying the backend optimizations as they might have changed some layers
892 if (options.m_Debug)
893 {
894 Optimizer::Pass(optGraph, MakeOptimizations(InsertDebugLayer()));
895 }
896
Derek Lamberti84da38b2019-06-13 11:40:08 +0100897 // Calculate the compatibility strategies for tensor handles
898 OptimizationResult strategyResult = SelectTensorHandleStrategy(optGraph,
899 backends,
900 tensorHandleFactoryRegistry,
Rob Hughes23214432019-11-05 11:27:36 +0000901 messages);
Derek Lamberti84da38b2019-06-13 11:40:08 +0100902 if (strategyResult.m_Error)
903 {
904 // Failed to apply the backend-specific optimizations
905 return IOptimizedNetworkPtr(nullptr, &IOptimizedNetwork::Destroy);
906 }
907
908 // Based on the tensor handle strategy determined above, insert copy layers where required.
Derek Lambertif674aa02019-08-01 15:56:25 +0100909 optGraph.AddCompatibilityLayers(backends, tensorHandleFactoryRegistry);
telsoa01c577f2c2018-08-31 09:22:23 +0100910
911 // Convert constants
Matteo Martincighadddddb2019-01-24 14:06:23 +0000912 Optimizer::Pass(optGraph, MakeOptimizations(ConvertConstantsFloatToHalf()));
913 Optimizer::Pass(optGraph, MakeOptimizations(ConvertConstantsHalfToFloat()));
telsoa01c577f2c2018-08-31 09:22:23 +0100914
Derek Lamberti84da38b2019-06-13 11:40:08 +0100915 // Run backend specific optimizations (deprecated)
Matteo Martincigh49124022019-01-11 13:25:59 +0000916 for (auto&& chosenBackend : backendSettings.m_SelectedBackends)
David Beck263e3492018-11-09 14:46:40 +0000917 {
918 auto factoryFun = BackendRegistryInstance().GetFactory(chosenBackend);
919 auto backendPtr = factoryFun();
920 BOOST_ASSERT(backendPtr.get() != nullptr);
921
Matteo Martincighed735042019-05-22 09:42:43 +0100922 ARMNN_NO_DEPRECATE_WARN_BEGIN
David Beck263e3492018-11-09 14:46:40 +0000923 auto backendSpecificOptimizations = backendPtr->GetOptimizations();
Matteo Martincighed735042019-05-22 09:42:43 +0100924 ARMNN_NO_DEPRECATE_WARN_END
925
David Beck263e3492018-11-09 14:46:40 +0000926 if (!backendSpecificOptimizations.empty())
927 {
928 Optimizer::Pass(optNetObjPtr->GetGraph(), backendSpecificOptimizations);
929 }
930 }
931
telsoa01c577f2c2018-08-31 09:22:23 +0100932 return optNet;
telsoa014fcda012018-03-09 14:13:49 +0000933}
934
935Network::Network()
Jan Eilers99d9d4a2019-11-06 10:02:16 +0000936: m_Graph(std::make_unique<Graph>()),
937 m_Guid(profiling::ProfilingService::Instance().NextGuid())
telsoa014fcda012018-03-09 14:13:49 +0000938{
939}
940
941Network::~Network()
942{
943}
944
Jan Eilers99d9d4a2019-11-06 10:02:16 +0000945Status Network::PrintGraph()
946{
947 m_Graph->Print();
948 return Status::Success;
949}
950
telsoa014fcda012018-03-09 14:13:49 +0000951IConnectableLayer* Network::AddInputLayer(LayerBindingId id, const char* name)
952{
953 return m_Graph->AddLayer<InputLayer>(id, name);
954}
955
Éanna Ó Catháin4e1e1362018-11-12 11:36:34 +0000956IConnectableLayer* Network::AddBatchToSpaceNdLayer(const BatchToSpaceNdDescriptor& batchToSpaceNdDescriptor,
957 const char* name)
958{
959 return m_Graph->AddLayer<BatchToSpaceNdLayer>(batchToSpaceNdDescriptor, name);
960}
961
Aron Virginas-Tar77bfb5e2019-10-16 17:45:38 +0100962IConnectableLayer* Network::AddComparisonLayer(const ComparisonDescriptor& comparisonDescriptor,
963 const char* name)
964{
965 return m_Graph->AddLayer<ComparisonLayer>(comparisonDescriptor, name);
966}
967
telsoa014fcda012018-03-09 14:13:49 +0000968IConnectableLayer* Network::AddFullyConnectedLayerImpl(const FullyConnectedDescriptor& fullyConnectedDescriptor,
telsoa01c577f2c2018-08-31 09:22:23 +0100969 const ConstTensor& weights,
Aron Virginas-Tarad402702019-02-22 17:03:44 +0000970 const Optional<ConstTensor>& biases,
telsoa01c577f2c2018-08-31 09:22:23 +0100971 const char* name)
telsoa014fcda012018-03-09 14:13:49 +0000972{
Aron Virginas-Tarad402702019-02-22 17:03:44 +0000973 if (fullyConnectedDescriptor.m_BiasEnabled && !biases.has_value())
telsoa014fcda012018-03-09 14:13:49 +0000974 {
Aron Virginas-Tarad402702019-02-22 17:03:44 +0000975 throw InvalidArgumentException("AddFullyConnectedLayer: biases cannot be empty");
telsoa014fcda012018-03-09 14:13:49 +0000976 }
977
978 const auto layer = m_Graph->AddLayer<FullyConnectedLayer>(fullyConnectedDescriptor, name);
979
980 layer->m_Weight = std::make_unique<ScopedCpuTensorHandle>(weights);
981
982 if (fullyConnectedDescriptor.m_BiasEnabled)
983 {
Aron Virginas-Tarad402702019-02-22 17:03:44 +0000984 layer->m_Bias = std::make_unique<ScopedCpuTensorHandle>(biases.value());
telsoa014fcda012018-03-09 14:13:49 +0000985 }
986
987 return layer;
988}
989
990IConnectableLayer* Network::AddFullyConnectedLayer(const FullyConnectedDescriptor& fullyConnectedDescriptor,
telsoa01c577f2c2018-08-31 09:22:23 +0100991 const ConstTensor& weights,
Aron Virginas-Tarad402702019-02-22 17:03:44 +0000992 const Optional<ConstTensor>& biases,
telsoa01c577f2c2018-08-31 09:22:23 +0100993 const char* name)
telsoa014fcda012018-03-09 14:13:49 +0000994{
Aron Virginas-Tarad402702019-02-22 17:03:44 +0000995 return AddFullyConnectedLayerImpl(fullyConnectedDescriptor, weights, biases, name);
telsoa014fcda012018-03-09 14:13:49 +0000996}
997
Aron Virginas-Tarad402702019-02-22 17:03:44 +0000998IConnectableLayer* Network::AddFullyConnectedLayer(const FullyConnectedDescriptor& fullyConnectedDescriptor,
999 const ConstTensor& weights,
1000 const char* name)
1001{
Matteo Martincighfc598e12019-05-14 10:36:13 +01001002 Optional<ConstTensor> biases;
Aron Virginas-Tarad402702019-02-22 17:03:44 +00001003 return AddFullyConnectedLayerImpl(fullyConnectedDescriptor, weights, biases, name);
1004}
1005
telsoa014fcda012018-03-09 14:13:49 +00001006IConnectableLayer* Network::AddFullyConnectedLayer(const FullyConnectedDescriptor& fullyConnectedDescriptor,
telsoa01c577f2c2018-08-31 09:22:23 +01001007 const ConstTensor& weights,
1008 const ConstTensor& biases,
1009 const char* name)
telsoa014fcda012018-03-09 14:13:49 +00001010{
Aron Virginas-Tarad402702019-02-22 17:03:44 +00001011 Optional<ConstTensor> optionalBiases(biases);
1012 return AddFullyConnectedLayerImpl(fullyConnectedDescriptor, weights, optionalBiases, name);
telsoa014fcda012018-03-09 14:13:49 +00001013}
1014
Jim Flynne242f2d2019-05-22 14:24:13 +01001015IConnectableLayer* Network::AddConcatLayer(const ConcatDescriptor& concatDescriptor,
Jim Flynn906f9462019-05-10 13:55:21 +01001016 const char* name)
1017{
Jim Flynne242f2d2019-05-22 14:24:13 +01001018 return m_Graph->AddLayer<ConcatLayer>(concatDescriptor, name);
Jim Flynn906f9462019-05-10 13:55:21 +01001019}
1020
telsoa014fcda012018-03-09 14:13:49 +00001021IConnectableLayer* Network::AddConvolution2dLayerImpl(const Convolution2dDescriptor& convolution2dDescriptor,
telsoa01c577f2c2018-08-31 09:22:23 +01001022 const ConstTensor& weights,
Aron Virginas-Tarad402702019-02-22 17:03:44 +00001023 const Optional<ConstTensor>& biases,
telsoa01c577f2c2018-08-31 09:22:23 +01001024 const char* name)
telsoa014fcda012018-03-09 14:13:49 +00001025{
Aron Virginas-Tarad402702019-02-22 17:03:44 +00001026 if (convolution2dDescriptor.m_BiasEnabled && !biases.has_value())
telsoa014fcda012018-03-09 14:13:49 +00001027 {
Aron Virginas-Tarad402702019-02-22 17:03:44 +00001028 throw InvalidArgumentException("AddConvolution2dLayer: biases cannot be empty");
telsoa014fcda012018-03-09 14:13:49 +00001029 }
1030
1031 const auto layer = m_Graph->AddLayer<Convolution2dLayer>(convolution2dDescriptor, name);
1032
1033 layer->m_Weight = std::make_unique<ScopedCpuTensorHandle>(weights);
1034
1035 if (convolution2dDescriptor.m_BiasEnabled)
1036 {
Aron Virginas-Tarad402702019-02-22 17:03:44 +00001037 layer->m_Bias = std::make_unique<ScopedCpuTensorHandle>(biases.value());
telsoa014fcda012018-03-09 14:13:49 +00001038 }
1039
1040 return layer;
1041}
1042
1043IConnectableLayer* Network::AddConvolution2dLayer(const Convolution2dDescriptor& convolution2dDescriptor,
telsoa01c577f2c2018-08-31 09:22:23 +01001044 const ConstTensor& weights,
Aron Virginas-Tarad402702019-02-22 17:03:44 +00001045 const Optional<ConstTensor>& biases,
telsoa01c577f2c2018-08-31 09:22:23 +01001046 const char* name)
telsoa014fcda012018-03-09 14:13:49 +00001047{
Aron Virginas-Tarad402702019-02-22 17:03:44 +00001048 return AddConvolution2dLayerImpl(convolution2dDescriptor, weights, biases, name);
telsoa014fcda012018-03-09 14:13:49 +00001049}
Aron Virginas-Tarad402702019-02-22 17:03:44 +00001050
Aron Virginas-Tarad402702019-02-22 17:03:44 +00001051IConnectableLayer* Network::AddConvolution2dLayer(const Convolution2dDescriptor& convolution2dDescriptor,
1052 const ConstTensor& weights,
1053 const char* name)
1054{
Matteo Martincighfc598e12019-05-14 10:36:13 +01001055 Optional<ConstTensor> biases;
Aron Virginas-Tarad402702019-02-22 17:03:44 +00001056 return AddConvolution2dLayerImpl(convolution2dDescriptor, weights, biases, name);
1057}
1058
telsoa014fcda012018-03-09 14:13:49 +00001059IConnectableLayer* Network::AddConvolution2dLayer(const Convolution2dDescriptor& convolution2dDescriptor,
telsoa01c577f2c2018-08-31 09:22:23 +01001060 const ConstTensor& weights,
1061 const ConstTensor& biases,
1062 const char* name)
telsoa014fcda012018-03-09 14:13:49 +00001063{
Aron Virginas-Tarad402702019-02-22 17:03:44 +00001064 Optional<ConstTensor> optionalBiases(biases);
1065 return AddConvolution2dLayerImpl(convolution2dDescriptor, weights, optionalBiases, name);
telsoa014fcda012018-03-09 14:13:49 +00001066}
1067
1068IConnectableLayer* Network::AddDepthwiseConvolution2dLayerImpl(
1069 const DepthwiseConvolution2dDescriptor& convolution2dDescriptor,
1070 const ConstTensor& weights,
Aron Virginas-Tarad402702019-02-22 17:03:44 +00001071 const Optional<ConstTensor>& biases,
telsoa014fcda012018-03-09 14:13:49 +00001072 const char* name)
1073{
Aron Virginas-Tarad402702019-02-22 17:03:44 +00001074 if (convolution2dDescriptor.m_BiasEnabled && !biases.has_value())
telsoa014fcda012018-03-09 14:13:49 +00001075 {
Aron Virginas-Tarad402702019-02-22 17:03:44 +00001076 throw InvalidArgumentException("AddDepthwiseConvolution2dLayer: biases cannot be empty");
telsoa014fcda012018-03-09 14:13:49 +00001077 }
1078
Matteo Martincigh3d6898c2019-01-15 16:11:44 +00001079 const auto layer = m_Graph->AddLayer<DepthwiseConvolution2dLayer>(convolution2dDescriptor, name);
telsoa014fcda012018-03-09 14:13:49 +00001080
1081 layer->m_Weight = std::make_unique<ScopedCpuTensorHandle>(weights);
1082
1083 if (convolution2dDescriptor.m_BiasEnabled)
1084 {
Aron Virginas-Tarad402702019-02-22 17:03:44 +00001085 layer->m_Bias = std::make_unique<ScopedCpuTensorHandle>(biases.value());
telsoa014fcda012018-03-09 14:13:49 +00001086 }
1087
1088 return layer;
1089}
1090
Aron Virginas-Tardd6247f2019-09-19 14:31:17 +01001091IConnectableLayer* Network::AddDepthToSpaceLayer(const DepthToSpaceDescriptor& depthToSpaceDescriptor,
1092 const char* name)
1093{
1094 return m_Graph->AddLayer<DepthToSpaceLayer>(depthToSpaceDescriptor, name);
1095}
1096
telsoa014fcda012018-03-09 14:13:49 +00001097IConnectableLayer* Network::AddDepthwiseConvolution2dLayer(
Aron Virginas-Tarad402702019-02-22 17:03:44 +00001098 const DepthwiseConvolution2dDescriptor& convolution2dDescriptor,
1099 const ConstTensor& weights,
1100 const Optional<ConstTensor>& biases,
1101 const char* name)
1102{
1103 return AddDepthwiseConvolution2dLayerImpl(convolution2dDescriptor, weights, biases, name);
1104}
1105
Aron Virginas-Tarad402702019-02-22 17:03:44 +00001106IConnectableLayer* Network::AddDepthwiseConvolution2dLayer(
telsoa014fcda012018-03-09 14:13:49 +00001107 const DepthwiseConvolution2dDescriptor& convolution2dDescriptor,
1108 const ConstTensor& weights,
1109 const char* name)
1110{
Matteo Martincighfc598e12019-05-14 10:36:13 +01001111 Optional<ConstTensor> biases;
Aron Virginas-Tarad402702019-02-22 17:03:44 +00001112 return AddDepthwiseConvolution2dLayerImpl(convolution2dDescriptor, weights, biases, name);
telsoa014fcda012018-03-09 14:13:49 +00001113}
Aron Virginas-Tarad402702019-02-22 17:03:44 +00001114
telsoa014fcda012018-03-09 14:13:49 +00001115IConnectableLayer* Network::AddDepthwiseConvolution2dLayer(
1116 const DepthwiseConvolution2dDescriptor& convolution2dDescriptor,
1117 const ConstTensor& weights,
1118 const ConstTensor& biases,
1119 const char* name)
1120{
Aron Virginas-Tarad402702019-02-22 17:03:44 +00001121 Optional<ConstTensor> optionalBiases(biases);
1122 return AddDepthwiseConvolution2dLayerImpl(convolution2dDescriptor, weights, optionalBiases, name);
telsoa014fcda012018-03-09 14:13:49 +00001123}
1124
Narumol Prangnawarat94dd5d82019-01-23 18:06:26 +00001125IConnectableLayer* Network::AddDetectionPostProcessLayer(const armnn::DetectionPostProcessDescriptor& descriptor,
Narumol Prangnawarat6d302bf2019-02-04 11:46:26 +00001126 const ConstTensor& anchors, const char* name)
Narumol Prangnawarat94dd5d82019-01-23 18:06:26 +00001127{
Narumol Prangnawarat6d302bf2019-02-04 11:46:26 +00001128 const auto layer = m_Graph->AddLayer<DetectionPostProcessLayer>(descriptor, name);
1129
1130 layer->m_Anchors = std::make_unique<ScopedCpuTensorHandle>(anchors);
1131
1132 return layer;
Narumol Prangnawarat94dd5d82019-01-23 18:06:26 +00001133}
1134
telsoa014fcda012018-03-09 14:13:49 +00001135IConnectableLayer* Network::AddPermuteLayer(const PermuteDescriptor& permuteDescriptor,
1136 const char* name)
1137{
1138 return m_Graph->AddLayer<PermuteLayer>(permuteDescriptor, name);
1139}
1140
1141IConnectableLayer* Network::AddPooling2dLayer(const Pooling2dDescriptor& pooling2dDescriptor,
1142 const char* name)
1143{
1144 return m_Graph->AddLayer<Pooling2dLayer>(pooling2dDescriptor, name);
1145}
1146
1147IConnectableLayer* Network::AddActivationLayer(const ActivationDescriptor& activationDescriptor,
1148 const char* name)
1149{
1150 return m_Graph->AddLayer<ActivationLayer>(activationDescriptor, name);
1151}
1152
Nikhil Rajee391d52019-09-05 17:50:44 +01001153IConnectableLayer* Network::AddArgMinMaxLayer(const ArgMinMaxDescriptor& argMinMaxDescriptor,
1154 const char* name)
1155{
1156 return m_Graph->AddLayer<ArgMinMaxLayer>(argMinMaxDescriptor, name);
1157}
1158
telsoa01c577f2c2018-08-31 09:22:23 +01001159IConnectableLayer* Network::AddNormalizationLayer(const NormalizationDescriptor&
1160normalizationDescriptor,
telsoa014fcda012018-03-09 14:13:49 +00001161 const char* name)
1162{
1163 return m_Graph->AddLayer<NormalizationLayer>(normalizationDescriptor, name);
1164}
1165
Aron Virginas-Tar636ab402019-09-16 14:27:45 +01001166IConnectableLayer* Network::AddSliceLayer(const SliceDescriptor& sliceDescriptor, const char* name)
1167{
1168 return m_Graph->AddLayer<SliceLayer>(sliceDescriptor, name);
1169}
1170
telsoa014fcda012018-03-09 14:13:49 +00001171IConnectableLayer* Network::AddSoftmaxLayer(const SoftmaxDescriptor& softmaxDescriptor,
1172 const char* name)
1173{
1174 return m_Graph->AddLayer<SoftmaxLayer>(softmaxDescriptor, name);
1175}
1176
1177IConnectableLayer* Network::AddSplitterLayer(const ViewsDescriptor& splitterDescriptor,
1178 const char* name)
1179{
1180 return m_Graph->AddLayer<SplitterLayer>(splitterDescriptor, name);
1181}
1182
Nattapat Chaimanowong5a4304a2018-11-28 10:44:37 +00001183IConnectableLayer* Network::AddMaximumLayer(const char* name)
1184{
1185 return m_Graph->AddLayer<MaximumLayer>(name);
1186}
1187
Éanna Ó Catháin20e58802018-12-04 10:29:06 +00001188IConnectableLayer* Network::AddMinimumLayer(const char* name)
1189{
1190 return m_Graph->AddLayer<MinimumLayer>(name);
1191}
1192
Jim Flynne242f2d2019-05-22 14:24:13 +01001193IConnectableLayer* Network::AddMergerLayer(const MergerDescriptor& mergerDescriptor,
Jim Flynn906f9462019-05-10 13:55:21 +01001194 const char* name)
telsoa014fcda012018-03-09 14:13:49 +00001195{
Jim Flynne242f2d2019-05-22 14:24:13 +01001196 return AddConcatLayer(mergerDescriptor, name);
telsoa014fcda012018-03-09 14:13:49 +00001197}
1198
Kevin May868eb142019-09-04 17:29:31 +01001199IConnectableLayer* Network::AddAbsLayer(const char * name)
1200{
1201 return m_Graph->AddLayer<AbsLayer>(name);
1202}
1203
telsoa014fcda012018-03-09 14:13:49 +00001204IConnectableLayer* Network::AddAdditionLayer(const char* name)
1205{
1206 return m_Graph->AddLayer<AdditionLayer>(name);
1207}
1208
1209IConnectableLayer* Network::AddMultiplicationLayer(const char* name)
1210{
1211 return m_Graph->AddLayer<MultiplicationLayer>(name);
1212}
1213
1214IConnectableLayer* Network::AddOutputLayer(LayerBindingId id, const char* name)
1215{
1216 return m_Graph->AddLayer<OutputLayer>(id, name);
1217}
1218
1219IConnectableLayer* Network::AddBatchNormalizationLayer(const BatchNormalizationDescriptor& desc,
1220 const ConstTensor& mean,
1221 const ConstTensor& variance,
1222 const ConstTensor& beta,
1223 const ConstTensor& gamma,
1224 const char* name)
1225{
1226 const auto layer = m_Graph->AddLayer<BatchNormalizationLayer>(desc, name);
1227
1228 layer->m_Mean = std::make_unique<ScopedCpuTensorHandle>(mean);
1229 layer->m_Variance = std::make_unique<ScopedCpuTensorHandle>(variance);
1230 layer->m_Beta = std::make_unique<ScopedCpuTensorHandle>(beta);
1231 layer->m_Gamma = std::make_unique<ScopedCpuTensorHandle>(gamma);
1232
1233 return layer;
1234}
1235
Aron Virginas-Tar169d2f12019-07-01 19:01:44 +01001236IConnectableLayer* Network::AddResizeBilinearLayer(const ResizeBilinearDescriptor& descriptor,
1237 const char* name)
telsoa014fcda012018-03-09 14:13:49 +00001238{
Aron Virginas-Tar169d2f12019-07-01 19:01:44 +01001239 ResizeDescriptor resizeDescriptor;
1240 resizeDescriptor.m_Method = ResizeMethod::Bilinear;
1241 resizeDescriptor.m_DataLayout = descriptor.m_DataLayout;
1242 resizeDescriptor.m_TargetWidth = descriptor.m_TargetWidth;
1243 resizeDescriptor.m_TargetHeight = descriptor.m_TargetHeight;
1244
1245 return m_Graph->AddLayer<ResizeLayer>(resizeDescriptor, name);
telsoa014fcda012018-03-09 14:13:49 +00001246}
1247
Teresa Charlina9075df2019-06-27 15:41:57 +01001248IConnectableLayer* Network::AddResizeLayer(const ResizeDescriptor&
1249resizeDescriptor, const char* name)
1250{
Aron Virginas-Tar169d2f12019-07-01 19:01:44 +01001251 return m_Graph->AddLayer<ResizeLayer>(resizeDescriptor, name);
Teresa Charlina9075df2019-06-27 15:41:57 +01001252}
1253
Kevin Mayce5045a2019-10-02 14:07:47 +01001254IConnectableLayer* Network::AddInstanceNormalizationLayer(const InstanceNormalizationDescriptor& desc,
1255 const char* name)
1256{
1257 return m_Graph->AddLayer<InstanceNormalizationLayer>(desc, name);
1258}
1259
Matteo Martincighbcd3c852018-09-28 14:14:12 +01001260IConnectableLayer* Network::AddL2NormalizationLayer(const L2NormalizationDescriptor& desc,
1261 const char* name)
telsoa014fcda012018-03-09 14:13:49 +00001262{
Matteo Martincighbcd3c852018-09-28 14:14:12 +01001263 return m_Graph->AddLayer<L2NormalizationLayer>(desc, name);
telsoa014fcda012018-03-09 14:13:49 +00001264}
1265
Aron Virginas-Tarf982dea2019-10-11 14:07:53 +01001266IConnectableLayer* Network::AddLogSoftmaxLayer(const LogSoftmaxDescriptor& desc,
1267 const char* name)
1268{
1269 return m_Graph->AddLayer<LogSoftmaxLayer>(desc, name);
1270}
1271
telsoa014fcda012018-03-09 14:13:49 +00001272IConnectableLayer* Network::AddConstantLayer(const ConstTensor& input, const char* name)
1273{
telsoa01c577f2c2018-08-31 09:22:23 +01001274 auto layer = m_Graph->AddLayer<ConstantLayer>(name);
1275
1276 layer->m_LayerOutput = std::make_unique<ScopedCpuTensorHandle>(input);
1277
1278 return layer;
telsoa014fcda012018-03-09 14:13:49 +00001279}
1280
telsoa01c577f2c2018-08-31 09:22:23 +01001281IConnectableLayer* Network::AddReshapeLayer(const ReshapeDescriptor& reshapeDescriptor,
1282 const char* name)
telsoa014fcda012018-03-09 14:13:49 +00001283{
1284 return m_Graph->AddLayer<ReshapeLayer>(reshapeDescriptor, name);
1285}
1286
Nattapat Chaimanowong207ef9a2018-11-02 10:57:25 +00001287IConnectableLayer* Network::AddSpaceToBatchNdLayer(const SpaceToBatchNdDescriptor& spaceToBatchNdDescriptor,
1288 const char* name)
1289{
1290 return m_Graph->AddLayer<SpaceToBatchNdLayer>(spaceToBatchNdDescriptor, name);
1291}
1292
Aron Virginas-Tar972af152019-06-11 14:14:03 +01001293IConnectableLayer* Network::AddSpaceToDepthLayer(const SpaceToDepthDescriptor& spaceToDepthDescriptor,
1294 const char* name)
1295{
1296 return m_Graph->AddLayer<SpaceToDepthLayer>(spaceToDepthDescriptor, name);
1297}
1298
telsoa014fcda012018-03-09 14:13:49 +00001299IConnectableLayer* Network::AddFloorLayer(const char* name)
1300{
1301 return m_Graph->AddLayer<FloorLayer>(name);
1302}
1303
telsoa01c577f2c2018-08-31 09:22:23 +01001304IConnectableLayer* Network::AddLstmLayer(const LstmDescriptor& descriptor,
1305 const LstmInputParams& params,
1306 const char* name)
1307{
1308 const auto layer = m_Graph->AddLayer<LstmLayer>(descriptor, name);
1309
1310 //Lstm Basic Parameters
1311 layer->m_BasicParameters.m_InputToForgetWeights =
1312 std::make_unique<ScopedCpuTensorHandle>(*(params.m_InputToForgetWeights));
1313 layer->m_BasicParameters.m_InputToCellWeights =
1314 std::make_unique<ScopedCpuTensorHandle>(*(params.m_InputToCellWeights));
1315 layer->m_BasicParameters.m_InputToOutputWeights =
1316 std::make_unique<ScopedCpuTensorHandle>(*(params.m_InputToOutputWeights));
1317 layer->m_BasicParameters.m_RecurrentToForgetWeights =
1318 std::make_unique<ScopedCpuTensorHandle>(*(params.m_RecurrentToForgetWeights));
1319 layer->m_BasicParameters.m_RecurrentToCellWeights =
1320 std::make_unique<ScopedCpuTensorHandle>(*(params.m_RecurrentToCellWeights));
1321 layer->m_BasicParameters.m_RecurrentToOutputWeights =
1322 std::make_unique<ScopedCpuTensorHandle>(*(params.m_RecurrentToOutputWeights));
1323 layer->m_BasicParameters.m_ForgetGateBias =
1324 std::make_unique<ScopedCpuTensorHandle>(*(params.m_ForgetGateBias));
1325 layer->m_BasicParameters.m_CellBias =
1326 std::make_unique<ScopedCpuTensorHandle>(*(params.m_CellBias));
1327 layer->m_BasicParameters.m_OutputGateBias =
1328 std::make_unique<ScopedCpuTensorHandle>(*(params.m_OutputGateBias));
1329
1330 //Lstm Cifg parameters
1331 if(!descriptor.m_CifgEnabled)
1332 {
1333 if(params.m_InputToInputWeights == nullptr)
1334 {
1335 throw InvalidArgumentException("AddLstmLayer: Input To Input Weights cannot be NULL");
1336 }
1337 if(params.m_RecurrentToInputWeights == nullptr)
1338 {
1339 throw InvalidArgumentException(
1340 "AddLstmLayer: Recurrent To Input Weights cannot be NULL");
1341 }
1342 if(params.m_InputGateBias == nullptr)
1343 {
1344 throw InvalidArgumentException("AddLstmLayer: Input Gate Bias cannot be NULL");
1345 }
1346 layer->m_CifgParameters.m_InputToInputWeights =
1347 std::make_unique<ScopedCpuTensorHandle>(*(params.m_InputToInputWeights));
1348 layer->m_CifgParameters.m_RecurrentToInputWeights =
1349 std::make_unique<ScopedCpuTensorHandle>(*(params.m_RecurrentToInputWeights));
1350 // In the VTS tests, cell-to-input weights may be null, even if the other CIFG params are not.
1351 if(params.m_CellToInputWeights != nullptr)
1352 {
1353 layer->m_CifgParameters.m_CellToInputWeights =
1354 std::make_unique<ScopedCpuTensorHandle>(*(params.m_CellToInputWeights));
1355 }
1356 layer->m_CifgParameters.m_InputGateBias =
1357 std::make_unique<ScopedCpuTensorHandle>(*(params.m_InputGateBias));
1358 }
1359
1360 //Lstm projection parameters
1361 if(descriptor.m_ProjectionEnabled)
1362 {
1363 if(params.m_ProjectionWeights == nullptr)
1364 {
1365 throw InvalidArgumentException("AddLstmLayer: Projection Weights cannot be NULL");
1366 }
1367 layer->m_ProjectionParameters.m_ProjectionWeights =
1368 std::make_unique<ScopedCpuTensorHandle>(*(params.m_ProjectionWeights));
1369 if(params.m_ProjectionBias != nullptr)
1370 {
1371 layer->m_ProjectionParameters.m_ProjectionBias =
1372 std::make_unique<ScopedCpuTensorHandle>(*(params.m_ProjectionBias));
1373 }
1374 }
1375
1376 //Lstm Peephole params
1377 if(descriptor.m_PeepholeEnabled)
1378 {
1379 if(params.m_CellToForgetWeights == nullptr)
1380 {
1381 throw InvalidArgumentException("AddLstmLayer: Cell To Forget Weights cannot be NULL");
1382 }
1383 if(params.m_CellToOutputWeights == nullptr)
1384 {
1385 throw InvalidArgumentException("AddLstmLayer: Cell To Output Weights cannot be NULL");
1386 }
1387 layer->m_PeepholeParameters.m_CellToForgetWeights =
1388 std::make_unique<ScopedCpuTensorHandle>(*(params.m_CellToForgetWeights));
1389 layer->m_PeepholeParameters.m_CellToOutputWeights =
1390 std::make_unique<ScopedCpuTensorHandle>(*(params.m_CellToOutputWeights));
1391 }
Jan Eilersf8c62972019-07-17 11:07:49 +01001392
1393 //Lstm Layer Normalization params
1394 if(descriptor.m_LayerNormEnabled)
1395 {
1396 if(!descriptor.m_CifgEnabled)
1397 {
1398 if(params.m_InputLayerNormWeights == nullptr)
1399 {
1400 throw InvalidArgumentException("AddLstmLayer: Input layer normalization weights cannot be NULL");
1401 }
1402 layer->m_LayerNormParameters.m_InputLayerNormWeights =
1403 std::make_unique<ScopedCpuTensorHandle>(*(params.m_InputLayerNormWeights));
1404 }
1405
1406 if(params.m_ForgetLayerNormWeights == nullptr)
1407 {
1408 throw InvalidArgumentException("AddLstmLayer: Forget layer normalization weights cannot be NULL");
1409 }
1410 if(params.m_CellLayerNormWeights == nullptr)
1411 {
1412 throw InvalidArgumentException("AddLstmLayer: Cell layer normalization weights cannot be NULL");
1413 }
1414 if(params.m_OutputLayerNormWeights == nullptr)
1415 {
1416 throw InvalidArgumentException("AddLstmLayer: Output layer normalization weights cannot be NULL");
1417 }
1418 layer->m_LayerNormParameters.m_ForgetLayerNormWeights =
1419 std::make_unique<ScopedCpuTensorHandle>(*(params.m_ForgetLayerNormWeights));
1420 layer->m_LayerNormParameters.m_CellLayerNormWeights =
1421 std::make_unique<ScopedCpuTensorHandle>(*(params.m_CellLayerNormWeights));
1422 layer->m_LayerNormParameters.m_OutputLayerNormWeights =
1423 std::make_unique<ScopedCpuTensorHandle>(*(params.m_OutputLayerNormWeights));
1424 }
telsoa01c577f2c2018-08-31 09:22:23 +01001425 return layer;
1426}
1427
Francis Murtaghe7a86a42018-08-29 12:42:10 +01001428IConnectableLayer* Network::AddDivisionLayer(const char* name)
1429{
1430 return m_Graph->AddLayer<DivisionLayer>(name);
1431}
1432
David Beck19526222018-09-12 16:00:08 +01001433IConnectableLayer* Network::AddSubtractionLayer(const char* name)
1434{
1435 return m_Graph->AddLayer<SubtractionLayer>(name);
1436}
1437
narpra0132b90462018-09-13 11:07:48 +01001438IConnectableLayer* Network::AddMeanLayer(const MeanDescriptor& meanDescriptor, const char* name)
1439{
1440 return m_Graph->AddLayer<MeanLayer>(meanDescriptor,name);
1441}
1442
Mohamed Nour Abouelseoud5662c202018-09-24 13:30:09 +01001443IConnectableLayer* Network::AddPadLayer(const PadDescriptor& padDescriptor, const char* name)
1444{
1445 return m_Graph->AddLayer<PadLayer>(padDescriptor,name);
1446}
1447
Derek Lambertia9cca6a2019-03-25 15:41:58 +00001448IConnectableLayer *Network::AddQuantizeLayer(const char *name)
1449{
1450 return m_Graph->AddLayer<QuantizeLayer>(name);
1451}
1452
Nattapat Chaimanowonge4294fd2019-03-28 09:56:53 +00001453IConnectableLayer* Network::AddDequantizeLayer(const char* name)
1454{
1455 return m_Graph->AddLayer<DequantizeLayer>(name);
1456}
1457
Conor Kennedy430b5d82018-11-14 15:28:28 +00001458IConnectableLayer* Network::AddStridedSliceLayer(const StridedSliceDescriptor& stridedSliceDescriptor,
1459 const char* name)
1460{
1461 return m_Graph->AddLayer<StridedSliceLayer>(stridedSliceDescriptor, name);
1462}
1463
Matteo Martincigh59a950c2018-12-13 12:48:25 +00001464IConnectableLayer* Network::AddGreaterLayer(const char* name)
1465{
Aron Virginas-Tar77bfb5e2019-10-16 17:45:38 +01001466 return AddComparisonLayer(ComparisonDescriptor(ComparisonOperation::Greater), name);
Matteo Martincigh59a950c2018-12-13 12:48:25 +00001467}
1468
FrancisMurtagh20995952018-12-17 12:11:36 +00001469IConnectableLayer* Network::AddEqualLayer(const char* name)
1470{
Aron Virginas-Tar77bfb5e2019-10-16 17:45:38 +01001471 return AddComparisonLayer(ComparisonDescriptor(ComparisonOperation::Equal), name);
FrancisMurtagh20995952018-12-17 12:11:36 +00001472}
1473
Mohamed Nour Abouelseouda1d3c6a2018-12-27 12:39:16 +00001474IConnectableLayer* Network::AddRsqrtLayer(const char * name)
1475{
1476 return m_Graph->AddLayer<RsqrtLayer>(name);
1477}
1478
narpra01b89b05f2019-01-16 09:53:09 +00001479IConnectableLayer* Network::AddGatherLayer(const char* name)
1480{
1481 return m_Graph->AddLayer<GatherLayer>(name);
1482}
1483
Nattapat Chaimanowong1f886302019-04-05 13:37:19 +01001484IConnectableLayer* Network::AddMergeLayer(const char* name)
1485{
1486 return m_Graph->AddLayer<MergeLayer>(name);
1487}
1488
Sadik Armaganeff363d2019-04-05 15:25:46 +01001489IConnectableLayer* Network::AddSwitchLayer(const char* name)
1490{
1491 return m_Graph->AddLayer<SwitchLayer>(name);
1492}
1493
Matteo Martincigh0e406ee2019-06-12 15:42:18 +01001494IConnectableLayer* Network::AddPreluLayer(const char* name)
1495{
1496 return m_Graph->AddLayer<PreluLayer>(name);
1497}
1498
Aron Virginas-Tar639fb042019-06-20 14:28:19 +01001499IConnectableLayer* Network::AddTransposeConvolution2dLayer(const TransposeConvolution2dDescriptor& descriptor,
1500 const ConstTensor& weights,
1501 const Optional<ConstTensor>& biases,
1502 const char* name)
1503{
1504 if (descriptor.m_BiasEnabled && !biases.has_value())
1505 {
1506 throw InvalidArgumentException("AddTransposeConvolution2dLayer: Biases cannot be empty");
1507 }
1508
1509 const auto layer = m_Graph->AddLayer<TransposeConvolution2dLayer>(descriptor, name);
1510
1511 layer->m_Weight = std::make_unique<ScopedCpuTensorHandle>(weights);
1512
1513 if (descriptor.m_BiasEnabled)
1514 {
1515 layer->m_Bias = std::make_unique<ScopedCpuTensorHandle>(biases.value());
1516 }
1517
1518 return layer;
1519}
1520
Matthew Jackson2b8c1da2019-07-04 14:59:16 +01001521IConnectableLayer* Network::AddStackLayer(const StackDescriptor& stackDescriptor,
1522 const char* name)
1523{
1524 return m_Graph->AddLayer<StackLayer>(stackDescriptor, name);
1525}
1526
Derek Lamberti013c3902019-10-21 10:46:16 +01001527
1528IConnectableLayer* Network::AddStandInLayer(const StandInDescriptor& desc,
1529 const char* name)
1530{
1531 return m_Graph->AddLayer<StandInLayer>(desc, name);
1532}
1533
James Conroyee18dc82019-07-17 11:27:46 +01001534IConnectableLayer* Network::AddQuantizedLstmLayer(const QuantizedLstmInputParams& params,
1535 const char* name)
1536{
1537 const auto layer = m_Graph->AddLayer<QuantizedLstmLayer>(name);
1538
1539 // InputToX weights
1540 layer->m_QuantizedLstmParameters.m_InputToInputWeights =
Francis Murtaghbb590b42019-08-14 09:51:36 +01001541 std::make_unique<ScopedCpuTensorHandle>(params.GetInputToInputWeights());
James Conroyee18dc82019-07-17 11:27:46 +01001542 layer->m_QuantizedLstmParameters.m_InputToForgetWeights =
Francis Murtaghbb590b42019-08-14 09:51:36 +01001543 std::make_unique<ScopedCpuTensorHandle>(params.GetInputToForgetWeights());
James Conroyee18dc82019-07-17 11:27:46 +01001544 layer->m_QuantizedLstmParameters.m_InputToCellWeights =
Francis Murtaghbb590b42019-08-14 09:51:36 +01001545 std::make_unique<ScopedCpuTensorHandle>(params.GetInputToCellWeights());
James Conroyee18dc82019-07-17 11:27:46 +01001546 layer->m_QuantizedLstmParameters.m_InputToOutputWeights =
Francis Murtaghbb590b42019-08-14 09:51:36 +01001547 std::make_unique<ScopedCpuTensorHandle>(params.GetInputToOutputWeights());
James Conroyee18dc82019-07-17 11:27:46 +01001548
1549 // RecurrentToX weights
1550 layer->m_QuantizedLstmParameters.m_RecurrentToInputWeights =
Francis Murtaghbb590b42019-08-14 09:51:36 +01001551 std::make_unique<ScopedCpuTensorHandle>(params.GetRecurrentToInputWeights());
James Conroyee18dc82019-07-17 11:27:46 +01001552 layer->m_QuantizedLstmParameters.m_RecurrentToForgetWeights =
Francis Murtaghbb590b42019-08-14 09:51:36 +01001553 std::make_unique<ScopedCpuTensorHandle>(params.GetRecurrentToForgetWeights());
James Conroyee18dc82019-07-17 11:27:46 +01001554 layer->m_QuantizedLstmParameters.m_RecurrentToCellWeights =
Francis Murtaghbb590b42019-08-14 09:51:36 +01001555 std::make_unique<ScopedCpuTensorHandle>(params.GetRecurrentToCellWeights());
James Conroyee18dc82019-07-17 11:27:46 +01001556 layer->m_QuantizedLstmParameters.m_RecurrentToOutputWeights =
Francis Murtaghbb590b42019-08-14 09:51:36 +01001557 std::make_unique<ScopedCpuTensorHandle>(params.GetRecurrentToOutputWeights());
James Conroyee18dc82019-07-17 11:27:46 +01001558
1559 // Bias
1560 layer->m_QuantizedLstmParameters.m_InputGateBias =
Francis Murtaghbb590b42019-08-14 09:51:36 +01001561 std::make_unique<ScopedCpuTensorHandle>(params.GetInputGateBias());
James Conroyee18dc82019-07-17 11:27:46 +01001562 layer->m_QuantizedLstmParameters.m_ForgetGateBias =
Francis Murtaghbb590b42019-08-14 09:51:36 +01001563 std::make_unique<ScopedCpuTensorHandle>(params.GetForgetGateBias());
James Conroyee18dc82019-07-17 11:27:46 +01001564 layer->m_QuantizedLstmParameters.m_CellBias =
Francis Murtaghbb590b42019-08-14 09:51:36 +01001565 std::make_unique<ScopedCpuTensorHandle>(params.GetCellBias());
James Conroyee18dc82019-07-17 11:27:46 +01001566 layer->m_QuantizedLstmParameters.m_OutputGateBias =
Francis Murtaghbb590b42019-08-14 09:51:36 +01001567 std::make_unique<ScopedCpuTensorHandle>(params.GetOutputGateBias());
James Conroyee18dc82019-07-17 11:27:46 +01001568
1569 return layer;
1570}
1571
Mike Kelly8c1701a2019-02-11 17:01:27 +00001572void Network::Accept(ILayerVisitor& visitor) const
1573{
1574 for (auto layer : GetGraph())
1575 {
1576 layer->Accept(visitor);
1577 };
1578}
1579
telsoa014fcda012018-03-09 14:13:49 +00001580OptimizedNetwork::OptimizedNetwork(std::unique_ptr<Graph> graph)
Jan Eilers99d9d4a2019-11-06 10:02:16 +00001581 : m_Graph(std::move(graph)),
1582 m_Guid(profiling::ProfilingService::Instance().NextGuid())
telsoa014fcda012018-03-09 14:13:49 +00001583{
1584}
1585
1586OptimizedNetwork::~OptimizedNetwork()
1587{
1588}
1589
1590} // namespace armnn