blob: 7edc6240a173e930e60044746e2d0dabaa27b62d [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>
Matteo Martincighe5b8eb92019-11-28 15:45:42 +000017#include <armnn/backends/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>
Matthew Benthamf48afc62020-01-15 17:55:08 +000024#include <armnn/Logging.hpp>
telsoa014fcda012018-03-09 14:13:49 +000025
Jan Eilers99d9d4a2019-11-06 10:02:16 +000026#include <ProfilingService.hpp>
27
telsoa014fcda012018-03-09 14:13:49 +000028#include <fcntl.h>
29#include <algorithm>
30#include <fstream>
31#include <memory>
telsoa01c577f2c2018-08-31 09:22:23 +010032#include <vector>
33#include <algorithm>
telsoa014fcda012018-03-09 14:13:49 +000034
35#include <boost/assert.hpp>
36#include <boost/format.hpp>
telsoa014fcda012018-03-09 14:13:49 +000037#include <boost/numeric/conversion/converter_policies.hpp>
38#include <boost/cast.hpp>
39
40namespace armnn
41{
42
43armnn::INetwork* INetwork::CreateRaw()
44{
45 return new Network();
46}
47
48armnn::INetworkPtr INetwork::Create()
49{
50 return INetworkPtr(CreateRaw(), &INetwork::Destroy);
51}
52
53void INetwork::Destroy(INetwork* network)
54{
55 delete boost::polymorphic_downcast<Network*>(network);
56}
57
telsoa014fcda012018-03-09 14:13:49 +000058void IOptimizedNetwork::Destroy(IOptimizedNetwork* network)
59{
60 delete boost::polymorphic_downcast<OptimizedNetwork*>(network);
61}
62
63Status OptimizedNetwork::PrintGraph()
64{
65 m_Graph->Print();
66 return Status::Success;
67}
68
surmeh01bceff2f2018-03-29 16:29:27 +010069Status OptimizedNetwork::SerializeToDot(std::ostream& stream) const
70{
71 return m_Graph->SerializeToDot(stream);
72}
73
Matteo Martincigh49124022019-01-11 13:25:59 +000074void ReportError(const std::string& errorMessage,
75 Optional<std::vector<std::string>&> errorMessages)
76{
77 std::stringstream fullErrorMessage;
78 fullErrorMessage << "ERROR: " << errorMessage;
Derek Lamberti08446972019-11-26 16:38:31 +000079 ARMNN_LOG(warning) << fullErrorMessage.str();
Matteo Martincigh49124022019-01-11 13:25:59 +000080 if (errorMessages)
81 {
82 errorMessages.value().push_back(fullErrorMessage.str());
83 }
84}
85
86void ReportWarning(const std::string& warningMessage,
87 Optional<std::vector<std::string>&> warningMessages)
88{
89 std::stringstream fullWarningMessage;
90 fullWarningMessage << "WARNING: " << warningMessage;
Derek Lamberti08446972019-11-26 16:38:31 +000091 ARMNN_LOG(warning) << fullWarningMessage.str();
Matteo Martincigh49124022019-01-11 13:25:59 +000092 if (warningMessages)
93 {
94 warningMessages.value().push_back(fullWarningMessage.str());
95 }
96}
97
jimfly016b0b53d2018-10-08 14:43:01 +010098bool CheckScaleSetOnQuantizedType(Layer* layer, Optional<std::vector<std::string>&> errMessages)
99{
100 bool noErrors = true;
101 unsigned int numOutputs = layer->GetNumOutputSlots();
102 for (unsigned int i = 0; i < numOutputs; i++) {
David Monahanb8554702019-04-25 16:03:38 +0100103 OutputSlot& outputSlot = layer->GetOutputSlot(i);
104 TensorInfo info = outputSlot.GetTensorInfo();
Derek Lambertif90c56d2020-01-10 17:14:08 +0000105 if (DataType::QAsymmU8 == info.GetDataType()) {
jimfly016b0b53d2018-10-08 14:43:01 +0100106 if (0.f == info.GetQuantizationScale()) {
107 noErrors = false;
108 std::stringstream ss;
Matteo Martincigh49124022019-01-11 13:25:59 +0000109 ss << "output " << i << " of layer " << GetLayerTypeAsCString(layer->GetType())
jimfly016b0b53d2018-10-08 14:43:01 +0100110 << " (" << layer->GetNameStr() << ") is of type"
111 << " Quantized 8 bit but its scale parameter has not been set";
Matteo Martincigh49124022019-01-11 13:25:59 +0000112 ReportError(ss.str(), errMessages);
jimfly016b0b53d2018-10-08 14:43:01 +0100113 }
David Monahanb8554702019-04-25 16:03:38 +0100114 // Softmax under QuantisedAsymm8 must always be scale (1.0f/256.0f) and offset 0
115 if ((info.GetQuantizationScale() != (1.0f / 256.0f) ||
116 info.GetQuantizationOffset() != 0) &&
117 layer->GetType() == armnn::LayerType::Softmax)
118 {
119 std::stringstream ss;
120 ss << "Quantization parameters for Softmax layer (Scale: " <<
121 info.GetQuantizationScale() << " and Offset: " << info.GetQuantizationOffset() <<
122 ") are incorrect and have been updated to Scale: 0.00390625 and Offset: 0";
Derek Lamberti08446972019-11-26 16:38:31 +0000123 ARMNN_LOG(warning) << ss.str();
David Monahanb8554702019-04-25 16:03:38 +0100124 info.SetQuantizationScale((1.0f /256.0f));
125 info.SetQuantizationOffset(0);
126 outputSlot.SetTensorInfo(info);
127 }
jimfly016b0b53d2018-10-08 14:43:01 +0100128 }
129 }
130 return noErrors;
131}
132
Matteo Martincigh49124022019-01-11 13:25:59 +0000133OptimizationResult AssignBackends(OptimizedNetwork* optNetObjPtr,
134 BackendSettings& backendSettings,
135 Graph::Iterator& firstLayer,
136 Graph::Iterator& lastLayer,
137 Optional<std::vector<std::string>&> errMessages)
telsoa014fcda012018-03-09 14:13:49 +0000138{
Matteo Martincigh49124022019-01-11 13:25:59 +0000139 OptimizationResult result;
telsoa014fcda012018-03-09 14:13:49 +0000140
Matteo Martincigh49124022019-01-11 13:25:59 +0000141 // Helper lambda to compose meaningful error message before returning with error
142 auto ReturnWithError = [&](const Layer* layer)
telsoa01c577f2c2018-08-31 09:22:23 +0100143 {
jimfly016b0b53d2018-10-08 14:43:01 +0100144 std::stringstream failureMsg;
Matteo Martincigh49124022019-01-11 13:25:59 +0000145 failureMsg << "Layer of type " << GetLayerTypeAsCString(layer->GetType())
146 << " is not supported on any preferred backend " << backendSettings.m_PreferredBackends;
147 ReportError(failureMsg.str(), errMessages);
148
149 result.m_Error = true;
150 return result;
telsoa01c577f2c2018-08-31 09:22:23 +0100151 };
152
Matteo Martincigh49124022019-01-11 13:25:59 +0000153 auto availablePreferredBackends = backendSettings.GetAvailablePreferredBackends();
154 if (availablePreferredBackends.empty())
telsoa01c577f2c2018-08-31 09:22:23 +0100155 {
Matteo Martincigh49124022019-01-11 13:25:59 +0000156 std::stringstream failureMsg;
157 failureMsg << "No preferred backends are available";
158 ReportError(failureMsg.str(), errMessages);
159
160 result.m_Error = true;
161 return result;
162 }
163
164 for (auto it = firstLayer; it != lastLayer; ++it)
165 {
166 auto layer = *it;
Aron Virginas-Tar87972be2019-11-13 15:16:28 +0000167
168 DataType dataTypeIn = layer->GetNumInputSlots() == 0 ? DataType::Float32 :
169 layer->GetInputSlot(0).GetConnectedOutputSlot()->GetTensorInfo().GetDataType();
170 DataType dataTypeOut = layer->GetNumOutputSlots() == 0 ? DataType::Float32 :
171 layer->GetOutputSlot(0).GetTensorInfo().GetDataType();
172
telsoa01c577f2c2018-08-31 09:22:23 +0100173 std::string reasonIfUnsupported;
174 bool found = false;
jimfly016b0b53d2018-10-08 14:43:01 +0100175 if (!CheckScaleSetOnQuantizedType(layer, errMessages))
176 {
177 // don't bomb immediately, find all the quantized outputs
178 // which haven't had a scale set and report them all back.
Matteo Martincigh49124022019-01-11 13:25:59 +0000179 result.m_Error = true;
jimfly016b0b53d2018-10-08 14:43:01 +0100180 }
Matteo Martincigh49124022019-01-11 13:25:59 +0000181
David Beckf0b48452018-10-19 15:20:56 +0100182 for (const auto& backend : availablePreferredBackends)
telsoa01c577f2c2018-08-31 09:22:23 +0100183 {
184 // need to set the compute device on the layer
185 // before we can check if it is supported
David Beck33f0ae02018-10-18 15:13:56 +0100186 layer->SetBackendId(backend);
Aron Virginas-Tar87972be2019-11-13 15:16:28 +0000187 if (!IWorkloadFactory::IsLayerSupported(*layer, EmptyOptional(), reasonIfUnsupported))
telsoa01c577f2c2018-08-31 09:22:23 +0100188 {
Aron Virginas-Tar87972be2019-11-13 15:16:28 +0000189 if (dataTypeIn == DataType::Float16 || dataTypeOut == DataType::Float16)
telsoa01c577f2c2018-08-31 09:22:23 +0100190 {
191 if (IWorkloadFactory::IsLayerSupported(*layer, DataType::Float32, reasonIfUnsupported)
192 && layer->GetType() != LayerType::ConvertFp32ToFp16
193 && layer->GetType() != LayerType::ConvertFp16ToFp32)
194 {
195 // Insert FP16 -> FP32 conversion layer before current layer
Aron Virginas-Tar87972be2019-11-13 15:16:28 +0000196 std::vector<ConvertFp16ToFp32Layer*> convertFp16ToFp32Layers;
197 if (dataTypeIn == DataType::Float16)
198 {
199 convertFp16ToFp32Layers =
200 InsertConvertFp16ToFp32LayersBefore(optNetObjPtr->GetGraph(), *layer);
201 }
telsoa01c577f2c2018-08-31 09:22:23 +0100202
203 // Insert FP32 -> FP16 conversion layer after current layer
Aron Virginas-Tar87972be2019-11-13 15:16:28 +0000204 std::vector<ConvertFp32ToFp16Layer*> convertFp32ToFp16Layers;
205 if (dataTypeOut == DataType::Float16)
206 {
207 convertFp32ToFp16Layers =
208 InsertConvertFp32ToFp16LayersAfter(optNetObjPtr->GetGraph(), *layer);
209 }
telsoa01c577f2c2018-08-31 09:22:23 +0100210
211 // Assign a supported backend to the newly introduced conversion layers
David Beckf0b48452018-10-19 15:20:56 +0100212 auto AssignFirstSupportedBackend = [&](Layer* layer, BackendId preferredBackend)
telsoa01c577f2c2018-08-31 09:22:23 +0100213 {
214 bool supportedBackendFound = false;
215 std::string reasonIfUnsupported;
216
217 // Try preferred backend first
David Beck33f0ae02018-10-18 15:13:56 +0100218 layer->SetBackendId(preferredBackend);
David Beck29c75de2018-10-23 13:35:58 +0100219 if (IWorkloadFactory::IsLayerSupported(*layer,
220 EmptyOptional(),
221 reasonIfUnsupported))
telsoa01c577f2c2018-08-31 09:22:23 +0100222 {
223 supportedBackendFound = true;
224 }
225 else
226 {
David Beckf0b48452018-10-19 15:20:56 +0100227 for (const auto& backend : availablePreferredBackends)
telsoa01c577f2c2018-08-31 09:22:23 +0100228 {
229 // Skip preferred backend (we already determined that it is not supported)
230 if (backend == preferredBackend)
231 {
232 continue;
233 }
234
David Beck33f0ae02018-10-18 15:13:56 +0100235 layer->SetBackendId(backend);
David Beck29c75de2018-10-23 13:35:58 +0100236 if (IWorkloadFactory::IsLayerSupported(*layer,
237 EmptyOptional(),
238 reasonIfUnsupported))
telsoa01c577f2c2018-08-31 09:22:23 +0100239 {
240 supportedBackendFound = true;
241 break;
242 }
243 }
244 }
245
246 return supportedBackendFound;
247 };
248
249 for (ConvertFp16ToFp32Layer* convertLayer : convertFp16ToFp32Layers)
250 {
251 if (!AssignFirstSupportedBackend(convertLayer, backend))
252 {
253 return ReturnWithError(convertLayer);
254 }
255 }
256
257 for (ConvertFp32ToFp16Layer* convertLayer : convertFp32ToFp16Layers)
258 {
259 if (!AssignFirstSupportedBackend(convertLayer, backend))
260 {
261 return ReturnWithError(convertLayer);
262 }
263 }
264
265 found = true;
266 break;
267 }
268 }
jimfly016b0b53d2018-10-08 14:43:01 +0100269 std::stringstream warningMsg;
Matteo Martincigh49124022019-01-11 13:25:59 +0000270 warningMsg << "Layer of type " << GetLayerTypeAsCString(layer->GetType())
David Beck33f0ae02018-10-18 15:13:56 +0100271 << " is not supported on requested backend " << layer->GetBackendId().Get()
Aron Virginas-Tar87972be2019-11-13 15:16:28 +0000272 << " for input data type " << GetDataTypeName(dataTypeIn)
273 << " and output data type " << GetDataTypeName(dataTypeOut)
jimfly016b0b53d2018-10-08 14:43:01 +0100274 << " (reason: " << reasonIfUnsupported
275 << "), falling back to the next backend.";
Matteo Martincigh49124022019-01-11 13:25:59 +0000276 ReportWarning(warningMsg.str(), errMessages);
telsoa01c577f2c2018-08-31 09:22:23 +0100277 }
278 else
279 {
280 found = true;
Matteo Martincigh49124022019-01-11 13:25:59 +0000281 backendSettings.m_SelectedBackends.insert(backend);
telsoa01c577f2c2018-08-31 09:22:23 +0100282 break;
283 }
284 }
285
286 // If the layer is unsupported by any devices, log and return a null network.
Matteo Martincigh49124022019-01-11 13:25:59 +0000287 if (!found)
288 {
telsoa01c577f2c2018-08-31 09:22:23 +0100289 // NOTE: if the layer is not an operation queue type AND we have not got CpuRef as a
290 // fallback we should set the compute device on the layer to CpuRef (these are not
291 // available as accelerated operations, or are only available under certain
292 // conditions, currently they comprise MemCopy, Constant, Permute)
293 armnn::LayerType layerType = layer->GetType();
Matteo Martincigh49124022019-01-11 13:25:59 +0000294 if (!backendSettings.IsCpuRefUsed() && (layerType == armnn::LayerType::MemCopy ||
295 layerType == armnn::LayerType::Constant ||
296 layerType == armnn::LayerType::Permute))
telsoa01c577f2c2018-08-31 09:22:23 +0100297 {
Matteo Martincigh49124022019-01-11 13:25:59 +0000298 BackendId cpuBackendId(armnn::Compute::CpuRef);
299 layer->SetBackendId(cpuBackendId);
300 backendSettings.m_SelectedBackends.insert(cpuBackendId);
telsoa01c577f2c2018-08-31 09:22:23 +0100301 }
302 else
303 {
304 return ReturnWithError(layer);
305 }
306 }
307 }
Matteo Martincigh49124022019-01-11 13:25:59 +0000308
309 return result;
310}
311
Matteo Martincighadddddb2019-01-24 14:06:23 +0000312OptimizationResult AssignBackends(OptimizedNetwork* optNetObjPtr,
313 BackendSettings& backendSettings,
Derek Lambertiff05cc52019-04-26 13:05:17 +0100314 SubgraphView& subgraph,
Matteo Martincighadddddb2019-01-24 14:06:23 +0000315 Optional<std::vector<std::string>&> errMessages)
Matteo Martincigh49124022019-01-11 13:25:59 +0000316{
Derek Lambertiff05cc52019-04-26 13:05:17 +0100317 Graph::Iterator firstLayer = subgraph.begin();
318 Graph::Iterator lastLayer = subgraph.end();
Matteo Martincighadddddb2019-01-24 14:06:23 +0000319 return AssignBackends(optNetObjPtr,
320 backendSettings,
321 firstLayer,
322 lastLayer,
323 errMessages);
324}
325
Derek Lamberti84da38b2019-06-13 11:40:08 +0100326BackendsMap CreateSupportedBackends(TensorHandleFactoryRegistry& handleFactoryRegistry,
327 BackendSettings& backendSettings)
328{
329 BackendsMap backends;
330 auto const& backendRegistry = BackendRegistryInstance();
331 for (auto&& selectedBackend : backendSettings.m_SupportedBackends)
332 {
333 auto backendFactory = backendRegistry.GetFactory(selectedBackend);
334 auto backendObjPtr = backendFactory();
335 BOOST_ASSERT(backendObjPtr);
336
337 backendObjPtr->RegisterTensorHandleFactories(handleFactoryRegistry);
338
339 backends[backendObjPtr->GetId()] = std::move(backendObjPtr);
340 }
341
342 return backends;
343}
344
Matteo Martincighadddddb2019-01-24 14:06:23 +0000345OptimizationResult ApplyBackendOptimizations(OptimizedNetwork* optNetObjPtr,
346 BackendSettings& backendSettings,
Derek Lamberti84da38b2019-06-13 11:40:08 +0100347 BackendsMap& backends,
Matteo Martincighadddddb2019-01-24 14:06:23 +0000348 Optional<std::vector<std::string>&> errMessages)
349{
350 BOOST_ASSERT(optNetObjPtr);
Matteo Martincigh49124022019-01-11 13:25:59 +0000351
352 OptimizationResult result;
353
Matteo Martincighadddddb2019-01-24 14:06:23 +0000354 // Get the optimized graph
355 Graph& optGraph = optNetObjPtr->GetGraph();
Matteo Martincigh49124022019-01-11 13:25:59 +0000356
Matteo Martincighadddddb2019-01-24 14:06:23 +0000357 // Run backend specific optimizations
Matteo Martincighadddddb2019-01-24 14:06:23 +0000358 for (auto&& selectedBackend : backendSettings.m_SelectedBackends)
Matteo Martincigh49124022019-01-11 13:25:59 +0000359 {
Derek Lamberti84da38b2019-06-13 11:40:08 +0100360 auto backendObjPtr = backends.find(selectedBackend)->second.get();
Matteo Martincighadddddb2019-01-24 14:06:23 +0000361 BOOST_ASSERT(backendObjPtr);
362
363 // Select sub-graphs based on backend
Derek Lambertiff05cc52019-04-26 13:05:17 +0100364 SubgraphViewSelector::Subgraphs subgraphs =
Rob Hughes65c32262019-07-23 15:33:39 +0100365 SubgraphViewSelector::SelectSubgraphs(optGraph,
Matteo Martincigh602af092019-05-01 10:31:27 +0100366 // Select layers assigned to the requested backend
367 [&backendObjPtr](const Layer& layer)
368 {
369 return layer.GetType() != LayerType::Input &&
370 layer.GetType() != LayerType::Output &&
371 layer.GetBackendId() == backendObjPtr->GetId();
372 });
Derek Lambertiff05cc52019-04-26 13:05:17 +0100373 if (subgraphs.empty())
Matteo Martincigh49124022019-01-11 13:25:59 +0000374 {
Matteo Martincighadddddb2019-01-24 14:06:23 +0000375 // No sub-graphs found, try with next selected backend
376 continue;
Matteo Martincigh49124022019-01-11 13:25:59 +0000377 }
Matteo Martincighadddddb2019-01-24 14:06:23 +0000378
379 // Try to optimize each sub-graph
Derek Lambertiff05cc52019-04-26 13:05:17 +0100380 for (auto& subgraph : subgraphs)
Matteo Martincigh49124022019-01-11 13:25:59 +0000381 {
Matteo Martincighadddddb2019-01-24 14:06:23 +0000382 // Try to optimize the current sub-graph
Matteo Martincigh84924332019-05-09 12:46:16 +0100383 OptimizationViews optimizationViews = backendObjPtr->OptimizeSubgraphView(*subgraph);
384 BOOST_ASSERT(optimizationViews.Validate(*subgraph));
Matteo Martincighadddddb2019-01-24 14:06:23 +0000385
386 // Optimization attempted, check the resulting optimized sub-graph
Matteo Martincigh84924332019-05-09 12:46:16 +0100387 for (auto& substitution : optimizationViews.GetSubstitutions())
Matteo Martincighadddddb2019-01-24 14:06:23 +0000388 {
389 // Sub-graph optimized, substitute the sub-graph with the new optimized one in the main optimized graph
Matteo Martincigh84924332019-05-09 12:46:16 +0100390 SubgraphView& replacementSubgraph = substitution.m_ReplacementSubgraph;
391 SubgraphView& substitutableSubgraph = substitution.m_SubstitutableSubgraph;
392 optGraph.SubstituteSubgraph(substitutableSubgraph, replacementSubgraph);
Matteo Martincighadddddb2019-01-24 14:06:23 +0000393
394 // Assign the current backend to the optimized sub-graph
Matteo Martincigh84924332019-05-09 12:46:16 +0100395 std::for_each(replacementSubgraph.begin(), replacementSubgraph.end(), [&selectedBackend](Layer* l)
Derek Lambertic2fe5fb2019-05-08 10:23:08 +0100396 {
397 BOOST_ASSERT(l);
398 l->SetBackendId(selectedBackend);
399 });
Matteo Martincighadddddb2019-01-24 14:06:23 +0000400 }
Derek Lambertic2fe5fb2019-05-08 10:23:08 +0100401
Matteo Martincigh84924332019-05-09 12:46:16 +0100402 if (!optimizationViews.GetFailedSubgraphs().empty())
Matteo Martincighadddddb2019-01-24 14:06:23 +0000403 {
Matteo Martincighadddddb2019-01-24 14:06:23 +0000404 std::stringstream warningMsg;
Derek Lambertic2fe5fb2019-05-08 10:23:08 +0100405 warningMsg << "Some sub-graph(s) failed to optimized on " << backendObjPtr->GetId() << " backend.";
Matteo Martincighadddddb2019-01-24 14:06:23 +0000406 ReportWarning(warningMsg.str(), errMessages);
407
408 // Failed to optimize the given sub-graph, re-assign the sub-graph layers to other available backends
Derek Lambertic2fe5fb2019-05-08 10:23:08 +0100409 BackendSettings settingsCopy(backendSettings);
Matteo Martincighadddddb2019-01-24 14:06:23 +0000410 if (!backendObjPtr->GetId().IsCpuRef())
411 {
412 // Add the current backend to the list of backends to ignore
Derek Lambertic2fe5fb2019-05-08 10:23:08 +0100413 settingsCopy.m_IgnoredBackends.insert(backendObjPtr->GetId());
Matteo Martincighadddddb2019-01-24 14:06:23 +0000414 }
Derek Lambertic2fe5fb2019-05-08 10:23:08 +0100415
416 int count=0;
Matteo Martincigh84924332019-05-09 12:46:16 +0100417 for (auto& failedSubgraph : optimizationViews.GetFailedSubgraphs())
Matteo Martincighadddddb2019-01-24 14:06:23 +0000418 {
Derek Lambertic2fe5fb2019-05-08 10:23:08 +0100419 // An error occurred: the optimization was attempted but not performed, try different backends
420 std::stringstream subgraphMsg;
421 subgraphMsg << "Re-assigning backends to " << failedSubgraph.GetLayers().size()
422 << " layers inside sub-graph " << count++;
Matteo Martincigh328d92b2019-07-04 17:52:55 +0100423 ReportWarning(subgraphMsg.str(), errMessages);
Derek Lambertic2fe5fb2019-05-08 10:23:08 +0100424
425 OptimizationResult reassignmentResult = AssignBackends(optNetObjPtr,
426 settingsCopy,
427 *subgraph,
428 errMessages);
429 if (reassignmentResult.m_Error)
430 {
431 // Failed to re-assign one of the remaining backends to each layer of the sub-graph
432 result.m_Error = true;
433 return result;
434 }
Matteo Martincighadddddb2019-01-24 14:06:23 +0000435 }
Matteo Martincigh49124022019-01-11 13:25:59 +0000436 }
437 }
438 }
439
440 return result;
441}
442
Derek Lamberti84da38b2019-06-13 11:40:08 +0100443bool RequiresCopy(ITensorHandleFactory::FactoryId src,
444 ITensorHandleFactory::FactoryId dst,
445 TensorHandleFactoryRegistry& registry)
446{
447 if (src != dst)
448 {
449 ITensorHandleFactory* srcFactory = registry.GetFactory(src);
450 ITensorHandleFactory* dstFactory = registry.GetFactory(dst);
451
Matteo Martincigha6539ed2019-08-27 13:43:32 +0100452 if (srcFactory && dstFactory &&
453 (srcFactory->GetExportFlags() & dstFactory->GetImportFlags()) != 0)
Derek Lamberti84da38b2019-06-13 11:40:08 +0100454 {
455 return false;
456 }
457 return true;
458 }
459 return false;
460}
461
462// Find the handle factory for the input layer which results in fewest required copies.
463ITensorHandleFactory::FactoryId CalculateSlotOptionForInput(BackendsMap& backends,
464 OutputSlot& slot,
465 TensorHandleFactoryRegistry& registry)
466{
467 Layer& layer = slot.GetOwningLayer();
468 BOOST_ASSERT(layer.GetType() == LayerType::Input);
469
470 // Explicitly select the tensorhandle factory for InputLayer because the rules for it are slightly different. It
471 // doesn't matter which backend it is assigned to because they all use the same implementation, which
472 // requires Map/Unmap support. This means that, so long as the handle type supports map/unmap semantics, we can
473 // select a factory with maximum compatibility with the layers connected to the InputLayer.
474
475 // First ensure the from backends can support the TensorHandeAPI
476 auto frmBackend = backends.find(layer.GetBackendId());
477 if (frmBackend == backends.end() ||
478 !frmBackend->second->SupportsTensorAllocatorAPI())
479 {
480 return ITensorHandleFactory::LegacyFactoryId;
481 }
482
483 // Go through all connections to the output slot and determine the TensorHandleFactory which results in the
484 // fewest copies.
485 std::map<ITensorHandleFactory::FactoryId, int> factoryScores;
486 int topScore = 0;
487 ITensorHandleFactory::FactoryId topChoice = ITensorHandleFactory::LegacyFactoryId;
488
489 for (auto&& connection : slot.GetConnections())
490 {
491 const Layer& connectedLayer = connection->GetOwningLayer();
492
493 auto toBackend = backends.find(connectedLayer.GetBackendId());
494 BOOST_ASSERT_MSG(toBackend != backends.end(), "Backend id not found for the connected layer");
495
496 if (!toBackend->second.get()->SupportsTensorAllocatorAPI())
497 {
498 // The destination backend does not support the tensor allocator API, move to the next one
499 continue;
500 }
501
502 auto dstPrefs = toBackend->second.get()->GetHandleFactoryPreferences();
503 for (auto&& dst : dstPrefs)
504 {
Derek Lambertif674aa02019-08-01 15:56:25 +0100505 // Input layers use the mem copy workload or import, so the selected factory must
506 // support either the map/unmap API or Import API
Derek Lamberti84da38b2019-06-13 11:40:08 +0100507 ITensorHandleFactory* factory = registry.GetFactory(dst);
Derek Lambertif674aa02019-08-01 15:56:25 +0100508 if (!factory->SupportsMapUnmap() &&
509 !CheckFlag(factory->GetImportFlags(), MemorySource::Malloc)) // Just support cpu mem imports for now
Derek Lamberti84da38b2019-06-13 11:40:08 +0100510 {
Derek Lambertif674aa02019-08-01 15:56:25 +0100511 // The current tensor handle factory does not support the map/unmap or import
512 // strategy, move to the next one
Derek Lamberti84da38b2019-06-13 11:40:08 +0100513 continue;
514 }
515
516 auto it = factoryScores.find(dst);
517 if (it == factoryScores.end())
518 {
519 // Add new score to the table
520 factoryScores[dst] = 0;
521 if (topChoice == ITensorHandleFactory::LegacyFactoryId)
522 {
523 topChoice = dst;
524 }
525 }
526 else
527 {
528 // Increase the score
529 factoryScores[dst]++;
530
531 // Track the best option
532 if (factoryScores[dst] > topScore)
533 {
534 topScore = factoryScores[dst];
535 topChoice = dst;
536 }
537 }
538 }
539 }
540
541 return topChoice;
542}
543
544// Find the handle factory for the output layer which results in fewest required copies.
545ITensorHandleFactory::FactoryId CalculateSlotOptionForOutput(BackendsMap& backends,
546 OutputSlot& slot,
547 TensorHandleFactoryRegistry& registry)
548{
Derek Lamberti94a88d22019-12-10 21:12:59 +0000549 boost::ignore_unused(backends, slot, registry);
550 return ITensorHandleFactory::DeferredFactoryId;
Derek Lamberti84da38b2019-06-13 11:40:08 +0100551}
552
553// For all handle factories supported on the source backend, we wish to find the one which requires the fewest copies
554// when considering all connections.
555ITensorHandleFactory::FactoryId CalculateSlotOption(BackendsMap& backends,
556 OutputSlot& outputSlot,
557 TensorHandleFactoryRegistry& registry)
558{
559 // First ensure the from backends can support the TensorHandeAPI
560 Layer& layer = outputSlot.GetOwningLayer();
561 auto frmBackend = backends.find(layer.GetBackendId());
562 if (frmBackend == backends.end() ||
563 !frmBackend->second->SupportsTensorAllocatorAPI())
564 {
565 return ITensorHandleFactory::LegacyFactoryId;
566 }
567
568 // Connections to Output Layers requires support for map/unmap on the TensorHandle.
569 bool requiresMapUnmap = false;
570 for (auto&& connection : outputSlot.GetConnections())
571 {
572 const Layer& connectedLayer = connection->GetOwningLayer();
573 if (connectedLayer.GetType() == LayerType::Output)
574 {
575 requiresMapUnmap = true;
576 }
577 }
578
579 IBackendInternal* srcBackend = frmBackend->second.get();
580 auto srcPrefs = srcBackend->GetHandleFactoryPreferences();
581
582 // Initialize the scores
583 std::map<ITensorHandleFactory::FactoryId, int> factoryScores;
584 for (auto&& pref : srcPrefs)
585 {
586 if (requiresMapUnmap) // Only consider factories that support map/unmap if required
587 {
588 ITensorHandleFactory* factory = registry.GetFactory(pref);
589 if (!factory->SupportsMapUnmap())
590 {
591 // The current tensor handle factory does not support the map/unmap strategy, move to the next one
592 continue;
593 }
594 }
595
596 auto it = factoryScores.find(pref);
597 if (it == factoryScores.end())
598 {
599 // Add new score to the table
600 factoryScores[pref] = 0;
601 }
602 }
603
604 // Score each handle factory based on how many times it requires copies on the slot connections
605 for (auto&& connection : outputSlot.GetConnections())
606 {
607 const Layer& connectedLayer = connection->GetOwningLayer();
608
609 auto toBackend = backends.find(connectedLayer.GetBackendId());
610 BOOST_ASSERT_MSG(toBackend != backends.end(), "Backend id not found for the connected layer");
611
612 auto dstPrefs = toBackend->second.get()->GetHandleFactoryPreferences();
613 for (auto&& src : srcPrefs)
614 {
615 if (factoryScores.find(src) == factoryScores.end()) // Don't consider excluded factories
616 {
617 continue;
618 }
619
620 for (auto&& dst : dstPrefs)
621 {
622 if (RequiresCopy(src, dst, registry))
623 {
624 // Copy avoided, increase the score
625 factoryScores[src]++;
626 break;
627 }
628 }
629 }
630 }
631
632 // Find the lowest score
633 int minScore = std::numeric_limits<int>::max();
634 for (auto it : factoryScores)
635 {
636 minScore = std::min(minScore, it.second);
637 }
638
639 // Collect factories matching the best(lowest) score
640 std::vector<ITensorHandleFactory::FactoryId> optimalFactories;
641 for (auto it : factoryScores)
642 {
643 if (it.second == minScore)
644 {
645 optimalFactories.push_back(it.first);
646 }
647 }
648
649 // For all compatible Factories matching the best score, find the preferred one for the current layer.
650 for (auto&& srcPref : srcPrefs)
651 {
652 for (auto&& comp : optimalFactories)
653 {
654 if (comp == srcPref)
655 {
656 return comp;
657 }
658 }
659 }
660
661 return ITensorHandleFactory::LegacyFactoryId;
662}
663
Derek Lambertif674aa02019-08-01 15:56:25 +0100664EdgeStrategy CalculateEdgeStrategy(BackendsMap& backends,
665 ITensorHandleFactory::FactoryId srcFactoryId,
666 const Layer& layer,
667 const Layer& connectedLayer,
668 TensorHandleFactoryRegistry& registry)
Derek Lamberti84da38b2019-06-13 11:40:08 +0100669{
670 auto toBackend = backends.find(connectedLayer.GetBackendId());
671 BOOST_ASSERT_MSG(toBackend != backends.end(), "Backend id not found for the connected layer");
672
673 auto dstPrefs = toBackend->second.get()->GetHandleFactoryPreferences();
674
675 // Legacy API check for backward compatibility
676 if (srcFactoryId == ITensorHandleFactory::LegacyFactoryId || dstPrefs.empty())
677 {
678 if (layer.GetBackendId() != connectedLayer.GetBackendId())
679 {
Derek Lambertif674aa02019-08-01 15:56:25 +0100680 return EdgeStrategy::CopyToTarget;
Derek Lamberti84da38b2019-06-13 11:40:08 +0100681 }
682 else
683 {
Derek Lambertif674aa02019-08-01 15:56:25 +0100684 return EdgeStrategy::DirectCompatibility;
Derek Lamberti84da38b2019-06-13 11:40:08 +0100685 }
686 }
687
688 // TensorHandleFactory API present, so perform more sophisticated strategies.
Derek Lambertif674aa02019-08-01 15:56:25 +0100689 // Dst Output layers don't require copy because they use import or map/unmap
Derek Lamberti84da38b2019-06-13 11:40:08 +0100690 if (connectedLayer.GetType() == LayerType::Output)
691 {
Derek Lambertif674aa02019-08-01 15:56:25 +0100692 return EdgeStrategy::DirectCompatibility;
Derek Lamberti84da38b2019-06-13 11:40:08 +0100693 }
694
695 // Search for direct match in prefs
696 for (auto&& pref : dstPrefs)
697 {
698 if (pref == srcFactoryId)
699 {
Derek Lambertif674aa02019-08-01 15:56:25 +0100700 return EdgeStrategy::DirectCompatibility;
Derek Lamberti84da38b2019-06-13 11:40:08 +0100701 }
702 }
703
704 // Search for export/import options
705 ITensorHandleFactory* srcFactory = registry.GetFactory(srcFactoryId);
Derek Lambertif674aa02019-08-01 15:56:25 +0100706 if (srcFactory->GetExportFlags() != 0)
Derek Lamberti84da38b2019-06-13 11:40:08 +0100707 {
708 for (auto&& pref : dstPrefs)
709 {
710 ITensorHandleFactory* dstFactory = registry.GetFactory(pref);
James Conroyffab16f2019-11-07 14:37:09 +0000711
James Conroy47e863d2019-11-18 17:07:43 +0000712 // Handles cases when a destPref is not listed in TensorHandleFactoryRegistry
James Conroyffab16f2019-11-07 14:37:09 +0000713 if (!dstFactory) {
James Conroy47e863d2019-11-18 17:07:43 +0000714 continue;
James Conroyffab16f2019-11-07 14:37:09 +0000715 }
716
Derek Lambertif674aa02019-08-01 15:56:25 +0100717 if ((dstFactory->GetImportFlags() & srcFactory->GetExportFlags()) != 0)
Derek Lamberti84da38b2019-06-13 11:40:08 +0100718 {
Derek Lambertif674aa02019-08-01 15:56:25 +0100719 return EdgeStrategy::ExportToTarget;
Derek Lamberti84da38b2019-06-13 11:40:08 +0100720 }
721 }
722 }
723
724 // Search for copy options via map/unmap
725 if (srcFactory->SupportsMapUnmap())
726 {
727 for (auto&& pref : dstPrefs)
728 {
729 ITensorHandleFactory* dstFactory = registry.GetFactory(pref);
James Conroy47e863d2019-11-18 17:07:43 +0000730 if (dstFactory && dstFactory->SupportsMapUnmap())
Derek Lamberti84da38b2019-06-13 11:40:08 +0100731 {
Derek Lambertif674aa02019-08-01 15:56:25 +0100732 return EdgeStrategy::CopyToTarget;
Derek Lamberti84da38b2019-06-13 11:40:08 +0100733 }
734 }
735 }
736
Derek Lambertif674aa02019-08-01 15:56:25 +0100737 return EdgeStrategy::Undefined;
Derek Lamberti84da38b2019-06-13 11:40:08 +0100738}
739
740// Select the TensorHandleFactories and the corresponding memory strategy
741OptimizationResult SelectTensorHandleStrategy(Graph& optGraph,
742 BackendsMap& backends,
743 TensorHandleFactoryRegistry& registry,
744 Optional<std::vector<std::string>&> errMessages)
745{
746 OptimizationResult result;
747
748 optGraph.ForEachLayer([&backends, &registry, &result, &errMessages](Layer* layer)
749 {
750 BOOST_ASSERT(layer);
751
752 // Lets make sure the backend is in our list of supported backends. Something went wrong during backend
753 // assignment if this check fails
754 BOOST_ASSERT(backends.find(layer->GetBackendId()) != backends.end());
755
756 // Check each output separately
757 for (unsigned int slotIdx = 0; slotIdx < layer->GetNumOutputSlots(); slotIdx++)
758 {
759 OutputSlot& outputSlot = layer->GetOutputSlot(slotIdx);
760
761 ITensorHandleFactory::FactoryId slotOption = ITensorHandleFactory::LegacyFactoryId;
762
763 // Calculate the factory to use which results in the fewest copies being made.
764 switch(layer->GetType())
765 {
766 case LayerType::Input:
767 slotOption = CalculateSlotOptionForInput(backends, outputSlot, registry);
768 break;
769 case LayerType::Output:
770 slotOption = CalculateSlotOptionForOutput(backends, outputSlot, registry);
771 break;
772 default:
773 slotOption = CalculateSlotOption(backends, outputSlot, registry);
774 break;
775 }
776 outputSlot.SetTensorHandleFactory(slotOption);
777
Derek Lambertif674aa02019-08-01 15:56:25 +0100778 // Now determine the "best" edge strategy for each connection given the slotOption.
Derek Lamberti84da38b2019-06-13 11:40:08 +0100779 unsigned int connectionIdx = 0;
780 for (auto&& connection : outputSlot.GetConnections())
781 {
782 const Layer& connectedLayer = connection->GetOwningLayer();
783
Derek Lambertif674aa02019-08-01 15:56:25 +0100784 EdgeStrategy strategy = CalculateEdgeStrategy(backends, slotOption, *layer, connectedLayer, registry);
Derek Lamberti84da38b2019-06-13 11:40:08 +0100785
Derek Lambertif674aa02019-08-01 15:56:25 +0100786 if (strategy == EdgeStrategy::Undefined)
Derek Lamberti84da38b2019-06-13 11:40:08 +0100787 {
788 result.m_Error = true;
789 if (errMessages)
790 {
791 errMessages.value().emplace_back("Could not find valid strategy required for compatibility"
792 " between backends.");
793 }
794 return;
795 }
796
Derek Lambertif674aa02019-08-01 15:56:25 +0100797 outputSlot.SetEdgeStrategy(connectionIdx, strategy);
Derek Lamberti84da38b2019-06-13 11:40:08 +0100798
799 connectionIdx++;
800 }
801 }
802 });
803
804 return result;
805}
806
Matteo Martincigh49124022019-01-11 13:25:59 +0000807IOptimizedNetworkPtr Optimize(const INetwork& inNetwork,
808 const std::vector<BackendId>& backendPreferences,
809 const IDeviceSpec& deviceSpec,
810 const OptimizerOptions& options,
Rob Hughes23214432019-11-05 11:27:36 +0000811 Optional<std::vector<std::string>&> messages)
Matteo Martincigh49124022019-01-11 13:25:59 +0000812{
813 if (backendPreferences.empty())
814 {
815 throw armnn::InvalidArgumentException("Invoked Optimize with no backends specified");
816 }
817
818 const Network& network = *boost::polymorphic_downcast<const Network*>(&inNetwork);
819 std::unique_ptr<Graph> graph = std::make_unique<Graph>(network.GetGraph());
820
821 auto optNet = IOptimizedNetworkPtr(new OptimizedNetwork(std::move(graph)), &IOptimizedNetwork::Destroy);
822
823 OptimizedNetwork* optNetObjPtr = boost::polymorphic_downcast<OptimizedNetwork*>(optNet.get());
824
Matteo Martincighadddddb2019-01-24 14:06:23 +0000825 // Get the optimized graph
826 Graph& optGraph = optNetObjPtr->GetGraph();
827
Matteo Martincigh49124022019-01-11 13:25:59 +0000828 // Perform optimisation passes
829 using namespace optimizations;
Matteo Martincighadddddb2019-01-24 14:06:23 +0000830 Optimizer::Pass(optGraph, MakeOptimizations(SquashEqualPermuteSiblings(),
831 SquashEqualReshapeSiblings(),
832 OptimizeInversePermutes(),
833 MovePermuteUp(),
834 PermuteAsReshape(),
Nina Drozd861985f2019-04-18 14:48:51 +0100835 OptimizeConsecutiveReshapes(),
Rob Hughes3a7d3a72019-09-24 16:59:56 +0100836 FoldPadIntoConvolution2d(),
837 PermuteAndBatchToSpaceAsDepthToSpace()));
Matteo Martincigh49124022019-01-11 13:25:59 +0000838
Matteo Martincighadddddb2019-01-24 14:06:23 +0000839 // Infer the tensor infos for all output slots. Throws an exception on failure
840 optGraph.InferTensorInfos();
Matteo Martincigh49124022019-01-11 13:25:59 +0000841
842 // If Fp32 to Fp16 optimization is set convert Fp32 network to Fp16
843 if (options.m_ReduceFp32ToFp16)
844 {
Matteo Martincighadddddb2019-01-24 14:06:23 +0000845 Optimizer::Pass(optGraph, MakeOptimizations(Fp32NetworkToFp16Converter()));
Derek Lambertidd6804b2019-11-27 09:29:57 +0000846 Optimizer::Pass(optGraph, MakeOptimizations(ConvertConstantsFloatToHalf()));
Matteo Martincigh49124022019-01-11 13:25:59 +0000847 }
848
849 // Initialize backend settings
850 BackendSettings backendSettings(backendPreferences, deviceSpec);
851 if (backendSettings.GetAvailablePreferredBackends().empty())
852 {
853 std::stringstream failureMsg;
854 failureMsg << "None of the preferred backends " << backendPreferences
855 << " are supported. Current platform provides " << backendSettings.m_SupportedBackends;
Rob Hughes23214432019-11-05 11:27:36 +0000856 ReportError(failureMsg.str(), messages);
Matteo Martincigh49124022019-01-11 13:25:59 +0000857 return IOptimizedNetworkPtr(nullptr, &IOptimizedNetwork::Destroy);
858 }
859
Derek Lamberti84da38b2019-06-13 11:40:08 +0100860 // Create a map to temporarily hold initialized backend objects
861 TensorHandleFactoryRegistry tensorHandleFactoryRegistry;
862 BackendsMap backends = CreateSupportedBackends(tensorHandleFactoryRegistry, backendSettings);
863
Matteo Martincigh49124022019-01-11 13:25:59 +0000864 // Assign an available backend to each layer
Matteo Martincighadddddb2019-01-24 14:06:23 +0000865 Graph::Iterator firstLayer = optGraph.begin();
866 Graph::Iterator lastLayer = optGraph.end();
Derek Lamberti84da38b2019-06-13 11:40:08 +0100867 OptimizationResult assignBackendsResult = AssignBackends(optNetObjPtr,
868 backendSettings,
869 firstLayer,
870 lastLayer,
Rob Hughes23214432019-11-05 11:27:36 +0000871 messages);
Derek Lamberti84da38b2019-06-13 11:40:08 +0100872 if (assignBackendsResult.m_Error)
Matteo Martincigh49124022019-01-11 13:25:59 +0000873 {
874 // Failed to assign a backend to each layer
jimfly016b0b53d2018-10-08 14:43:01 +0100875 return IOptimizedNetworkPtr(nullptr, &IOptimizedNetwork::Destroy);
876 }
telsoa01c577f2c2018-08-31 09:22:23 +0100877
Matteo Martincighadddddb2019-01-24 14:06:23 +0000878 Optimizer::Pass(optGraph, MakeOptimizations(OptimizeInverseConversionsFp16(),
879 OptimizeInverseConversionsFp32()));
telsoa01c577f2c2018-08-31 09:22:23 +0100880
Matteo Martincighadddddb2019-01-24 14:06:23 +0000881 // Apply the backend-specific optimizations
882 OptimizationResult backendOptimizationResult = ApplyBackendOptimizations(optNetObjPtr,
883 backendSettings,
Derek Lamberti84da38b2019-06-13 11:40:08 +0100884 backends,
Rob Hughes23214432019-11-05 11:27:36 +0000885 messages);
Matteo Martincighadddddb2019-01-24 14:06:23 +0000886 if (backendOptimizationResult.m_Error)
Matteo Martincigh49124022019-01-11 13:25:59 +0000887 {
Matteo Martincighadddddb2019-01-24 14:06:23 +0000888 // Failed to apply the backend-specific optimizations
889 return IOptimizedNetworkPtr(nullptr, &IOptimizedNetwork::Destroy);
Matteo Martincigh49124022019-01-11 13:25:59 +0000890 }
891
Matteo Martincighadddddb2019-01-24 14:06:23 +0000892 // If the debug flag is set, then insert a DebugLayer after each layer
893 // Doing this after applying the backend optimizations as they might have changed some layers
894 if (options.m_Debug)
895 {
896 Optimizer::Pass(optGraph, MakeOptimizations(InsertDebugLayer()));
897 }
898
Derek Lamberti84da38b2019-06-13 11:40:08 +0100899 // Calculate the compatibility strategies for tensor handles
900 OptimizationResult strategyResult = SelectTensorHandleStrategy(optGraph,
901 backends,
902 tensorHandleFactoryRegistry,
Rob Hughes23214432019-11-05 11:27:36 +0000903 messages);
Derek Lamberti84da38b2019-06-13 11:40:08 +0100904 if (strategyResult.m_Error)
905 {
906 // Failed to apply the backend-specific optimizations
907 return IOptimizedNetworkPtr(nullptr, &IOptimizedNetwork::Destroy);
908 }
909
910 // Based on the tensor handle strategy determined above, insert copy layers where required.
Derek Lambertif674aa02019-08-01 15:56:25 +0100911 optGraph.AddCompatibilityLayers(backends, tensorHandleFactoryRegistry);
telsoa01c577f2c2018-08-31 09:22:23 +0100912
913 // Convert constants
Matteo Martincighadddddb2019-01-24 14:06:23 +0000914 Optimizer::Pass(optGraph, MakeOptimizations(ConvertConstantsFloatToHalf()));
915 Optimizer::Pass(optGraph, MakeOptimizations(ConvertConstantsHalfToFloat()));
telsoa01c577f2c2018-08-31 09:22:23 +0100916
Derek Lamberti84da38b2019-06-13 11:40:08 +0100917 // Run backend specific optimizations (deprecated)
Matteo Martincigh49124022019-01-11 13:25:59 +0000918 for (auto&& chosenBackend : backendSettings.m_SelectedBackends)
David Beck263e3492018-11-09 14:46:40 +0000919 {
920 auto factoryFun = BackendRegistryInstance().GetFactory(chosenBackend);
921 auto backendPtr = factoryFun();
922 BOOST_ASSERT(backendPtr.get() != nullptr);
923
Matteo Martincighed735042019-05-22 09:42:43 +0100924 ARMNN_NO_DEPRECATE_WARN_BEGIN
David Beck263e3492018-11-09 14:46:40 +0000925 auto backendSpecificOptimizations = backendPtr->GetOptimizations();
Matteo Martincighed735042019-05-22 09:42:43 +0100926 ARMNN_NO_DEPRECATE_WARN_END
927
David Beck263e3492018-11-09 14:46:40 +0000928 if (!backendSpecificOptimizations.empty())
929 {
930 Optimizer::Pass(optNetObjPtr->GetGraph(), backendSpecificOptimizations);
931 }
932 }
933
telsoa01c577f2c2018-08-31 09:22:23 +0100934 return optNet;
telsoa014fcda012018-03-09 14:13:49 +0000935}
936
937Network::Network()
Jan Eilers99d9d4a2019-11-06 10:02:16 +0000938: m_Graph(std::make_unique<Graph>()),
939 m_Guid(profiling::ProfilingService::Instance().NextGuid())
telsoa014fcda012018-03-09 14:13:49 +0000940{
941}
942
943Network::~Network()
944{
945}
946
Jan Eilers99d9d4a2019-11-06 10:02:16 +0000947Status Network::PrintGraph()
948{
949 m_Graph->Print();
950 return Status::Success;
951}
952
telsoa014fcda012018-03-09 14:13:49 +0000953IConnectableLayer* Network::AddInputLayer(LayerBindingId id, const char* name)
954{
955 return m_Graph->AddLayer<InputLayer>(id, name);
956}
957
Éanna Ó Catháin4e1e1362018-11-12 11:36:34 +0000958IConnectableLayer* Network::AddBatchToSpaceNdLayer(const BatchToSpaceNdDescriptor& batchToSpaceNdDescriptor,
959 const char* name)
960{
961 return m_Graph->AddLayer<BatchToSpaceNdLayer>(batchToSpaceNdDescriptor, name);
962}
963
Aron Virginas-Tar77bfb5e2019-10-16 17:45:38 +0100964IConnectableLayer* Network::AddComparisonLayer(const ComparisonDescriptor& comparisonDescriptor,
965 const char* name)
966{
967 return m_Graph->AddLayer<ComparisonLayer>(comparisonDescriptor, name);
968}
969
josh minor4a3c6102020-01-06 16:40:46 -0600970IConnectableLayer* Network::AddElementwiseUnaryLayer(const ElementwiseUnaryDescriptor& elementwiseUnaryDescriptor,
971 const char* name)
972{
973 return m_Graph->AddLayer<ElementwiseUnaryLayer>(elementwiseUnaryDescriptor, name);
974}
975
telsoa014fcda012018-03-09 14:13:49 +0000976IConnectableLayer* Network::AddFullyConnectedLayerImpl(const FullyConnectedDescriptor& fullyConnectedDescriptor,
telsoa01c577f2c2018-08-31 09:22:23 +0100977 const ConstTensor& weights,
Aron Virginas-Tarad402702019-02-22 17:03:44 +0000978 const Optional<ConstTensor>& biases,
telsoa01c577f2c2018-08-31 09:22:23 +0100979 const char* name)
telsoa014fcda012018-03-09 14:13:49 +0000980{
Aron Virginas-Tarad402702019-02-22 17:03:44 +0000981 if (fullyConnectedDescriptor.m_BiasEnabled && !biases.has_value())
telsoa014fcda012018-03-09 14:13:49 +0000982 {
Aron Virginas-Tarad402702019-02-22 17:03:44 +0000983 throw InvalidArgumentException("AddFullyConnectedLayer: biases cannot be empty");
telsoa014fcda012018-03-09 14:13:49 +0000984 }
985
986 const auto layer = m_Graph->AddLayer<FullyConnectedLayer>(fullyConnectedDescriptor, name);
987
988 layer->m_Weight = std::make_unique<ScopedCpuTensorHandle>(weights);
989
990 if (fullyConnectedDescriptor.m_BiasEnabled)
991 {
Aron Virginas-Tarad402702019-02-22 17:03:44 +0000992 layer->m_Bias = std::make_unique<ScopedCpuTensorHandle>(biases.value());
telsoa014fcda012018-03-09 14:13:49 +0000993 }
994
995 return layer;
996}
997
998IConnectableLayer* Network::AddFullyConnectedLayer(const FullyConnectedDescriptor& fullyConnectedDescriptor,
telsoa01c577f2c2018-08-31 09:22:23 +0100999 const ConstTensor& weights,
Aron Virginas-Tarad402702019-02-22 17:03:44 +00001000 const Optional<ConstTensor>& biases,
telsoa01c577f2c2018-08-31 09:22:23 +01001001 const char* name)
telsoa014fcda012018-03-09 14:13:49 +00001002{
Aron Virginas-Tarad402702019-02-22 17:03:44 +00001003 return AddFullyConnectedLayerImpl(fullyConnectedDescriptor, weights, biases, name);
telsoa014fcda012018-03-09 14:13:49 +00001004}
1005
Aron Virginas-Tarad402702019-02-22 17:03:44 +00001006IConnectableLayer* Network::AddFullyConnectedLayer(const FullyConnectedDescriptor& fullyConnectedDescriptor,
1007 const ConstTensor& weights,
1008 const char* name)
1009{
Matteo Martincighfc598e12019-05-14 10:36:13 +01001010 Optional<ConstTensor> biases;
Aron Virginas-Tarad402702019-02-22 17:03:44 +00001011 return AddFullyConnectedLayerImpl(fullyConnectedDescriptor, weights, biases, name);
1012}
1013
telsoa014fcda012018-03-09 14:13:49 +00001014IConnectableLayer* Network::AddFullyConnectedLayer(const FullyConnectedDescriptor& fullyConnectedDescriptor,
telsoa01c577f2c2018-08-31 09:22:23 +01001015 const ConstTensor& weights,
1016 const ConstTensor& biases,
1017 const char* name)
telsoa014fcda012018-03-09 14:13:49 +00001018{
Aron Virginas-Tarad402702019-02-22 17:03:44 +00001019 Optional<ConstTensor> optionalBiases(biases);
1020 return AddFullyConnectedLayerImpl(fullyConnectedDescriptor, weights, optionalBiases, name);
telsoa014fcda012018-03-09 14:13:49 +00001021}
1022
Jim Flynne242f2d2019-05-22 14:24:13 +01001023IConnectableLayer* Network::AddConcatLayer(const ConcatDescriptor& concatDescriptor,
Jim Flynn906f9462019-05-10 13:55:21 +01001024 const char* name)
1025{
Jim Flynne242f2d2019-05-22 14:24:13 +01001026 return m_Graph->AddLayer<ConcatLayer>(concatDescriptor, name);
Jim Flynn906f9462019-05-10 13:55:21 +01001027}
1028
telsoa014fcda012018-03-09 14:13:49 +00001029IConnectableLayer* Network::AddConvolution2dLayerImpl(const Convolution2dDescriptor& convolution2dDescriptor,
telsoa01c577f2c2018-08-31 09:22:23 +01001030 const ConstTensor& weights,
Aron Virginas-Tarad402702019-02-22 17:03:44 +00001031 const Optional<ConstTensor>& biases,
telsoa01c577f2c2018-08-31 09:22:23 +01001032 const char* name)
telsoa014fcda012018-03-09 14:13:49 +00001033{
Aron Virginas-Tarad402702019-02-22 17:03:44 +00001034 if (convolution2dDescriptor.m_BiasEnabled && !biases.has_value())
telsoa014fcda012018-03-09 14:13:49 +00001035 {
Aron Virginas-Tarad402702019-02-22 17:03:44 +00001036 throw InvalidArgumentException("AddConvolution2dLayer: biases cannot be empty");
telsoa014fcda012018-03-09 14:13:49 +00001037 }
1038
1039 const auto layer = m_Graph->AddLayer<Convolution2dLayer>(convolution2dDescriptor, name);
1040
1041 layer->m_Weight = std::make_unique<ScopedCpuTensorHandle>(weights);
1042
1043 if (convolution2dDescriptor.m_BiasEnabled)
1044 {
Aron Virginas-Tarad402702019-02-22 17:03:44 +00001045 layer->m_Bias = std::make_unique<ScopedCpuTensorHandle>(biases.value());
telsoa014fcda012018-03-09 14:13:49 +00001046 }
1047
1048 return layer;
1049}
1050
1051IConnectableLayer* Network::AddConvolution2dLayer(const Convolution2dDescriptor& convolution2dDescriptor,
telsoa01c577f2c2018-08-31 09:22:23 +01001052 const ConstTensor& weights,
Aron Virginas-Tarad402702019-02-22 17:03:44 +00001053 const Optional<ConstTensor>& biases,
telsoa01c577f2c2018-08-31 09:22:23 +01001054 const char* name)
telsoa014fcda012018-03-09 14:13:49 +00001055{
Aron Virginas-Tarad402702019-02-22 17:03:44 +00001056 return AddConvolution2dLayerImpl(convolution2dDescriptor, weights, biases, name);
telsoa014fcda012018-03-09 14:13:49 +00001057}
Aron Virginas-Tarad402702019-02-22 17:03:44 +00001058
Aron Virginas-Tarad402702019-02-22 17:03:44 +00001059IConnectableLayer* Network::AddConvolution2dLayer(const Convolution2dDescriptor& convolution2dDescriptor,
1060 const ConstTensor& weights,
1061 const char* name)
1062{
Matteo Martincighfc598e12019-05-14 10:36:13 +01001063 Optional<ConstTensor> biases;
Aron Virginas-Tarad402702019-02-22 17:03:44 +00001064 return AddConvolution2dLayerImpl(convolution2dDescriptor, weights, biases, name);
1065}
1066
telsoa014fcda012018-03-09 14:13:49 +00001067IConnectableLayer* Network::AddConvolution2dLayer(const Convolution2dDescriptor& convolution2dDescriptor,
telsoa01c577f2c2018-08-31 09:22:23 +01001068 const ConstTensor& weights,
1069 const ConstTensor& biases,
1070 const char* name)
telsoa014fcda012018-03-09 14:13:49 +00001071{
Aron Virginas-Tarad402702019-02-22 17:03:44 +00001072 Optional<ConstTensor> optionalBiases(biases);
1073 return AddConvolution2dLayerImpl(convolution2dDescriptor, weights, optionalBiases, name);
telsoa014fcda012018-03-09 14:13:49 +00001074}
1075
1076IConnectableLayer* Network::AddDepthwiseConvolution2dLayerImpl(
1077 const DepthwiseConvolution2dDescriptor& convolution2dDescriptor,
1078 const ConstTensor& weights,
Aron Virginas-Tarad402702019-02-22 17:03:44 +00001079 const Optional<ConstTensor>& biases,
telsoa014fcda012018-03-09 14:13:49 +00001080 const char* name)
1081{
Aron Virginas-Tarad402702019-02-22 17:03:44 +00001082 if (convolution2dDescriptor.m_BiasEnabled && !biases.has_value())
telsoa014fcda012018-03-09 14:13:49 +00001083 {
Aron Virginas-Tarad402702019-02-22 17:03:44 +00001084 throw InvalidArgumentException("AddDepthwiseConvolution2dLayer: biases cannot be empty");
telsoa014fcda012018-03-09 14:13:49 +00001085 }
1086
Matteo Martincigh3d6898c2019-01-15 16:11:44 +00001087 const auto layer = m_Graph->AddLayer<DepthwiseConvolution2dLayer>(convolution2dDescriptor, name);
telsoa014fcda012018-03-09 14:13:49 +00001088
1089 layer->m_Weight = std::make_unique<ScopedCpuTensorHandle>(weights);
1090
1091 if (convolution2dDescriptor.m_BiasEnabled)
1092 {
Aron Virginas-Tarad402702019-02-22 17:03:44 +00001093 layer->m_Bias = std::make_unique<ScopedCpuTensorHandle>(biases.value());
telsoa014fcda012018-03-09 14:13:49 +00001094 }
1095
1096 return layer;
1097}
1098
Aron Virginas-Tardd6247f2019-09-19 14:31:17 +01001099IConnectableLayer* Network::AddDepthToSpaceLayer(const DepthToSpaceDescriptor& depthToSpaceDescriptor,
1100 const char* name)
1101{
1102 return m_Graph->AddLayer<DepthToSpaceLayer>(depthToSpaceDescriptor, name);
1103}
1104
telsoa014fcda012018-03-09 14:13:49 +00001105IConnectableLayer* Network::AddDepthwiseConvolution2dLayer(
Aron Virginas-Tarad402702019-02-22 17:03:44 +00001106 const DepthwiseConvolution2dDescriptor& convolution2dDescriptor,
1107 const ConstTensor& weights,
1108 const Optional<ConstTensor>& biases,
1109 const char* name)
1110{
1111 return AddDepthwiseConvolution2dLayerImpl(convolution2dDescriptor, weights, biases, name);
1112}
1113
Aron Virginas-Tarad402702019-02-22 17:03:44 +00001114IConnectableLayer* Network::AddDepthwiseConvolution2dLayer(
telsoa014fcda012018-03-09 14:13:49 +00001115 const DepthwiseConvolution2dDescriptor& convolution2dDescriptor,
1116 const ConstTensor& weights,
1117 const char* name)
1118{
Matteo Martincighfc598e12019-05-14 10:36:13 +01001119 Optional<ConstTensor> biases;
Aron Virginas-Tarad402702019-02-22 17:03:44 +00001120 return AddDepthwiseConvolution2dLayerImpl(convolution2dDescriptor, weights, biases, name);
telsoa014fcda012018-03-09 14:13:49 +00001121}
Aron Virginas-Tarad402702019-02-22 17:03:44 +00001122
telsoa014fcda012018-03-09 14:13:49 +00001123IConnectableLayer* Network::AddDepthwiseConvolution2dLayer(
1124 const DepthwiseConvolution2dDescriptor& convolution2dDescriptor,
1125 const ConstTensor& weights,
1126 const ConstTensor& biases,
1127 const char* name)
1128{
Aron Virginas-Tarad402702019-02-22 17:03:44 +00001129 Optional<ConstTensor> optionalBiases(biases);
1130 return AddDepthwiseConvolution2dLayerImpl(convolution2dDescriptor, weights, optionalBiases, name);
telsoa014fcda012018-03-09 14:13:49 +00001131}
1132
Narumol Prangnawarat94dd5d82019-01-23 18:06:26 +00001133IConnectableLayer* Network::AddDetectionPostProcessLayer(const armnn::DetectionPostProcessDescriptor& descriptor,
Narumol Prangnawarat6d302bf2019-02-04 11:46:26 +00001134 const ConstTensor& anchors, const char* name)
Narumol Prangnawarat94dd5d82019-01-23 18:06:26 +00001135{
Narumol Prangnawarat6d302bf2019-02-04 11:46:26 +00001136 const auto layer = m_Graph->AddLayer<DetectionPostProcessLayer>(descriptor, name);
1137
1138 layer->m_Anchors = std::make_unique<ScopedCpuTensorHandle>(anchors);
1139
1140 return layer;
Narumol Prangnawarat94dd5d82019-01-23 18:06:26 +00001141}
1142
telsoa014fcda012018-03-09 14:13:49 +00001143IConnectableLayer* Network::AddPermuteLayer(const PermuteDescriptor& permuteDescriptor,
1144 const char* name)
1145{
1146 return m_Graph->AddLayer<PermuteLayer>(permuteDescriptor, name);
1147}
1148
1149IConnectableLayer* Network::AddPooling2dLayer(const Pooling2dDescriptor& pooling2dDescriptor,
1150 const char* name)
1151{
1152 return m_Graph->AddLayer<Pooling2dLayer>(pooling2dDescriptor, name);
1153}
1154
1155IConnectableLayer* Network::AddActivationLayer(const ActivationDescriptor& activationDescriptor,
1156 const char* name)
1157{
1158 return m_Graph->AddLayer<ActivationLayer>(activationDescriptor, name);
1159}
1160
Nikhil Rajee391d52019-09-05 17:50:44 +01001161IConnectableLayer* Network::AddArgMinMaxLayer(const ArgMinMaxDescriptor& argMinMaxDescriptor,
1162 const char* name)
1163{
1164 return m_Graph->AddLayer<ArgMinMaxLayer>(argMinMaxDescriptor, name);
1165}
1166
telsoa01c577f2c2018-08-31 09:22:23 +01001167IConnectableLayer* Network::AddNormalizationLayer(const NormalizationDescriptor&
1168normalizationDescriptor,
telsoa014fcda012018-03-09 14:13:49 +00001169 const char* name)
1170{
1171 return m_Graph->AddLayer<NormalizationLayer>(normalizationDescriptor, name);
1172}
1173
Aron Virginas-Tar636ab402019-09-16 14:27:45 +01001174IConnectableLayer* Network::AddSliceLayer(const SliceDescriptor& sliceDescriptor, const char* name)
1175{
1176 return m_Graph->AddLayer<SliceLayer>(sliceDescriptor, name);
1177}
1178
telsoa014fcda012018-03-09 14:13:49 +00001179IConnectableLayer* Network::AddSoftmaxLayer(const SoftmaxDescriptor& softmaxDescriptor,
1180 const char* name)
1181{
1182 return m_Graph->AddLayer<SoftmaxLayer>(softmaxDescriptor, name);
1183}
1184
1185IConnectableLayer* Network::AddSplitterLayer(const ViewsDescriptor& splitterDescriptor,
1186 const char* name)
1187{
1188 return m_Graph->AddLayer<SplitterLayer>(splitterDescriptor, name);
1189}
1190
Nattapat Chaimanowong5a4304a2018-11-28 10:44:37 +00001191IConnectableLayer* Network::AddMaximumLayer(const char* name)
1192{
1193 return m_Graph->AddLayer<MaximumLayer>(name);
1194}
1195
Éanna Ó Catháin20e58802018-12-04 10:29:06 +00001196IConnectableLayer* Network::AddMinimumLayer(const char* name)
1197{
1198 return m_Graph->AddLayer<MinimumLayer>(name);
1199}
1200
Jim Flynne242f2d2019-05-22 14:24:13 +01001201IConnectableLayer* Network::AddMergerLayer(const MergerDescriptor& mergerDescriptor,
Jim Flynn906f9462019-05-10 13:55:21 +01001202 const char* name)
telsoa014fcda012018-03-09 14:13:49 +00001203{
Jim Flynne242f2d2019-05-22 14:24:13 +01001204 return AddConcatLayer(mergerDescriptor, name);
telsoa014fcda012018-03-09 14:13:49 +00001205}
1206
Kevin May868eb142019-09-04 17:29:31 +01001207IConnectableLayer* Network::AddAbsLayer(const char * name)
1208{
josh minor4a3c6102020-01-06 16:40:46 -06001209 return AddElementwiseUnaryLayer(ElementwiseUnaryDescriptor(UnaryOperation::Abs), name);
Kevin May868eb142019-09-04 17:29:31 +01001210}
1211
telsoa014fcda012018-03-09 14:13:49 +00001212IConnectableLayer* Network::AddAdditionLayer(const char* name)
1213{
1214 return m_Graph->AddLayer<AdditionLayer>(name);
1215}
1216
1217IConnectableLayer* Network::AddMultiplicationLayer(const char* name)
1218{
1219 return m_Graph->AddLayer<MultiplicationLayer>(name);
1220}
1221
1222IConnectableLayer* Network::AddOutputLayer(LayerBindingId id, const char* name)
1223{
1224 return m_Graph->AddLayer<OutputLayer>(id, name);
1225}
1226
1227IConnectableLayer* Network::AddBatchNormalizationLayer(const BatchNormalizationDescriptor& desc,
1228 const ConstTensor& mean,
1229 const ConstTensor& variance,
1230 const ConstTensor& beta,
1231 const ConstTensor& gamma,
1232 const char* name)
1233{
1234 const auto layer = m_Graph->AddLayer<BatchNormalizationLayer>(desc, name);
1235
1236 layer->m_Mean = std::make_unique<ScopedCpuTensorHandle>(mean);
1237 layer->m_Variance = std::make_unique<ScopedCpuTensorHandle>(variance);
1238 layer->m_Beta = std::make_unique<ScopedCpuTensorHandle>(beta);
1239 layer->m_Gamma = std::make_unique<ScopedCpuTensorHandle>(gamma);
1240
1241 return layer;
1242}
1243
Aron Virginas-Tar169d2f12019-07-01 19:01:44 +01001244IConnectableLayer* Network::AddResizeBilinearLayer(const ResizeBilinearDescriptor& descriptor,
1245 const char* name)
telsoa014fcda012018-03-09 14:13:49 +00001246{
Aron Virginas-Tar169d2f12019-07-01 19:01:44 +01001247 ResizeDescriptor resizeDescriptor;
1248 resizeDescriptor.m_Method = ResizeMethod::Bilinear;
1249 resizeDescriptor.m_DataLayout = descriptor.m_DataLayout;
1250 resizeDescriptor.m_TargetWidth = descriptor.m_TargetWidth;
1251 resizeDescriptor.m_TargetHeight = descriptor.m_TargetHeight;
1252
1253 return m_Graph->AddLayer<ResizeLayer>(resizeDescriptor, name);
telsoa014fcda012018-03-09 14:13:49 +00001254}
1255
Teresa Charlina9075df2019-06-27 15:41:57 +01001256IConnectableLayer* Network::AddResizeLayer(const ResizeDescriptor&
1257resizeDescriptor, const char* name)
1258{
Aron Virginas-Tar169d2f12019-07-01 19:01:44 +01001259 return m_Graph->AddLayer<ResizeLayer>(resizeDescriptor, name);
Teresa Charlina9075df2019-06-27 15:41:57 +01001260}
1261
Kevin Mayce5045a2019-10-02 14:07:47 +01001262IConnectableLayer* Network::AddInstanceNormalizationLayer(const InstanceNormalizationDescriptor& desc,
1263 const char* name)
1264{
1265 return m_Graph->AddLayer<InstanceNormalizationLayer>(desc, name);
1266}
1267
Matteo Martincighbcd3c852018-09-28 14:14:12 +01001268IConnectableLayer* Network::AddL2NormalizationLayer(const L2NormalizationDescriptor& desc,
1269 const char* name)
telsoa014fcda012018-03-09 14:13:49 +00001270{
Matteo Martincighbcd3c852018-09-28 14:14:12 +01001271 return m_Graph->AddLayer<L2NormalizationLayer>(desc, name);
telsoa014fcda012018-03-09 14:13:49 +00001272}
1273
Aron Virginas-Tarf982dea2019-10-11 14:07:53 +01001274IConnectableLayer* Network::AddLogSoftmaxLayer(const LogSoftmaxDescriptor& desc,
1275 const char* name)
1276{
1277 return m_Graph->AddLayer<LogSoftmaxLayer>(desc, name);
1278}
1279
telsoa014fcda012018-03-09 14:13:49 +00001280IConnectableLayer* Network::AddConstantLayer(const ConstTensor& input, const char* name)
1281{
telsoa01c577f2c2018-08-31 09:22:23 +01001282 auto layer = m_Graph->AddLayer<ConstantLayer>(name);
1283
1284 layer->m_LayerOutput = std::make_unique<ScopedCpuTensorHandle>(input);
1285
1286 return layer;
telsoa014fcda012018-03-09 14:13:49 +00001287}
1288
telsoa01c577f2c2018-08-31 09:22:23 +01001289IConnectableLayer* Network::AddReshapeLayer(const ReshapeDescriptor& reshapeDescriptor,
1290 const char* name)
telsoa014fcda012018-03-09 14:13:49 +00001291{
1292 return m_Graph->AddLayer<ReshapeLayer>(reshapeDescriptor, name);
1293}
1294
Nattapat Chaimanowong207ef9a2018-11-02 10:57:25 +00001295IConnectableLayer* Network::AddSpaceToBatchNdLayer(const SpaceToBatchNdDescriptor& spaceToBatchNdDescriptor,
1296 const char* name)
1297{
1298 return m_Graph->AddLayer<SpaceToBatchNdLayer>(spaceToBatchNdDescriptor, name);
1299}
1300
Aron Virginas-Tar972af152019-06-11 14:14:03 +01001301IConnectableLayer* Network::AddSpaceToDepthLayer(const SpaceToDepthDescriptor& spaceToDepthDescriptor,
1302 const char* name)
1303{
1304 return m_Graph->AddLayer<SpaceToDepthLayer>(spaceToDepthDescriptor, name);
1305}
1306
telsoa014fcda012018-03-09 14:13:49 +00001307IConnectableLayer* Network::AddFloorLayer(const char* name)
1308{
1309 return m_Graph->AddLayer<FloorLayer>(name);
1310}
1311
telsoa01c577f2c2018-08-31 09:22:23 +01001312IConnectableLayer* Network::AddLstmLayer(const LstmDescriptor& descriptor,
1313 const LstmInputParams& params,
1314 const char* name)
1315{
1316 const auto layer = m_Graph->AddLayer<LstmLayer>(descriptor, name);
1317
1318 //Lstm Basic Parameters
1319 layer->m_BasicParameters.m_InputToForgetWeights =
1320 std::make_unique<ScopedCpuTensorHandle>(*(params.m_InputToForgetWeights));
1321 layer->m_BasicParameters.m_InputToCellWeights =
1322 std::make_unique<ScopedCpuTensorHandle>(*(params.m_InputToCellWeights));
1323 layer->m_BasicParameters.m_InputToOutputWeights =
1324 std::make_unique<ScopedCpuTensorHandle>(*(params.m_InputToOutputWeights));
1325 layer->m_BasicParameters.m_RecurrentToForgetWeights =
1326 std::make_unique<ScopedCpuTensorHandle>(*(params.m_RecurrentToForgetWeights));
1327 layer->m_BasicParameters.m_RecurrentToCellWeights =
1328 std::make_unique<ScopedCpuTensorHandle>(*(params.m_RecurrentToCellWeights));
1329 layer->m_BasicParameters.m_RecurrentToOutputWeights =
1330 std::make_unique<ScopedCpuTensorHandle>(*(params.m_RecurrentToOutputWeights));
1331 layer->m_BasicParameters.m_ForgetGateBias =
1332 std::make_unique<ScopedCpuTensorHandle>(*(params.m_ForgetGateBias));
1333 layer->m_BasicParameters.m_CellBias =
1334 std::make_unique<ScopedCpuTensorHandle>(*(params.m_CellBias));
1335 layer->m_BasicParameters.m_OutputGateBias =
1336 std::make_unique<ScopedCpuTensorHandle>(*(params.m_OutputGateBias));
1337
1338 //Lstm Cifg parameters
1339 if(!descriptor.m_CifgEnabled)
1340 {
1341 if(params.m_InputToInputWeights == nullptr)
1342 {
1343 throw InvalidArgumentException("AddLstmLayer: Input To Input Weights cannot be NULL");
1344 }
1345 if(params.m_RecurrentToInputWeights == nullptr)
1346 {
1347 throw InvalidArgumentException(
1348 "AddLstmLayer: Recurrent To Input Weights cannot be NULL");
1349 }
1350 if(params.m_InputGateBias == nullptr)
1351 {
1352 throw InvalidArgumentException("AddLstmLayer: Input Gate Bias cannot be NULL");
1353 }
1354 layer->m_CifgParameters.m_InputToInputWeights =
1355 std::make_unique<ScopedCpuTensorHandle>(*(params.m_InputToInputWeights));
1356 layer->m_CifgParameters.m_RecurrentToInputWeights =
1357 std::make_unique<ScopedCpuTensorHandle>(*(params.m_RecurrentToInputWeights));
1358 // In the VTS tests, cell-to-input weights may be null, even if the other CIFG params are not.
1359 if(params.m_CellToInputWeights != nullptr)
1360 {
1361 layer->m_CifgParameters.m_CellToInputWeights =
1362 std::make_unique<ScopedCpuTensorHandle>(*(params.m_CellToInputWeights));
1363 }
1364 layer->m_CifgParameters.m_InputGateBias =
1365 std::make_unique<ScopedCpuTensorHandle>(*(params.m_InputGateBias));
1366 }
1367
1368 //Lstm projection parameters
1369 if(descriptor.m_ProjectionEnabled)
1370 {
1371 if(params.m_ProjectionWeights == nullptr)
1372 {
1373 throw InvalidArgumentException("AddLstmLayer: Projection Weights cannot be NULL");
1374 }
1375 layer->m_ProjectionParameters.m_ProjectionWeights =
1376 std::make_unique<ScopedCpuTensorHandle>(*(params.m_ProjectionWeights));
1377 if(params.m_ProjectionBias != nullptr)
1378 {
1379 layer->m_ProjectionParameters.m_ProjectionBias =
1380 std::make_unique<ScopedCpuTensorHandle>(*(params.m_ProjectionBias));
1381 }
1382 }
1383
1384 //Lstm Peephole params
1385 if(descriptor.m_PeepholeEnabled)
1386 {
1387 if(params.m_CellToForgetWeights == nullptr)
1388 {
1389 throw InvalidArgumentException("AddLstmLayer: Cell To Forget Weights cannot be NULL");
1390 }
1391 if(params.m_CellToOutputWeights == nullptr)
1392 {
1393 throw InvalidArgumentException("AddLstmLayer: Cell To Output Weights cannot be NULL");
1394 }
1395 layer->m_PeepholeParameters.m_CellToForgetWeights =
1396 std::make_unique<ScopedCpuTensorHandle>(*(params.m_CellToForgetWeights));
1397 layer->m_PeepholeParameters.m_CellToOutputWeights =
1398 std::make_unique<ScopedCpuTensorHandle>(*(params.m_CellToOutputWeights));
1399 }
Jan Eilersf8c62972019-07-17 11:07:49 +01001400
1401 //Lstm Layer Normalization params
1402 if(descriptor.m_LayerNormEnabled)
1403 {
1404 if(!descriptor.m_CifgEnabled)
1405 {
1406 if(params.m_InputLayerNormWeights == nullptr)
1407 {
1408 throw InvalidArgumentException("AddLstmLayer: Input layer normalization weights cannot be NULL");
1409 }
1410 layer->m_LayerNormParameters.m_InputLayerNormWeights =
1411 std::make_unique<ScopedCpuTensorHandle>(*(params.m_InputLayerNormWeights));
1412 }
1413
1414 if(params.m_ForgetLayerNormWeights == nullptr)
1415 {
1416 throw InvalidArgumentException("AddLstmLayer: Forget layer normalization weights cannot be NULL");
1417 }
1418 if(params.m_CellLayerNormWeights == nullptr)
1419 {
1420 throw InvalidArgumentException("AddLstmLayer: Cell layer normalization weights cannot be NULL");
1421 }
1422 if(params.m_OutputLayerNormWeights == nullptr)
1423 {
1424 throw InvalidArgumentException("AddLstmLayer: Output layer normalization weights cannot be NULL");
1425 }
1426 layer->m_LayerNormParameters.m_ForgetLayerNormWeights =
1427 std::make_unique<ScopedCpuTensorHandle>(*(params.m_ForgetLayerNormWeights));
1428 layer->m_LayerNormParameters.m_CellLayerNormWeights =
1429 std::make_unique<ScopedCpuTensorHandle>(*(params.m_CellLayerNormWeights));
1430 layer->m_LayerNormParameters.m_OutputLayerNormWeights =
1431 std::make_unique<ScopedCpuTensorHandle>(*(params.m_OutputLayerNormWeights));
1432 }
telsoa01c577f2c2018-08-31 09:22:23 +01001433 return layer;
1434}
1435
Francis Murtaghe7a86a42018-08-29 12:42:10 +01001436IConnectableLayer* Network::AddDivisionLayer(const char* name)
1437{
1438 return m_Graph->AddLayer<DivisionLayer>(name);
1439}
1440
David Beck19526222018-09-12 16:00:08 +01001441IConnectableLayer* Network::AddSubtractionLayer(const char* name)
1442{
1443 return m_Graph->AddLayer<SubtractionLayer>(name);
1444}
1445
narpra0132b90462018-09-13 11:07:48 +01001446IConnectableLayer* Network::AddMeanLayer(const MeanDescriptor& meanDescriptor, const char* name)
1447{
1448 return m_Graph->AddLayer<MeanLayer>(meanDescriptor,name);
1449}
1450
Mohamed Nour Abouelseoud5662c202018-09-24 13:30:09 +01001451IConnectableLayer* Network::AddPadLayer(const PadDescriptor& padDescriptor, const char* name)
1452{
1453 return m_Graph->AddLayer<PadLayer>(padDescriptor,name);
1454}
1455
Derek Lambertia9cca6a2019-03-25 15:41:58 +00001456IConnectableLayer *Network::AddQuantizeLayer(const char *name)
1457{
1458 return m_Graph->AddLayer<QuantizeLayer>(name);
1459}
1460
Nattapat Chaimanowonge4294fd2019-03-28 09:56:53 +00001461IConnectableLayer* Network::AddDequantizeLayer(const char* name)
1462{
1463 return m_Graph->AddLayer<DequantizeLayer>(name);
1464}
1465
Conor Kennedy430b5d82018-11-14 15:28:28 +00001466IConnectableLayer* Network::AddStridedSliceLayer(const StridedSliceDescriptor& stridedSliceDescriptor,
1467 const char* name)
1468{
1469 return m_Graph->AddLayer<StridedSliceLayer>(stridedSliceDescriptor, name);
1470}
1471
Matteo Martincigh59a950c2018-12-13 12:48:25 +00001472IConnectableLayer* Network::AddGreaterLayer(const char* name)
1473{
Aron Virginas-Tar77bfb5e2019-10-16 17:45:38 +01001474 return AddComparisonLayer(ComparisonDescriptor(ComparisonOperation::Greater), name);
Matteo Martincigh59a950c2018-12-13 12:48:25 +00001475}
1476
FrancisMurtagh20995952018-12-17 12:11:36 +00001477IConnectableLayer* Network::AddEqualLayer(const char* name)
1478{
Aron Virginas-Tar77bfb5e2019-10-16 17:45:38 +01001479 return AddComparisonLayer(ComparisonDescriptor(ComparisonOperation::Equal), name);
FrancisMurtagh20995952018-12-17 12:11:36 +00001480}
1481
Mohamed Nour Abouelseouda1d3c6a2018-12-27 12:39:16 +00001482IConnectableLayer* Network::AddRsqrtLayer(const char * name)
1483{
josh minor4a3c6102020-01-06 16:40:46 -06001484 return AddElementwiseUnaryLayer(ElementwiseUnaryDescriptor(UnaryOperation::Rsqrt), name);
Mohamed Nour Abouelseouda1d3c6a2018-12-27 12:39:16 +00001485}
1486
narpra01b89b05f2019-01-16 09:53:09 +00001487IConnectableLayer* Network::AddGatherLayer(const char* name)
1488{
1489 return m_Graph->AddLayer<GatherLayer>(name);
1490}
1491
Nattapat Chaimanowong1f886302019-04-05 13:37:19 +01001492IConnectableLayer* Network::AddMergeLayer(const char* name)
1493{
1494 return m_Graph->AddLayer<MergeLayer>(name);
1495}
1496
Sadik Armaganeff363d2019-04-05 15:25:46 +01001497IConnectableLayer* Network::AddSwitchLayer(const char* name)
1498{
1499 return m_Graph->AddLayer<SwitchLayer>(name);
1500}
1501
Matteo Martincigh0e406ee2019-06-12 15:42:18 +01001502IConnectableLayer* Network::AddPreluLayer(const char* name)
1503{
1504 return m_Graph->AddLayer<PreluLayer>(name);
1505}
1506
Aron Virginas-Tar639fb042019-06-20 14:28:19 +01001507IConnectableLayer* Network::AddTransposeConvolution2dLayer(const TransposeConvolution2dDescriptor& descriptor,
1508 const ConstTensor& weights,
1509 const Optional<ConstTensor>& biases,
1510 const char* name)
1511{
1512 if (descriptor.m_BiasEnabled && !biases.has_value())
1513 {
1514 throw InvalidArgumentException("AddTransposeConvolution2dLayer: Biases cannot be empty");
1515 }
1516
1517 const auto layer = m_Graph->AddLayer<TransposeConvolution2dLayer>(descriptor, name);
1518
1519 layer->m_Weight = std::make_unique<ScopedCpuTensorHandle>(weights);
1520
1521 if (descriptor.m_BiasEnabled)
1522 {
1523 layer->m_Bias = std::make_unique<ScopedCpuTensorHandle>(biases.value());
1524 }
1525
1526 return layer;
1527}
1528
Matthew Jackson2b8c1da2019-07-04 14:59:16 +01001529IConnectableLayer* Network::AddStackLayer(const StackDescriptor& stackDescriptor,
1530 const char* name)
1531{
1532 return m_Graph->AddLayer<StackLayer>(stackDescriptor, name);
1533}
1534
Derek Lamberti013c3902019-10-21 10:46:16 +01001535
1536IConnectableLayer* Network::AddStandInLayer(const StandInDescriptor& desc,
1537 const char* name)
1538{
1539 return m_Graph->AddLayer<StandInLayer>(desc, name);
1540}
1541
James Conroyee18dc82019-07-17 11:27:46 +01001542IConnectableLayer* Network::AddQuantizedLstmLayer(const QuantizedLstmInputParams& params,
1543 const char* name)
1544{
1545 const auto layer = m_Graph->AddLayer<QuantizedLstmLayer>(name);
1546
1547 // InputToX weights
1548 layer->m_QuantizedLstmParameters.m_InputToInputWeights =
Francis Murtaghbb590b42019-08-14 09:51:36 +01001549 std::make_unique<ScopedCpuTensorHandle>(params.GetInputToInputWeights());
James Conroyee18dc82019-07-17 11:27:46 +01001550 layer->m_QuantizedLstmParameters.m_InputToForgetWeights =
Francis Murtaghbb590b42019-08-14 09:51:36 +01001551 std::make_unique<ScopedCpuTensorHandle>(params.GetInputToForgetWeights());
James Conroyee18dc82019-07-17 11:27:46 +01001552 layer->m_QuantizedLstmParameters.m_InputToCellWeights =
Francis Murtaghbb590b42019-08-14 09:51:36 +01001553 std::make_unique<ScopedCpuTensorHandle>(params.GetInputToCellWeights());
James Conroyee18dc82019-07-17 11:27:46 +01001554 layer->m_QuantizedLstmParameters.m_InputToOutputWeights =
Francis Murtaghbb590b42019-08-14 09:51:36 +01001555 std::make_unique<ScopedCpuTensorHandle>(params.GetInputToOutputWeights());
James Conroyee18dc82019-07-17 11:27:46 +01001556
1557 // RecurrentToX weights
1558 layer->m_QuantizedLstmParameters.m_RecurrentToInputWeights =
Francis Murtaghbb590b42019-08-14 09:51:36 +01001559 std::make_unique<ScopedCpuTensorHandle>(params.GetRecurrentToInputWeights());
James Conroyee18dc82019-07-17 11:27:46 +01001560 layer->m_QuantizedLstmParameters.m_RecurrentToForgetWeights =
Francis Murtaghbb590b42019-08-14 09:51:36 +01001561 std::make_unique<ScopedCpuTensorHandle>(params.GetRecurrentToForgetWeights());
James Conroyee18dc82019-07-17 11:27:46 +01001562 layer->m_QuantizedLstmParameters.m_RecurrentToCellWeights =
Francis Murtaghbb590b42019-08-14 09:51:36 +01001563 std::make_unique<ScopedCpuTensorHandle>(params.GetRecurrentToCellWeights());
James Conroyee18dc82019-07-17 11:27:46 +01001564 layer->m_QuantizedLstmParameters.m_RecurrentToOutputWeights =
Francis Murtaghbb590b42019-08-14 09:51:36 +01001565 std::make_unique<ScopedCpuTensorHandle>(params.GetRecurrentToOutputWeights());
James Conroyee18dc82019-07-17 11:27:46 +01001566
1567 // Bias
1568 layer->m_QuantizedLstmParameters.m_InputGateBias =
Francis Murtaghbb590b42019-08-14 09:51:36 +01001569 std::make_unique<ScopedCpuTensorHandle>(params.GetInputGateBias());
James Conroyee18dc82019-07-17 11:27:46 +01001570 layer->m_QuantizedLstmParameters.m_ForgetGateBias =
Francis Murtaghbb590b42019-08-14 09:51:36 +01001571 std::make_unique<ScopedCpuTensorHandle>(params.GetForgetGateBias());
James Conroyee18dc82019-07-17 11:27:46 +01001572 layer->m_QuantizedLstmParameters.m_CellBias =
Francis Murtaghbb590b42019-08-14 09:51:36 +01001573 std::make_unique<ScopedCpuTensorHandle>(params.GetCellBias());
James Conroyee18dc82019-07-17 11:27:46 +01001574 layer->m_QuantizedLstmParameters.m_OutputGateBias =
Francis Murtaghbb590b42019-08-14 09:51:36 +01001575 std::make_unique<ScopedCpuTensorHandle>(params.GetOutputGateBias());
James Conroyee18dc82019-07-17 11:27:46 +01001576
1577 return layer;
1578}
1579
Mike Kelly8c1701a2019-02-11 17:01:27 +00001580void Network::Accept(ILayerVisitor& visitor) const
1581{
1582 for (auto layer : GetGraph())
1583 {
1584 layer->Accept(visitor);
1585 };
1586}
1587
telsoa014fcda012018-03-09 14:13:49 +00001588OptimizedNetwork::OptimizedNetwork(std::unique_ptr<Graph> graph)
Jan Eilers99d9d4a2019-11-06 10:02:16 +00001589 : m_Graph(std::move(graph)),
1590 m_Guid(profiling::ProfilingService::Instance().NextGuid())
telsoa014fcda012018-03-09 14:13:49 +00001591{
1592}
1593
1594OptimizedNetwork::~OptimizedNetwork()
1595{
1596}
1597
1598} // namespace armnn