IVGCVSW-3722 Add front end support for ArgMinMax
[platform/upstream/armnn.git] / src / armnn / Network.cpp
1 //
2 // Copyright © 2017 Arm Ltd. All rights reserved.
3 // SPDX-License-Identifier: MIT
4 //
5
6 #include "Network.hpp"
7 #include "Graph.hpp"
8 #include "Layer.hpp"
9 #include "DeviceSpec.hpp"
10 #include "Optimizer.hpp"
11 #include "SubgraphViewSelector.hpp"
12 #include "BackendSettings.hpp"
13 #include "optimizations/All.hpp"
14
15 #include <backendsCommon/CpuTensorHandle.hpp>
16 #include <backendsCommon/WorkloadFactory.hpp>
17 #include <backendsCommon/BackendRegistry.hpp>
18 #include <backendsCommon/IBackendInternal.hpp>
19 #include <backendsCommon/TensorHandleFactoryRegistry.hpp>
20
21 #include <armnn/Exceptions.hpp>
22 #include <armnn/Utils.hpp>
23 #include <armnn/TypesUtils.hpp>
24
25 #include <fcntl.h>
26 #include <algorithm>
27 #include <fstream>
28 #include <memory>
29 #include <vector>
30 #include <algorithm>
31
32 #include <boost/assert.hpp>
33 #include <boost/format.hpp>
34 #include <boost/log/trivial.hpp>
35 #include <boost/numeric/conversion/converter_policies.hpp>
36 #include <boost/cast.hpp>
37
38 namespace armnn
39 {
40
41 armnn::INetwork* INetwork::CreateRaw()
42 {
43     return new Network();
44 }
45
46 armnn::INetworkPtr INetwork::Create()
47 {
48     return INetworkPtr(CreateRaw(), &INetwork::Destroy);
49 }
50
51 void INetwork::Destroy(INetwork* network)
52 {
53     delete boost::polymorphic_downcast<Network*>(network);
54 }
55
56 Status Network::PrintGraph()
57 {
58     m_Graph->Print();
59     return Status::Success;
60 }
61
62 void IOptimizedNetwork::Destroy(IOptimizedNetwork* network)
63 {
64     delete boost::polymorphic_downcast<OptimizedNetwork*>(network);
65 }
66
67 Status OptimizedNetwork::PrintGraph()
68 {
69     m_Graph->Print();
70     return Status::Success;
71 }
72
73 Status OptimizedNetwork::SerializeToDot(std::ostream& stream) const
74 {
75     return m_Graph->SerializeToDot(stream);
76 }
77
78
79
80 void ReportError(const std::string& errorMessage,
81                  Optional<std::vector<std::string>&> errorMessages)
82 {
83     std::stringstream fullErrorMessage;
84     fullErrorMessage << "ERROR: " << errorMessage;
85     BOOST_LOG_TRIVIAL(warning) << fullErrorMessage.str();
86     if (errorMessages)
87     {
88         errorMessages.value().push_back(fullErrorMessage.str());
89     }
90 }
91
92 void ReportWarning(const std::string& warningMessage,
93                    Optional<std::vector<std::string>&> warningMessages)
94 {
95     std::stringstream fullWarningMessage;
96     fullWarningMessage << "WARNING: " << warningMessage;
97     BOOST_LOG_TRIVIAL(warning) << fullWarningMessage.str();
98     if (warningMessages)
99     {
100         warningMessages.value().push_back(fullWarningMessage.str());
101     }
102 }
103
104 bool CheckScaleSetOnQuantizedType(Layer* layer, Optional<std::vector<std::string>&> errMessages)
105 {
106     bool noErrors = true;
107     unsigned int numOutputs = layer->GetNumOutputSlots();
108     for (unsigned int i = 0; i < numOutputs; i++) {
109         OutputSlot& outputSlot = layer->GetOutputSlot(i);
110         TensorInfo info = outputSlot.GetTensorInfo();
111         if (DataType::QuantisedAsymm8 == info.GetDataType()) {
112             if (0.f == info.GetQuantizationScale()) {
113                 noErrors = false;
114                 std::stringstream ss;
115                 ss << "output " << i << " of layer " << GetLayerTypeAsCString(layer->GetType())
116                    << " (" << layer->GetNameStr() << ") is of type"
117                    << " Quantized 8 bit but its scale parameter has not been set";
118                 ReportError(ss.str(), errMessages);
119             }
120             // Softmax under QuantisedAsymm8 must always be scale (1.0f/256.0f) and offset 0
121             if ((info.GetQuantizationScale() != (1.0f / 256.0f) ||
122                  info.GetQuantizationOffset() != 0) &&
123                  layer->GetType() == armnn::LayerType::Softmax)
124             {
125                 std::stringstream ss;
126                 ss << "Quantization parameters for Softmax layer (Scale: " <<
127                 info.GetQuantizationScale() << " and Offset: " << info.GetQuantizationOffset() <<
128                 ") are incorrect and have been updated to Scale: 0.00390625 and Offset: 0";
129                 BOOST_LOG_TRIVIAL(warning) << ss.str();
130                 info.SetQuantizationScale((1.0f /256.0f));
131                 info.SetQuantizationOffset(0);
132                 outputSlot.SetTensorInfo(info);
133             }
134         }
135     }
136     return noErrors;
137 }
138
139 OptimizationResult AssignBackends(OptimizedNetwork* optNetObjPtr,
140                                   BackendSettings& backendSettings,
141                                   Graph::Iterator& firstLayer,
142                                   Graph::Iterator& lastLayer,
143                                   Optional<std::vector<std::string>&> errMessages)
144 {
145     OptimizationResult result;
146
147     // Helper lambda to compose meaningful error message before returning with error
148     auto ReturnWithError = [&](const Layer* layer)
149     {
150         std::stringstream failureMsg;
151         failureMsg << "Layer of type " << GetLayerTypeAsCString(layer->GetType())
152                    << " is not supported on any preferred backend " << backendSettings.m_PreferredBackends;
153         ReportError(failureMsg.str(), errMessages);
154
155         result.m_Error = true;
156         return result;
157     };
158
159     auto availablePreferredBackends = backendSettings.GetAvailablePreferredBackends();
160     if (availablePreferredBackends.empty())
161     {
162         std::stringstream failureMsg;
163         failureMsg << "No preferred backends are available";
164         ReportError(failureMsg.str(), errMessages);
165
166         result.m_Error = true;
167         return result;
168     }
169
170     for (auto it = firstLayer; it != lastLayer; ++it)
171     {
172         auto layer = *it;
173         DataType dataType = layer->GetDataType();
174         std::string reasonIfUnsupported;
175         bool found = false;
176         if (!CheckScaleSetOnQuantizedType(layer, errMessages))
177         {
178             // don't bomb immediately, find all the quantized outputs
179             // which haven't had a scale set and report them all back.
180             result.m_Error = true;
181         }
182
183         for (const auto& backend : availablePreferredBackends)
184         {
185             // need to set the compute device on the layer
186             // before we can check if it is supported
187             layer->SetBackendId(backend);
188             if (!IWorkloadFactory::IsLayerSupported(*layer, dataType, reasonIfUnsupported))
189             {
190                 if (dataType == DataType::Float16)
191                 {
192                     if (IWorkloadFactory::IsLayerSupported(*layer, DataType::Float32, reasonIfUnsupported)
193                         && layer->GetType() != LayerType::ConvertFp32ToFp16
194                         && layer->GetType() != LayerType::ConvertFp16ToFp32)
195                     {
196                         // Insert FP16 -> FP32 conversion layer before current layer
197                         std::vector<ConvertFp16ToFp32Layer*> convertFp16ToFp32Layers =
198                             InsertConvertFp16ToFp32LayersBefore(optNetObjPtr->GetGraph(), *layer);
199
200                         // Insert FP32 -> FP16 conversion layer after current layer
201                         std::vector<ConvertFp32ToFp16Layer*> convertFp32ToFp16Layers =
202                             InsertConvertFp32ToFp16LayersAfter(optNetObjPtr->GetGraph(), *layer);
203
204                         // Assign a supported backend to the newly introduced conversion layers
205                         auto AssignFirstSupportedBackend = [&](Layer* layer, BackendId preferredBackend)
206                         {
207                             bool supportedBackendFound = false;
208                             std::string reasonIfUnsupported;
209
210                             // Try preferred backend first
211                             layer->SetBackendId(preferredBackend);
212                             if (IWorkloadFactory::IsLayerSupported(*layer,
213                                                                    EmptyOptional(),
214                                                                    reasonIfUnsupported))
215                             {
216                                 supportedBackendFound = true;
217                             }
218                             else
219                             {
220                                 for (const auto& backend : availablePreferredBackends)
221                                 {
222                                     // Skip preferred backend (we already determined that it is not supported)
223                                     if (backend == preferredBackend)
224                                     {
225                                         continue;
226                                     }
227
228                                     layer->SetBackendId(backend);
229                                     if (IWorkloadFactory::IsLayerSupported(*layer,
230                                                                            EmptyOptional(),
231                                                                            reasonIfUnsupported))
232                                     {
233                                         supportedBackendFound = true;
234                                         break;
235                                     }
236                                 }
237                             }
238
239                             return supportedBackendFound;
240                         };
241
242                         for (ConvertFp16ToFp32Layer* convertLayer : convertFp16ToFp32Layers)
243                         {
244                             if (!AssignFirstSupportedBackend(convertLayer, backend))
245                             {
246                                 return ReturnWithError(convertLayer);
247                             }
248                         }
249
250                         for (ConvertFp32ToFp16Layer* convertLayer : convertFp32ToFp16Layers)
251                         {
252                             if (!AssignFirstSupportedBackend(convertLayer, backend))
253                             {
254                                 return ReturnWithError(convertLayer);
255                             }
256                         }
257
258                         found = true;
259                         break;
260                     }
261                 }
262                 std::stringstream warningMsg;
263                 warningMsg << "Layer of type " << GetLayerTypeAsCString(layer->GetType())
264                            << " is not supported on requested backend " << layer->GetBackendId().Get()
265                            << " for data type " << GetDataTypeName(dataType)
266                            << " (reason: " << reasonIfUnsupported
267                            << "), falling back to the next backend.";
268                 ReportWarning(warningMsg.str(), errMessages);
269             }
270             else
271             {
272                 found = true;
273                 backendSettings.m_SelectedBackends.insert(backend);
274                 break;
275             }
276         }
277
278         // If the layer is unsupported by any devices, log and return a null network.
279         if (!found)
280         {
281             // NOTE: if the layer is not an operation queue type AND we have not got CpuRef as a
282             //       fallback we should set the compute device on the layer to CpuRef (these are not
283             //       available as accelerated operations, or are only available under certain
284             //       conditions, currently they comprise MemCopy, Constant, Permute)
285             armnn::LayerType layerType = layer->GetType();
286             if (!backendSettings.IsCpuRefUsed() && (layerType == armnn::LayerType::MemCopy ||
287                                                     layerType == armnn::LayerType::Constant ||
288                                                     layerType == armnn::LayerType::Permute))
289             {
290                 BackendId cpuBackendId(armnn::Compute::CpuRef);
291                 layer->SetBackendId(cpuBackendId);
292                 backendSettings.m_SelectedBackends.insert(cpuBackendId);
293             }
294             else
295             {
296                 return ReturnWithError(layer);
297             }
298         }
299     }
300
301     return result;
302 }
303
304 OptimizationResult AssignBackends(OptimizedNetwork* optNetObjPtr,
305                                   BackendSettings& backendSettings,
306                                   SubgraphView& subgraph,
307                                   Optional<std::vector<std::string>&> errMessages)
308 {
309     Graph::Iterator firstLayer = subgraph.begin();
310     Graph::Iterator lastLayer  = subgraph.end();
311     return AssignBackends(optNetObjPtr,
312                           backendSettings,
313                           firstLayer,
314                           lastLayer,
315                           errMessages);
316 }
317
318 BackendsMap CreateSupportedBackends(TensorHandleFactoryRegistry& handleFactoryRegistry,
319                                     BackendSettings& backendSettings)
320 {
321     BackendsMap backends;
322     auto const& backendRegistry = BackendRegistryInstance();
323     for (auto&& selectedBackend : backendSettings.m_SupportedBackends)
324     {
325         auto backendFactory = backendRegistry.GetFactory(selectedBackend);
326         auto backendObjPtr = backendFactory();
327         BOOST_ASSERT(backendObjPtr);
328
329         backendObjPtr->RegisterTensorHandleFactories(handleFactoryRegistry);
330
331         backends[backendObjPtr->GetId()] = std::move(backendObjPtr);
332     }
333
334     return backends;
335 }
336
337 OptimizationResult ApplyBackendOptimizations(OptimizedNetwork* optNetObjPtr,
338                                              BackendSettings& backendSettings,
339                                              BackendsMap& backends,
340                                              Optional<std::vector<std::string>&> errMessages)
341 {
342     BOOST_ASSERT(optNetObjPtr);
343
344     OptimizationResult result;
345
346     // Get the optimized graph
347     Graph& optGraph = optNetObjPtr->GetGraph();
348
349     // Run backend specific optimizations
350     for (auto&& selectedBackend : backendSettings.m_SelectedBackends)
351     {
352         auto backendObjPtr = backends.find(selectedBackend)->second.get();
353         BOOST_ASSERT(backendObjPtr);
354
355         // Select sub-graphs based on backend
356         SubgraphViewSelector::Subgraphs subgraphs =
357                 SubgraphViewSelector::SelectSubgraphs(optGraph,
358                                                       // Select layers assigned to the requested backend
359                                                       [&backendObjPtr](const Layer& layer)
360                                                       {
361                                                           return layer.GetType() != LayerType::Input &&
362                                                                  layer.GetType() != LayerType::Output &&
363                                                                  layer.GetBackendId() == backendObjPtr->GetId();
364                                                       });
365         if (subgraphs.empty())
366         {
367             // No sub-graphs found, try with next selected backend
368             continue;
369         }
370
371         // Try to optimize each sub-graph
372         for (auto& subgraph : subgraphs)
373         {
374             // Try to optimize the current sub-graph
375             OptimizationViews optimizationViews = backendObjPtr->OptimizeSubgraphView(*subgraph);
376             BOOST_ASSERT(optimizationViews.Validate(*subgraph));
377
378             // Optimization attempted, check the resulting optimized sub-graph
379             for (auto& substitution : optimizationViews.GetSubstitutions())
380             {
381                 // Sub-graph optimized, substitute the sub-graph with the new optimized one in the main optimized graph
382                 SubgraphView& replacementSubgraph   = substitution.m_ReplacementSubgraph;
383                 SubgraphView& substitutableSubgraph = substitution.m_SubstitutableSubgraph;
384                 optGraph.SubstituteSubgraph(substitutableSubgraph, replacementSubgraph);
385
386                 // Assign the current backend to the optimized sub-graph
387                 std::for_each(replacementSubgraph.begin(), replacementSubgraph.end(), [&selectedBackend](Layer* l)
388                     {
389                         BOOST_ASSERT(l);
390                         l->SetBackendId(selectedBackend);
391                     });
392             }
393
394             if (!optimizationViews.GetFailedSubgraphs().empty())
395             {
396                 std::stringstream warningMsg;
397                 warningMsg << "Some sub-graph(s) failed to optimized on " << backendObjPtr->GetId() << " backend.";
398                 ReportWarning(warningMsg.str(), errMessages);
399
400                 // Failed to optimize the given sub-graph, re-assign the sub-graph layers to other available backends
401                 BackendSettings settingsCopy(backendSettings);
402                 if (!backendObjPtr->GetId().IsCpuRef())
403                 {
404                     // Add the current backend to the list of backends to ignore
405                     settingsCopy.m_IgnoredBackends.insert(backendObjPtr->GetId());
406                 }
407
408                 int count=0;
409                 for (auto& failedSubgraph : optimizationViews.GetFailedSubgraphs())
410                 {
411                     // An error occurred: the optimization was attempted but not performed, try different backends
412                     std::stringstream subgraphMsg;
413                     subgraphMsg << "Re-assigning backends to " << failedSubgraph.GetLayers().size()
414                                 << " layers inside sub-graph " << count++;
415                     ReportWarning(subgraphMsg.str(), errMessages);
416
417                     OptimizationResult reassignmentResult = AssignBackends(optNetObjPtr,
418                                                                            settingsCopy,
419                                                                            *subgraph,
420                                                                            errMessages);
421                     if (reassignmentResult.m_Error)
422                     {
423                         // Failed to re-assign one of the remaining backends to each layer of the sub-graph
424                         result.m_Error = true;
425                         return result;
426                     }
427                 }
428             }
429         }
430     }
431
432     return result;
433 }
434
435 bool RequiresCopy(ITensorHandleFactory::FactoryId src,
436                   ITensorHandleFactory::FactoryId dst,
437                   TensorHandleFactoryRegistry& registry)
438 {
439     if (src != dst)
440     {
441         ITensorHandleFactory* srcFactory = registry.GetFactory(src);
442         ITensorHandleFactory* dstFactory = registry.GetFactory(dst);
443
444         if (srcFactory && dstFactory &&
445             (srcFactory->GetExportFlags() & dstFactory->GetImportFlags()) != 0)
446         {
447             return false;
448         }
449         return true;
450     }
451     return false;
452 }
453
454 // Find the handle factory for the input layer which results in fewest required copies.
455 ITensorHandleFactory::FactoryId CalculateSlotOptionForInput(BackendsMap& backends,
456                                                             OutputSlot& slot,
457                                                             TensorHandleFactoryRegistry& registry)
458 {
459     Layer& layer = slot.GetOwningLayer();
460     BOOST_ASSERT(layer.GetType() == LayerType::Input);
461
462     // Explicitly select the tensorhandle factory for InputLayer because the rules for it are slightly different. It
463     // doesn't matter which backend it is assigned to because they all use the same implementation, which
464     // requires Map/Unmap support. This means that, so long as the handle type supports map/unmap semantics, we can
465     // select a factory with maximum compatibility with the layers connected to the InputLayer.
466
467     // First ensure the from backends can support the TensorHandeAPI
468     auto frmBackend = backends.find(layer.GetBackendId());
469     if (frmBackend == backends.end() ||
470         !frmBackend->second->SupportsTensorAllocatorAPI())
471     {
472         return ITensorHandleFactory::LegacyFactoryId;
473     }
474
475     // Go through all connections to the output slot and determine the TensorHandleFactory which results in the
476     // fewest copies.
477     std::map<ITensorHandleFactory::FactoryId, int> factoryScores;
478     int topScore = 0;
479     ITensorHandleFactory::FactoryId topChoice = ITensorHandleFactory::LegacyFactoryId;
480
481     for (auto&& connection : slot.GetConnections())
482     {
483         const Layer& connectedLayer = connection->GetOwningLayer();
484
485         auto toBackend = backends.find(connectedLayer.GetBackendId());
486         BOOST_ASSERT_MSG(toBackend != backends.end(), "Backend id not found for the connected layer");
487
488         if (!toBackend->second.get()->SupportsTensorAllocatorAPI())
489         {
490             // The destination backend does not support the tensor allocator API, move to the next one
491             continue;
492         }
493
494         auto dstPrefs = toBackend->second.get()->GetHandleFactoryPreferences();
495         for (auto&& dst : dstPrefs)
496         {
497             // Input layers use the mem copy workload or import, so the selected factory must
498             // support either the map/unmap API or Import API
499             ITensorHandleFactory* factory = registry.GetFactory(dst);
500             if (!factory->SupportsMapUnmap() &&
501                 !CheckFlag(factory->GetImportFlags(), MemorySource::Malloc)) // Just support cpu mem imports for now
502             {
503                 // The current tensor handle factory does not support the map/unmap or import
504                 // strategy, move to the next one
505                 continue;
506             }
507
508             auto it = factoryScores.find(dst);
509             if (it == factoryScores.end())
510             {
511                 // Add new score to the table
512                 factoryScores[dst] = 0;
513                 if (topChoice == ITensorHandleFactory::LegacyFactoryId)
514                 {
515                     topChoice = dst;
516                 }
517             }
518             else
519             {
520                 // Increase the score
521                 factoryScores[dst]++;
522
523                 // Track the best option
524                 if (factoryScores[dst] > topScore)
525                 {
526                     topScore = factoryScores[dst];
527                     topChoice = dst;
528                 }
529             }
530         }
531     }
532
533     return topChoice;
534 }
535
536 // Find the handle factory for the output layer which results in fewest required copies.
537 ITensorHandleFactory::FactoryId CalculateSlotOptionForOutput(BackendsMap& backends,
538                                                             OutputSlot& slot,
539                                                             TensorHandleFactoryRegistry& registry)
540 {
541    return ITensorHandleFactory::DeferredFactoryId;
542 }
543
544 // For all handle factories supported on the source backend, we wish to find the one which requires the fewest copies
545 // when considering all connections.
546 ITensorHandleFactory::FactoryId CalculateSlotOption(BackendsMap& backends,
547                                                     OutputSlot& outputSlot,
548                                                     TensorHandleFactoryRegistry& registry)
549 {
550     // First ensure the from backends can support the TensorHandeAPI
551     Layer& layer = outputSlot.GetOwningLayer();
552     auto frmBackend = backends.find(layer.GetBackendId());
553     if (frmBackend == backends.end() ||
554         !frmBackend->second->SupportsTensorAllocatorAPI())
555     {
556         return ITensorHandleFactory::LegacyFactoryId;
557     }
558
559     // Connections to Output Layers requires support for map/unmap on the TensorHandle.
560     bool requiresMapUnmap = false;
561     for (auto&& connection : outputSlot.GetConnections())
562     {
563         const Layer& connectedLayer = connection->GetOwningLayer();
564         if (connectedLayer.GetType() == LayerType::Output)
565         {
566             requiresMapUnmap = true;
567         }
568     }
569
570     IBackendInternal* srcBackend = frmBackend->second.get();
571     auto srcPrefs = srcBackend->GetHandleFactoryPreferences();
572
573     // Initialize the scores
574     std::map<ITensorHandleFactory::FactoryId, int> factoryScores;
575     for (auto&& pref : srcPrefs)
576     {
577         if (requiresMapUnmap) // Only consider factories that support map/unmap if required
578         {
579             ITensorHandleFactory* factory = registry.GetFactory(pref);
580             if (!factory->SupportsMapUnmap())
581             {
582                 // The current tensor handle factory does not support the map/unmap strategy, move to the next one
583                 continue;
584             }
585         }
586
587         auto it = factoryScores.find(pref);
588         if (it == factoryScores.end())
589         {
590             // Add new score to the table
591             factoryScores[pref] = 0;
592         }
593     }
594
595     // Score each handle factory based on how many times it requires copies on the slot connections
596     for (auto&& connection : outputSlot.GetConnections())
597     {
598         const Layer& connectedLayer = connection->GetOwningLayer();
599
600         auto toBackend = backends.find(connectedLayer.GetBackendId());
601         BOOST_ASSERT_MSG(toBackend != backends.end(), "Backend id not found for the connected layer");
602
603         auto dstPrefs = toBackend->second.get()->GetHandleFactoryPreferences();
604         for (auto&& src : srcPrefs)
605         {
606             if (factoryScores.find(src) == factoryScores.end()) // Don't consider excluded factories
607             {
608                 continue;
609             }
610
611             for (auto&& dst : dstPrefs)
612             {
613                 if (RequiresCopy(src, dst, registry))
614                 {
615                     // Copy avoided, increase the score
616                     factoryScores[src]++;
617                     break;
618                 }
619             }
620         }
621     }
622
623     // Find the lowest score
624     int minScore = std::numeric_limits<int>::max();
625     for (auto it : factoryScores)
626     {
627         minScore = std::min(minScore, it.second);
628     }
629
630     // Collect factories matching the best(lowest) score
631     std::vector<ITensorHandleFactory::FactoryId> optimalFactories;
632     for (auto it : factoryScores)
633     {
634         if (it.second == minScore)
635         {
636             optimalFactories.push_back(it.first);
637         }
638     }
639
640     // For all compatible Factories matching the best score, find the preferred one for the current layer.
641     for (auto&& srcPref : srcPrefs)
642     {
643         for (auto&& comp : optimalFactories)
644         {
645             if (comp == srcPref)
646             {
647                 return comp;
648             }
649         }
650     }
651
652     return ITensorHandleFactory::LegacyFactoryId;
653 }
654
655 EdgeStrategy CalculateEdgeStrategy(BackendsMap& backends,
656                                    ITensorHandleFactory::FactoryId srcFactoryId,
657                                    const Layer& layer,
658                                    const Layer& connectedLayer,
659                                    TensorHandleFactoryRegistry& registry)
660 {
661     auto toBackend = backends.find(connectedLayer.GetBackendId());
662     BOOST_ASSERT_MSG(toBackend != backends.end(), "Backend id not found for the connected layer");
663
664     auto dstPrefs = toBackend->second.get()->GetHandleFactoryPreferences();
665
666     // Legacy API check for backward compatibility
667     if (srcFactoryId == ITensorHandleFactory::LegacyFactoryId || dstPrefs.empty())
668     {
669         if (layer.GetBackendId() != connectedLayer.GetBackendId())
670         {
671             return EdgeStrategy::CopyToTarget;
672         }
673         else
674         {
675             return EdgeStrategy::DirectCompatibility;
676         }
677     }
678
679     // TensorHandleFactory API present, so perform more sophisticated strategies.
680     // Dst Output layers don't require copy because they use import or map/unmap
681     if (connectedLayer.GetType() == LayerType::Output)
682     {
683         return EdgeStrategy::DirectCompatibility;
684     }
685
686     // Search for direct match in prefs
687     for (auto&& pref : dstPrefs)
688     {
689         if (pref == srcFactoryId)
690         {
691             return EdgeStrategy::DirectCompatibility;
692         }
693     }
694
695     // Search for export/import options
696     ITensorHandleFactory* srcFactory = registry.GetFactory(srcFactoryId);
697     if (srcFactory->GetExportFlags() != 0)
698     {
699         for (auto&& pref : dstPrefs)
700         {
701             ITensorHandleFactory* dstFactory = registry.GetFactory(pref);
702             if ((dstFactory->GetImportFlags() & srcFactory->GetExportFlags()) != 0)
703             {
704                 return EdgeStrategy::ExportToTarget;
705             }
706         }
707     }
708
709     // Search for copy options via map/unmap
710     if (srcFactory->SupportsMapUnmap())
711     {
712         for (auto&& pref : dstPrefs)
713         {
714             ITensorHandleFactory* dstFactory = registry.GetFactory(pref);
715             if (dstFactory->SupportsMapUnmap())
716             {
717                 return EdgeStrategy::CopyToTarget;
718             }
719         }
720     }
721
722     return EdgeStrategy::Undefined;
723 }
724
725 // Select the TensorHandleFactories and the corresponding memory strategy
726 OptimizationResult SelectTensorHandleStrategy(Graph& optGraph,
727                                               BackendsMap& backends,
728                                               TensorHandleFactoryRegistry& registry,
729                                               Optional<std::vector<std::string>&> errMessages)
730 {
731     OptimizationResult result;
732
733     optGraph.ForEachLayer([&backends, &registry, &result, &errMessages](Layer* layer)
734     {
735         BOOST_ASSERT(layer);
736
737         // Lets make sure the backend is in our list of supported backends. Something went wrong during backend
738         // assignment if this check fails
739         BOOST_ASSERT(backends.find(layer->GetBackendId()) != backends.end());
740
741         // Check each output separately
742         for (unsigned int slotIdx = 0; slotIdx < layer->GetNumOutputSlots(); slotIdx++)
743         {
744             OutputSlot& outputSlot = layer->GetOutputSlot(slotIdx);
745
746             ITensorHandleFactory::FactoryId slotOption = ITensorHandleFactory::LegacyFactoryId;
747
748             // Calculate the factory to use which results in the fewest copies being made.
749             switch(layer->GetType())
750             {
751                 case LayerType::Input:
752                     slotOption = CalculateSlotOptionForInput(backends, outputSlot, registry);
753                     break;
754                 case LayerType::Output:
755                     slotOption = CalculateSlotOptionForOutput(backends, outputSlot, registry);
756                     break;
757                 default:
758                     slotOption = CalculateSlotOption(backends, outputSlot, registry);
759                     break;
760             }
761             outputSlot.SetTensorHandleFactory(slotOption);
762
763             // Now determine the "best" edge strategy for each connection given the slotOption.
764             unsigned int connectionIdx = 0;
765             for (auto&& connection : outputSlot.GetConnections())
766             {
767                 const Layer& connectedLayer = connection->GetOwningLayer();
768
769                 EdgeStrategy strategy = CalculateEdgeStrategy(backends, slotOption, *layer, connectedLayer, registry);
770
771                 if (strategy == EdgeStrategy::Undefined)
772                 {
773                     result.m_Error = true;
774                     if (errMessages)
775                     {
776                         errMessages.value().emplace_back("Could not find valid strategy required for compatibility"
777                                                          " between backends.");
778                     }
779                     return;
780                 }
781
782                 outputSlot.SetEdgeStrategy(connectionIdx, strategy);
783
784                 connectionIdx++;
785             }
786         }
787     });
788
789     return result;
790 }
791
792 IOptimizedNetworkPtr Optimize(const INetwork& inNetwork,
793                               const std::vector<BackendId>& backendPreferences,
794                               const IDeviceSpec& deviceSpec,
795                               const OptimizerOptions& options,
796                               Optional<std::vector<std::string>&> errMessages)
797 {
798     if (backendPreferences.empty())
799     {
800         throw armnn::InvalidArgumentException("Invoked Optimize with no backends specified");
801     }
802
803     const Network& network = *boost::polymorphic_downcast<const Network*>(&inNetwork);
804     std::unique_ptr<Graph> graph = std::make_unique<Graph>(network.GetGraph());
805
806     auto optNet = IOptimizedNetworkPtr(new OptimizedNetwork(std::move(graph)), &IOptimizedNetwork::Destroy);
807
808     OptimizedNetwork* optNetObjPtr = boost::polymorphic_downcast<OptimizedNetwork*>(optNet.get());
809
810     // Get the optimized graph
811     Graph& optGraph = optNetObjPtr->GetGraph();
812
813     // Perform optimisation passes
814     using namespace optimizations;
815     Optimizer::Pass(optGraph, MakeOptimizations(SquashEqualPermuteSiblings(),
816                                                 SquashEqualReshapeSiblings(),
817                                                 OptimizeInversePermutes(),
818                                                 MovePermuteUp(),
819                                                 PermuteAsReshape(),
820                                                 OptimizeConsecutiveReshapes(),
821                                                 FoldPadIntoConvolution2d()));
822
823     // Infer the tensor infos for all output slots. Throws an exception on failure
824     optGraph.InferTensorInfos();
825
826     // If Fp32 to Fp16 optimization is set convert Fp32 network to Fp16
827     if (options.m_ReduceFp32ToFp16)
828     {
829         Optimizer::Pass(optGraph, MakeOptimizations(Fp32NetworkToFp16Converter()));
830     }
831
832     // Initialize backend settings
833     BackendSettings backendSettings(backendPreferences, deviceSpec);
834     if (backendSettings.GetAvailablePreferredBackends().empty())
835     {
836         std::stringstream failureMsg;
837         failureMsg << "None of the preferred backends " << backendPreferences
838                    << " are supported. Current platform provides " << backendSettings.m_SupportedBackends;
839         ReportError(failureMsg.str(), errMessages);
840         return IOptimizedNetworkPtr(nullptr, &IOptimizedNetwork::Destroy);
841     }
842
843     // Create a map to temporarily hold initialized backend objects
844     TensorHandleFactoryRegistry tensorHandleFactoryRegistry;
845     BackendsMap backends = CreateSupportedBackends(tensorHandleFactoryRegistry, backendSettings);
846
847     // Assign an available backend to each layer
848     Graph::Iterator firstLayer = optGraph.begin();
849     Graph::Iterator lastLayer  = optGraph.end();
850     OptimizationResult assignBackendsResult = AssignBackends(optNetObjPtr,
851                                                              backendSettings,
852                                                              firstLayer,
853                                                              lastLayer,
854                                                              errMessages);
855     if (assignBackendsResult.m_Error)
856     {
857         // Failed to assign a backend to each layer
858         return IOptimizedNetworkPtr(nullptr, &IOptimizedNetwork::Destroy);
859     }
860
861     Optimizer::Pass(optGraph, MakeOptimizations(OptimizeInverseConversionsFp16(),
862                                                 OptimizeInverseConversionsFp32()));
863
864     // Apply the backend-specific optimizations
865     OptimizationResult backendOptimizationResult = ApplyBackendOptimizations(optNetObjPtr,
866                                                                              backendSettings,
867                                                                              backends,
868                                                                              errMessages);
869     if (backendOptimizationResult.m_Error)
870     {
871         // Failed to apply the backend-specific optimizations
872         return IOptimizedNetworkPtr(nullptr, &IOptimizedNetwork::Destroy);
873     }
874
875     // If the debug flag is set, then insert a DebugLayer after each layer
876     // Doing this after applying the backend optimizations as they might have changed some layers
877     if (options.m_Debug)
878     {
879         Optimizer::Pass(optGraph, MakeOptimizations(InsertDebugLayer()));
880     }
881
882     // Calculate the compatibility strategies for tensor handles
883     OptimizationResult strategyResult = SelectTensorHandleStrategy(optGraph,
884                                                                    backends,
885                                                                    tensorHandleFactoryRegistry,
886                                                                    errMessages);
887     if (strategyResult.m_Error)
888     {
889         // Failed to apply the backend-specific optimizations
890         return IOptimizedNetworkPtr(nullptr, &IOptimizedNetwork::Destroy);
891     }
892
893     // Based on the tensor handle strategy determined above, insert copy layers where required.
894     optGraph.AddCompatibilityLayers(backends, tensorHandleFactoryRegistry);
895
896     // Convert constants
897     Optimizer::Pass(optGraph, MakeOptimizations(ConvertConstantsFloatToHalf()));
898     Optimizer::Pass(optGraph, MakeOptimizations(ConvertConstantsHalfToFloat()));
899
900     // Run backend specific optimizations (deprecated)
901     for (auto&& chosenBackend : backendSettings.m_SelectedBackends)
902     {
903         auto factoryFun = BackendRegistryInstance().GetFactory(chosenBackend);
904         auto backendPtr = factoryFun();
905         BOOST_ASSERT(backendPtr.get() != nullptr);
906
907         ARMNN_NO_DEPRECATE_WARN_BEGIN
908         auto backendSpecificOptimizations = backendPtr->GetOptimizations();
909         ARMNN_NO_DEPRECATE_WARN_END
910
911         if (!backendSpecificOptimizations.empty())
912         {
913             Optimizer::Pass(optNetObjPtr->GetGraph(), backendSpecificOptimizations);
914         }
915     }
916
917     return optNet;
918 }
919
920 Network::Network()
921 : m_Graph(std::make_unique<Graph>())
922 {
923 }
924
925 Network::~Network()
926 {
927 }
928
929 IConnectableLayer* Network::AddInputLayer(LayerBindingId id, const char* name)
930 {
931     return m_Graph->AddLayer<InputLayer>(id, name);
932 }
933
934 IConnectableLayer* Network::AddBatchToSpaceNdLayer(const BatchToSpaceNdDescriptor& batchToSpaceNdDescriptor,
935                                             const char* name)
936 {
937     return m_Graph->AddLayer<BatchToSpaceNdLayer>(batchToSpaceNdDescriptor, name);
938 }
939
940 IConnectableLayer* Network::AddFullyConnectedLayerImpl(const FullyConnectedDescriptor& fullyConnectedDescriptor,
941                                                        const ConstTensor& weights,
942                                                        const Optional<ConstTensor>& biases,
943                                                        const char* name)
944 {
945     if (fullyConnectedDescriptor.m_BiasEnabled && !biases.has_value())
946     {
947         throw InvalidArgumentException("AddFullyConnectedLayer: biases cannot be empty");
948     }
949
950     const auto layer = m_Graph->AddLayer<FullyConnectedLayer>(fullyConnectedDescriptor, name);
951
952     layer->m_Weight = std::make_unique<ScopedCpuTensorHandle>(weights);
953
954     if (fullyConnectedDescriptor.m_BiasEnabled)
955     {
956         layer->m_Bias = std::make_unique<ScopedCpuTensorHandle>(biases.value());
957     }
958
959     return layer;
960 }
961
962 IConnectableLayer* Network::AddFullyConnectedLayer(const FullyConnectedDescriptor& fullyConnectedDescriptor,
963                                                    const ConstTensor& weights,
964                                                    const Optional<ConstTensor>& biases,
965                                                    const char* name)
966 {
967     return AddFullyConnectedLayerImpl(fullyConnectedDescriptor, weights, biases, name);
968 }
969
970 IConnectableLayer* Network::AddFullyConnectedLayer(const FullyConnectedDescriptor& fullyConnectedDescriptor,
971                                                    const ConstTensor& weights,
972                                                    const char* name)
973 {
974     Optional<ConstTensor> biases;
975     return AddFullyConnectedLayerImpl(fullyConnectedDescriptor, weights, biases, name);
976 }
977
978 IConnectableLayer* Network::AddFullyConnectedLayer(const FullyConnectedDescriptor& fullyConnectedDescriptor,
979                                                    const ConstTensor& weights,
980                                                    const ConstTensor& biases,
981                                                    const char* name)
982 {
983     Optional<ConstTensor> optionalBiases(biases);
984     return AddFullyConnectedLayerImpl(fullyConnectedDescriptor, weights, optionalBiases, name);
985 }
986
987 IConnectableLayer* Network::AddConcatLayer(const ConcatDescriptor& concatDescriptor,
988                                            const char* name)
989 {
990     return m_Graph->AddLayer<ConcatLayer>(concatDescriptor, name);
991 }
992
993 IConnectableLayer* Network::AddConvolution2dLayerImpl(const Convolution2dDescriptor& convolution2dDescriptor,
994                                                       const ConstTensor& weights,
995                                                       const Optional<ConstTensor>& biases,
996                                                       const char* name)
997 {
998     if (convolution2dDescriptor.m_BiasEnabled && !biases.has_value())
999     {
1000         throw InvalidArgumentException("AddConvolution2dLayer: biases cannot be empty");
1001     }
1002
1003     const auto layer = m_Graph->AddLayer<Convolution2dLayer>(convolution2dDescriptor, name);
1004
1005     layer->m_Weight = std::make_unique<ScopedCpuTensorHandle>(weights);
1006
1007     if (convolution2dDescriptor.m_BiasEnabled)
1008     {
1009         layer->m_Bias = std::make_unique<ScopedCpuTensorHandle>(biases.value());
1010     }
1011
1012     return layer;
1013 }
1014
1015 IConnectableLayer* Network::AddConvolution2dLayer(const Convolution2dDescriptor& convolution2dDescriptor,
1016                                                   const ConstTensor& weights,
1017                                                   const Optional<ConstTensor>& biases,
1018                                                   const char* name)
1019 {
1020     return AddConvolution2dLayerImpl(convolution2dDescriptor, weights, biases, name);
1021 }
1022
1023 IConnectableLayer* Network::AddConvolution2dLayer(const Convolution2dDescriptor& convolution2dDescriptor,
1024                                                   const ConstTensor& weights,
1025                                                   const char* name)
1026 {
1027     Optional<ConstTensor> biases;
1028     return AddConvolution2dLayerImpl(convolution2dDescriptor, weights, biases, name);
1029 }
1030
1031 IConnectableLayer* Network::AddConvolution2dLayer(const Convolution2dDescriptor& convolution2dDescriptor,
1032                                                   const ConstTensor& weights,
1033                                                   const ConstTensor& biases,
1034                                                   const char* name)
1035 {
1036     Optional<ConstTensor> optionalBiases(biases);
1037     return AddConvolution2dLayerImpl(convolution2dDescriptor, weights, optionalBiases, name);
1038 }
1039
1040 IConnectableLayer* Network::AddDepthwiseConvolution2dLayerImpl(
1041     const DepthwiseConvolution2dDescriptor& convolution2dDescriptor,
1042     const ConstTensor& weights,
1043     const Optional<ConstTensor>& biases,
1044     const char* name)
1045 {
1046     if (convolution2dDescriptor.m_BiasEnabled && !biases.has_value())
1047     {
1048         throw InvalidArgumentException("AddDepthwiseConvolution2dLayer: biases cannot be empty");
1049     }
1050
1051     const auto layer = m_Graph->AddLayer<DepthwiseConvolution2dLayer>(convolution2dDescriptor, name);
1052
1053     layer->m_Weight = std::make_unique<ScopedCpuTensorHandle>(weights);
1054
1055     if (convolution2dDescriptor.m_BiasEnabled)
1056     {
1057         layer->m_Bias = std::make_unique<ScopedCpuTensorHandle>(biases.value());
1058     }
1059
1060     return layer;
1061 }
1062
1063 IConnectableLayer* Network::AddDepthwiseConvolution2dLayer(
1064         const DepthwiseConvolution2dDescriptor& convolution2dDescriptor,
1065         const ConstTensor& weights,
1066         const Optional<ConstTensor>& biases,
1067         const char* name)
1068 {
1069     return AddDepthwiseConvolution2dLayerImpl(convolution2dDescriptor, weights, biases, name);
1070 }
1071
1072 IConnectableLayer* Network::AddDepthwiseConvolution2dLayer(
1073     const DepthwiseConvolution2dDescriptor& convolution2dDescriptor,
1074     const ConstTensor& weights,
1075     const char* name)
1076 {
1077     Optional<ConstTensor> biases;
1078     return AddDepthwiseConvolution2dLayerImpl(convolution2dDescriptor, weights, biases, name);
1079 }
1080
1081 IConnectableLayer* Network::AddDepthwiseConvolution2dLayer(
1082     const DepthwiseConvolution2dDescriptor& convolution2dDescriptor,
1083     const ConstTensor& weights,
1084     const ConstTensor& biases,
1085     const char* name)
1086 {
1087     Optional<ConstTensor> optionalBiases(biases);
1088     return AddDepthwiseConvolution2dLayerImpl(convolution2dDescriptor, weights, optionalBiases, name);
1089 }
1090
1091 IConnectableLayer* Network::AddDetectionPostProcessLayer(const armnn::DetectionPostProcessDescriptor& descriptor,
1092                                                          const ConstTensor& anchors, const char* name)
1093 {
1094     const auto layer = m_Graph->AddLayer<DetectionPostProcessLayer>(descriptor, name);
1095
1096     layer->m_Anchors = std::make_unique<ScopedCpuTensorHandle>(anchors);
1097
1098     return layer;
1099 }
1100
1101 IConnectableLayer* Network::AddPermuteLayer(const PermuteDescriptor& permuteDescriptor,
1102                                             const char* name)
1103 {
1104     return m_Graph->AddLayer<PermuteLayer>(permuteDescriptor, name);
1105 }
1106
1107 IConnectableLayer* Network::AddPooling2dLayer(const Pooling2dDescriptor& pooling2dDescriptor,
1108     const char* name)
1109 {
1110     return m_Graph->AddLayer<Pooling2dLayer>(pooling2dDescriptor, name);
1111 }
1112
1113 IConnectableLayer* Network::AddActivationLayer(const ActivationDescriptor& activationDescriptor,
1114     const char* name)
1115 {
1116     return m_Graph->AddLayer<ActivationLayer>(activationDescriptor, name);
1117 }
1118
1119 IConnectableLayer* Network::AddArgMinMaxLayer(const ArgMinMaxDescriptor& argMinMaxDescriptor,
1120                                               const char* name)
1121 {
1122     return m_Graph->AddLayer<ArgMinMaxLayer>(argMinMaxDescriptor, name);
1123 }
1124
1125 IConnectableLayer* Network::AddNormalizationLayer(const NormalizationDescriptor&
1126 normalizationDescriptor,
1127     const char* name)
1128 {
1129     return m_Graph->AddLayer<NormalizationLayer>(normalizationDescriptor, name);
1130 }
1131
1132 IConnectableLayer* Network::AddSoftmaxLayer(const SoftmaxDescriptor& softmaxDescriptor,
1133     const char* name)
1134 {
1135     return m_Graph->AddLayer<SoftmaxLayer>(softmaxDescriptor, name);
1136 }
1137
1138 IConnectableLayer* Network::AddSplitterLayer(const ViewsDescriptor& splitterDescriptor,
1139     const char* name)
1140 {
1141     return m_Graph->AddLayer<SplitterLayer>(splitterDescriptor, name);
1142 }
1143
1144 IConnectableLayer* Network::AddMaximumLayer(const char* name)
1145 {
1146     return m_Graph->AddLayer<MaximumLayer>(name);
1147 }
1148
1149 IConnectableLayer* Network::AddMinimumLayer(const char* name)
1150 {
1151     return m_Graph->AddLayer<MinimumLayer>(name);
1152 }
1153
1154 IConnectableLayer* Network::AddMergerLayer(const MergerDescriptor& mergerDescriptor,
1155                                            const char* name)
1156 {
1157     return AddConcatLayer(mergerDescriptor, name);
1158 }
1159
1160 IConnectableLayer* Network::AddAbsLayer(const char * name)
1161 {
1162     return m_Graph->AddLayer<AbsLayer>(name);
1163 }
1164
1165 IConnectableLayer* Network::AddAdditionLayer(const char* name)
1166 {
1167     return m_Graph->AddLayer<AdditionLayer>(name);
1168 }
1169
1170 IConnectableLayer* Network::AddMultiplicationLayer(const char* name)
1171 {
1172     return m_Graph->AddLayer<MultiplicationLayer>(name);
1173 }
1174
1175 IConnectableLayer* Network::AddOutputLayer(LayerBindingId id, const char* name)
1176 {
1177     return m_Graph->AddLayer<OutputLayer>(id, name);
1178 }
1179
1180 IConnectableLayer* Network::AddBatchNormalizationLayer(const BatchNormalizationDescriptor& desc,
1181                                                        const ConstTensor&                  mean,
1182                                                        const ConstTensor&                  variance,
1183                                                        const ConstTensor&                  beta,
1184                                                        const ConstTensor&                  gamma,
1185                                                        const char*                         name)
1186 {
1187     const auto layer = m_Graph->AddLayer<BatchNormalizationLayer>(desc, name);
1188
1189     layer->m_Mean = std::make_unique<ScopedCpuTensorHandle>(mean);
1190     layer->m_Variance = std::make_unique<ScopedCpuTensorHandle>(variance);
1191     layer->m_Beta = std::make_unique<ScopedCpuTensorHandle>(beta);
1192     layer->m_Gamma = std::make_unique<ScopedCpuTensorHandle>(gamma);
1193
1194     return layer;
1195 }
1196
1197 IConnectableLayer* Network::AddResizeBilinearLayer(const ResizeBilinearDescriptor& descriptor,
1198                                                    const char* name)
1199 {
1200     ResizeDescriptor resizeDescriptor;
1201     resizeDescriptor.m_Method       = ResizeMethod::Bilinear;
1202     resizeDescriptor.m_DataLayout   = descriptor.m_DataLayout;
1203     resizeDescriptor.m_TargetWidth  = descriptor.m_TargetWidth;
1204     resizeDescriptor.m_TargetHeight = descriptor.m_TargetHeight;
1205
1206     return m_Graph->AddLayer<ResizeLayer>(resizeDescriptor, name);
1207 }
1208
1209 IConnectableLayer* Network::AddResizeLayer(const ResizeDescriptor&
1210 resizeDescriptor, const char* name)
1211 {
1212     return m_Graph->AddLayer<ResizeLayer>(resizeDescriptor, name);
1213 }
1214
1215 IConnectableLayer* Network::AddL2NormalizationLayer(const L2NormalizationDescriptor& desc,
1216                                                     const char* name)
1217 {
1218     return m_Graph->AddLayer<L2NormalizationLayer>(desc, name);
1219 }
1220
1221 IConnectableLayer* Network::AddConstantLayer(const ConstTensor& input, const char* name)
1222 {
1223     auto layer = m_Graph->AddLayer<ConstantLayer>(name);
1224
1225     layer->m_LayerOutput = std::make_unique<ScopedCpuTensorHandle>(input);
1226
1227     return layer;
1228 }
1229
1230 IConnectableLayer* Network::AddReshapeLayer(const ReshapeDescriptor& reshapeDescriptor,
1231                                             const char* name)
1232 {
1233     return m_Graph->AddLayer<ReshapeLayer>(reshapeDescriptor, name);
1234 }
1235
1236 IConnectableLayer* Network::AddSpaceToBatchNdLayer(const SpaceToBatchNdDescriptor& spaceToBatchNdDescriptor,
1237                                                    const char* name)
1238 {
1239     return m_Graph->AddLayer<SpaceToBatchNdLayer>(spaceToBatchNdDescriptor, name);
1240 }
1241
1242 IConnectableLayer* Network::AddSpaceToDepthLayer(const SpaceToDepthDescriptor& spaceToDepthDescriptor,
1243                                                  const char* name)
1244 {
1245     return m_Graph->AddLayer<SpaceToDepthLayer>(spaceToDepthDescriptor, name);
1246 }
1247
1248 IConnectableLayer* Network::AddFloorLayer(const char* name)
1249 {
1250     return m_Graph->AddLayer<FloorLayer>(name);
1251 }
1252
1253 IConnectableLayer* Network::AddLstmLayer(const LstmDescriptor&  descriptor,
1254                                          const LstmInputParams& params,
1255                                          const char* name)
1256 {
1257     const auto layer = m_Graph->AddLayer<LstmLayer>(descriptor, name);
1258
1259     //Lstm Basic Parameters
1260     layer->m_BasicParameters.m_InputToForgetWeights =
1261         std::make_unique<ScopedCpuTensorHandle>(*(params.m_InputToForgetWeights));
1262     layer->m_BasicParameters.m_InputToCellWeights =
1263         std::make_unique<ScopedCpuTensorHandle>(*(params.m_InputToCellWeights));
1264     layer->m_BasicParameters.m_InputToOutputWeights =
1265         std::make_unique<ScopedCpuTensorHandle>(*(params.m_InputToOutputWeights));
1266     layer->m_BasicParameters.m_RecurrentToForgetWeights =
1267         std::make_unique<ScopedCpuTensorHandle>(*(params.m_RecurrentToForgetWeights));
1268     layer->m_BasicParameters.m_RecurrentToCellWeights =
1269         std::make_unique<ScopedCpuTensorHandle>(*(params.m_RecurrentToCellWeights));
1270     layer->m_BasicParameters.m_RecurrentToOutputWeights =
1271         std::make_unique<ScopedCpuTensorHandle>(*(params.m_RecurrentToOutputWeights));
1272     layer->m_BasicParameters.m_ForgetGateBias =
1273             std::make_unique<ScopedCpuTensorHandle>(*(params.m_ForgetGateBias));
1274     layer->m_BasicParameters.m_CellBias =
1275             std::make_unique<ScopedCpuTensorHandle>(*(params.m_CellBias));
1276     layer->m_BasicParameters.m_OutputGateBias =
1277             std::make_unique<ScopedCpuTensorHandle>(*(params.m_OutputGateBias));
1278
1279     //Lstm Cifg parameters
1280     if(!descriptor.m_CifgEnabled)
1281     {
1282         if(params.m_InputToInputWeights == nullptr)
1283         {
1284             throw InvalidArgumentException("AddLstmLayer: Input To Input Weights cannot be NULL");
1285         }
1286         if(params.m_RecurrentToInputWeights == nullptr)
1287         {
1288             throw InvalidArgumentException(
1289                     "AddLstmLayer: Recurrent To Input Weights cannot be NULL");
1290         }
1291         if(params.m_InputGateBias == nullptr)
1292         {
1293             throw InvalidArgumentException("AddLstmLayer: Input Gate Bias cannot be NULL");
1294         }
1295         layer->m_CifgParameters.m_InputToInputWeights =
1296             std::make_unique<ScopedCpuTensorHandle>(*(params.m_InputToInputWeights));
1297         layer->m_CifgParameters.m_RecurrentToInputWeights =
1298             std::make_unique<ScopedCpuTensorHandle>(*(params.m_RecurrentToInputWeights));
1299         // In the VTS tests, cell-to-input weights may be null, even if the other CIFG params are not.
1300         if(params.m_CellToInputWeights != nullptr)
1301         {
1302             layer->m_CifgParameters.m_CellToInputWeights =
1303                     std::make_unique<ScopedCpuTensorHandle>(*(params.m_CellToInputWeights));
1304         }
1305         layer->m_CifgParameters.m_InputGateBias =
1306             std::make_unique<ScopedCpuTensorHandle>(*(params.m_InputGateBias));
1307     }
1308
1309     //Lstm projection parameters
1310     if(descriptor.m_ProjectionEnabled)
1311     {
1312         if(params.m_ProjectionWeights == nullptr)
1313         {
1314             throw InvalidArgumentException("AddLstmLayer: Projection Weights cannot be NULL");
1315         }
1316         layer->m_ProjectionParameters.m_ProjectionWeights =
1317             std::make_unique<ScopedCpuTensorHandle>(*(params.m_ProjectionWeights));
1318         if(params.m_ProjectionBias != nullptr)
1319         {
1320             layer->m_ProjectionParameters.m_ProjectionBias =
1321                 std::make_unique<ScopedCpuTensorHandle>(*(params.m_ProjectionBias));
1322         }
1323     }
1324
1325     //Lstm Peephole params
1326     if(descriptor.m_PeepholeEnabled)
1327     {
1328         if(params.m_CellToForgetWeights == nullptr)
1329         {
1330             throw InvalidArgumentException("AddLstmLayer: Cell To Forget Weights cannot be NULL");
1331         }
1332         if(params.m_CellToOutputWeights == nullptr)
1333         {
1334             throw InvalidArgumentException("AddLstmLayer: Cell To Output Weights cannot be NULL");
1335         }
1336         layer->m_PeepholeParameters.m_CellToForgetWeights =
1337             std::make_unique<ScopedCpuTensorHandle>(*(params.m_CellToForgetWeights));
1338         layer->m_PeepholeParameters.m_CellToOutputWeights =
1339             std::make_unique<ScopedCpuTensorHandle>(*(params.m_CellToOutputWeights));
1340     }
1341
1342     //Lstm Layer Normalization params
1343     if(descriptor.m_LayerNormEnabled)
1344     {
1345         if(!descriptor.m_CifgEnabled)
1346         {
1347             if(params.m_InputLayerNormWeights == nullptr)
1348             {
1349                 throw InvalidArgumentException("AddLstmLayer: Input layer normalization weights cannot be NULL");
1350             }
1351             layer->m_LayerNormParameters.m_InputLayerNormWeights =
1352                     std::make_unique<ScopedCpuTensorHandle>(*(params.m_InputLayerNormWeights));
1353         }
1354
1355         if(params.m_ForgetLayerNormWeights == nullptr)
1356         {
1357             throw InvalidArgumentException("AddLstmLayer: Forget layer normalization weights cannot be NULL");
1358         }
1359         if(params.m_CellLayerNormWeights == nullptr)
1360         {
1361             throw InvalidArgumentException("AddLstmLayer: Cell layer normalization weights cannot be NULL");
1362         }
1363         if(params.m_OutputLayerNormWeights == nullptr)
1364         {
1365             throw InvalidArgumentException("AddLstmLayer: Output layer normalization weights cannot be NULL");
1366         }
1367         layer->m_LayerNormParameters.m_ForgetLayerNormWeights =
1368                 std::make_unique<ScopedCpuTensorHandle>(*(params.m_ForgetLayerNormWeights));
1369         layer->m_LayerNormParameters.m_CellLayerNormWeights =
1370                 std::make_unique<ScopedCpuTensorHandle>(*(params.m_CellLayerNormWeights));
1371         layer->m_LayerNormParameters.m_OutputLayerNormWeights =
1372                 std::make_unique<ScopedCpuTensorHandle>(*(params.m_OutputLayerNormWeights));
1373     }
1374     return layer;
1375 }
1376
1377 IConnectableLayer* Network::AddDivisionLayer(const char* name)
1378 {
1379     return m_Graph->AddLayer<DivisionLayer>(name);
1380 }
1381
1382 IConnectableLayer* Network::AddSubtractionLayer(const char* name)
1383 {
1384     return m_Graph->AddLayer<SubtractionLayer>(name);
1385 }
1386
1387 IConnectableLayer* Network::AddMeanLayer(const MeanDescriptor& meanDescriptor, const char* name)
1388 {
1389     return m_Graph->AddLayer<MeanLayer>(meanDescriptor,name);
1390 }
1391
1392 IConnectableLayer* Network::AddPadLayer(const PadDescriptor& padDescriptor, const char* name)
1393 {
1394     return m_Graph->AddLayer<PadLayer>(padDescriptor,name);
1395 }
1396
1397 IConnectableLayer *Network::AddQuantizeLayer(const char *name)
1398 {
1399     return m_Graph->AddLayer<QuantizeLayer>(name);
1400 }
1401
1402 IConnectableLayer* Network::AddDequantizeLayer(const char* name)
1403 {
1404     return m_Graph->AddLayer<DequantizeLayer>(name);
1405 }
1406
1407 IConnectableLayer* Network::AddStridedSliceLayer(const StridedSliceDescriptor& stridedSliceDescriptor,
1408                                                  const char* name)
1409 {
1410     return m_Graph->AddLayer<StridedSliceLayer>(stridedSliceDescriptor, name);
1411 }
1412
1413 IConnectableLayer* Network::AddGreaterLayer(const char* name)
1414 {
1415     return m_Graph->AddLayer<GreaterLayer>(name);
1416 }
1417
1418 IConnectableLayer* Network::AddEqualLayer(const char* name)
1419 {
1420     return m_Graph->AddLayer<EqualLayer>(name);
1421 }
1422
1423 IConnectableLayer* Network::AddRsqrtLayer(const char * name)
1424 {
1425     return m_Graph->AddLayer<RsqrtLayer>(name);
1426 }
1427
1428 IConnectableLayer* Network::AddGatherLayer(const char* name)
1429 {
1430     return m_Graph->AddLayer<GatherLayer>(name);
1431 }
1432
1433 IConnectableLayer* Network::AddMergeLayer(const char* name)
1434 {
1435     return m_Graph->AddLayer<MergeLayer>(name);
1436 }
1437
1438 IConnectableLayer* Network::AddSwitchLayer(const char* name)
1439 {
1440     return m_Graph->AddLayer<SwitchLayer>(name);
1441 }
1442
1443 IConnectableLayer* Network::AddPreluLayer(const char* name)
1444 {
1445     return m_Graph->AddLayer<PreluLayer>(name);
1446 }
1447
1448 IConnectableLayer* Network::AddTransposeConvolution2dLayer(const TransposeConvolution2dDescriptor& descriptor,
1449                                                            const ConstTensor& weights,
1450                                                            const Optional<ConstTensor>& biases,
1451                                                            const char* name)
1452 {
1453     if (descriptor.m_BiasEnabled && !biases.has_value())
1454     {
1455         throw InvalidArgumentException("AddTransposeConvolution2dLayer: Biases cannot be empty");
1456     }
1457
1458     const auto layer = m_Graph->AddLayer<TransposeConvolution2dLayer>(descriptor, name);
1459
1460     layer->m_Weight = std::make_unique<ScopedCpuTensorHandle>(weights);
1461
1462     if (descriptor.m_BiasEnabled)
1463     {
1464         layer->m_Bias = std::make_unique<ScopedCpuTensorHandle>(biases.value());
1465     }
1466
1467     return layer;
1468 }
1469
1470 IConnectableLayer* Network::AddStackLayer(const StackDescriptor& stackDescriptor,
1471                                           const char* name)
1472 {
1473     return m_Graph->AddLayer<StackLayer>(stackDescriptor, name);
1474 }
1475
1476 IConnectableLayer* Network::AddQuantizedLstmLayer(const QuantizedLstmInputParams& params,
1477                                                   const char* name)
1478 {
1479     const auto layer = m_Graph->AddLayer<QuantizedLstmLayer>(name);
1480
1481     // InputToX weights
1482     layer->m_QuantizedLstmParameters.m_InputToInputWeights =
1483             std::make_unique<ScopedCpuTensorHandle>(params.GetInputToInputWeights());
1484     layer->m_QuantizedLstmParameters.m_InputToForgetWeights =
1485             std::make_unique<ScopedCpuTensorHandle>(params.GetInputToForgetWeights());
1486     layer->m_QuantizedLstmParameters.m_InputToCellWeights =
1487             std::make_unique<ScopedCpuTensorHandle>(params.GetInputToCellWeights());
1488     layer->m_QuantizedLstmParameters.m_InputToOutputWeights =
1489             std::make_unique<ScopedCpuTensorHandle>(params.GetInputToOutputWeights());
1490
1491     // RecurrentToX weights
1492     layer->m_QuantizedLstmParameters.m_RecurrentToInputWeights =
1493             std::make_unique<ScopedCpuTensorHandle>(params.GetRecurrentToInputWeights());
1494     layer->m_QuantizedLstmParameters.m_RecurrentToForgetWeights =
1495             std::make_unique<ScopedCpuTensorHandle>(params.GetRecurrentToForgetWeights());
1496     layer->m_QuantizedLstmParameters.m_RecurrentToCellWeights =
1497             std::make_unique<ScopedCpuTensorHandle>(params.GetRecurrentToCellWeights());
1498     layer->m_QuantizedLstmParameters.m_RecurrentToOutputWeights =
1499             std::make_unique<ScopedCpuTensorHandle>(params.GetRecurrentToOutputWeights());
1500
1501     // Bias
1502     layer->m_QuantizedLstmParameters.m_InputGateBias =
1503             std::make_unique<ScopedCpuTensorHandle>(params.GetInputGateBias());
1504     layer->m_QuantizedLstmParameters.m_ForgetGateBias =
1505             std::make_unique<ScopedCpuTensorHandle>(params.GetForgetGateBias());
1506     layer->m_QuantizedLstmParameters.m_CellBias =
1507             std::make_unique<ScopedCpuTensorHandle>(params.GetCellBias());
1508     layer->m_QuantizedLstmParameters.m_OutputGateBias =
1509             std::make_unique<ScopedCpuTensorHandle>(params.GetOutputGateBias());
1510
1511     return layer;
1512 }
1513
1514 void Network::Accept(ILayerVisitor& visitor) const
1515 {
1516     for (auto layer : GetGraph())
1517     {
1518         layer->Accept(visitor);
1519     };
1520 }
1521
1522 OptimizedNetwork::OptimizedNetwork(std::unique_ptr<Graph> graph)
1523     : m_Graph(std::move(graph))
1524 {
1525 }
1526
1527 OptimizedNetwork::~OptimizedNetwork()
1528 {
1529 }
1530
1531 } // namespace armnn