blob: c2da4da41edd38103ba20b978df5047176d2f03a [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>
Jan Eilers8eb25602020-03-09 12:13:48 +000025#include <armnn/utility/IgnoreUnused.hpp>
telsoa014fcda012018-03-09 14:13:49 +000026
Jan Eilers99d9d4a2019-11-06 10:02:16 +000027#include <ProfilingService.hpp>
28
telsoa014fcda012018-03-09 14:13:49 +000029#include <fcntl.h>
30#include <algorithm>
31#include <fstream>
32#include <memory>
telsoa01c577f2c2018-08-31 09:22:23 +010033#include <vector>
34#include <algorithm>
telsoa014fcda012018-03-09 14:13:49 +000035
36#include <boost/assert.hpp>
37#include <boost/format.hpp>
telsoa014fcda012018-03-09 14:13:49 +000038#include <boost/numeric/conversion/converter_policies.hpp>
39#include <boost/cast.hpp>
40
41namespace armnn
42{
43
44armnn::INetwork* INetwork::CreateRaw()
45{
46 return new Network();
47}
48
49armnn::INetworkPtr INetwork::Create()
50{
51 return INetworkPtr(CreateRaw(), &INetwork::Destroy);
52}
53
54void INetwork::Destroy(INetwork* network)
55{
56 delete boost::polymorphic_downcast<Network*>(network);
57}
58
telsoa014fcda012018-03-09 14:13:49 +000059void IOptimizedNetwork::Destroy(IOptimizedNetwork* network)
60{
61 delete boost::polymorphic_downcast<OptimizedNetwork*>(network);
62}
63
64Status OptimizedNetwork::PrintGraph()
65{
66 m_Graph->Print();
67 return Status::Success;
68}
69
surmeh01bceff2f2018-03-29 16:29:27 +010070Status OptimizedNetwork::SerializeToDot(std::ostream& stream) const
71{
72 return m_Graph->SerializeToDot(stream);
73}
74
Matteo Martincigh49124022019-01-11 13:25:59 +000075void ReportError(const std::string& errorMessage,
76 Optional<std::vector<std::string>&> errorMessages)
77{
78 std::stringstream fullErrorMessage;
79 fullErrorMessage << "ERROR: " << errorMessage;
Derek Lamberti08446972019-11-26 16:38:31 +000080 ARMNN_LOG(warning) << fullErrorMessage.str();
Matteo Martincigh49124022019-01-11 13:25:59 +000081 if (errorMessages)
82 {
83 errorMessages.value().push_back(fullErrorMessage.str());
84 }
85}
86
87void ReportWarning(const std::string& warningMessage,
88 Optional<std::vector<std::string>&> warningMessages)
89{
90 std::stringstream fullWarningMessage;
91 fullWarningMessage << "WARNING: " << warningMessage;
Derek Lamberti08446972019-11-26 16:38:31 +000092 ARMNN_LOG(warning) << fullWarningMessage.str();
Matteo Martincigh49124022019-01-11 13:25:59 +000093 if (warningMessages)
94 {
95 warningMessages.value().push_back(fullWarningMessage.str());
96 }
97}
98
Derek Lamberti4a9e24b2020-01-03 16:53:38 +000099OptimizationResult ReturnWithError(OptimizationResult res,
100 const Layer* layer,
101 const BackendSettings& backendSettings,
102 Optional<std::vector<std::string>&> errMessages)
103{
104 std::stringstream failureMsg;
105 failureMsg << "Layer of type " << GetLayerTypeAsCString(layer->GetType())
106 << " is not supported on any preferred backend " << backendSettings.m_PreferredBackends;
107 ReportError(failureMsg.str(), errMessages);
108
109 res.m_Error = true;
110 return res;
111}
112
113
jimfly016b0b53d2018-10-08 14:43:01 +0100114bool CheckScaleSetOnQuantizedType(Layer* layer, Optional<std::vector<std::string>&> errMessages)
115{
116 bool noErrors = true;
117 unsigned int numOutputs = layer->GetNumOutputSlots();
118 for (unsigned int i = 0; i < numOutputs; i++) {
David Monahanb8554702019-04-25 16:03:38 +0100119 OutputSlot& outputSlot = layer->GetOutputSlot(i);
120 TensorInfo info = outputSlot.GetTensorInfo();
Derek Lambertif90c56d2020-01-10 17:14:08 +0000121 if (DataType::QAsymmU8 == info.GetDataType()) {
jimfly016b0b53d2018-10-08 14:43:01 +0100122 if (0.f == info.GetQuantizationScale()) {
123 noErrors = false;
124 std::stringstream ss;
Matteo Martincigh49124022019-01-11 13:25:59 +0000125 ss << "output " << i << " of layer " << GetLayerTypeAsCString(layer->GetType())
jimfly016b0b53d2018-10-08 14:43:01 +0100126 << " (" << layer->GetNameStr() << ") is of type"
127 << " Quantized 8 bit but its scale parameter has not been set";
Matteo Martincigh49124022019-01-11 13:25:59 +0000128 ReportError(ss.str(), errMessages);
jimfly016b0b53d2018-10-08 14:43:01 +0100129 }
David Monahanb8554702019-04-25 16:03:38 +0100130 // Softmax under QuantisedAsymm8 must always be scale (1.0f/256.0f) and offset 0
131 if ((info.GetQuantizationScale() != (1.0f / 256.0f) ||
132 info.GetQuantizationOffset() != 0) &&
133 layer->GetType() == armnn::LayerType::Softmax)
134 {
135 std::stringstream ss;
136 ss << "Quantization parameters for Softmax layer (Scale: " <<
137 info.GetQuantizationScale() << " and Offset: " << info.GetQuantizationOffset() <<
138 ") are incorrect and have been updated to Scale: 0.00390625 and Offset: 0";
Derek Lamberti08446972019-11-26 16:38:31 +0000139 ARMNN_LOG(warning) << ss.str();
David Monahanb8554702019-04-25 16:03:38 +0100140 info.SetQuantizationScale((1.0f /256.0f));
141 info.SetQuantizationOffset(0);
142 outputSlot.SetTensorInfo(info);
143 }
jimfly016b0b53d2018-10-08 14:43:01 +0100144 }
145 }
146 return noErrors;
147}
148
Derek Lamberti4a9e24b2020-01-03 16:53:38 +0000149OptimizationResult AttemptBackendAssignment(BackendSettings& backendSettings,
150 Graph& graph,
151 Layer* layer,
152 BackendId backend,
153 DataType dataTypeIn,
154 DataType dataTypeOut,
155 const std::vector<BackendId>& availablePreferredBackends,
156 std::string& reasonIfUnsupported,
157 Optional<std::vector<std::string>&> errMessages)
158{
159 OptimizationResult result;
160
161 // Helper lambda to compose meaningful error message before returning with error
162 auto ReturnError = [&](const Layer* layer)
163 {
164 return ReturnWithError(result, layer, backendSettings, errMessages);
165 };
166
167 // need to set the compute device on the layer
168 // before we can check if it is supported
169 layer->SetBackendId(backend);
170 if (!IWorkloadFactory::IsLayerSupported(*layer, EmptyOptional(), reasonIfUnsupported))
171 {
172 if (dataTypeIn == DataType::Float16 || dataTypeOut == DataType::Float16)
173 {
174 if (IWorkloadFactory::IsLayerSupported(*layer, DataType::Float32, reasonIfUnsupported)
175 && layer->GetType() != LayerType::ConvertFp32ToFp16
176 && layer->GetType() != LayerType::ConvertFp16ToFp32)
177 {
178 // Insert FP16 -> FP32 conversion layer before current layer
179 std::vector<ConvertFp16ToFp32Layer*> convertFp16ToFp32Layers;
180 if (dataTypeIn == DataType::Float16)
181 {
182 convertFp16ToFp32Layers =
183 InsertConvertFp16ToFp32LayersBefore(graph, *layer);
184 }
185
186 // Insert FP32 -> FP16 conversion layer after current layer
187 std::vector<ConvertFp32ToFp16Layer*> convertFp32ToFp16Layers;
188 if (dataTypeOut == DataType::Float16)
189 {
190 convertFp32ToFp16Layers =
191 InsertConvertFp32ToFp16LayersAfter(graph, *layer);
192 }
193
194 // Assign a supported backend to the newly introduced conversion layers
195 auto AssignFirstSupportedBackend = [&](Layer* layer, BackendId preferredBackend)
196 {
197 bool supportedBackendFound = false;
198 std::string reasonIfUnsupported;
199
200 // Try preferred backend first
201 layer->SetBackendId(preferredBackend);
202 if (IWorkloadFactory::IsLayerSupported(*layer,
203 EmptyOptional(),
204 reasonIfUnsupported))
205 {
206 supportedBackendFound = true;
207 }
208 else
209 {
210 for (const auto& backend : availablePreferredBackends)
211 {
212 // Skip preferred backend (we already determined that it is not supported)
213 if (backend == preferredBackend)
214 {
215 continue;
216 }
217
218 layer->SetBackendId(backend);
219 if (IWorkloadFactory::IsLayerSupported(*layer,
220 EmptyOptional(),
221 reasonIfUnsupported))
222 {
223 supportedBackendFound = true;
224 break;
225 }
226 }
227 }
228
229 return supportedBackendFound;
230 };
231
232 for (ConvertFp16ToFp32Layer* convertLayer : convertFp16ToFp32Layers)
233 {
234 if (!AssignFirstSupportedBackend(convertLayer, backend))
235 {
236 return ReturnError(convertLayer);
237 }
238 }
239
240 for (ConvertFp32ToFp16Layer* convertLayer : convertFp32ToFp16Layers)
241 {
242 if (!AssignFirstSupportedBackend(convertLayer, backend))
243 {
244 return ReturnError(convertLayer);
245 }
246 }
247
248 return result;
249 }
250 }
Narumol Prangnawaratbc7ffb52020-03-20 15:01:01 +0000251 else if (dataTypeIn == DataType::BFloat16 || dataTypeOut == DataType::BFloat16)
252 {
253 if (IWorkloadFactory::IsLayerSupported(*layer, DataType::Float32, reasonIfUnsupported)
254 && layer->GetType() != LayerType::ConvertFp32ToBf16
255 && layer->GetType() != LayerType::ConvertBf16ToFp32)
256 {
257 // Insert BF16 -> FP32 conversion layer before current layer
258 std::vector<ConvertBf16ToFp32Layer*> convertBf16ToFp32Layers;
259 if (dataTypeIn == DataType::BFloat16)
260 {
261 convertBf16ToFp32Layers =
262 InsertConvertBf16ToFp32LayersBefore(graph, *layer);
263 }
264
265 // Insert FP32 -> BF16 conversion layer after current layer
266 std::vector<ConvertFp32ToBf16Layer*> convertFp32ToBf16Layers;
267 if (dataTypeOut == DataType::BFloat16)
268 {
269 convertFp32ToBf16Layers =
270 InsertConvertFp32ToBf16LayersAfter(graph, *layer);
271 }
272
273 // Assign a supported backend to the newly introduced conversion layers
274 auto AssignFirstSupportedBackend = [&](Layer* layer, BackendId preferredBackend)
275 {
276 bool supportedBackendFound = false;
277 std::string reasonIfUnsupported;
278
279 // Try preferred backend first
280 layer->SetBackendId(preferredBackend);
281 if (IWorkloadFactory::IsLayerSupported(*layer,
282 EmptyOptional(),
283 reasonIfUnsupported))
284 {
285 supportedBackendFound = true;
286 }
287 else
288 {
289 for (const auto& backend : availablePreferredBackends)
290 {
291 // Skip preferred backend (we already determined that it is not supported)
292 if (backend == preferredBackend)
293 {
294 continue;
295 }
296
297 layer->SetBackendId(backend);
298 if (IWorkloadFactory::IsLayerSupported(*layer,
299 EmptyOptional(),
300 reasonIfUnsupported))
301 {
302 supportedBackendFound = true;
303 break;
304 }
305 }
306 }
307
308 return supportedBackendFound;
309 };
310
311 for (ConvertBf16ToFp32Layer* convertLayer : convertBf16ToFp32Layers)
312 {
313 if (!AssignFirstSupportedBackend(convertLayer, backend))
314 {
315 return ReturnError(convertLayer);
316 }
317 }
318
319 for (ConvertFp32ToBf16Layer* convertLayer : convertFp32ToBf16Layers)
320 {
321 if (!AssignFirstSupportedBackend(convertLayer, backend))
322 {
323 return ReturnError(convertLayer);
324 }
325 }
326
327 return result;
328 }
329 }
330
Derek Lamberti4a9e24b2020-01-03 16:53:38 +0000331 std::stringstream warningMsg;
332 warningMsg << "Layer of type " << GetLayerTypeAsCString(layer->GetType())
333 << " is not supported on requested backend " << layer->GetBackendId().Get()
334 << " for input data type " << GetDataTypeName(dataTypeIn)
335 << " and output data type " << GetDataTypeName(dataTypeOut)
336 << " (reason: " << reasonIfUnsupported
337 << "), falling back to the next backend.";
338 ReportWarning(warningMsg.str(), errMessages);
339
340 return OptimizationResult(true, false);
341 }
342 else
343 {
344 return result;
345 }
346}
347
348
Matteo Martincigh49124022019-01-11 13:25:59 +0000349OptimizationResult AssignBackends(OptimizedNetwork* optNetObjPtr,
350 BackendSettings& backendSettings,
351 Graph::Iterator& firstLayer,
352 Graph::Iterator& lastLayer,
353 Optional<std::vector<std::string>&> errMessages)
telsoa014fcda012018-03-09 14:13:49 +0000354{
Matteo Martincigh49124022019-01-11 13:25:59 +0000355 OptimizationResult result;
telsoa014fcda012018-03-09 14:13:49 +0000356
Matteo Martincigh49124022019-01-11 13:25:59 +0000357 // Helper lambda to compose meaningful error message before returning with error
Derek Lamberti4a9e24b2020-01-03 16:53:38 +0000358 auto ReturnError = [&](const Layer* layer)
359 {
360 return ReturnWithError(result, layer, backendSettings, errMessages);
361 };
Matteo Martincigh49124022019-01-11 13:25:59 +0000362
telsoa01c577f2c2018-08-31 09:22:23 +0100363
Matteo Martincigh49124022019-01-11 13:25:59 +0000364 auto availablePreferredBackends = backendSettings.GetAvailablePreferredBackends();
365 if (availablePreferredBackends.empty())
telsoa01c577f2c2018-08-31 09:22:23 +0100366 {
Matteo Martincigh49124022019-01-11 13:25:59 +0000367 std::stringstream failureMsg;
368 failureMsg << "No preferred backends are available";
369 ReportError(failureMsg.str(), errMessages);
370
371 result.m_Error = true;
372 return result;
373 }
374
375 for (auto it = firstLayer; it != lastLayer; ++it)
376 {
377 auto layer = *it;
Aron Virginas-Tar87972be2019-11-13 15:16:28 +0000378
379 DataType dataTypeIn = layer->GetNumInputSlots() == 0 ? DataType::Float32 :
380 layer->GetInputSlot(0).GetConnectedOutputSlot()->GetTensorInfo().GetDataType();
381 DataType dataTypeOut = layer->GetNumOutputSlots() == 0 ? DataType::Float32 :
382 layer->GetOutputSlot(0).GetTensorInfo().GetDataType();
383
telsoa01c577f2c2018-08-31 09:22:23 +0100384 std::string reasonIfUnsupported;
385 bool found = false;
jimfly016b0b53d2018-10-08 14:43:01 +0100386 if (!CheckScaleSetOnQuantizedType(layer, errMessages))
387 {
388 // don't bomb immediately, find all the quantized outputs
389 // which haven't had a scale set and report them all back.
Matteo Martincigh49124022019-01-11 13:25:59 +0000390 result.m_Error = true;
jimfly016b0b53d2018-10-08 14:43:01 +0100391 }
Matteo Martincigh49124022019-01-11 13:25:59 +0000392
Derek Lamberti4a9e24b2020-01-03 16:53:38 +0000393 // First try assign layer to hint backend
394 if (layer->GetBackendHint().has_value() &&
395 backendSettings.IsBackendSupported(layer->GetBackendHint().value()) &&
396 AttemptBackendAssignment(backendSettings,
397 optNetObjPtr->GetGraph(),
398 layer,
399 layer->GetBackendHint().value(),
400 dataTypeIn,
401 dataTypeOut,
402 availablePreferredBackends,
403 reasonIfUnsupported,
404 errMessages).IsOk())
telsoa01c577f2c2018-08-31 09:22:23 +0100405 {
Derek Lamberti4a9e24b2020-01-03 16:53:38 +0000406 found = true;
407 backendSettings.m_SelectedBackends.insert(layer->GetBackendHint().value());
408 }
409 else
410 {
411 // Try assign layer to prefered list of backends
412 for (const auto& backend : availablePreferredBackends)
telsoa01c577f2c2018-08-31 09:22:23 +0100413 {
Derek Lamberti4a9e24b2020-01-03 16:53:38 +0000414 if (layer->GetBackendHint().has_value() &&
415 layer->GetBackendHint().value() == backend)
telsoa01c577f2c2018-08-31 09:22:23 +0100416 {
Derek Lamberti4a9e24b2020-01-03 16:53:38 +0000417 continue; //Don't re-test the backend hint
telsoa01c577f2c2018-08-31 09:22:23 +0100418 }
Derek Lamberti4a9e24b2020-01-03 16:53:38 +0000419
420 OptimizationResult res = AttemptBackendAssignment(backendSettings,
421 optNetObjPtr->GetGraph(),
422 layer,
423 backend,
424 dataTypeIn,
425 dataTypeOut,
426 availablePreferredBackends,
427 reasonIfUnsupported,
428 errMessages);
429
430 if (res.IsOk())
431 {
432 found = true;
433 backendSettings.m_SelectedBackends.insert(backend);
434 break;
435 }
436 else if (res.IsError())
437 {
438 return res; // Cannot continue.
439 // Note: we don't need to log the error as it would already
440 // be logged in AttemptBackendAssignment().
441 }
442 else
443 {
444 BOOST_ASSERT_MSG(res.IsWarningOnly(), "OptimizationResult in unexpected state.");
445 }
telsoa01c577f2c2018-08-31 09:22:23 +0100446 }
447 }
448
449 // If the layer is unsupported by any devices, log and return a null network.
Matteo Martincigh49124022019-01-11 13:25:59 +0000450 if (!found)
451 {
telsoa01c577f2c2018-08-31 09:22:23 +0100452 // NOTE: if the layer is not an operation queue type AND we have not got CpuRef as a
453 // fallback we should set the compute device on the layer to CpuRef (these are not
454 // available as accelerated operations, or are only available under certain
455 // conditions, currently they comprise MemCopy, Constant, Permute)
456 armnn::LayerType layerType = layer->GetType();
Matteo Martincigh49124022019-01-11 13:25:59 +0000457 if (!backendSettings.IsCpuRefUsed() && (layerType == armnn::LayerType::MemCopy ||
458 layerType == armnn::LayerType::Constant ||
459 layerType == armnn::LayerType::Permute))
telsoa01c577f2c2018-08-31 09:22:23 +0100460 {
Matteo Martincigh49124022019-01-11 13:25:59 +0000461 BackendId cpuBackendId(armnn::Compute::CpuRef);
462 layer->SetBackendId(cpuBackendId);
463 backendSettings.m_SelectedBackends.insert(cpuBackendId);
telsoa01c577f2c2018-08-31 09:22:23 +0100464 }
465 else
466 {
Derek Lamberti4a9e24b2020-01-03 16:53:38 +0000467 return ReturnError(layer);
telsoa01c577f2c2018-08-31 09:22:23 +0100468 }
469 }
470 }
Matteo Martincigh49124022019-01-11 13:25:59 +0000471
472 return result;
473}
474
Matteo Martincighadddddb2019-01-24 14:06:23 +0000475OptimizationResult AssignBackends(OptimizedNetwork* optNetObjPtr,
476 BackendSettings& backendSettings,
Derek Lambertiff05cc52019-04-26 13:05:17 +0100477 SubgraphView& subgraph,
Matteo Martincighadddddb2019-01-24 14:06:23 +0000478 Optional<std::vector<std::string>&> errMessages)
Matteo Martincigh49124022019-01-11 13:25:59 +0000479{
Derek Lambertiff05cc52019-04-26 13:05:17 +0100480 Graph::Iterator firstLayer = subgraph.begin();
481 Graph::Iterator lastLayer = subgraph.end();
Matteo Martincighadddddb2019-01-24 14:06:23 +0000482 return AssignBackends(optNetObjPtr,
483 backendSettings,
484 firstLayer,
485 lastLayer,
486 errMessages);
487}
488
Derek Lamberti84da38b2019-06-13 11:40:08 +0100489BackendsMap CreateSupportedBackends(TensorHandleFactoryRegistry& handleFactoryRegistry,
490 BackendSettings& backendSettings)
491{
492 BackendsMap backends;
493 auto const& backendRegistry = BackendRegistryInstance();
494 for (auto&& selectedBackend : backendSettings.m_SupportedBackends)
495 {
496 auto backendFactory = backendRegistry.GetFactory(selectedBackend);
497 auto backendObjPtr = backendFactory();
498 BOOST_ASSERT(backendObjPtr);
499
500 backendObjPtr->RegisterTensorHandleFactories(handleFactoryRegistry);
501
502 backends[backendObjPtr->GetId()] = std::move(backendObjPtr);
503 }
504
505 return backends;
506}
507
Matteo Martincighadddddb2019-01-24 14:06:23 +0000508OptimizationResult ApplyBackendOptimizations(OptimizedNetwork* optNetObjPtr,
509 BackendSettings& backendSettings,
Derek Lamberti84da38b2019-06-13 11:40:08 +0100510 BackendsMap& backends,
Matteo Martincighadddddb2019-01-24 14:06:23 +0000511 Optional<std::vector<std::string>&> errMessages)
512{
513 BOOST_ASSERT(optNetObjPtr);
Matteo Martincigh49124022019-01-11 13:25:59 +0000514
515 OptimizationResult result;
516
Matteo Martincighadddddb2019-01-24 14:06:23 +0000517 // Get the optimized graph
518 Graph& optGraph = optNetObjPtr->GetGraph();
Matteo Martincigh49124022019-01-11 13:25:59 +0000519
Matteo Martincighadddddb2019-01-24 14:06:23 +0000520 // Run backend specific optimizations
Matteo Martincighadddddb2019-01-24 14:06:23 +0000521 for (auto&& selectedBackend : backendSettings.m_SelectedBackends)
Matteo Martincigh49124022019-01-11 13:25:59 +0000522 {
Derek Lamberti84da38b2019-06-13 11:40:08 +0100523 auto backendObjPtr = backends.find(selectedBackend)->second.get();
Matteo Martincighadddddb2019-01-24 14:06:23 +0000524 BOOST_ASSERT(backendObjPtr);
525
526 // Select sub-graphs based on backend
Derek Lambertiff05cc52019-04-26 13:05:17 +0100527 SubgraphViewSelector::Subgraphs subgraphs =
Rob Hughes65c32262019-07-23 15:33:39 +0100528 SubgraphViewSelector::SelectSubgraphs(optGraph,
Matteo Martincigh602af092019-05-01 10:31:27 +0100529 // Select layers assigned to the requested backend
530 [&backendObjPtr](const Layer& layer)
531 {
532 return layer.GetType() != LayerType::Input &&
533 layer.GetType() != LayerType::Output &&
534 layer.GetBackendId() == backendObjPtr->GetId();
535 });
Derek Lambertiff05cc52019-04-26 13:05:17 +0100536 if (subgraphs.empty())
Matteo Martincigh49124022019-01-11 13:25:59 +0000537 {
Matteo Martincighadddddb2019-01-24 14:06:23 +0000538 // No sub-graphs found, try with next selected backend
539 continue;
Matteo Martincigh49124022019-01-11 13:25:59 +0000540 }
Matteo Martincighadddddb2019-01-24 14:06:23 +0000541
542 // Try to optimize each sub-graph
Derek Lambertiff05cc52019-04-26 13:05:17 +0100543 for (auto& subgraph : subgraphs)
Matteo Martincigh49124022019-01-11 13:25:59 +0000544 {
Matteo Martincighadddddb2019-01-24 14:06:23 +0000545 // Try to optimize the current sub-graph
Matteo Martincigh84924332019-05-09 12:46:16 +0100546 OptimizationViews optimizationViews = backendObjPtr->OptimizeSubgraphView(*subgraph);
547 BOOST_ASSERT(optimizationViews.Validate(*subgraph));
Matteo Martincighadddddb2019-01-24 14:06:23 +0000548
549 // Optimization attempted, check the resulting optimized sub-graph
Matteo Martincigh84924332019-05-09 12:46:16 +0100550 for (auto& substitution : optimizationViews.GetSubstitutions())
Matteo Martincighadddddb2019-01-24 14:06:23 +0000551 {
552 // Sub-graph optimized, substitute the sub-graph with the new optimized one in the main optimized graph
Matteo Martincigh84924332019-05-09 12:46:16 +0100553 SubgraphView& replacementSubgraph = substitution.m_ReplacementSubgraph;
554 SubgraphView& substitutableSubgraph = substitution.m_SubstitutableSubgraph;
555 optGraph.SubstituteSubgraph(substitutableSubgraph, replacementSubgraph);
Matteo Martincighadddddb2019-01-24 14:06:23 +0000556
557 // Assign the current backend to the optimized sub-graph
Matteo Martincigh84924332019-05-09 12:46:16 +0100558 std::for_each(replacementSubgraph.begin(), replacementSubgraph.end(), [&selectedBackend](Layer* l)
Derek Lambertic2fe5fb2019-05-08 10:23:08 +0100559 {
560 BOOST_ASSERT(l);
561 l->SetBackendId(selectedBackend);
562 });
Matteo Martincighadddddb2019-01-24 14:06:23 +0000563 }
Derek Lambertic2fe5fb2019-05-08 10:23:08 +0100564
Matteo Martincigh84924332019-05-09 12:46:16 +0100565 if (!optimizationViews.GetFailedSubgraphs().empty())
Matteo Martincighadddddb2019-01-24 14:06:23 +0000566 {
Matteo Martincighadddddb2019-01-24 14:06:23 +0000567 std::stringstream warningMsg;
Derek Lambertic2fe5fb2019-05-08 10:23:08 +0100568 warningMsg << "Some sub-graph(s) failed to optimized on " << backendObjPtr->GetId() << " backend.";
Matteo Martincighadddddb2019-01-24 14:06:23 +0000569 ReportWarning(warningMsg.str(), errMessages);
570
571 // Failed to optimize the given sub-graph, re-assign the sub-graph layers to other available backends
Derek Lambertic2fe5fb2019-05-08 10:23:08 +0100572 BackendSettings settingsCopy(backendSettings);
Matteo Martincighadddddb2019-01-24 14:06:23 +0000573 if (!backendObjPtr->GetId().IsCpuRef())
574 {
575 // Add the current backend to the list of backends to ignore
Derek Lambertic2fe5fb2019-05-08 10:23:08 +0100576 settingsCopy.m_IgnoredBackends.insert(backendObjPtr->GetId());
Matteo Martincighadddddb2019-01-24 14:06:23 +0000577 }
Derek Lambertic2fe5fb2019-05-08 10:23:08 +0100578
579 int count=0;
Matteo Martincigh84924332019-05-09 12:46:16 +0100580 for (auto& failedSubgraph : optimizationViews.GetFailedSubgraphs())
Matteo Martincighadddddb2019-01-24 14:06:23 +0000581 {
Derek Lambertic2fe5fb2019-05-08 10:23:08 +0100582 // An error occurred: the optimization was attempted but not performed, try different backends
583 std::stringstream subgraphMsg;
584 subgraphMsg << "Re-assigning backends to " << failedSubgraph.GetLayers().size()
585 << " layers inside sub-graph " << count++;
Matteo Martincigh328d92b2019-07-04 17:52:55 +0100586 ReportWarning(subgraphMsg.str(), errMessages);
Derek Lambertic2fe5fb2019-05-08 10:23:08 +0100587
588 OptimizationResult reassignmentResult = AssignBackends(optNetObjPtr,
589 settingsCopy,
590 *subgraph,
591 errMessages);
592 if (reassignmentResult.m_Error)
593 {
594 // Failed to re-assign one of the remaining backends to each layer of the sub-graph
595 result.m_Error = true;
596 return result;
597 }
Matteo Martincighadddddb2019-01-24 14:06:23 +0000598 }
Matteo Martincigh49124022019-01-11 13:25:59 +0000599 }
600 }
601 }
602
603 return result;
604}
605
Derek Lamberti84da38b2019-06-13 11:40:08 +0100606bool RequiresCopy(ITensorHandleFactory::FactoryId src,
607 ITensorHandleFactory::FactoryId dst,
608 TensorHandleFactoryRegistry& registry)
609{
610 if (src != dst)
611 {
612 ITensorHandleFactory* srcFactory = registry.GetFactory(src);
613 ITensorHandleFactory* dstFactory = registry.GetFactory(dst);
614
Matteo Martincigha6539ed2019-08-27 13:43:32 +0100615 if (srcFactory && dstFactory &&
616 (srcFactory->GetExportFlags() & dstFactory->GetImportFlags()) != 0)
Derek Lamberti84da38b2019-06-13 11:40:08 +0100617 {
618 return false;
619 }
620 return true;
621 }
622 return false;
623}
624
625// Find the handle factory for the input layer which results in fewest required copies.
626ITensorHandleFactory::FactoryId CalculateSlotOptionForInput(BackendsMap& backends,
627 OutputSlot& slot,
628 TensorHandleFactoryRegistry& registry)
629{
630 Layer& layer = slot.GetOwningLayer();
631 BOOST_ASSERT(layer.GetType() == LayerType::Input);
632
633 // Explicitly select the tensorhandle factory for InputLayer because the rules for it are slightly different. It
634 // doesn't matter which backend it is assigned to because they all use the same implementation, which
635 // requires Map/Unmap support. This means that, so long as the handle type supports map/unmap semantics, we can
636 // select a factory with maximum compatibility with the layers connected to the InputLayer.
637
638 // First ensure the from backends can support the TensorHandeAPI
639 auto frmBackend = backends.find(layer.GetBackendId());
640 if (frmBackend == backends.end() ||
641 !frmBackend->second->SupportsTensorAllocatorAPI())
642 {
643 return ITensorHandleFactory::LegacyFactoryId;
644 }
645
646 // Go through all connections to the output slot and determine the TensorHandleFactory which results in the
647 // fewest copies.
648 std::map<ITensorHandleFactory::FactoryId, int> factoryScores;
649 int topScore = 0;
650 ITensorHandleFactory::FactoryId topChoice = ITensorHandleFactory::LegacyFactoryId;
651
652 for (auto&& connection : slot.GetConnections())
653 {
654 const Layer& connectedLayer = connection->GetOwningLayer();
655
656 auto toBackend = backends.find(connectedLayer.GetBackendId());
657 BOOST_ASSERT_MSG(toBackend != backends.end(), "Backend id not found for the connected layer");
658
659 if (!toBackend->second.get()->SupportsTensorAllocatorAPI())
660 {
661 // The destination backend does not support the tensor allocator API, move to the next one
662 continue;
663 }
664
665 auto dstPrefs = toBackend->second.get()->GetHandleFactoryPreferences();
666 for (auto&& dst : dstPrefs)
667 {
Derek Lambertif674aa02019-08-01 15:56:25 +0100668 // Input layers use the mem copy workload or import, so the selected factory must
669 // support either the map/unmap API or Import API
Derek Lamberti84da38b2019-06-13 11:40:08 +0100670 ITensorHandleFactory* factory = registry.GetFactory(dst);
Derek Lambertif674aa02019-08-01 15:56:25 +0100671 if (!factory->SupportsMapUnmap() &&
672 !CheckFlag(factory->GetImportFlags(), MemorySource::Malloc)) // Just support cpu mem imports for now
Derek Lamberti84da38b2019-06-13 11:40:08 +0100673 {
Derek Lambertif674aa02019-08-01 15:56:25 +0100674 // The current tensor handle factory does not support the map/unmap or import
675 // strategy, move to the next one
Derek Lamberti84da38b2019-06-13 11:40:08 +0100676 continue;
677 }
678
679 auto it = factoryScores.find(dst);
680 if (it == factoryScores.end())
681 {
682 // Add new score to the table
683 factoryScores[dst] = 0;
684 if (topChoice == ITensorHandleFactory::LegacyFactoryId)
685 {
686 topChoice = dst;
687 }
688 }
689 else
690 {
691 // Increase the score
692 factoryScores[dst]++;
693
694 // Track the best option
695 if (factoryScores[dst] > topScore)
696 {
697 topScore = factoryScores[dst];
698 topChoice = dst;
699 }
700 }
701 }
702 }
703
704 return topChoice;
705}
706
707// Find the handle factory for the output layer which results in fewest required copies.
708ITensorHandleFactory::FactoryId CalculateSlotOptionForOutput(BackendsMap& backends,
709 OutputSlot& slot,
710 TensorHandleFactoryRegistry& registry)
711{
Jan Eilers8eb25602020-03-09 12:13:48 +0000712 IgnoreUnused(backends, slot, registry);
Derek Lamberti94a88d22019-12-10 21:12:59 +0000713 return ITensorHandleFactory::DeferredFactoryId;
Derek Lamberti84da38b2019-06-13 11:40:08 +0100714}
715
716// For all handle factories supported on the source backend, we wish to find the one which requires the fewest copies
717// when considering all connections.
718ITensorHandleFactory::FactoryId CalculateSlotOption(BackendsMap& backends,
719 OutputSlot& outputSlot,
720 TensorHandleFactoryRegistry& registry)
721{
722 // First ensure the from backends can support the TensorHandeAPI
723 Layer& layer = outputSlot.GetOwningLayer();
724 auto frmBackend = backends.find(layer.GetBackendId());
725 if (frmBackend == backends.end() ||
726 !frmBackend->second->SupportsTensorAllocatorAPI())
727 {
728 return ITensorHandleFactory::LegacyFactoryId;
729 }
730
731 // Connections to Output Layers requires support for map/unmap on the TensorHandle.
732 bool requiresMapUnmap = false;
733 for (auto&& connection : outputSlot.GetConnections())
734 {
735 const Layer& connectedLayer = connection->GetOwningLayer();
736 if (connectedLayer.GetType() == LayerType::Output)
737 {
738 requiresMapUnmap = true;
739 }
740 }
741
742 IBackendInternal* srcBackend = frmBackend->second.get();
743 auto srcPrefs = srcBackend->GetHandleFactoryPreferences();
744
745 // Initialize the scores
746 std::map<ITensorHandleFactory::FactoryId, int> factoryScores;
747 for (auto&& pref : srcPrefs)
748 {
749 if (requiresMapUnmap) // Only consider factories that support map/unmap if required
750 {
751 ITensorHandleFactory* factory = registry.GetFactory(pref);
752 if (!factory->SupportsMapUnmap())
753 {
754 // The current tensor handle factory does not support the map/unmap strategy, move to the next one
755 continue;
756 }
757 }
758
759 auto it = factoryScores.find(pref);
760 if (it == factoryScores.end())
761 {
762 // Add new score to the table
763 factoryScores[pref] = 0;
764 }
765 }
766
767 // Score each handle factory based on how many times it requires copies on the slot connections
768 for (auto&& connection : outputSlot.GetConnections())
769 {
770 const Layer& connectedLayer = connection->GetOwningLayer();
771
772 auto toBackend = backends.find(connectedLayer.GetBackendId());
773 BOOST_ASSERT_MSG(toBackend != backends.end(), "Backend id not found for the connected layer");
774
775 auto dstPrefs = toBackend->second.get()->GetHandleFactoryPreferences();
776 for (auto&& src : srcPrefs)
777 {
778 if (factoryScores.find(src) == factoryScores.end()) // Don't consider excluded factories
779 {
780 continue;
781 }
782
783 for (auto&& dst : dstPrefs)
784 {
785 if (RequiresCopy(src, dst, registry))
786 {
787 // Copy avoided, increase the score
788 factoryScores[src]++;
789 break;
790 }
791 }
792 }
793 }
794
795 // Find the lowest score
796 int minScore = std::numeric_limits<int>::max();
797 for (auto it : factoryScores)
798 {
799 minScore = std::min(minScore, it.second);
800 }
801
802 // Collect factories matching the best(lowest) score
803 std::vector<ITensorHandleFactory::FactoryId> optimalFactories;
804 for (auto it : factoryScores)
805 {
806 if (it.second == minScore)
807 {
808 optimalFactories.push_back(it.first);
809 }
810 }
811
812 // For all compatible Factories matching the best score, find the preferred one for the current layer.
813 for (auto&& srcPref : srcPrefs)
814 {
815 for (auto&& comp : optimalFactories)
816 {
817 if (comp == srcPref)
818 {
819 return comp;
820 }
821 }
822 }
823
824 return ITensorHandleFactory::LegacyFactoryId;
825}
826
Derek Lambertif674aa02019-08-01 15:56:25 +0100827EdgeStrategy CalculateEdgeStrategy(BackendsMap& backends,
828 ITensorHandleFactory::FactoryId srcFactoryId,
829 const Layer& layer,
830 const Layer& connectedLayer,
831 TensorHandleFactoryRegistry& registry)
Derek Lamberti84da38b2019-06-13 11:40:08 +0100832{
833 auto toBackend = backends.find(connectedLayer.GetBackendId());
834 BOOST_ASSERT_MSG(toBackend != backends.end(), "Backend id not found for the connected layer");
835
836 auto dstPrefs = toBackend->second.get()->GetHandleFactoryPreferences();
837
838 // Legacy API check for backward compatibility
839 if (srcFactoryId == ITensorHandleFactory::LegacyFactoryId || dstPrefs.empty())
840 {
841 if (layer.GetBackendId() != connectedLayer.GetBackendId())
842 {
Derek Lambertif674aa02019-08-01 15:56:25 +0100843 return EdgeStrategy::CopyToTarget;
Derek Lamberti84da38b2019-06-13 11:40:08 +0100844 }
845 else
846 {
Derek Lambertif674aa02019-08-01 15:56:25 +0100847 return EdgeStrategy::DirectCompatibility;
Derek Lamberti84da38b2019-06-13 11:40:08 +0100848 }
849 }
850
851 // TensorHandleFactory API present, so perform more sophisticated strategies.
Derek Lambertif674aa02019-08-01 15:56:25 +0100852 // Dst Output layers don't require copy because they use import or map/unmap
Derek Lamberti84da38b2019-06-13 11:40:08 +0100853 if (connectedLayer.GetType() == LayerType::Output)
854 {
Derek Lambertif674aa02019-08-01 15:56:25 +0100855 return EdgeStrategy::DirectCompatibility;
Derek Lamberti84da38b2019-06-13 11:40:08 +0100856 }
857
858 // Search for direct match in prefs
859 for (auto&& pref : dstPrefs)
860 {
861 if (pref == srcFactoryId)
862 {
Derek Lambertif674aa02019-08-01 15:56:25 +0100863 return EdgeStrategy::DirectCompatibility;
Derek Lamberti84da38b2019-06-13 11:40:08 +0100864 }
865 }
866
867 // Search for export/import options
868 ITensorHandleFactory* srcFactory = registry.GetFactory(srcFactoryId);
Derek Lambertif674aa02019-08-01 15:56:25 +0100869 if (srcFactory->GetExportFlags() != 0)
Derek Lamberti84da38b2019-06-13 11:40:08 +0100870 {
871 for (auto&& pref : dstPrefs)
872 {
873 ITensorHandleFactory* dstFactory = registry.GetFactory(pref);
James Conroyffab16f2019-11-07 14:37:09 +0000874
James Conroy47e863d2019-11-18 17:07:43 +0000875 // Handles cases when a destPref is not listed in TensorHandleFactoryRegistry
James Conroyffab16f2019-11-07 14:37:09 +0000876 if (!dstFactory) {
James Conroy47e863d2019-11-18 17:07:43 +0000877 continue;
James Conroyffab16f2019-11-07 14:37:09 +0000878 }
879
Derek Lambertif674aa02019-08-01 15:56:25 +0100880 if ((dstFactory->GetImportFlags() & srcFactory->GetExportFlags()) != 0)
Derek Lamberti84da38b2019-06-13 11:40:08 +0100881 {
Derek Lambertif674aa02019-08-01 15:56:25 +0100882 return EdgeStrategy::ExportToTarget;
Derek Lamberti84da38b2019-06-13 11:40:08 +0100883 }
884 }
885 }
886
887 // Search for copy options via map/unmap
888 if (srcFactory->SupportsMapUnmap())
889 {
890 for (auto&& pref : dstPrefs)
891 {
892 ITensorHandleFactory* dstFactory = registry.GetFactory(pref);
James Conroy47e863d2019-11-18 17:07:43 +0000893 if (dstFactory && dstFactory->SupportsMapUnmap())
Derek Lamberti84da38b2019-06-13 11:40:08 +0100894 {
Derek Lambertif674aa02019-08-01 15:56:25 +0100895 return EdgeStrategy::CopyToTarget;
Derek Lamberti84da38b2019-06-13 11:40:08 +0100896 }
897 }
898 }
899
Derek Lambertif674aa02019-08-01 15:56:25 +0100900 return EdgeStrategy::Undefined;
Derek Lamberti84da38b2019-06-13 11:40:08 +0100901}
902
903// Select the TensorHandleFactories and the corresponding memory strategy
904OptimizationResult SelectTensorHandleStrategy(Graph& optGraph,
905 BackendsMap& backends,
906 TensorHandleFactoryRegistry& registry,
907 Optional<std::vector<std::string>&> errMessages)
908{
909 OptimizationResult result;
910
911 optGraph.ForEachLayer([&backends, &registry, &result, &errMessages](Layer* layer)
912 {
913 BOOST_ASSERT(layer);
914
915 // Lets make sure the backend is in our list of supported backends. Something went wrong during backend
916 // assignment if this check fails
917 BOOST_ASSERT(backends.find(layer->GetBackendId()) != backends.end());
918
919 // Check each output separately
920 for (unsigned int slotIdx = 0; slotIdx < layer->GetNumOutputSlots(); slotIdx++)
921 {
922 OutputSlot& outputSlot = layer->GetOutputSlot(slotIdx);
923
924 ITensorHandleFactory::FactoryId slotOption = ITensorHandleFactory::LegacyFactoryId;
925
926 // Calculate the factory to use which results in the fewest copies being made.
927 switch(layer->GetType())
928 {
929 case LayerType::Input:
930 slotOption = CalculateSlotOptionForInput(backends, outputSlot, registry);
931 break;
932 case LayerType::Output:
933 slotOption = CalculateSlotOptionForOutput(backends, outputSlot, registry);
934 break;
935 default:
936 slotOption = CalculateSlotOption(backends, outputSlot, registry);
937 break;
938 }
939 outputSlot.SetTensorHandleFactory(slotOption);
940
Derek Lambertif674aa02019-08-01 15:56:25 +0100941 // Now determine the "best" edge strategy for each connection given the slotOption.
Derek Lamberti84da38b2019-06-13 11:40:08 +0100942 unsigned int connectionIdx = 0;
943 for (auto&& connection : outputSlot.GetConnections())
944 {
945 const Layer& connectedLayer = connection->GetOwningLayer();
946
Derek Lambertif674aa02019-08-01 15:56:25 +0100947 EdgeStrategy strategy = CalculateEdgeStrategy(backends, slotOption, *layer, connectedLayer, registry);
Derek Lamberti84da38b2019-06-13 11:40:08 +0100948
Derek Lambertif674aa02019-08-01 15:56:25 +0100949 if (strategy == EdgeStrategy::Undefined)
Derek Lamberti84da38b2019-06-13 11:40:08 +0100950 {
951 result.m_Error = true;
952 if (errMessages)
953 {
954 errMessages.value().emplace_back("Could not find valid strategy required for compatibility"
955 " between backends.");
956 }
957 return;
958 }
959
Derek Lambertif674aa02019-08-01 15:56:25 +0100960 outputSlot.SetEdgeStrategy(connectionIdx, strategy);
Derek Lamberti84da38b2019-06-13 11:40:08 +0100961
962 connectionIdx++;
963 }
964 }
965 });
966
967 return result;
968}
969
Matteo Martincigh49124022019-01-11 13:25:59 +0000970IOptimizedNetworkPtr Optimize(const INetwork& inNetwork,
971 const std::vector<BackendId>& backendPreferences,
972 const IDeviceSpec& deviceSpec,
973 const OptimizerOptions& options,
Rob Hughes23214432019-11-05 11:27:36 +0000974 Optional<std::vector<std::string>&> messages)
Matteo Martincigh49124022019-01-11 13:25:59 +0000975{
976 if (backendPreferences.empty())
977 {
978 throw armnn::InvalidArgumentException("Invoked Optimize with no backends specified");
979 }
980
Narumol Prangnawaratbc7ffb52020-03-20 15:01:01 +0000981 if (options.m_ReduceFp32ToFp16 && options.m_ReduceFp32ToBf16)
982 {
983 throw InvalidArgumentException("BFloat16 and Float16 optimization cannot be enabled at the same time.");
984 }
985
Matteo Martincigh49124022019-01-11 13:25:59 +0000986 const Network& network = *boost::polymorphic_downcast<const Network*>(&inNetwork);
987 std::unique_ptr<Graph> graph = std::make_unique<Graph>(network.GetGraph());
988
989 auto optNet = IOptimizedNetworkPtr(new OptimizedNetwork(std::move(graph)), &IOptimizedNetwork::Destroy);
990
991 OptimizedNetwork* optNetObjPtr = boost::polymorphic_downcast<OptimizedNetwork*>(optNet.get());
992
Matteo Martincighadddddb2019-01-24 14:06:23 +0000993 // Get the optimized graph
994 Graph& optGraph = optNetObjPtr->GetGraph();
995
Matteo Martincigh49124022019-01-11 13:25:59 +0000996 // Perform optimisation passes
997 using namespace optimizations;
Matteo Martincighadddddb2019-01-24 14:06:23 +0000998 Optimizer::Pass(optGraph, MakeOptimizations(SquashEqualPermuteSiblings(),
Mike Kelly490b7be2020-03-03 12:39:09 +0000999 SquashEqualTransposeSiblings(),
Matteo Martincighadddddb2019-01-24 14:06:23 +00001000 SquashEqualReshapeSiblings(),
1001 OptimizeInversePermutes(),
Mike Kelly490b7be2020-03-03 12:39:09 +00001002 OptimizeInverseTransposes(),
Matteo Martincighadddddb2019-01-24 14:06:23 +00001003 MovePermuteUp(),
Mike Kelly490b7be2020-03-03 12:39:09 +00001004 MoveTransposeUp(),
Matteo Martincighadddddb2019-01-24 14:06:23 +00001005 PermuteAsReshape(),
Mike Kelly490b7be2020-03-03 12:39:09 +00001006 TransposeAsReshape(),
Nina Drozd861985f2019-04-18 14:48:51 +01001007 OptimizeConsecutiveReshapes(),
Rob Hughes3a7d3a72019-09-24 16:59:56 +01001008 FoldPadIntoConvolution2d(),
Mike Kelly490b7be2020-03-03 12:39:09 +00001009 PermuteAndBatchToSpaceAsDepthToSpace(),
1010 TransposeAndBatchToSpaceAsDepthToSpace()));
Matteo Martincigh49124022019-01-11 13:25:59 +00001011
Matteo Martincighadddddb2019-01-24 14:06:23 +00001012 // Infer the tensor infos for all output slots. Throws an exception on failure
1013 optGraph.InferTensorInfos();
Matteo Martincigh49124022019-01-11 13:25:59 +00001014
1015 // If Fp32 to Fp16 optimization is set convert Fp32 network to Fp16
1016 if (options.m_ReduceFp32ToFp16)
1017 {
Matteo Martincighadddddb2019-01-24 14:06:23 +00001018 Optimizer::Pass(optGraph, MakeOptimizations(Fp32NetworkToFp16Converter()));
Derek Lambertidd6804b2019-11-27 09:29:57 +00001019 Optimizer::Pass(optGraph, MakeOptimizations(ConvertConstantsFloatToHalf()));
Matteo Martincigh49124022019-01-11 13:25:59 +00001020 }
1021
Narumol Prangnawaratbc7ffb52020-03-20 15:01:01 +00001022 // If Fp32 to Bf16 optimization is set convert Fp32 network to Bf16
Narumol Prangnawarat57ef0082020-03-26 09:20:43 +00001023 // Convert input of Convolution2d and FullyConnected from Fp32 to Bf16
1024 // Only Constant weight of Convolution2d and FullyConnected are converted from Fp32 to Bf16
Narumol Prangnawaratbc7ffb52020-03-20 15:01:01 +00001025 if (options.m_ReduceFp32ToBf16)
1026 {
1027 Optimizer::Pass(optGraph, MakeOptimizations(Fp32NetworkToBf16Converter()));
Narumol Prangnawaratbc7ffb52020-03-20 15:01:01 +00001028 }
1029
Matteo Martincigh49124022019-01-11 13:25:59 +00001030 // Initialize backend settings
1031 BackendSettings backendSettings(backendPreferences, deviceSpec);
1032 if (backendSettings.GetAvailablePreferredBackends().empty())
1033 {
1034 std::stringstream failureMsg;
1035 failureMsg << "None of the preferred backends " << backendPreferences
1036 << " are supported. Current platform provides " << backendSettings.m_SupportedBackends;
Rob Hughes23214432019-11-05 11:27:36 +00001037 ReportError(failureMsg.str(), messages);
Matteo Martincigh49124022019-01-11 13:25:59 +00001038 return IOptimizedNetworkPtr(nullptr, &IOptimizedNetwork::Destroy);
1039 }
1040
Derek Lamberti84da38b2019-06-13 11:40:08 +01001041 // Create a map to temporarily hold initialized backend objects
1042 TensorHandleFactoryRegistry tensorHandleFactoryRegistry;
1043 BackendsMap backends = CreateSupportedBackends(tensorHandleFactoryRegistry, backendSettings);
1044
Matteo Martincigh49124022019-01-11 13:25:59 +00001045 // Assign an available backend to each layer
Matteo Martincighadddddb2019-01-24 14:06:23 +00001046 Graph::Iterator firstLayer = optGraph.begin();
1047 Graph::Iterator lastLayer = optGraph.end();
Derek Lamberti84da38b2019-06-13 11:40:08 +01001048 OptimizationResult assignBackendsResult = AssignBackends(optNetObjPtr,
1049 backendSettings,
1050 firstLayer,
1051 lastLayer,
Rob Hughes23214432019-11-05 11:27:36 +00001052 messages);
Derek Lamberti84da38b2019-06-13 11:40:08 +01001053 if (assignBackendsResult.m_Error)
Matteo Martincigh49124022019-01-11 13:25:59 +00001054 {
1055 // Failed to assign a backend to each layer
jimfly016b0b53d2018-10-08 14:43:01 +01001056 return IOptimizedNetworkPtr(nullptr, &IOptimizedNetwork::Destroy);
1057 }
telsoa01c577f2c2018-08-31 09:22:23 +01001058
Matteo Martincighadddddb2019-01-24 14:06:23 +00001059 Optimizer::Pass(optGraph, MakeOptimizations(OptimizeInverseConversionsFp16(),
1060 OptimizeInverseConversionsFp32()));
telsoa01c577f2c2018-08-31 09:22:23 +01001061
Matteo Martincighadddddb2019-01-24 14:06:23 +00001062 // Apply the backend-specific optimizations
1063 OptimizationResult backendOptimizationResult = ApplyBackendOptimizations(optNetObjPtr,
1064 backendSettings,
Derek Lamberti84da38b2019-06-13 11:40:08 +01001065 backends,
Rob Hughes23214432019-11-05 11:27:36 +00001066 messages);
Matteo Martincighadddddb2019-01-24 14:06:23 +00001067 if (backendOptimizationResult.m_Error)
Matteo Martincigh49124022019-01-11 13:25:59 +00001068 {
Matteo Martincighadddddb2019-01-24 14:06:23 +00001069 // Failed to apply the backend-specific optimizations
1070 return IOptimizedNetworkPtr(nullptr, &IOptimizedNetwork::Destroy);
Matteo Martincigh49124022019-01-11 13:25:59 +00001071 }
1072
Matteo Martincighadddddb2019-01-24 14:06:23 +00001073 // If the debug flag is set, then insert a DebugLayer after each layer
1074 // Doing this after applying the backend optimizations as they might have changed some layers
1075 if (options.m_Debug)
1076 {
1077 Optimizer::Pass(optGraph, MakeOptimizations(InsertDebugLayer()));
1078 }
1079
Derek Lamberti84da38b2019-06-13 11:40:08 +01001080 // Calculate the compatibility strategies for tensor handles
1081 OptimizationResult strategyResult = SelectTensorHandleStrategy(optGraph,
1082 backends,
1083 tensorHandleFactoryRegistry,
Rob Hughes23214432019-11-05 11:27:36 +00001084 messages);
Derek Lamberti84da38b2019-06-13 11:40:08 +01001085 if (strategyResult.m_Error)
1086 {
1087 // Failed to apply the backend-specific optimizations
1088 return IOptimizedNetworkPtr(nullptr, &IOptimizedNetwork::Destroy);
1089 }
1090
1091 // Based on the tensor handle strategy determined above, insert copy layers where required.
Derek Lambertif674aa02019-08-01 15:56:25 +01001092 optGraph.AddCompatibilityLayers(backends, tensorHandleFactoryRegistry);
telsoa01c577f2c2018-08-31 09:22:23 +01001093
1094 // Convert constants
Matteo Martincighadddddb2019-01-24 14:06:23 +00001095 Optimizer::Pass(optGraph, MakeOptimizations(ConvertConstantsFloatToHalf()));
1096 Optimizer::Pass(optGraph, MakeOptimizations(ConvertConstantsHalfToFloat()));
telsoa01c577f2c2018-08-31 09:22:23 +01001097
Derek Lamberti84da38b2019-06-13 11:40:08 +01001098 // Run backend specific optimizations (deprecated)
Matteo Martincigh49124022019-01-11 13:25:59 +00001099 for (auto&& chosenBackend : backendSettings.m_SelectedBackends)
David Beck263e3492018-11-09 14:46:40 +00001100 {
1101 auto factoryFun = BackendRegistryInstance().GetFactory(chosenBackend);
1102 auto backendPtr = factoryFun();
1103 BOOST_ASSERT(backendPtr.get() != nullptr);
1104
Matteo Martincighed735042019-05-22 09:42:43 +01001105 ARMNN_NO_DEPRECATE_WARN_BEGIN
David Beck263e3492018-11-09 14:46:40 +00001106 auto backendSpecificOptimizations = backendPtr->GetOptimizations();
Matteo Martincighed735042019-05-22 09:42:43 +01001107 ARMNN_NO_DEPRECATE_WARN_END
1108
David Beck263e3492018-11-09 14:46:40 +00001109 if (!backendSpecificOptimizations.empty())
1110 {
1111 Optimizer::Pass(optNetObjPtr->GetGraph(), backendSpecificOptimizations);
1112 }
1113 }
1114
telsoa01c577f2c2018-08-31 09:22:23 +01001115 return optNet;
telsoa014fcda012018-03-09 14:13:49 +00001116}
1117
1118Network::Network()
Sadik Armagan3184c902020-03-18 10:57:30 +00001119: m_Graph(std::make_unique<Graph>())
telsoa014fcda012018-03-09 14:13:49 +00001120{
1121}
1122
1123Network::~Network()
1124{
1125}
1126
Jan Eilers99d9d4a2019-11-06 10:02:16 +00001127Status Network::PrintGraph()
1128{
1129 m_Graph->Print();
1130 return Status::Success;
1131}
1132
telsoa014fcda012018-03-09 14:13:49 +00001133IConnectableLayer* Network::AddInputLayer(LayerBindingId id, const char* name)
1134{
1135 return m_Graph->AddLayer<InputLayer>(id, name);
1136}
1137
Éanna Ó Catháin4e1e1362018-11-12 11:36:34 +00001138IConnectableLayer* Network::AddBatchToSpaceNdLayer(const BatchToSpaceNdDescriptor& batchToSpaceNdDescriptor,
1139 const char* name)
1140{
1141 return m_Graph->AddLayer<BatchToSpaceNdLayer>(batchToSpaceNdDescriptor, name);
1142}
1143
Aron Virginas-Tar77bfb5e2019-10-16 17:45:38 +01001144IConnectableLayer* Network::AddComparisonLayer(const ComparisonDescriptor& comparisonDescriptor,
1145 const char* name)
1146{
1147 return m_Graph->AddLayer<ComparisonLayer>(comparisonDescriptor, name);
1148}
1149
josh minor4a3c6102020-01-06 16:40:46 -06001150IConnectableLayer* Network::AddElementwiseUnaryLayer(const ElementwiseUnaryDescriptor& elementwiseUnaryDescriptor,
1151 const char* name)
1152{
1153 return m_Graph->AddLayer<ElementwiseUnaryLayer>(elementwiseUnaryDescriptor, name);
1154}
1155
telsoa014fcda012018-03-09 14:13:49 +00001156IConnectableLayer* Network::AddFullyConnectedLayerImpl(const FullyConnectedDescriptor& fullyConnectedDescriptor,
telsoa01c577f2c2018-08-31 09:22:23 +01001157 const ConstTensor& weights,
Aron Virginas-Tarad402702019-02-22 17:03:44 +00001158 const Optional<ConstTensor>& biases,
telsoa01c577f2c2018-08-31 09:22:23 +01001159 const char* name)
telsoa014fcda012018-03-09 14:13:49 +00001160{
Aron Virginas-Tarad402702019-02-22 17:03:44 +00001161 if (fullyConnectedDescriptor.m_BiasEnabled && !biases.has_value())
telsoa014fcda012018-03-09 14:13:49 +00001162 {
Aron Virginas-Tarad402702019-02-22 17:03:44 +00001163 throw InvalidArgumentException("AddFullyConnectedLayer: biases cannot be empty");
telsoa014fcda012018-03-09 14:13:49 +00001164 }
1165
1166 const auto layer = m_Graph->AddLayer<FullyConnectedLayer>(fullyConnectedDescriptor, name);
1167
1168 layer->m_Weight = std::make_unique<ScopedCpuTensorHandle>(weights);
1169
1170 if (fullyConnectedDescriptor.m_BiasEnabled)
1171 {
Aron Virginas-Tarad402702019-02-22 17:03:44 +00001172 layer->m_Bias = std::make_unique<ScopedCpuTensorHandle>(biases.value());
telsoa014fcda012018-03-09 14:13:49 +00001173 }
1174
1175 return layer;
1176}
1177
1178IConnectableLayer* Network::AddFullyConnectedLayer(const FullyConnectedDescriptor& fullyConnectedDescriptor,
telsoa01c577f2c2018-08-31 09:22:23 +01001179 const ConstTensor& weights,
Aron Virginas-Tarad402702019-02-22 17:03:44 +00001180 const Optional<ConstTensor>& biases,
telsoa01c577f2c2018-08-31 09:22:23 +01001181 const char* name)
telsoa014fcda012018-03-09 14:13:49 +00001182{
Aron Virginas-Tarad402702019-02-22 17:03:44 +00001183 return AddFullyConnectedLayerImpl(fullyConnectedDescriptor, weights, biases, name);
telsoa014fcda012018-03-09 14:13:49 +00001184}
1185
Aron Virginas-Tarad402702019-02-22 17:03:44 +00001186IConnectableLayer* Network::AddFullyConnectedLayer(const FullyConnectedDescriptor& fullyConnectedDescriptor,
1187 const ConstTensor& weights,
1188 const char* name)
1189{
Matteo Martincighfc598e12019-05-14 10:36:13 +01001190 Optional<ConstTensor> biases;
Aron Virginas-Tarad402702019-02-22 17:03:44 +00001191 return AddFullyConnectedLayerImpl(fullyConnectedDescriptor, weights, biases, name);
1192}
1193
telsoa014fcda012018-03-09 14:13:49 +00001194IConnectableLayer* Network::AddFullyConnectedLayer(const FullyConnectedDescriptor& fullyConnectedDescriptor,
telsoa01c577f2c2018-08-31 09:22:23 +01001195 const ConstTensor& weights,
1196 const ConstTensor& biases,
1197 const char* name)
telsoa014fcda012018-03-09 14:13:49 +00001198{
Aron Virginas-Tarad402702019-02-22 17:03:44 +00001199 Optional<ConstTensor> optionalBiases(biases);
1200 return AddFullyConnectedLayerImpl(fullyConnectedDescriptor, weights, optionalBiases, name);
telsoa014fcda012018-03-09 14:13:49 +00001201}
1202
Jim Flynne242f2d2019-05-22 14:24:13 +01001203IConnectableLayer* Network::AddConcatLayer(const ConcatDescriptor& concatDescriptor,
Jim Flynn906f9462019-05-10 13:55:21 +01001204 const char* name)
1205{
Jim Flynne242f2d2019-05-22 14:24:13 +01001206 return m_Graph->AddLayer<ConcatLayer>(concatDescriptor, name);
Jim Flynn906f9462019-05-10 13:55:21 +01001207}
1208
telsoa014fcda012018-03-09 14:13:49 +00001209IConnectableLayer* Network::AddConvolution2dLayerImpl(const Convolution2dDescriptor& convolution2dDescriptor,
telsoa01c577f2c2018-08-31 09:22:23 +01001210 const ConstTensor& weights,
Aron Virginas-Tarad402702019-02-22 17:03:44 +00001211 const Optional<ConstTensor>& biases,
telsoa01c577f2c2018-08-31 09:22:23 +01001212 const char* name)
telsoa014fcda012018-03-09 14:13:49 +00001213{
Aron Virginas-Tarad402702019-02-22 17:03:44 +00001214 if (convolution2dDescriptor.m_BiasEnabled && !biases.has_value())
telsoa014fcda012018-03-09 14:13:49 +00001215 {
Aron Virginas-Tarad402702019-02-22 17:03:44 +00001216 throw InvalidArgumentException("AddConvolution2dLayer: biases cannot be empty");
telsoa014fcda012018-03-09 14:13:49 +00001217 }
1218
1219 const auto layer = m_Graph->AddLayer<Convolution2dLayer>(convolution2dDescriptor, name);
1220
1221 layer->m_Weight = std::make_unique<ScopedCpuTensorHandle>(weights);
1222
1223 if (convolution2dDescriptor.m_BiasEnabled)
1224 {
Aron Virginas-Tarad402702019-02-22 17:03:44 +00001225 layer->m_Bias = std::make_unique<ScopedCpuTensorHandle>(biases.value());
telsoa014fcda012018-03-09 14:13:49 +00001226 }
1227
1228 return layer;
1229}
1230
1231IConnectableLayer* Network::AddConvolution2dLayer(const Convolution2dDescriptor& convolution2dDescriptor,
telsoa01c577f2c2018-08-31 09:22:23 +01001232 const ConstTensor& weights,
Aron Virginas-Tarad402702019-02-22 17:03:44 +00001233 const Optional<ConstTensor>& biases,
telsoa01c577f2c2018-08-31 09:22:23 +01001234 const char* name)
telsoa014fcda012018-03-09 14:13:49 +00001235{
Aron Virginas-Tarad402702019-02-22 17:03:44 +00001236 return AddConvolution2dLayerImpl(convolution2dDescriptor, weights, biases, name);
telsoa014fcda012018-03-09 14:13:49 +00001237}
Aron Virginas-Tarad402702019-02-22 17:03:44 +00001238
Aron Virginas-Tarad402702019-02-22 17:03:44 +00001239IConnectableLayer* Network::AddConvolution2dLayer(const Convolution2dDescriptor& convolution2dDescriptor,
1240 const ConstTensor& weights,
1241 const char* name)
1242{
Matteo Martincighfc598e12019-05-14 10:36:13 +01001243 Optional<ConstTensor> biases;
Aron Virginas-Tarad402702019-02-22 17:03:44 +00001244 return AddConvolution2dLayerImpl(convolution2dDescriptor, weights, biases, name);
1245}
1246
telsoa014fcda012018-03-09 14:13:49 +00001247IConnectableLayer* Network::AddConvolution2dLayer(const Convolution2dDescriptor& convolution2dDescriptor,
telsoa01c577f2c2018-08-31 09:22:23 +01001248 const ConstTensor& weights,
1249 const ConstTensor& biases,
1250 const char* name)
telsoa014fcda012018-03-09 14:13:49 +00001251{
Aron Virginas-Tarad402702019-02-22 17:03:44 +00001252 Optional<ConstTensor> optionalBiases(biases);
1253 return AddConvolution2dLayerImpl(convolution2dDescriptor, weights, optionalBiases, name);
telsoa014fcda012018-03-09 14:13:49 +00001254}
1255
1256IConnectableLayer* Network::AddDepthwiseConvolution2dLayerImpl(
1257 const DepthwiseConvolution2dDescriptor& convolution2dDescriptor,
1258 const ConstTensor& weights,
Aron Virginas-Tarad402702019-02-22 17:03:44 +00001259 const Optional<ConstTensor>& biases,
telsoa014fcda012018-03-09 14:13:49 +00001260 const char* name)
1261{
Aron Virginas-Tarad402702019-02-22 17:03:44 +00001262 if (convolution2dDescriptor.m_BiasEnabled && !biases.has_value())
telsoa014fcda012018-03-09 14:13:49 +00001263 {
Aron Virginas-Tarad402702019-02-22 17:03:44 +00001264 throw InvalidArgumentException("AddDepthwiseConvolution2dLayer: biases cannot be empty");
telsoa014fcda012018-03-09 14:13:49 +00001265 }
1266
Matteo Martincigh3d6898c2019-01-15 16:11:44 +00001267 const auto layer = m_Graph->AddLayer<DepthwiseConvolution2dLayer>(convolution2dDescriptor, name);
telsoa014fcda012018-03-09 14:13:49 +00001268
1269 layer->m_Weight = std::make_unique<ScopedCpuTensorHandle>(weights);
1270
1271 if (convolution2dDescriptor.m_BiasEnabled)
1272 {
Aron Virginas-Tarad402702019-02-22 17:03:44 +00001273 layer->m_Bias = std::make_unique<ScopedCpuTensorHandle>(biases.value());
telsoa014fcda012018-03-09 14:13:49 +00001274 }
1275
1276 return layer;
1277}
1278
Aron Virginas-Tardd6247f2019-09-19 14:31:17 +01001279IConnectableLayer* Network::AddDepthToSpaceLayer(const DepthToSpaceDescriptor& depthToSpaceDescriptor,
1280 const char* name)
1281{
1282 return m_Graph->AddLayer<DepthToSpaceLayer>(depthToSpaceDescriptor, name);
1283}
1284
telsoa014fcda012018-03-09 14:13:49 +00001285IConnectableLayer* Network::AddDepthwiseConvolution2dLayer(
Aron Virginas-Tarad402702019-02-22 17:03:44 +00001286 const DepthwiseConvolution2dDescriptor& convolution2dDescriptor,
1287 const ConstTensor& weights,
1288 const Optional<ConstTensor>& biases,
1289 const char* name)
1290{
1291 return AddDepthwiseConvolution2dLayerImpl(convolution2dDescriptor, weights, biases, name);
1292}
1293
Aron Virginas-Tarad402702019-02-22 17:03:44 +00001294IConnectableLayer* Network::AddDepthwiseConvolution2dLayer(
telsoa014fcda012018-03-09 14:13:49 +00001295 const DepthwiseConvolution2dDescriptor& convolution2dDescriptor,
1296 const ConstTensor& weights,
1297 const char* name)
1298{
Matteo Martincighfc598e12019-05-14 10:36:13 +01001299 Optional<ConstTensor> biases;
Aron Virginas-Tarad402702019-02-22 17:03:44 +00001300 return AddDepthwiseConvolution2dLayerImpl(convolution2dDescriptor, weights, biases, name);
telsoa014fcda012018-03-09 14:13:49 +00001301}
Aron Virginas-Tarad402702019-02-22 17:03:44 +00001302
telsoa014fcda012018-03-09 14:13:49 +00001303IConnectableLayer* Network::AddDepthwiseConvolution2dLayer(
1304 const DepthwiseConvolution2dDescriptor& convolution2dDescriptor,
1305 const ConstTensor& weights,
1306 const ConstTensor& biases,
1307 const char* name)
1308{
Aron Virginas-Tarad402702019-02-22 17:03:44 +00001309 Optional<ConstTensor> optionalBiases(biases);
1310 return AddDepthwiseConvolution2dLayerImpl(convolution2dDescriptor, weights, optionalBiases, name);
telsoa014fcda012018-03-09 14:13:49 +00001311}
1312
Narumol Prangnawarat94dd5d82019-01-23 18:06:26 +00001313IConnectableLayer* Network::AddDetectionPostProcessLayer(const armnn::DetectionPostProcessDescriptor& descriptor,
Narumol Prangnawarat6d302bf2019-02-04 11:46:26 +00001314 const ConstTensor& anchors, const char* name)
Narumol Prangnawarat94dd5d82019-01-23 18:06:26 +00001315{
Narumol Prangnawarat6d302bf2019-02-04 11:46:26 +00001316 const auto layer = m_Graph->AddLayer<DetectionPostProcessLayer>(descriptor, name);
1317
1318 layer->m_Anchors = std::make_unique<ScopedCpuTensorHandle>(anchors);
1319
1320 return layer;
Narumol Prangnawarat94dd5d82019-01-23 18:06:26 +00001321}
1322
telsoa014fcda012018-03-09 14:13:49 +00001323IConnectableLayer* Network::AddPermuteLayer(const PermuteDescriptor& permuteDescriptor,
1324 const char* name)
1325{
1326 return m_Graph->AddLayer<PermuteLayer>(permuteDescriptor, name);
1327}
1328
1329IConnectableLayer* Network::AddPooling2dLayer(const Pooling2dDescriptor& pooling2dDescriptor,
1330 const char* name)
1331{
1332 return m_Graph->AddLayer<Pooling2dLayer>(pooling2dDescriptor, name);
1333}
1334
1335IConnectableLayer* Network::AddActivationLayer(const ActivationDescriptor& activationDescriptor,
1336 const char* name)
1337{
1338 return m_Graph->AddLayer<ActivationLayer>(activationDescriptor, name);
1339}
1340
Nikhil Rajee391d52019-09-05 17:50:44 +01001341IConnectableLayer* Network::AddArgMinMaxLayer(const ArgMinMaxDescriptor& argMinMaxDescriptor,
1342 const char* name)
1343{
1344 return m_Graph->AddLayer<ArgMinMaxLayer>(argMinMaxDescriptor, name);
1345}
1346
telsoa01c577f2c2018-08-31 09:22:23 +01001347IConnectableLayer* Network::AddNormalizationLayer(const NormalizationDescriptor&
1348normalizationDescriptor,
telsoa014fcda012018-03-09 14:13:49 +00001349 const char* name)
1350{
1351 return m_Graph->AddLayer<NormalizationLayer>(normalizationDescriptor, name);
1352}
1353
Aron Virginas-Tar636ab402019-09-16 14:27:45 +01001354IConnectableLayer* Network::AddSliceLayer(const SliceDescriptor& sliceDescriptor, const char* name)
1355{
1356 return m_Graph->AddLayer<SliceLayer>(sliceDescriptor, name);
1357}
1358
telsoa014fcda012018-03-09 14:13:49 +00001359IConnectableLayer* Network::AddSoftmaxLayer(const SoftmaxDescriptor& softmaxDescriptor,
1360 const char* name)
1361{
1362 return m_Graph->AddLayer<SoftmaxLayer>(softmaxDescriptor, name);
1363}
1364
1365IConnectableLayer* Network::AddSplitterLayer(const ViewsDescriptor& splitterDescriptor,
1366 const char* name)
1367{
1368 return m_Graph->AddLayer<SplitterLayer>(splitterDescriptor, name);
1369}
1370
Nattapat Chaimanowong5a4304a2018-11-28 10:44:37 +00001371IConnectableLayer* Network::AddMaximumLayer(const char* name)
1372{
1373 return m_Graph->AddLayer<MaximumLayer>(name);
1374}
1375
Éanna Ó Catháin20e58802018-12-04 10:29:06 +00001376IConnectableLayer* Network::AddMinimumLayer(const char* name)
1377{
1378 return m_Graph->AddLayer<MinimumLayer>(name);
1379}
1380
Jim Flynne242f2d2019-05-22 14:24:13 +01001381IConnectableLayer* Network::AddMergerLayer(const MergerDescriptor& mergerDescriptor,
Jim Flynn906f9462019-05-10 13:55:21 +01001382 const char* name)
telsoa014fcda012018-03-09 14:13:49 +00001383{
Jim Flynne242f2d2019-05-22 14:24:13 +01001384 return AddConcatLayer(mergerDescriptor, name);
telsoa014fcda012018-03-09 14:13:49 +00001385}
1386
Kevin May868eb142019-09-04 17:29:31 +01001387IConnectableLayer* Network::AddAbsLayer(const char * name)
1388{
josh minor4a3c6102020-01-06 16:40:46 -06001389 return AddElementwiseUnaryLayer(ElementwiseUnaryDescriptor(UnaryOperation::Abs), name);
Kevin May868eb142019-09-04 17:29:31 +01001390}
1391
telsoa014fcda012018-03-09 14:13:49 +00001392IConnectableLayer* Network::AddAdditionLayer(const char* name)
1393{
1394 return m_Graph->AddLayer<AdditionLayer>(name);
1395}
1396
1397IConnectableLayer* Network::AddMultiplicationLayer(const char* name)
1398{
1399 return m_Graph->AddLayer<MultiplicationLayer>(name);
1400}
1401
1402IConnectableLayer* Network::AddOutputLayer(LayerBindingId id, const char* name)
1403{
1404 return m_Graph->AddLayer<OutputLayer>(id, name);
1405}
1406
1407IConnectableLayer* Network::AddBatchNormalizationLayer(const BatchNormalizationDescriptor& desc,
1408 const ConstTensor& mean,
1409 const ConstTensor& variance,
1410 const ConstTensor& beta,
1411 const ConstTensor& gamma,
1412 const char* name)
1413{
1414 const auto layer = m_Graph->AddLayer<BatchNormalizationLayer>(desc, name);
1415
1416 layer->m_Mean = std::make_unique<ScopedCpuTensorHandle>(mean);
1417 layer->m_Variance = std::make_unique<ScopedCpuTensorHandle>(variance);
1418 layer->m_Beta = std::make_unique<ScopedCpuTensorHandle>(beta);
1419 layer->m_Gamma = std::make_unique<ScopedCpuTensorHandle>(gamma);
1420
1421 return layer;
1422}
1423
Aron Virginas-Tar169d2f12019-07-01 19:01:44 +01001424IConnectableLayer* Network::AddResizeBilinearLayer(const ResizeBilinearDescriptor& descriptor,
1425 const char* name)
telsoa014fcda012018-03-09 14:13:49 +00001426{
Aron Virginas-Tar169d2f12019-07-01 19:01:44 +01001427 ResizeDescriptor resizeDescriptor;
1428 resizeDescriptor.m_Method = ResizeMethod::Bilinear;
1429 resizeDescriptor.m_DataLayout = descriptor.m_DataLayout;
1430 resizeDescriptor.m_TargetWidth = descriptor.m_TargetWidth;
1431 resizeDescriptor.m_TargetHeight = descriptor.m_TargetHeight;
1432
1433 return m_Graph->AddLayer<ResizeLayer>(resizeDescriptor, name);
telsoa014fcda012018-03-09 14:13:49 +00001434}
1435
Teresa Charlina9075df2019-06-27 15:41:57 +01001436IConnectableLayer* Network::AddResizeLayer(const ResizeDescriptor&
1437resizeDescriptor, const char* name)
1438{
Aron Virginas-Tar169d2f12019-07-01 19:01:44 +01001439 return m_Graph->AddLayer<ResizeLayer>(resizeDescriptor, name);
Teresa Charlina9075df2019-06-27 15:41:57 +01001440}
1441
Kevin Mayce5045a2019-10-02 14:07:47 +01001442IConnectableLayer* Network::AddInstanceNormalizationLayer(const InstanceNormalizationDescriptor& desc,
1443 const char* name)
1444{
1445 return m_Graph->AddLayer<InstanceNormalizationLayer>(desc, name);
1446}
1447
Matteo Martincighbcd3c852018-09-28 14:14:12 +01001448IConnectableLayer* Network::AddL2NormalizationLayer(const L2NormalizationDescriptor& desc,
1449 const char* name)
telsoa014fcda012018-03-09 14:13:49 +00001450{
Matteo Martincighbcd3c852018-09-28 14:14:12 +01001451 return m_Graph->AddLayer<L2NormalizationLayer>(desc, name);
telsoa014fcda012018-03-09 14:13:49 +00001452}
1453
Aron Virginas-Tarf982dea2019-10-11 14:07:53 +01001454IConnectableLayer* Network::AddLogSoftmaxLayer(const LogSoftmaxDescriptor& desc,
1455 const char* name)
1456{
1457 return m_Graph->AddLayer<LogSoftmaxLayer>(desc, name);
1458}
1459
telsoa014fcda012018-03-09 14:13:49 +00001460IConnectableLayer* Network::AddConstantLayer(const ConstTensor& input, const char* name)
1461{
telsoa01c577f2c2018-08-31 09:22:23 +01001462 auto layer = m_Graph->AddLayer<ConstantLayer>(name);
1463
1464 layer->m_LayerOutput = std::make_unique<ScopedCpuTensorHandle>(input);
1465
1466 return layer;
telsoa014fcda012018-03-09 14:13:49 +00001467}
1468
telsoa01c577f2c2018-08-31 09:22:23 +01001469IConnectableLayer* Network::AddReshapeLayer(const ReshapeDescriptor& reshapeDescriptor,
1470 const char* name)
telsoa014fcda012018-03-09 14:13:49 +00001471{
1472 return m_Graph->AddLayer<ReshapeLayer>(reshapeDescriptor, name);
1473}
1474
Nattapat Chaimanowong207ef9a2018-11-02 10:57:25 +00001475IConnectableLayer* Network::AddSpaceToBatchNdLayer(const SpaceToBatchNdDescriptor& spaceToBatchNdDescriptor,
1476 const char* name)
1477{
1478 return m_Graph->AddLayer<SpaceToBatchNdLayer>(spaceToBatchNdDescriptor, name);
1479}
1480
Aron Virginas-Tar972af152019-06-11 14:14:03 +01001481IConnectableLayer* Network::AddSpaceToDepthLayer(const SpaceToDepthDescriptor& spaceToDepthDescriptor,
1482 const char* name)
1483{
1484 return m_Graph->AddLayer<SpaceToDepthLayer>(spaceToDepthDescriptor, name);
1485}
1486
telsoa014fcda012018-03-09 14:13:49 +00001487IConnectableLayer* Network::AddFloorLayer(const char* name)
1488{
1489 return m_Graph->AddLayer<FloorLayer>(name);
1490}
1491
telsoa01c577f2c2018-08-31 09:22:23 +01001492IConnectableLayer* Network::AddLstmLayer(const LstmDescriptor& descriptor,
1493 const LstmInputParams& params,
1494 const char* name)
1495{
1496 const auto layer = m_Graph->AddLayer<LstmLayer>(descriptor, name);
1497
1498 //Lstm Basic Parameters
1499 layer->m_BasicParameters.m_InputToForgetWeights =
1500 std::make_unique<ScopedCpuTensorHandle>(*(params.m_InputToForgetWeights));
1501 layer->m_BasicParameters.m_InputToCellWeights =
1502 std::make_unique<ScopedCpuTensorHandle>(*(params.m_InputToCellWeights));
1503 layer->m_BasicParameters.m_InputToOutputWeights =
1504 std::make_unique<ScopedCpuTensorHandle>(*(params.m_InputToOutputWeights));
1505 layer->m_BasicParameters.m_RecurrentToForgetWeights =
1506 std::make_unique<ScopedCpuTensorHandle>(*(params.m_RecurrentToForgetWeights));
1507 layer->m_BasicParameters.m_RecurrentToCellWeights =
1508 std::make_unique<ScopedCpuTensorHandle>(*(params.m_RecurrentToCellWeights));
1509 layer->m_BasicParameters.m_RecurrentToOutputWeights =
1510 std::make_unique<ScopedCpuTensorHandle>(*(params.m_RecurrentToOutputWeights));
1511 layer->m_BasicParameters.m_ForgetGateBias =
1512 std::make_unique<ScopedCpuTensorHandle>(*(params.m_ForgetGateBias));
1513 layer->m_BasicParameters.m_CellBias =
1514 std::make_unique<ScopedCpuTensorHandle>(*(params.m_CellBias));
1515 layer->m_BasicParameters.m_OutputGateBias =
1516 std::make_unique<ScopedCpuTensorHandle>(*(params.m_OutputGateBias));
1517
1518 //Lstm Cifg parameters
1519 if(!descriptor.m_CifgEnabled)
1520 {
1521 if(params.m_InputToInputWeights == nullptr)
1522 {
Jan Eilerse2062cd2020-03-30 15:07:45 +01001523 throw InvalidArgumentException("AddLstmLayer: Input To Input Weights cannot be NULL "
1524 "when CIFG is disabled.");
telsoa01c577f2c2018-08-31 09:22:23 +01001525 }
1526 if(params.m_RecurrentToInputWeights == nullptr)
1527 {
1528 throw InvalidArgumentException(
Jan Eilerse2062cd2020-03-30 15:07:45 +01001529 "AddLstmLayer: Recurrent To Input Weights cannot be NULL "
1530 "when CIFG is disabled.");
telsoa01c577f2c2018-08-31 09:22:23 +01001531 }
1532 if(params.m_InputGateBias == nullptr)
1533 {
Jan Eilerse2062cd2020-03-30 15:07:45 +01001534 throw InvalidArgumentException("AddLstmLayer: Input Gate Bias cannot be NULL "
1535 "when CIFG is disabled.");
telsoa01c577f2c2018-08-31 09:22:23 +01001536 }
1537 layer->m_CifgParameters.m_InputToInputWeights =
1538 std::make_unique<ScopedCpuTensorHandle>(*(params.m_InputToInputWeights));
1539 layer->m_CifgParameters.m_RecurrentToInputWeights =
1540 std::make_unique<ScopedCpuTensorHandle>(*(params.m_RecurrentToInputWeights));
telsoa01c577f2c2018-08-31 09:22:23 +01001541 layer->m_CifgParameters.m_InputGateBias =
1542 std::make_unique<ScopedCpuTensorHandle>(*(params.m_InputGateBias));
1543 }
1544
1545 //Lstm projection parameters
1546 if(descriptor.m_ProjectionEnabled)
1547 {
1548 if(params.m_ProjectionWeights == nullptr)
1549 {
Jan Eilerse2062cd2020-03-30 15:07:45 +01001550 throw InvalidArgumentException("AddLstmLayer: Projection Weights cannot be NULL "
1551 "when projection is enabled.");
telsoa01c577f2c2018-08-31 09:22:23 +01001552 }
1553 layer->m_ProjectionParameters.m_ProjectionWeights =
1554 std::make_unique<ScopedCpuTensorHandle>(*(params.m_ProjectionWeights));
1555 if(params.m_ProjectionBias != nullptr)
1556 {
1557 layer->m_ProjectionParameters.m_ProjectionBias =
1558 std::make_unique<ScopedCpuTensorHandle>(*(params.m_ProjectionBias));
1559 }
1560 }
1561
1562 //Lstm Peephole params
1563 if(descriptor.m_PeepholeEnabled)
1564 {
Jan Eilerse2062cd2020-03-30 15:07:45 +01001565 if(!descriptor.m_CifgEnabled)
1566 {
1567 if(params.m_CellToInputWeights == nullptr)
1568 {
1569 throw InvalidArgumentException("AddLstmLayer: Cell To Input Weights cannot be NULL "
1570 "when Peephole is enabled and CIFG disabled.");
1571 }
1572
1573 layer->m_PeepholeParameters.m_CellToInputWeights =
1574 std::make_unique<ScopedCpuTensorHandle>(*(params.m_CellToInputWeights));
1575 }
1576
telsoa01c577f2c2018-08-31 09:22:23 +01001577 if(params.m_CellToForgetWeights == nullptr)
1578 {
Jan Eilerse2062cd2020-03-30 15:07:45 +01001579 throw InvalidArgumentException("AddLstmLayer: Cell To Forget Weights cannot be NULL "
1580 "when Peephole is enabled.");
telsoa01c577f2c2018-08-31 09:22:23 +01001581 }
1582 if(params.m_CellToOutputWeights == nullptr)
1583 {
Jan Eilerse2062cd2020-03-30 15:07:45 +01001584 throw InvalidArgumentException("AddLstmLayer: Cell To Output Weights cannot be NULL "
1585 "when Peephole is enabled.");
telsoa01c577f2c2018-08-31 09:22:23 +01001586 }
Jan Eilerse2062cd2020-03-30 15:07:45 +01001587
telsoa01c577f2c2018-08-31 09:22:23 +01001588 layer->m_PeepholeParameters.m_CellToForgetWeights =
1589 std::make_unique<ScopedCpuTensorHandle>(*(params.m_CellToForgetWeights));
1590 layer->m_PeepholeParameters.m_CellToOutputWeights =
1591 std::make_unique<ScopedCpuTensorHandle>(*(params.m_CellToOutputWeights));
1592 }
Jan Eilersf8c62972019-07-17 11:07:49 +01001593
1594 //Lstm Layer Normalization params
1595 if(descriptor.m_LayerNormEnabled)
1596 {
1597 if(!descriptor.m_CifgEnabled)
1598 {
1599 if(params.m_InputLayerNormWeights == nullptr)
1600 {
Jan Eilerse2062cd2020-03-30 15:07:45 +01001601 throw InvalidArgumentException("AddLstmLayer: Input layer normalization weights cannot be NULL "
1602 "when layer normalization is enabled and CIFG disabled.");
Jan Eilersf8c62972019-07-17 11:07:49 +01001603 }
1604 layer->m_LayerNormParameters.m_InputLayerNormWeights =
1605 std::make_unique<ScopedCpuTensorHandle>(*(params.m_InputLayerNormWeights));
1606 }
1607
1608 if(params.m_ForgetLayerNormWeights == nullptr)
1609 {
Jan Eilerse2062cd2020-03-30 15:07:45 +01001610 throw InvalidArgumentException("AddLstmLayer: Forget layer normalization weights cannot be NULL "
1611 "when layer normalization is enabled.");
Jan Eilersf8c62972019-07-17 11:07:49 +01001612 }
1613 if(params.m_CellLayerNormWeights == nullptr)
1614 {
Jan Eilerse2062cd2020-03-30 15:07:45 +01001615 throw InvalidArgumentException("AddLstmLayer: Cell layer normalization weights cannot be NULL "
1616 "when layer normalization is enabled.");
Jan Eilersf8c62972019-07-17 11:07:49 +01001617 }
1618 if(params.m_OutputLayerNormWeights == nullptr)
1619 {
Jan Eilerse2062cd2020-03-30 15:07:45 +01001620 throw InvalidArgumentException("AddLstmLayer: Output layer normalization weights cannot be NULL "
1621 "when layer normalization is enabled.");
Jan Eilersf8c62972019-07-17 11:07:49 +01001622 }
1623 layer->m_LayerNormParameters.m_ForgetLayerNormWeights =
1624 std::make_unique<ScopedCpuTensorHandle>(*(params.m_ForgetLayerNormWeights));
1625 layer->m_LayerNormParameters.m_CellLayerNormWeights =
1626 std::make_unique<ScopedCpuTensorHandle>(*(params.m_CellLayerNormWeights));
1627 layer->m_LayerNormParameters.m_OutputLayerNormWeights =
1628 std::make_unique<ScopedCpuTensorHandle>(*(params.m_OutputLayerNormWeights));
1629 }
telsoa01c577f2c2018-08-31 09:22:23 +01001630 return layer;
1631}
1632
Francis Murtaghe7a86a42018-08-29 12:42:10 +01001633IConnectableLayer* Network::AddDivisionLayer(const char* name)
1634{
1635 return m_Graph->AddLayer<DivisionLayer>(name);
1636}
1637
David Beck19526222018-09-12 16:00:08 +01001638IConnectableLayer* Network::AddSubtractionLayer(const char* name)
1639{
1640 return m_Graph->AddLayer<SubtractionLayer>(name);
1641}
1642
narpra0132b90462018-09-13 11:07:48 +01001643IConnectableLayer* Network::AddMeanLayer(const MeanDescriptor& meanDescriptor, const char* name)
1644{
1645 return m_Graph->AddLayer<MeanLayer>(meanDescriptor,name);
1646}
1647
Mohamed Nour Abouelseoud5662c202018-09-24 13:30:09 +01001648IConnectableLayer* Network::AddPadLayer(const PadDescriptor& padDescriptor, const char* name)
1649{
1650 return m_Graph->AddLayer<PadLayer>(padDescriptor,name);
1651}
1652
Derek Lambertia9cca6a2019-03-25 15:41:58 +00001653IConnectableLayer *Network::AddQuantizeLayer(const char *name)
1654{
1655 return m_Graph->AddLayer<QuantizeLayer>(name);
1656}
1657
Nattapat Chaimanowonge4294fd2019-03-28 09:56:53 +00001658IConnectableLayer* Network::AddDequantizeLayer(const char* name)
1659{
1660 return m_Graph->AddLayer<DequantizeLayer>(name);
1661}
1662
Conor Kennedy430b5d82018-11-14 15:28:28 +00001663IConnectableLayer* Network::AddStridedSliceLayer(const StridedSliceDescriptor& stridedSliceDescriptor,
1664 const char* name)
1665{
1666 return m_Graph->AddLayer<StridedSliceLayer>(stridedSliceDescriptor, name);
1667}
1668
Matteo Martincigh59a950c2018-12-13 12:48:25 +00001669IConnectableLayer* Network::AddGreaterLayer(const char* name)
1670{
Aron Virginas-Tar77bfb5e2019-10-16 17:45:38 +01001671 return AddComparisonLayer(ComparisonDescriptor(ComparisonOperation::Greater), name);
Matteo Martincigh59a950c2018-12-13 12:48:25 +00001672}
1673
FrancisMurtagh20995952018-12-17 12:11:36 +00001674IConnectableLayer* Network::AddEqualLayer(const char* name)
1675{
Aron Virginas-Tar77bfb5e2019-10-16 17:45:38 +01001676 return AddComparisonLayer(ComparisonDescriptor(ComparisonOperation::Equal), name);
FrancisMurtagh20995952018-12-17 12:11:36 +00001677}
1678
Mohamed Nour Abouelseouda1d3c6a2018-12-27 12:39:16 +00001679IConnectableLayer* Network::AddRsqrtLayer(const char * name)
1680{
josh minor4a3c6102020-01-06 16:40:46 -06001681 return AddElementwiseUnaryLayer(ElementwiseUnaryDescriptor(UnaryOperation::Rsqrt), name);
Mohamed Nour Abouelseouda1d3c6a2018-12-27 12:39:16 +00001682}
1683
narpra01b89b05f2019-01-16 09:53:09 +00001684IConnectableLayer* Network::AddGatherLayer(const char* name)
1685{
1686 return m_Graph->AddLayer<GatherLayer>(name);
1687}
1688
Nattapat Chaimanowong1f886302019-04-05 13:37:19 +01001689IConnectableLayer* Network::AddMergeLayer(const char* name)
1690{
1691 return m_Graph->AddLayer<MergeLayer>(name);
1692}
1693
Sadik Armaganeff363d2019-04-05 15:25:46 +01001694IConnectableLayer* Network::AddSwitchLayer(const char* name)
1695{
1696 return m_Graph->AddLayer<SwitchLayer>(name);
1697}
1698
Matteo Martincigh0e406ee2019-06-12 15:42:18 +01001699IConnectableLayer* Network::AddPreluLayer(const char* name)
1700{
1701 return m_Graph->AddLayer<PreluLayer>(name);
1702}
1703
Aron Virginas-Tar639fb042019-06-20 14:28:19 +01001704IConnectableLayer* Network::AddTransposeConvolution2dLayer(const TransposeConvolution2dDescriptor& descriptor,
1705 const ConstTensor& weights,
1706 const Optional<ConstTensor>& biases,
1707 const char* name)
1708{
1709 if (descriptor.m_BiasEnabled && !biases.has_value())
1710 {
1711 throw InvalidArgumentException("AddTransposeConvolution2dLayer: Biases cannot be empty");
1712 }
1713
1714 const auto layer = m_Graph->AddLayer<TransposeConvolution2dLayer>(descriptor, name);
1715
1716 layer->m_Weight = std::make_unique<ScopedCpuTensorHandle>(weights);
1717
1718 if (descriptor.m_BiasEnabled)
1719 {
1720 layer->m_Bias = std::make_unique<ScopedCpuTensorHandle>(biases.value());
1721 }
1722
1723 return layer;
1724}
1725
Mike Kellyc9ea45a2020-02-28 18:11:58 +00001726IConnectableLayer* Network::AddTransposeLayer(const TransposeDescriptor& transposeDescriptor,
1727 const char* name)
1728{
1729 return m_Graph->AddLayer<TransposeLayer>(transposeDescriptor, name);
1730}
1731
Matthew Jackson2b8c1da2019-07-04 14:59:16 +01001732IConnectableLayer* Network::AddStackLayer(const StackDescriptor& stackDescriptor,
1733 const char* name)
1734{
1735 return m_Graph->AddLayer<StackLayer>(stackDescriptor, name);
1736}
1737
Derek Lamberti013c3902019-10-21 10:46:16 +01001738
1739IConnectableLayer* Network::AddStandInLayer(const StandInDescriptor& desc,
1740 const char* name)
1741{
1742 return m_Graph->AddLayer<StandInLayer>(desc, name);
1743}
1744
James Conroyee18dc82019-07-17 11:27:46 +01001745IConnectableLayer* Network::AddQuantizedLstmLayer(const QuantizedLstmInputParams& params,
1746 const char* name)
1747{
1748 const auto layer = m_Graph->AddLayer<QuantizedLstmLayer>(name);
1749
1750 // InputToX weights
1751 layer->m_QuantizedLstmParameters.m_InputToInputWeights =
Francis Murtaghbb590b42019-08-14 09:51:36 +01001752 std::make_unique<ScopedCpuTensorHandle>(params.GetInputToInputWeights());
James Conroyee18dc82019-07-17 11:27:46 +01001753 layer->m_QuantizedLstmParameters.m_InputToForgetWeights =
Francis Murtaghbb590b42019-08-14 09:51:36 +01001754 std::make_unique<ScopedCpuTensorHandle>(params.GetInputToForgetWeights());
James Conroyee18dc82019-07-17 11:27:46 +01001755 layer->m_QuantizedLstmParameters.m_InputToCellWeights =
Francis Murtaghbb590b42019-08-14 09:51:36 +01001756 std::make_unique<ScopedCpuTensorHandle>(params.GetInputToCellWeights());
James Conroyee18dc82019-07-17 11:27:46 +01001757 layer->m_QuantizedLstmParameters.m_InputToOutputWeights =
Francis Murtaghbb590b42019-08-14 09:51:36 +01001758 std::make_unique<ScopedCpuTensorHandle>(params.GetInputToOutputWeights());
James Conroyee18dc82019-07-17 11:27:46 +01001759
1760 // RecurrentToX weights
1761 layer->m_QuantizedLstmParameters.m_RecurrentToInputWeights =
Francis Murtaghbb590b42019-08-14 09:51:36 +01001762 std::make_unique<ScopedCpuTensorHandle>(params.GetRecurrentToInputWeights());
James Conroyee18dc82019-07-17 11:27:46 +01001763 layer->m_QuantizedLstmParameters.m_RecurrentToForgetWeights =
Francis Murtaghbb590b42019-08-14 09:51:36 +01001764 std::make_unique<ScopedCpuTensorHandle>(params.GetRecurrentToForgetWeights());
James Conroyee18dc82019-07-17 11:27:46 +01001765 layer->m_QuantizedLstmParameters.m_RecurrentToCellWeights =
Francis Murtaghbb590b42019-08-14 09:51:36 +01001766 std::make_unique<ScopedCpuTensorHandle>(params.GetRecurrentToCellWeights());
James Conroyee18dc82019-07-17 11:27:46 +01001767 layer->m_QuantizedLstmParameters.m_RecurrentToOutputWeights =
Francis Murtaghbb590b42019-08-14 09:51:36 +01001768 std::make_unique<ScopedCpuTensorHandle>(params.GetRecurrentToOutputWeights());
James Conroyee18dc82019-07-17 11:27:46 +01001769
1770 // Bias
1771 layer->m_QuantizedLstmParameters.m_InputGateBias =
Francis Murtaghbb590b42019-08-14 09:51:36 +01001772 std::make_unique<ScopedCpuTensorHandle>(params.GetInputGateBias());
James Conroyee18dc82019-07-17 11:27:46 +01001773 layer->m_QuantizedLstmParameters.m_ForgetGateBias =
Francis Murtaghbb590b42019-08-14 09:51:36 +01001774 std::make_unique<ScopedCpuTensorHandle>(params.GetForgetGateBias());
James Conroyee18dc82019-07-17 11:27:46 +01001775 layer->m_QuantizedLstmParameters.m_CellBias =
Francis Murtaghbb590b42019-08-14 09:51:36 +01001776 std::make_unique<ScopedCpuTensorHandle>(params.GetCellBias());
James Conroyee18dc82019-07-17 11:27:46 +01001777 layer->m_QuantizedLstmParameters.m_OutputGateBias =
Francis Murtaghbb590b42019-08-14 09:51:36 +01001778 std::make_unique<ScopedCpuTensorHandle>(params.GetOutputGateBias());
James Conroyee18dc82019-07-17 11:27:46 +01001779
1780 return layer;
1781}
1782
James Conroy586a9aa2020-03-20 08:49:33 +00001783IConnectableLayer* Network::AddQLstmLayer(const QLstmDescriptor& descriptor,
1784 const LstmInputParams& params,
1785 const char* name)
1786{
1787 const auto layer = m_Graph->AddLayer<QLstmLayer>(descriptor, name);
1788
1789 // QLstm Basic Parameters
1790 layer->m_BasicParameters.m_InputToForgetWeights =
1791 std::make_unique<ScopedCpuTensorHandle>(*(params.m_InputToForgetWeights));
1792 layer->m_BasicParameters.m_InputToCellWeights =
1793 std::make_unique<ScopedCpuTensorHandle>(*(params.m_InputToCellWeights));
1794 layer->m_BasicParameters.m_InputToOutputWeights =
1795 std::make_unique<ScopedCpuTensorHandle>(*(params.m_InputToOutputWeights));
1796 layer->m_BasicParameters.m_RecurrentToForgetWeights =
1797 std::make_unique<ScopedCpuTensorHandle>(*(params.m_RecurrentToForgetWeights));
1798 layer->m_BasicParameters.m_RecurrentToCellWeights =
1799 std::make_unique<ScopedCpuTensorHandle>(*(params.m_RecurrentToCellWeights));
1800 layer->m_BasicParameters.m_RecurrentToOutputWeights =
1801 std::make_unique<ScopedCpuTensorHandle>(*(params.m_RecurrentToOutputWeights));
1802 layer->m_BasicParameters.m_ForgetGateBias =
1803 std::make_unique<ScopedCpuTensorHandle>(*(params.m_ForgetGateBias));
1804 layer->m_BasicParameters.m_CellBias =
1805 std::make_unique<ScopedCpuTensorHandle>(*(params.m_CellBias));
1806 layer->m_BasicParameters.m_OutputGateBias =
1807 std::make_unique<ScopedCpuTensorHandle>(*(params.m_OutputGateBias));
1808
1809 // QLstm Cifg parameters
1810 if(!descriptor.m_CifgEnabled)
1811 {
1812 if(params.m_InputToInputWeights == nullptr)
1813 {
1814 throw InvalidArgumentException("AddQLstmLayer: Input To Input Weights cannot be NULL");
1815 }
1816
1817 if(params.m_RecurrentToInputWeights == nullptr)
1818 {
1819 throw InvalidArgumentException(
1820 "AddQLstmLayer: Recurrent To Input Weights cannot be NULL");
1821 }
1822
1823 if(params.m_InputGateBias == nullptr)
1824 {
1825 throw InvalidArgumentException("AddQLstmLayer: Input Gate Bias cannot be NULL");
1826 }
1827
1828 layer->m_CifgParameters.m_InputToInputWeights =
1829 std::make_unique<ScopedCpuTensorHandle>(*(params.m_InputToInputWeights));
1830 layer->m_CifgParameters.m_RecurrentToInputWeights =
1831 std::make_unique<ScopedCpuTensorHandle>(*(params.m_RecurrentToInputWeights));
1832 layer->m_CifgParameters.m_InputGateBias =
1833 std::make_unique<ScopedCpuTensorHandle>(*(params.m_InputGateBias));
1834 }
1835
1836 // QLstm Projection parameters
1837 if(descriptor.m_ProjectionEnabled)
1838 {
1839 if(params.m_ProjectionWeights == nullptr)
1840 {
1841 throw InvalidArgumentException("AddQLstmLayer: Projection Weights cannot be NULL");
1842 }
1843
1844 if(params.m_ProjectionBias == nullptr)
1845 {
1846 throw InvalidArgumentException("AddQLstmLayer: Projection Biases cannot be NULL");
1847 }
1848
1849 layer->m_ProjectionParameters.m_ProjectionWeights =
1850 std::make_unique<ScopedCpuTensorHandle>(*(params.m_ProjectionWeights));
1851 layer->m_ProjectionParameters.m_ProjectionBias =
1852 std::make_unique<ScopedCpuTensorHandle>(*(params.m_ProjectionBias));
1853 }
1854
1855 // QLstm Peephole params
1856 if(descriptor.m_PeepholeEnabled)
1857 {
1858 if(params.m_CellToForgetWeights == nullptr)
1859 {
1860 throw InvalidArgumentException("AddQLstmLayer: Cell To Forget Weights cannot be NULL");
1861 }
1862
1863 if(params.m_CellToOutputWeights == nullptr)
1864 {
1865 throw InvalidArgumentException("AddQLstmLayer: Cell To Output Weights cannot be NULL");
1866 }
1867
1868 if(!descriptor.m_CifgEnabled)
1869 {
1870 if(params.m_CellToInputWeights == nullptr)
1871 {
1872 throw InvalidArgumentException("AddQLstmLayer: Cell To Input Weights cannot be NULL");
1873 }
1874
1875 layer->m_PeepholeParameters.m_CellToInputWeights =
1876 std::make_unique<ScopedCpuTensorHandle>(*(params.m_CellToInputWeights));
1877 }
1878
1879 layer->m_PeepholeParameters.m_CellToForgetWeights =
1880 std::make_unique<ScopedCpuTensorHandle>(*(params.m_CellToForgetWeights));
1881 layer->m_PeepholeParameters.m_CellToOutputWeights =
1882 std::make_unique<ScopedCpuTensorHandle>(*(params.m_CellToOutputWeights));
1883 }
1884
1885 // QLstm Layer Normalization params
1886 if(descriptor.m_LayerNormEnabled)
1887 {
1888 if(params.m_ForgetLayerNormWeights == nullptr)
1889 {
1890 throw InvalidArgumentException("AddQLstmLayer: Forget layer normalization weights cannot be NULL");
1891 }
1892
1893 if(params.m_CellLayerNormWeights == nullptr)
1894 {
1895 throw InvalidArgumentException("AddQLstmLayer: Cell layer normalization weights cannot be NULL");
1896 }
1897
1898 if(params.m_OutputLayerNormWeights == nullptr)
1899 {
1900 throw InvalidArgumentException("AddQLstmLayer: Output layer normalization weights cannot be NULL");
1901 }
1902
1903 if(!descriptor.m_CifgEnabled)
1904 {
1905 if(params.m_InputLayerNormWeights == nullptr)
1906 {
1907 throw InvalidArgumentException("AddQLstmLayer: Input layer normalization weights cannot be NULL");
1908 }
1909
1910 layer->m_LayerNormParameters.m_InputLayerNormWeights =
1911 std::make_unique<ScopedCpuTensorHandle>(*(params.m_InputLayerNormWeights));
1912 }
1913
1914 layer->m_LayerNormParameters.m_ForgetLayerNormWeights =
1915 std::make_unique<ScopedCpuTensorHandle>(*(params.m_ForgetLayerNormWeights));
1916 layer->m_LayerNormParameters.m_CellLayerNormWeights =
1917 std::make_unique<ScopedCpuTensorHandle>(*(params.m_CellLayerNormWeights));
1918 layer->m_LayerNormParameters.m_OutputLayerNormWeights =
1919 std::make_unique<ScopedCpuTensorHandle>(*(params.m_OutputLayerNormWeights));
1920 }
1921 return layer;
1922}
1923
Mike Kelly8c1701a2019-02-11 17:01:27 +00001924void Network::Accept(ILayerVisitor& visitor) const
1925{
1926 for (auto layer : GetGraph())
1927 {
1928 layer->Accept(visitor);
1929 };
1930}
1931
telsoa014fcda012018-03-09 14:13:49 +00001932OptimizedNetwork::OptimizedNetwork(std::unique_ptr<Graph> graph)
Sadik Armagan3184c902020-03-18 10:57:30 +00001933 : m_Graph(std::move(graph)), m_Guid(profiling::ProfilingService::GetNextGuid())
telsoa014fcda012018-03-09 14:13:49 +00001934{
1935}
1936
1937OptimizedNetwork::~OptimizedNetwork()
1938{
1939}
1940
1941} // namespace armnn