blob: b1cb5a871a96a5417d2043844514cb21eb67deba [file] [log] [blame]
telsoa014fcda012018-03-09 14:13:49 +00001//
2// Copyright © 2017 Arm Ltd. All rights reserved.
David Beckecb56cd2018-09-05 12:52:57 +01003// SPDX-License-Identifier: MIT
telsoa014fcda012018-03-09 14:13:49 +00004//
Matteo Martincigh49124022019-01-11 13:25:59 +00005
telsoa014fcda012018-03-09 14:13:49 +00006#include "Network.hpp"
7#include "Graph.hpp"
8#include "Layer.hpp"
telsoa01c577f2c2018-08-31 09:22:23 +01009#include "DeviceSpec.hpp"
telsoa014fcda012018-03-09 14:13:49 +000010#include "Optimizer.hpp"
Derek Lambertiff05cc52019-04-26 13:05:17 +010011#include "SubgraphViewSelector.hpp"
Matteo Martincigh49124022019-01-11 13:25:59 +000012#include "BackendSettings.hpp"
David Beckac42efd2018-09-26 17:41:13 +010013#include "optimizations/All.hpp"
telsoa014fcda012018-03-09 14:13:49 +000014
Aron Virginas-Tarc9cc8042018-11-01 16:15:57 +000015#include <backendsCommon/CpuTensorHandle.hpp>
16#include <backendsCommon/WorkloadFactory.hpp>
David Beck263e3492018-11-09 14:46:40 +000017#include <backendsCommon/IBackendInternal.hpp>
Derek Lamberti84da38b2019-06-13 11:40:08 +010018#include <backendsCommon/TensorHandleFactoryRegistry.hpp>
David Beckac42efd2018-09-26 17:41:13 +010019
20#include <armnn/Exceptions.hpp>
telsoa014fcda012018-03-09 14:13:49 +000021#include <armnn/Utils.hpp>
telsoa01c577f2c2018-08-31 09:22:23 +010022#include <armnn/TypesUtils.hpp>
Matteo Martincighc601aa62019-10-29 15:03:22 +000023#include <armnn/BackendRegistry.hpp>
telsoa014fcda012018-03-09 14:13:49 +000024
25#include <fcntl.h>
26#include <algorithm>
27#include <fstream>
28#include <memory>
telsoa01c577f2c2018-08-31 09:22:23 +010029#include <vector>
30#include <algorithm>
telsoa014fcda012018-03-09 14:13:49 +000031
32#include <boost/assert.hpp>
33#include <boost/format.hpp>
34#include <boost/log/trivial.hpp>
35#include <boost/numeric/conversion/converter_policies.hpp>
36#include <boost/cast.hpp>
37
38namespace armnn
39{
40
41armnn::INetwork* INetwork::CreateRaw()
42{
43 return new Network();
44}
45
46armnn::INetworkPtr INetwork::Create()
47{
48 return INetworkPtr(CreateRaw(), &INetwork::Destroy);
49}
50
51void INetwork::Destroy(INetwork* network)
52{
53 delete boost::polymorphic_downcast<Network*>(network);
54}
55
56Status Network::PrintGraph()
57{
58 m_Graph->Print();
59 return Status::Success;
60}
61
62void IOptimizedNetwork::Destroy(IOptimizedNetwork* network)
63{
64 delete boost::polymorphic_downcast<OptimizedNetwork*>(network);
65}
66
67Status OptimizedNetwork::PrintGraph()
68{
69 m_Graph->Print();
70 return Status::Success;
71}
72
surmeh01bceff2f2018-03-29 16:29:27 +010073Status OptimizedNetwork::SerializeToDot(std::ostream& stream) const
74{
75 return m_Graph->SerializeToDot(stream);
76}
77
Matteo Martincigh49124022019-01-11 13:25:59 +000078
Matteo Martincigh49124022019-01-11 13:25:59 +000079
80void ReportError(const std::string& errorMessage,
81 Optional<std::vector<std::string>&> errorMessages)
82{
83 std::stringstream fullErrorMessage;
84 fullErrorMessage << "ERROR: " << errorMessage;
85 BOOST_LOG_TRIVIAL(warning) << fullErrorMessage.str();
86 if (errorMessages)
87 {
88 errorMessages.value().push_back(fullErrorMessage.str());
89 }
90}
91
92void ReportWarning(const std::string& warningMessage,
93 Optional<std::vector<std::string>&> warningMessages)
94{
95 std::stringstream fullWarningMessage;
96 fullWarningMessage << "WARNING: " << warningMessage;
97 BOOST_LOG_TRIVIAL(warning) << fullWarningMessage.str();
98 if (warningMessages)
99 {
100 warningMessages.value().push_back(fullWarningMessage.str());
101 }
102}
103
jimfly016b0b53d2018-10-08 14:43:01 +0100104bool CheckScaleSetOnQuantizedType(Layer* layer, Optional<std::vector<std::string>&> errMessages)
105{
106 bool noErrors = true;
107 unsigned int numOutputs = layer->GetNumOutputSlots();
108 for (unsigned int i = 0; i < numOutputs; i++) {
David Monahanb8554702019-04-25 16:03:38 +0100109 OutputSlot& outputSlot = layer->GetOutputSlot(i);
110 TensorInfo info = outputSlot.GetTensorInfo();
jimfly016b0b53d2018-10-08 14:43:01 +0100111 if (DataType::QuantisedAsymm8 == info.GetDataType()) {
112 if (0.f == info.GetQuantizationScale()) {
113 noErrors = false;
114 std::stringstream ss;
Matteo Martincigh49124022019-01-11 13:25:59 +0000115 ss << "output " << i << " of layer " << GetLayerTypeAsCString(layer->GetType())
jimfly016b0b53d2018-10-08 14:43:01 +0100116 << " (" << layer->GetNameStr() << ") is of type"
117 << " Quantized 8 bit but its scale parameter has not been set";
Matteo Martincigh49124022019-01-11 13:25:59 +0000118 ReportError(ss.str(), errMessages);
jimfly016b0b53d2018-10-08 14:43:01 +0100119 }
David Monahanb8554702019-04-25 16:03:38 +0100120 // Softmax under QuantisedAsymm8 must always be scale (1.0f/256.0f) and offset 0
121 if ((info.GetQuantizationScale() != (1.0f / 256.0f) ||
122 info.GetQuantizationOffset() != 0) &&
123 layer->GetType() == armnn::LayerType::Softmax)
124 {
125 std::stringstream ss;
126 ss << "Quantization parameters for Softmax layer (Scale: " <<
127 info.GetQuantizationScale() << " and Offset: " << info.GetQuantizationOffset() <<
128 ") are incorrect and have been updated to Scale: 0.00390625 and Offset: 0";
129 BOOST_LOG_TRIVIAL(warning) << ss.str();
130 info.SetQuantizationScale((1.0f /256.0f));
131 info.SetQuantizationOffset(0);
132 outputSlot.SetTensorInfo(info);
133 }
jimfly016b0b53d2018-10-08 14:43:01 +0100134 }
135 }
136 return noErrors;
137}
138
Matteo Martincigh49124022019-01-11 13:25:59 +0000139OptimizationResult AssignBackends(OptimizedNetwork* optNetObjPtr,
140 BackendSettings& backendSettings,
141 Graph::Iterator& firstLayer,
142 Graph::Iterator& lastLayer,
143 Optional<std::vector<std::string>&> errMessages)
telsoa014fcda012018-03-09 14:13:49 +0000144{
Matteo Martincigh49124022019-01-11 13:25:59 +0000145 OptimizationResult result;
telsoa014fcda012018-03-09 14:13:49 +0000146
Matteo Martincigh49124022019-01-11 13:25:59 +0000147 // Helper lambda to compose meaningful error message before returning with error
148 auto ReturnWithError = [&](const Layer* layer)
telsoa01c577f2c2018-08-31 09:22:23 +0100149 {
jimfly016b0b53d2018-10-08 14:43:01 +0100150 std::stringstream failureMsg;
Matteo Martincigh49124022019-01-11 13:25:59 +0000151 failureMsg << "Layer of type " << GetLayerTypeAsCString(layer->GetType())
152 << " is not supported on any preferred backend " << backendSettings.m_PreferredBackends;
153 ReportError(failureMsg.str(), errMessages);
154
155 result.m_Error = true;
156 return result;
telsoa01c577f2c2018-08-31 09:22:23 +0100157 };
158
Matteo Martincigh49124022019-01-11 13:25:59 +0000159 auto availablePreferredBackends = backendSettings.GetAvailablePreferredBackends();
160 if (availablePreferredBackends.empty())
telsoa01c577f2c2018-08-31 09:22:23 +0100161 {
Matteo Martincigh49124022019-01-11 13:25:59 +0000162 std::stringstream failureMsg;
163 failureMsg << "No preferred backends are available";
164 ReportError(failureMsg.str(), errMessages);
165
166 result.m_Error = true;
167 return result;
168 }
169
170 for (auto it = firstLayer; it != lastLayer; ++it)
171 {
172 auto layer = *it;
telsoa01c577f2c2018-08-31 09:22:23 +0100173 DataType dataType = layer->GetDataType();
174 std::string reasonIfUnsupported;
175 bool found = false;
jimfly016b0b53d2018-10-08 14:43:01 +0100176 if (!CheckScaleSetOnQuantizedType(layer, errMessages))
177 {
178 // don't bomb immediately, find all the quantized outputs
179 // which haven't had a scale set and report them all back.
Matteo Martincigh49124022019-01-11 13:25:59 +0000180 result.m_Error = true;
jimfly016b0b53d2018-10-08 14:43:01 +0100181 }
Matteo Martincigh49124022019-01-11 13:25:59 +0000182
David Beckf0b48452018-10-19 15:20:56 +0100183 for (const auto& backend : availablePreferredBackends)
telsoa01c577f2c2018-08-31 09:22:23 +0100184 {
185 // need to set the compute device on the layer
186 // before we can check if it is supported
David Beck33f0ae02018-10-18 15:13:56 +0100187 layer->SetBackendId(backend);
telsoa01c577f2c2018-08-31 09:22:23 +0100188 if (!IWorkloadFactory::IsLayerSupported(*layer, dataType, reasonIfUnsupported))
189 {
190 if (dataType == DataType::Float16)
191 {
192 if (IWorkloadFactory::IsLayerSupported(*layer, DataType::Float32, reasonIfUnsupported)
193 && layer->GetType() != LayerType::ConvertFp32ToFp16
194 && layer->GetType() != LayerType::ConvertFp16ToFp32)
195 {
196 // Insert FP16 -> FP32 conversion layer before current layer
197 std::vector<ConvertFp16ToFp32Layer*> convertFp16ToFp32Layers =
198 InsertConvertFp16ToFp32LayersBefore(optNetObjPtr->GetGraph(), *layer);
199
200 // Insert FP32 -> FP16 conversion layer after current layer
201 std::vector<ConvertFp32ToFp16Layer*> convertFp32ToFp16Layers =
202 InsertConvertFp32ToFp16LayersAfter(optNetObjPtr->GetGraph(), *layer);
203
204 // Assign a supported backend to the newly introduced conversion layers
David Beckf0b48452018-10-19 15:20:56 +0100205 auto AssignFirstSupportedBackend = [&](Layer* layer, BackendId preferredBackend)
telsoa01c577f2c2018-08-31 09:22:23 +0100206 {
207 bool supportedBackendFound = false;
208 std::string reasonIfUnsupported;
209
210 // Try preferred backend first
David Beck33f0ae02018-10-18 15:13:56 +0100211 layer->SetBackendId(preferredBackend);
David Beck29c75de2018-10-23 13:35:58 +0100212 if (IWorkloadFactory::IsLayerSupported(*layer,
213 EmptyOptional(),
214 reasonIfUnsupported))
telsoa01c577f2c2018-08-31 09:22:23 +0100215 {
216 supportedBackendFound = true;
217 }
218 else
219 {
David Beckf0b48452018-10-19 15:20:56 +0100220 for (const auto& backend : availablePreferredBackends)
telsoa01c577f2c2018-08-31 09:22:23 +0100221 {
222 // Skip preferred backend (we already determined that it is not supported)
223 if (backend == preferredBackend)
224 {
225 continue;
226 }
227
David Beck33f0ae02018-10-18 15:13:56 +0100228 layer->SetBackendId(backend);
David Beck29c75de2018-10-23 13:35:58 +0100229 if (IWorkloadFactory::IsLayerSupported(*layer,
230 EmptyOptional(),
231 reasonIfUnsupported))
telsoa01c577f2c2018-08-31 09:22:23 +0100232 {
233 supportedBackendFound = true;
234 break;
235 }
236 }
237 }
238
239 return supportedBackendFound;
240 };
241
242 for (ConvertFp16ToFp32Layer* convertLayer : convertFp16ToFp32Layers)
243 {
244 if (!AssignFirstSupportedBackend(convertLayer, backend))
245 {
246 return ReturnWithError(convertLayer);
247 }
248 }
249
250 for (ConvertFp32ToFp16Layer* convertLayer : convertFp32ToFp16Layers)
251 {
252 if (!AssignFirstSupportedBackend(convertLayer, backend))
253 {
254 return ReturnWithError(convertLayer);
255 }
256 }
257
258 found = true;
259 break;
260 }
261 }
jimfly016b0b53d2018-10-08 14:43:01 +0100262 std::stringstream warningMsg;
Matteo Martincigh49124022019-01-11 13:25:59 +0000263 warningMsg << "Layer of type " << GetLayerTypeAsCString(layer->GetType())
David Beck33f0ae02018-10-18 15:13:56 +0100264 << " is not supported on requested backend " << layer->GetBackendId().Get()
jimfly016b0b53d2018-10-08 14:43:01 +0100265 << " for data type " << GetDataTypeName(dataType)
266 << " (reason: " << reasonIfUnsupported
267 << "), falling back to the next backend.";
Matteo Martincigh49124022019-01-11 13:25:59 +0000268 ReportWarning(warningMsg.str(), errMessages);
telsoa01c577f2c2018-08-31 09:22:23 +0100269 }
270 else
271 {
272 found = true;
Matteo Martincigh49124022019-01-11 13:25:59 +0000273 backendSettings.m_SelectedBackends.insert(backend);
telsoa01c577f2c2018-08-31 09:22:23 +0100274 break;
275 }
276 }
277
278 // If the layer is unsupported by any devices, log and return a null network.
Matteo Martincigh49124022019-01-11 13:25:59 +0000279 if (!found)
280 {
telsoa01c577f2c2018-08-31 09:22:23 +0100281 // NOTE: if the layer is not an operation queue type AND we have not got CpuRef as a
282 // fallback we should set the compute device on the layer to CpuRef (these are not
283 // available as accelerated operations, or are only available under certain
284 // conditions, currently they comprise MemCopy, Constant, Permute)
285 armnn::LayerType layerType = layer->GetType();
Matteo Martincigh49124022019-01-11 13:25:59 +0000286 if (!backendSettings.IsCpuRefUsed() && (layerType == armnn::LayerType::MemCopy ||
287 layerType == armnn::LayerType::Constant ||
288 layerType == armnn::LayerType::Permute))
telsoa01c577f2c2018-08-31 09:22:23 +0100289 {
Matteo Martincigh49124022019-01-11 13:25:59 +0000290 BackendId cpuBackendId(armnn::Compute::CpuRef);
291 layer->SetBackendId(cpuBackendId);
292 backendSettings.m_SelectedBackends.insert(cpuBackendId);
telsoa01c577f2c2018-08-31 09:22:23 +0100293 }
294 else
295 {
296 return ReturnWithError(layer);
297 }
298 }
299 }
Matteo Martincigh49124022019-01-11 13:25:59 +0000300
301 return result;
302}
303
Matteo Martincighadddddb2019-01-24 14:06:23 +0000304OptimizationResult AssignBackends(OptimizedNetwork* optNetObjPtr,
305 BackendSettings& backendSettings,
Derek Lambertiff05cc52019-04-26 13:05:17 +0100306 SubgraphView& subgraph,
Matteo Martincighadddddb2019-01-24 14:06:23 +0000307 Optional<std::vector<std::string>&> errMessages)
Matteo Martincigh49124022019-01-11 13:25:59 +0000308{
Derek Lambertiff05cc52019-04-26 13:05:17 +0100309 Graph::Iterator firstLayer = subgraph.begin();
310 Graph::Iterator lastLayer = subgraph.end();
Matteo Martincighadddddb2019-01-24 14:06:23 +0000311 return AssignBackends(optNetObjPtr,
312 backendSettings,
313 firstLayer,
314 lastLayer,
315 errMessages);
316}
317
Derek Lamberti84da38b2019-06-13 11:40:08 +0100318BackendsMap CreateSupportedBackends(TensorHandleFactoryRegistry& handleFactoryRegistry,
319 BackendSettings& backendSettings)
320{
321 BackendsMap backends;
322 auto const& backendRegistry = BackendRegistryInstance();
323 for (auto&& selectedBackend : backendSettings.m_SupportedBackends)
324 {
325 auto backendFactory = backendRegistry.GetFactory(selectedBackend);
326 auto backendObjPtr = backendFactory();
327 BOOST_ASSERT(backendObjPtr);
328
329 backendObjPtr->RegisterTensorHandleFactories(handleFactoryRegistry);
330
331 backends[backendObjPtr->GetId()] = std::move(backendObjPtr);
332 }
333
334 return backends;
335}
336
Matteo Martincighadddddb2019-01-24 14:06:23 +0000337OptimizationResult ApplyBackendOptimizations(OptimizedNetwork* optNetObjPtr,
338 BackendSettings& backendSettings,
Derek Lamberti84da38b2019-06-13 11:40:08 +0100339 BackendsMap& backends,
Matteo Martincighadddddb2019-01-24 14:06:23 +0000340 Optional<std::vector<std::string>&> errMessages)
341{
342 BOOST_ASSERT(optNetObjPtr);
Matteo Martincigh49124022019-01-11 13:25:59 +0000343
344 OptimizationResult result;
345
Matteo Martincighadddddb2019-01-24 14:06:23 +0000346 // Get the optimized graph
347 Graph& optGraph = optNetObjPtr->GetGraph();
Matteo Martincigh49124022019-01-11 13:25:59 +0000348
Matteo Martincighadddddb2019-01-24 14:06:23 +0000349 // Run backend specific optimizations
Matteo Martincighadddddb2019-01-24 14:06:23 +0000350 for (auto&& selectedBackend : backendSettings.m_SelectedBackends)
Matteo Martincigh49124022019-01-11 13:25:59 +0000351 {
Derek Lamberti84da38b2019-06-13 11:40:08 +0100352 auto backendObjPtr = backends.find(selectedBackend)->second.get();
Matteo Martincighadddddb2019-01-24 14:06:23 +0000353 BOOST_ASSERT(backendObjPtr);
354
355 // Select sub-graphs based on backend
Derek Lambertiff05cc52019-04-26 13:05:17 +0100356 SubgraphViewSelector::Subgraphs subgraphs =
Rob Hughes65c32262019-07-23 15:33:39 +0100357 SubgraphViewSelector::SelectSubgraphs(optGraph,
Matteo Martincigh602af092019-05-01 10:31:27 +0100358 // 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 });
Derek Lambertiff05cc52019-04-26 13:05:17 +0100365 if (subgraphs.empty())
Matteo Martincigh49124022019-01-11 13:25:59 +0000366 {
Matteo Martincighadddddb2019-01-24 14:06:23 +0000367 // No sub-graphs found, try with next selected backend
368 continue;
Matteo Martincigh49124022019-01-11 13:25:59 +0000369 }
Matteo Martincighadddddb2019-01-24 14:06:23 +0000370
371 // Try to optimize each sub-graph
Derek Lambertiff05cc52019-04-26 13:05:17 +0100372 for (auto& subgraph : subgraphs)
Matteo Martincigh49124022019-01-11 13:25:59 +0000373 {
Matteo Martincighadddddb2019-01-24 14:06:23 +0000374 // Try to optimize the current sub-graph
Matteo Martincigh84924332019-05-09 12:46:16 +0100375 OptimizationViews optimizationViews = backendObjPtr->OptimizeSubgraphView(*subgraph);
376 BOOST_ASSERT(optimizationViews.Validate(*subgraph));
Matteo Martincighadddddb2019-01-24 14:06:23 +0000377
378 // Optimization attempted, check the resulting optimized sub-graph
Matteo Martincigh84924332019-05-09 12:46:16 +0100379 for (auto& substitution : optimizationViews.GetSubstitutions())
Matteo Martincighadddddb2019-01-24 14:06:23 +0000380 {
381 // Sub-graph optimized, substitute the sub-graph with the new optimized one in the main optimized graph
Matteo Martincigh84924332019-05-09 12:46:16 +0100382 SubgraphView& replacementSubgraph = substitution.m_ReplacementSubgraph;
383 SubgraphView& substitutableSubgraph = substitution.m_SubstitutableSubgraph;
384 optGraph.SubstituteSubgraph(substitutableSubgraph, replacementSubgraph);
Matteo Martincighadddddb2019-01-24 14:06:23 +0000385
386 // Assign the current backend to the optimized sub-graph
Matteo Martincigh84924332019-05-09 12:46:16 +0100387 std::for_each(replacementSubgraph.begin(), replacementSubgraph.end(), [&selectedBackend](Layer* l)
Derek Lambertic2fe5fb2019-05-08 10:23:08 +0100388 {
389 BOOST_ASSERT(l);
390 l->SetBackendId(selectedBackend);
391 });
Matteo Martincighadddddb2019-01-24 14:06:23 +0000392 }
Derek Lambertic2fe5fb2019-05-08 10:23:08 +0100393
Matteo Martincigh84924332019-05-09 12:46:16 +0100394 if (!optimizationViews.GetFailedSubgraphs().empty())
Matteo Martincighadddddb2019-01-24 14:06:23 +0000395 {
Matteo Martincighadddddb2019-01-24 14:06:23 +0000396 std::stringstream warningMsg;
Derek Lambertic2fe5fb2019-05-08 10:23:08 +0100397 warningMsg << "Some sub-graph(s) failed to optimized on " << backendObjPtr->GetId() << " backend.";
Matteo Martincighadddddb2019-01-24 14:06:23 +0000398 ReportWarning(warningMsg.str(), errMessages);
399
400 // Failed to optimize the given sub-graph, re-assign the sub-graph layers to other available backends
Derek Lambertic2fe5fb2019-05-08 10:23:08 +0100401 BackendSettings settingsCopy(backendSettings);
Matteo Martincighadddddb2019-01-24 14:06:23 +0000402 if (!backendObjPtr->GetId().IsCpuRef())
403 {
404 // Add the current backend to the list of backends to ignore
Derek Lambertic2fe5fb2019-05-08 10:23:08 +0100405 settingsCopy.m_IgnoredBackends.insert(backendObjPtr->GetId());
Matteo Martincighadddddb2019-01-24 14:06:23 +0000406 }
Derek Lambertic2fe5fb2019-05-08 10:23:08 +0100407
408 int count=0;
Matteo Martincigh84924332019-05-09 12:46:16 +0100409 for (auto& failedSubgraph : optimizationViews.GetFailedSubgraphs())
Matteo Martincighadddddb2019-01-24 14:06:23 +0000410 {
Derek Lambertic2fe5fb2019-05-08 10:23:08 +0100411 // 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++;
Matteo Martincigh328d92b2019-07-04 17:52:55 +0100415 ReportWarning(subgraphMsg.str(), errMessages);
Derek Lambertic2fe5fb2019-05-08 10:23:08 +0100416
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 }
Matteo Martincighadddddb2019-01-24 14:06:23 +0000427 }
Matteo Martincigh49124022019-01-11 13:25:59 +0000428 }
429 }
430 }
431
432 return result;
433}
434
Derek Lamberti84da38b2019-06-13 11:40:08 +0100435bool 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
Matteo Martincigha6539ed2019-08-27 13:43:32 +0100444 if (srcFactory && dstFactory &&
445 (srcFactory->GetExportFlags() & dstFactory->GetImportFlags()) != 0)
Derek Lamberti84da38b2019-06-13 11:40:08 +0100446 {
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.
455ITensorHandleFactory::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 {
Derek Lambertif674aa02019-08-01 15:56:25 +0100497 // Input layers use the mem copy workload or import, so the selected factory must
498 // support either the map/unmap API or Import API
Derek Lamberti84da38b2019-06-13 11:40:08 +0100499 ITensorHandleFactory* factory = registry.GetFactory(dst);
Derek Lambertif674aa02019-08-01 15:56:25 +0100500 if (!factory->SupportsMapUnmap() &&
501 !CheckFlag(factory->GetImportFlags(), MemorySource::Malloc)) // Just support cpu mem imports for now
Derek Lamberti84da38b2019-06-13 11:40:08 +0100502 {
Derek Lambertif674aa02019-08-01 15:56:25 +0100503 // The current tensor handle factory does not support the map/unmap or import
504 // strategy, move to the next one
Derek Lamberti84da38b2019-06-13 11:40:08 +0100505 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.
537ITensorHandleFactory::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.
546ITensorHandleFactory::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
Derek Lambertif674aa02019-08-01 15:56:25 +0100655EdgeStrategy CalculateEdgeStrategy(BackendsMap& backends,
656 ITensorHandleFactory::FactoryId srcFactoryId,
657 const Layer& layer,
658 const Layer& connectedLayer,
659 TensorHandleFactoryRegistry& registry)
Derek Lamberti84da38b2019-06-13 11:40:08 +0100660{
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 {
Derek Lambertif674aa02019-08-01 15:56:25 +0100671 return EdgeStrategy::CopyToTarget;
Derek Lamberti84da38b2019-06-13 11:40:08 +0100672 }
673 else
674 {
Derek Lambertif674aa02019-08-01 15:56:25 +0100675 return EdgeStrategy::DirectCompatibility;
Derek Lamberti84da38b2019-06-13 11:40:08 +0100676 }
677 }
678
679 // TensorHandleFactory API present, so perform more sophisticated strategies.
Derek Lambertif674aa02019-08-01 15:56:25 +0100680 // Dst Output layers don't require copy because they use import or map/unmap
Derek Lamberti84da38b2019-06-13 11:40:08 +0100681 if (connectedLayer.GetType() == LayerType::Output)
682 {
Derek Lambertif674aa02019-08-01 15:56:25 +0100683 return EdgeStrategy::DirectCompatibility;
Derek Lamberti84da38b2019-06-13 11:40:08 +0100684 }
685
686 // Search for direct match in prefs
687 for (auto&& pref : dstPrefs)
688 {
689 if (pref == srcFactoryId)
690 {
Derek Lambertif674aa02019-08-01 15:56:25 +0100691 return EdgeStrategy::DirectCompatibility;
Derek Lamberti84da38b2019-06-13 11:40:08 +0100692 }
693 }
694
695 // Search for export/import options
696 ITensorHandleFactory* srcFactory = registry.GetFactory(srcFactoryId);
Derek Lambertif674aa02019-08-01 15:56:25 +0100697 if (srcFactory->GetExportFlags() != 0)
Derek Lamberti84da38b2019-06-13 11:40:08 +0100698 {
699 for (auto&& pref : dstPrefs)
700 {
701 ITensorHandleFactory* dstFactory = registry.GetFactory(pref);
James Conroyffab16f2019-11-07 14:37:09 +0000702
703 // Handles some cases where dstFactory is null when Neon memory import is disabled
704 if (!dstFactory) {
705 return EdgeStrategy::CopyToTarget;
706 }
707
Derek Lambertif674aa02019-08-01 15:56:25 +0100708 if ((dstFactory->GetImportFlags() & srcFactory->GetExportFlags()) != 0)
Derek Lamberti84da38b2019-06-13 11:40:08 +0100709 {
Derek Lambertif674aa02019-08-01 15:56:25 +0100710 return EdgeStrategy::ExportToTarget;
Derek Lamberti84da38b2019-06-13 11:40:08 +0100711 }
712 }
713 }
714
715 // Search for copy options via map/unmap
716 if (srcFactory->SupportsMapUnmap())
717 {
718 for (auto&& pref : dstPrefs)
719 {
720 ITensorHandleFactory* dstFactory = registry.GetFactory(pref);
721 if (dstFactory->SupportsMapUnmap())
722 {
Derek Lambertif674aa02019-08-01 15:56:25 +0100723 return EdgeStrategy::CopyToTarget;
Derek Lamberti84da38b2019-06-13 11:40:08 +0100724 }
725 }
726 }
727
Derek Lambertif674aa02019-08-01 15:56:25 +0100728 return EdgeStrategy::Undefined;
Derek Lamberti84da38b2019-06-13 11:40:08 +0100729}
730
731// Select the TensorHandleFactories and the corresponding memory strategy
732OptimizationResult SelectTensorHandleStrategy(Graph& optGraph,
733 BackendsMap& backends,
734 TensorHandleFactoryRegistry& registry,
735 Optional<std::vector<std::string>&> errMessages)
736{
737 OptimizationResult result;
738
739 optGraph.ForEachLayer([&backends, &registry, &result, &errMessages](Layer* layer)
740 {
741 BOOST_ASSERT(layer);
742
743 // Lets make sure the backend is in our list of supported backends. Something went wrong during backend
744 // assignment if this check fails
745 BOOST_ASSERT(backends.find(layer->GetBackendId()) != backends.end());
746
747 // Check each output separately
748 for (unsigned int slotIdx = 0; slotIdx < layer->GetNumOutputSlots(); slotIdx++)
749 {
750 OutputSlot& outputSlot = layer->GetOutputSlot(slotIdx);
751
752 ITensorHandleFactory::FactoryId slotOption = ITensorHandleFactory::LegacyFactoryId;
753
754 // Calculate the factory to use which results in the fewest copies being made.
755 switch(layer->GetType())
756 {
757 case LayerType::Input:
758 slotOption = CalculateSlotOptionForInput(backends, outputSlot, registry);
759 break;
760 case LayerType::Output:
761 slotOption = CalculateSlotOptionForOutput(backends, outputSlot, registry);
762 break;
763 default:
764 slotOption = CalculateSlotOption(backends, outputSlot, registry);
765 break;
766 }
767 outputSlot.SetTensorHandleFactory(slotOption);
768
Derek Lambertif674aa02019-08-01 15:56:25 +0100769 // Now determine the "best" edge strategy for each connection given the slotOption.
Derek Lamberti84da38b2019-06-13 11:40:08 +0100770 unsigned int connectionIdx = 0;
771 for (auto&& connection : outputSlot.GetConnections())
772 {
773 const Layer& connectedLayer = connection->GetOwningLayer();
774
Derek Lambertif674aa02019-08-01 15:56:25 +0100775 EdgeStrategy strategy = CalculateEdgeStrategy(backends, slotOption, *layer, connectedLayer, registry);
Derek Lamberti84da38b2019-06-13 11:40:08 +0100776
Derek Lambertif674aa02019-08-01 15:56:25 +0100777 if (strategy == EdgeStrategy::Undefined)
Derek Lamberti84da38b2019-06-13 11:40:08 +0100778 {
779 result.m_Error = true;
780 if (errMessages)
781 {
782 errMessages.value().emplace_back("Could not find valid strategy required for compatibility"
783 " between backends.");
784 }
785 return;
786 }
787
Derek Lambertif674aa02019-08-01 15:56:25 +0100788 outputSlot.SetEdgeStrategy(connectionIdx, strategy);
Derek Lamberti84da38b2019-06-13 11:40:08 +0100789
790 connectionIdx++;
791 }
792 }
793 });
794
795 return result;
796}
797
Matteo Martincigh49124022019-01-11 13:25:59 +0000798IOptimizedNetworkPtr Optimize(const INetwork& inNetwork,
799 const std::vector<BackendId>& backendPreferences,
800 const IDeviceSpec& deviceSpec,
801 const OptimizerOptions& options,
Rob Hughes23214432019-11-05 11:27:36 +0000802 Optional<std::vector<std::string>&> messages)
Matteo Martincigh49124022019-01-11 13:25:59 +0000803{
804 if (backendPreferences.empty())
805 {
806 throw armnn::InvalidArgumentException("Invoked Optimize with no backends specified");
807 }
808
809 const Network& network = *boost::polymorphic_downcast<const Network*>(&inNetwork);
810 std::unique_ptr<Graph> graph = std::make_unique<Graph>(network.GetGraph());
811
812 auto optNet = IOptimizedNetworkPtr(new OptimizedNetwork(std::move(graph)), &IOptimizedNetwork::Destroy);
813
814 OptimizedNetwork* optNetObjPtr = boost::polymorphic_downcast<OptimizedNetwork*>(optNet.get());
815
Matteo Martincighadddddb2019-01-24 14:06:23 +0000816 // Get the optimized graph
817 Graph& optGraph = optNetObjPtr->GetGraph();
818
Matteo Martincigh49124022019-01-11 13:25:59 +0000819 // Perform optimisation passes
820 using namespace optimizations;
Matteo Martincighadddddb2019-01-24 14:06:23 +0000821 Optimizer::Pass(optGraph, MakeOptimizations(SquashEqualPermuteSiblings(),
822 SquashEqualReshapeSiblings(),
823 OptimizeInversePermutes(),
824 MovePermuteUp(),
825 PermuteAsReshape(),
Nina Drozd861985f2019-04-18 14:48:51 +0100826 OptimizeConsecutiveReshapes(),
Rob Hughes3a7d3a72019-09-24 16:59:56 +0100827 FoldPadIntoConvolution2d(),
828 PermuteAndBatchToSpaceAsDepthToSpace()));
Matteo Martincigh49124022019-01-11 13:25:59 +0000829
Matteo Martincighadddddb2019-01-24 14:06:23 +0000830 // Infer the tensor infos for all output slots. Throws an exception on failure
831 optGraph.InferTensorInfos();
Matteo Martincigh49124022019-01-11 13:25:59 +0000832
833 // If Fp32 to Fp16 optimization is set convert Fp32 network to Fp16
834 if (options.m_ReduceFp32ToFp16)
835 {
Matteo Martincighadddddb2019-01-24 14:06:23 +0000836 Optimizer::Pass(optGraph, MakeOptimizations(Fp32NetworkToFp16Converter()));
Matteo Martincigh49124022019-01-11 13:25:59 +0000837 }
838
839 // Initialize backend settings
840 BackendSettings backendSettings(backendPreferences, deviceSpec);
841 if (backendSettings.GetAvailablePreferredBackends().empty())
842 {
843 std::stringstream failureMsg;
844 failureMsg << "None of the preferred backends " << backendPreferences
845 << " are supported. Current platform provides " << backendSettings.m_SupportedBackends;
Rob Hughes23214432019-11-05 11:27:36 +0000846 ReportError(failureMsg.str(), messages);
Matteo Martincigh49124022019-01-11 13:25:59 +0000847 return IOptimizedNetworkPtr(nullptr, &IOptimizedNetwork::Destroy);
848 }
849
Derek Lamberti84da38b2019-06-13 11:40:08 +0100850 // Create a map to temporarily hold initialized backend objects
851 TensorHandleFactoryRegistry tensorHandleFactoryRegistry;
852 BackendsMap backends = CreateSupportedBackends(tensorHandleFactoryRegistry, backendSettings);
853
Matteo Martincigh49124022019-01-11 13:25:59 +0000854 // Assign an available backend to each layer
Matteo Martincighadddddb2019-01-24 14:06:23 +0000855 Graph::Iterator firstLayer = optGraph.begin();
856 Graph::Iterator lastLayer = optGraph.end();
Derek Lamberti84da38b2019-06-13 11:40:08 +0100857 OptimizationResult assignBackendsResult = AssignBackends(optNetObjPtr,
858 backendSettings,
859 firstLayer,
860 lastLayer,
Rob Hughes23214432019-11-05 11:27:36 +0000861 messages);
Derek Lamberti84da38b2019-06-13 11:40:08 +0100862 if (assignBackendsResult.m_Error)
Matteo Martincigh49124022019-01-11 13:25:59 +0000863 {
864 // Failed to assign a backend to each layer
jimfly016b0b53d2018-10-08 14:43:01 +0100865 return IOptimizedNetworkPtr(nullptr, &IOptimizedNetwork::Destroy);
866 }
telsoa01c577f2c2018-08-31 09:22:23 +0100867
Matteo Martincighadddddb2019-01-24 14:06:23 +0000868 Optimizer::Pass(optGraph, MakeOptimizations(OptimizeInverseConversionsFp16(),
869 OptimizeInverseConversionsFp32()));
telsoa01c577f2c2018-08-31 09:22:23 +0100870
Matteo Martincighadddddb2019-01-24 14:06:23 +0000871 // Apply the backend-specific optimizations
872 OptimizationResult backendOptimizationResult = ApplyBackendOptimizations(optNetObjPtr,
873 backendSettings,
Derek Lamberti84da38b2019-06-13 11:40:08 +0100874 backends,
Rob Hughes23214432019-11-05 11:27:36 +0000875 messages);
Matteo Martincighadddddb2019-01-24 14:06:23 +0000876 if (backendOptimizationResult.m_Error)
Matteo Martincigh49124022019-01-11 13:25:59 +0000877 {
Matteo Martincighadddddb2019-01-24 14:06:23 +0000878 // Failed to apply the backend-specific optimizations
879 return IOptimizedNetworkPtr(nullptr, &IOptimizedNetwork::Destroy);
Matteo Martincigh49124022019-01-11 13:25:59 +0000880 }
881
Matteo Martincighadddddb2019-01-24 14:06:23 +0000882 // If the debug flag is set, then insert a DebugLayer after each layer
883 // Doing this after applying the backend optimizations as they might have changed some layers
884 if (options.m_Debug)
885 {
886 Optimizer::Pass(optGraph, MakeOptimizations(InsertDebugLayer()));
887 }
888
Derek Lamberti84da38b2019-06-13 11:40:08 +0100889 // Calculate the compatibility strategies for tensor handles
890 OptimizationResult strategyResult = SelectTensorHandleStrategy(optGraph,
891 backends,
892 tensorHandleFactoryRegistry,
Rob Hughes23214432019-11-05 11:27:36 +0000893 messages);
Derek Lamberti84da38b2019-06-13 11:40:08 +0100894 if (strategyResult.m_Error)
895 {
896 // Failed to apply the backend-specific optimizations
897 return IOptimizedNetworkPtr(nullptr, &IOptimizedNetwork::Destroy);
898 }
899
900 // Based on the tensor handle strategy determined above, insert copy layers where required.
Derek Lambertif674aa02019-08-01 15:56:25 +0100901 optGraph.AddCompatibilityLayers(backends, tensorHandleFactoryRegistry);
telsoa01c577f2c2018-08-31 09:22:23 +0100902
903 // Convert constants
Matteo Martincighadddddb2019-01-24 14:06:23 +0000904 Optimizer::Pass(optGraph, MakeOptimizations(ConvertConstantsFloatToHalf()));
905 Optimizer::Pass(optGraph, MakeOptimizations(ConvertConstantsHalfToFloat()));
telsoa01c577f2c2018-08-31 09:22:23 +0100906
Derek Lamberti84da38b2019-06-13 11:40:08 +0100907 // Run backend specific optimizations (deprecated)
Matteo Martincigh49124022019-01-11 13:25:59 +0000908 for (auto&& chosenBackend : backendSettings.m_SelectedBackends)
David Beck263e3492018-11-09 14:46:40 +0000909 {
910 auto factoryFun = BackendRegistryInstance().GetFactory(chosenBackend);
911 auto backendPtr = factoryFun();
912 BOOST_ASSERT(backendPtr.get() != nullptr);
913
Matteo Martincighed735042019-05-22 09:42:43 +0100914 ARMNN_NO_DEPRECATE_WARN_BEGIN
David Beck263e3492018-11-09 14:46:40 +0000915 auto backendSpecificOptimizations = backendPtr->GetOptimizations();
Matteo Martincighed735042019-05-22 09:42:43 +0100916 ARMNN_NO_DEPRECATE_WARN_END
917
David Beck263e3492018-11-09 14:46:40 +0000918 if (!backendSpecificOptimizations.empty())
919 {
920 Optimizer::Pass(optNetObjPtr->GetGraph(), backendSpecificOptimizations);
921 }
922 }
923
telsoa01c577f2c2018-08-31 09:22:23 +0100924 return optNet;
telsoa014fcda012018-03-09 14:13:49 +0000925}
926
927Network::Network()
928: m_Graph(std::make_unique<Graph>())
929{
930}
931
932Network::~Network()
933{
934}
935
936IConnectableLayer* Network::AddInputLayer(LayerBindingId id, const char* name)
937{
938 return m_Graph->AddLayer<InputLayer>(id, name);
939}
940
Éanna Ó Catháin4e1e1362018-11-12 11:36:34 +0000941IConnectableLayer* Network::AddBatchToSpaceNdLayer(const BatchToSpaceNdDescriptor& batchToSpaceNdDescriptor,
942 const char* name)
943{
944 return m_Graph->AddLayer<BatchToSpaceNdLayer>(batchToSpaceNdDescriptor, name);
945}
946
Aron Virginas-Tar77bfb5e2019-10-16 17:45:38 +0100947IConnectableLayer* Network::AddComparisonLayer(const ComparisonDescriptor& comparisonDescriptor,
948 const char* name)
949{
950 return m_Graph->AddLayer<ComparisonLayer>(comparisonDescriptor, name);
951}
952
telsoa014fcda012018-03-09 14:13:49 +0000953IConnectableLayer* Network::AddFullyConnectedLayerImpl(const FullyConnectedDescriptor& fullyConnectedDescriptor,
telsoa01c577f2c2018-08-31 09:22:23 +0100954 const ConstTensor& weights,
Aron Virginas-Tarad402702019-02-22 17:03:44 +0000955 const Optional<ConstTensor>& biases,
telsoa01c577f2c2018-08-31 09:22:23 +0100956 const char* name)
telsoa014fcda012018-03-09 14:13:49 +0000957{
Aron Virginas-Tarad402702019-02-22 17:03:44 +0000958 if (fullyConnectedDescriptor.m_BiasEnabled && !biases.has_value())
telsoa014fcda012018-03-09 14:13:49 +0000959 {
Aron Virginas-Tarad402702019-02-22 17:03:44 +0000960 throw InvalidArgumentException("AddFullyConnectedLayer: biases cannot be empty");
telsoa014fcda012018-03-09 14:13:49 +0000961 }
962
963 const auto layer = m_Graph->AddLayer<FullyConnectedLayer>(fullyConnectedDescriptor, name);
964
965 layer->m_Weight = std::make_unique<ScopedCpuTensorHandle>(weights);
966
967 if (fullyConnectedDescriptor.m_BiasEnabled)
968 {
Aron Virginas-Tarad402702019-02-22 17:03:44 +0000969 layer->m_Bias = std::make_unique<ScopedCpuTensorHandle>(biases.value());
telsoa014fcda012018-03-09 14:13:49 +0000970 }
971
972 return layer;
973}
974
975IConnectableLayer* Network::AddFullyConnectedLayer(const FullyConnectedDescriptor& fullyConnectedDescriptor,
telsoa01c577f2c2018-08-31 09:22:23 +0100976 const ConstTensor& weights,
Aron Virginas-Tarad402702019-02-22 17:03:44 +0000977 const Optional<ConstTensor>& biases,
telsoa01c577f2c2018-08-31 09:22:23 +0100978 const char* name)
telsoa014fcda012018-03-09 14:13:49 +0000979{
Aron Virginas-Tarad402702019-02-22 17:03:44 +0000980 return AddFullyConnectedLayerImpl(fullyConnectedDescriptor, weights, biases, name);
telsoa014fcda012018-03-09 14:13:49 +0000981}
982
Aron Virginas-Tarad402702019-02-22 17:03:44 +0000983IConnectableLayer* Network::AddFullyConnectedLayer(const FullyConnectedDescriptor& fullyConnectedDescriptor,
984 const ConstTensor& weights,
985 const char* name)
986{
Matteo Martincighfc598e12019-05-14 10:36:13 +0100987 Optional<ConstTensor> biases;
Aron Virginas-Tarad402702019-02-22 17:03:44 +0000988 return AddFullyConnectedLayerImpl(fullyConnectedDescriptor, weights, biases, name);
989}
990
telsoa014fcda012018-03-09 14:13:49 +0000991IConnectableLayer* Network::AddFullyConnectedLayer(const FullyConnectedDescriptor& fullyConnectedDescriptor,
telsoa01c577f2c2018-08-31 09:22:23 +0100992 const ConstTensor& weights,
993 const ConstTensor& biases,
994 const char* name)
telsoa014fcda012018-03-09 14:13:49 +0000995{
Aron Virginas-Tarad402702019-02-22 17:03:44 +0000996 Optional<ConstTensor> optionalBiases(biases);
997 return AddFullyConnectedLayerImpl(fullyConnectedDescriptor, weights, optionalBiases, name);
telsoa014fcda012018-03-09 14:13:49 +0000998}
999
Jim Flynne242f2d2019-05-22 14:24:13 +01001000IConnectableLayer* Network::AddConcatLayer(const ConcatDescriptor& concatDescriptor,
Jim Flynn906f9462019-05-10 13:55:21 +01001001 const char* name)
1002{
Jim Flynne242f2d2019-05-22 14:24:13 +01001003 return m_Graph->AddLayer<ConcatLayer>(concatDescriptor, name);
Jim Flynn906f9462019-05-10 13:55:21 +01001004}
1005
telsoa014fcda012018-03-09 14:13:49 +00001006IConnectableLayer* Network::AddConvolution2dLayerImpl(const Convolution2dDescriptor& convolution2dDescriptor,
telsoa01c577f2c2018-08-31 09:22:23 +01001007 const ConstTensor& weights,
Aron Virginas-Tarad402702019-02-22 17:03:44 +00001008 const Optional<ConstTensor>& biases,
telsoa01c577f2c2018-08-31 09:22:23 +01001009 const char* name)
telsoa014fcda012018-03-09 14:13:49 +00001010{
Aron Virginas-Tarad402702019-02-22 17:03:44 +00001011 if (convolution2dDescriptor.m_BiasEnabled && !biases.has_value())
telsoa014fcda012018-03-09 14:13:49 +00001012 {
Aron Virginas-Tarad402702019-02-22 17:03:44 +00001013 throw InvalidArgumentException("AddConvolution2dLayer: biases cannot be empty");
telsoa014fcda012018-03-09 14:13:49 +00001014 }
1015
1016 const auto layer = m_Graph->AddLayer<Convolution2dLayer>(convolution2dDescriptor, name);
1017
1018 layer->m_Weight = std::make_unique<ScopedCpuTensorHandle>(weights);
1019
1020 if (convolution2dDescriptor.m_BiasEnabled)
1021 {
Aron Virginas-Tarad402702019-02-22 17:03:44 +00001022 layer->m_Bias = std::make_unique<ScopedCpuTensorHandle>(biases.value());
telsoa014fcda012018-03-09 14:13:49 +00001023 }
1024
1025 return layer;
1026}
1027
1028IConnectableLayer* Network::AddConvolution2dLayer(const Convolution2dDescriptor& convolution2dDescriptor,
telsoa01c577f2c2018-08-31 09:22:23 +01001029 const ConstTensor& weights,
Aron Virginas-Tarad402702019-02-22 17:03:44 +00001030 const Optional<ConstTensor>& biases,
telsoa01c577f2c2018-08-31 09:22:23 +01001031 const char* name)
telsoa014fcda012018-03-09 14:13:49 +00001032{
Aron Virginas-Tarad402702019-02-22 17:03:44 +00001033 return AddConvolution2dLayerImpl(convolution2dDescriptor, weights, biases, name);
telsoa014fcda012018-03-09 14:13:49 +00001034}
Aron Virginas-Tarad402702019-02-22 17:03:44 +00001035
Aron Virginas-Tarad402702019-02-22 17:03:44 +00001036IConnectableLayer* Network::AddConvolution2dLayer(const Convolution2dDescriptor& convolution2dDescriptor,
1037 const ConstTensor& weights,
1038 const char* name)
1039{
Matteo Martincighfc598e12019-05-14 10:36:13 +01001040 Optional<ConstTensor> biases;
Aron Virginas-Tarad402702019-02-22 17:03:44 +00001041 return AddConvolution2dLayerImpl(convolution2dDescriptor, weights, biases, name);
1042}
1043
telsoa014fcda012018-03-09 14:13:49 +00001044IConnectableLayer* Network::AddConvolution2dLayer(const Convolution2dDescriptor& convolution2dDescriptor,
telsoa01c577f2c2018-08-31 09:22:23 +01001045 const ConstTensor& weights,
1046 const ConstTensor& biases,
1047 const char* name)
telsoa014fcda012018-03-09 14:13:49 +00001048{
Aron Virginas-Tarad402702019-02-22 17:03:44 +00001049 Optional<ConstTensor> optionalBiases(biases);
1050 return AddConvolution2dLayerImpl(convolution2dDescriptor, weights, optionalBiases, name);
telsoa014fcda012018-03-09 14:13:49 +00001051}
1052
1053IConnectableLayer* Network::AddDepthwiseConvolution2dLayerImpl(
1054 const DepthwiseConvolution2dDescriptor& convolution2dDescriptor,
1055 const ConstTensor& weights,
Aron Virginas-Tarad402702019-02-22 17:03:44 +00001056 const Optional<ConstTensor>& biases,
telsoa014fcda012018-03-09 14:13:49 +00001057 const char* name)
1058{
Aron Virginas-Tarad402702019-02-22 17:03:44 +00001059 if (convolution2dDescriptor.m_BiasEnabled && !biases.has_value())
telsoa014fcda012018-03-09 14:13:49 +00001060 {
Aron Virginas-Tarad402702019-02-22 17:03:44 +00001061 throw InvalidArgumentException("AddDepthwiseConvolution2dLayer: biases cannot be empty");
telsoa014fcda012018-03-09 14:13:49 +00001062 }
1063
Matteo Martincigh3d6898c2019-01-15 16:11:44 +00001064 const auto layer = m_Graph->AddLayer<DepthwiseConvolution2dLayer>(convolution2dDescriptor, name);
telsoa014fcda012018-03-09 14:13:49 +00001065
1066 layer->m_Weight = std::make_unique<ScopedCpuTensorHandle>(weights);
1067
1068 if (convolution2dDescriptor.m_BiasEnabled)
1069 {
Aron Virginas-Tarad402702019-02-22 17:03:44 +00001070 layer->m_Bias = std::make_unique<ScopedCpuTensorHandle>(biases.value());
telsoa014fcda012018-03-09 14:13:49 +00001071 }
1072
1073 return layer;
1074}
1075
Aron Virginas-Tardd6247f2019-09-19 14:31:17 +01001076IConnectableLayer* Network::AddDepthToSpaceLayer(const DepthToSpaceDescriptor& depthToSpaceDescriptor,
1077 const char* name)
1078{
1079 return m_Graph->AddLayer<DepthToSpaceLayer>(depthToSpaceDescriptor, name);
1080}
1081
telsoa014fcda012018-03-09 14:13:49 +00001082IConnectableLayer* Network::AddDepthwiseConvolution2dLayer(
Aron Virginas-Tarad402702019-02-22 17:03:44 +00001083 const DepthwiseConvolution2dDescriptor& convolution2dDescriptor,
1084 const ConstTensor& weights,
1085 const Optional<ConstTensor>& biases,
1086 const char* name)
1087{
1088 return AddDepthwiseConvolution2dLayerImpl(convolution2dDescriptor, weights, biases, name);
1089}
1090
Aron Virginas-Tarad402702019-02-22 17:03:44 +00001091IConnectableLayer* Network::AddDepthwiseConvolution2dLayer(
telsoa014fcda012018-03-09 14:13:49 +00001092 const DepthwiseConvolution2dDescriptor& convolution2dDescriptor,
1093 const ConstTensor& weights,
1094 const char* name)
1095{
Matteo Martincighfc598e12019-05-14 10:36:13 +01001096 Optional<ConstTensor> biases;
Aron Virginas-Tarad402702019-02-22 17:03:44 +00001097 return AddDepthwiseConvolution2dLayerImpl(convolution2dDescriptor, weights, biases, name);
telsoa014fcda012018-03-09 14:13:49 +00001098}
Aron Virginas-Tarad402702019-02-22 17:03:44 +00001099
telsoa014fcda012018-03-09 14:13:49 +00001100IConnectableLayer* Network::AddDepthwiseConvolution2dLayer(
1101 const DepthwiseConvolution2dDescriptor& convolution2dDescriptor,
1102 const ConstTensor& weights,
1103 const ConstTensor& biases,
1104 const char* name)
1105{
Aron Virginas-Tarad402702019-02-22 17:03:44 +00001106 Optional<ConstTensor> optionalBiases(biases);
1107 return AddDepthwiseConvolution2dLayerImpl(convolution2dDescriptor, weights, optionalBiases, name);
telsoa014fcda012018-03-09 14:13:49 +00001108}
1109
Narumol Prangnawarat94dd5d82019-01-23 18:06:26 +00001110IConnectableLayer* Network::AddDetectionPostProcessLayer(const armnn::DetectionPostProcessDescriptor& descriptor,
Narumol Prangnawarat6d302bf2019-02-04 11:46:26 +00001111 const ConstTensor& anchors, const char* name)
Narumol Prangnawarat94dd5d82019-01-23 18:06:26 +00001112{
Narumol Prangnawarat6d302bf2019-02-04 11:46:26 +00001113 const auto layer = m_Graph->AddLayer<DetectionPostProcessLayer>(descriptor, name);
1114
1115 layer->m_Anchors = std::make_unique<ScopedCpuTensorHandle>(anchors);
1116
1117 return layer;
Narumol Prangnawarat94dd5d82019-01-23 18:06:26 +00001118}
1119
telsoa014fcda012018-03-09 14:13:49 +00001120IConnectableLayer* Network::AddPermuteLayer(const PermuteDescriptor& permuteDescriptor,
1121 const char* name)
1122{
1123 return m_Graph->AddLayer<PermuteLayer>(permuteDescriptor, name);
1124}
1125
1126IConnectableLayer* Network::AddPooling2dLayer(const Pooling2dDescriptor& pooling2dDescriptor,
1127 const char* name)
1128{
1129 return m_Graph->AddLayer<Pooling2dLayer>(pooling2dDescriptor, name);
1130}
1131
1132IConnectableLayer* Network::AddActivationLayer(const ActivationDescriptor& activationDescriptor,
1133 const char* name)
1134{
1135 return m_Graph->AddLayer<ActivationLayer>(activationDescriptor, name);
1136}
1137
Nikhil Rajee391d52019-09-05 17:50:44 +01001138IConnectableLayer* Network::AddArgMinMaxLayer(const ArgMinMaxDescriptor& argMinMaxDescriptor,
1139 const char* name)
1140{
1141 return m_Graph->AddLayer<ArgMinMaxLayer>(argMinMaxDescriptor, name);
1142}
1143
telsoa01c577f2c2018-08-31 09:22:23 +01001144IConnectableLayer* Network::AddNormalizationLayer(const NormalizationDescriptor&
1145normalizationDescriptor,
telsoa014fcda012018-03-09 14:13:49 +00001146 const char* name)
1147{
1148 return m_Graph->AddLayer<NormalizationLayer>(normalizationDescriptor, name);
1149}
1150
Aron Virginas-Tar636ab402019-09-16 14:27:45 +01001151IConnectableLayer* Network::AddSliceLayer(const SliceDescriptor& sliceDescriptor, const char* name)
1152{
1153 return m_Graph->AddLayer<SliceLayer>(sliceDescriptor, name);
1154}
1155
telsoa014fcda012018-03-09 14:13:49 +00001156IConnectableLayer* Network::AddSoftmaxLayer(const SoftmaxDescriptor& softmaxDescriptor,
1157 const char* name)
1158{
1159 return m_Graph->AddLayer<SoftmaxLayer>(softmaxDescriptor, name);
1160}
1161
1162IConnectableLayer* Network::AddSplitterLayer(const ViewsDescriptor& splitterDescriptor,
1163 const char* name)
1164{
1165 return m_Graph->AddLayer<SplitterLayer>(splitterDescriptor, name);
1166}
1167
Nattapat Chaimanowong5a4304a2018-11-28 10:44:37 +00001168IConnectableLayer* Network::AddMaximumLayer(const char* name)
1169{
1170 return m_Graph->AddLayer<MaximumLayer>(name);
1171}
1172
Éanna Ó Catháin20e58802018-12-04 10:29:06 +00001173IConnectableLayer* Network::AddMinimumLayer(const char* name)
1174{
1175 return m_Graph->AddLayer<MinimumLayer>(name);
1176}
1177
Jim Flynne242f2d2019-05-22 14:24:13 +01001178IConnectableLayer* Network::AddMergerLayer(const MergerDescriptor& mergerDescriptor,
Jim Flynn906f9462019-05-10 13:55:21 +01001179 const char* name)
telsoa014fcda012018-03-09 14:13:49 +00001180{
Jim Flynne242f2d2019-05-22 14:24:13 +01001181 return AddConcatLayer(mergerDescriptor, name);
telsoa014fcda012018-03-09 14:13:49 +00001182}
1183
Kevin May868eb142019-09-04 17:29:31 +01001184IConnectableLayer* Network::AddAbsLayer(const char * name)
1185{
1186 return m_Graph->AddLayer<AbsLayer>(name);
1187}
1188
telsoa014fcda012018-03-09 14:13:49 +00001189IConnectableLayer* Network::AddAdditionLayer(const char* name)
1190{
1191 return m_Graph->AddLayer<AdditionLayer>(name);
1192}
1193
1194IConnectableLayer* Network::AddMultiplicationLayer(const char* name)
1195{
1196 return m_Graph->AddLayer<MultiplicationLayer>(name);
1197}
1198
1199IConnectableLayer* Network::AddOutputLayer(LayerBindingId id, const char* name)
1200{
1201 return m_Graph->AddLayer<OutputLayer>(id, name);
1202}
1203
1204IConnectableLayer* Network::AddBatchNormalizationLayer(const BatchNormalizationDescriptor& desc,
1205 const ConstTensor& mean,
1206 const ConstTensor& variance,
1207 const ConstTensor& beta,
1208 const ConstTensor& gamma,
1209 const char* name)
1210{
1211 const auto layer = m_Graph->AddLayer<BatchNormalizationLayer>(desc, name);
1212
1213 layer->m_Mean = std::make_unique<ScopedCpuTensorHandle>(mean);
1214 layer->m_Variance = std::make_unique<ScopedCpuTensorHandle>(variance);
1215 layer->m_Beta = std::make_unique<ScopedCpuTensorHandle>(beta);
1216 layer->m_Gamma = std::make_unique<ScopedCpuTensorHandle>(gamma);
1217
1218 return layer;
1219}
1220
Aron Virginas-Tar169d2f12019-07-01 19:01:44 +01001221IConnectableLayer* Network::AddResizeBilinearLayer(const ResizeBilinearDescriptor& descriptor,
1222 const char* name)
telsoa014fcda012018-03-09 14:13:49 +00001223{
Aron Virginas-Tar169d2f12019-07-01 19:01:44 +01001224 ResizeDescriptor resizeDescriptor;
1225 resizeDescriptor.m_Method = ResizeMethod::Bilinear;
1226 resizeDescriptor.m_DataLayout = descriptor.m_DataLayout;
1227 resizeDescriptor.m_TargetWidth = descriptor.m_TargetWidth;
1228 resizeDescriptor.m_TargetHeight = descriptor.m_TargetHeight;
1229
1230 return m_Graph->AddLayer<ResizeLayer>(resizeDescriptor, name);
telsoa014fcda012018-03-09 14:13:49 +00001231}
1232
Teresa Charlina9075df2019-06-27 15:41:57 +01001233IConnectableLayer* Network::AddResizeLayer(const ResizeDescriptor&
1234resizeDescriptor, const char* name)
1235{
Aron Virginas-Tar169d2f12019-07-01 19:01:44 +01001236 return m_Graph->AddLayer<ResizeLayer>(resizeDescriptor, name);
Teresa Charlina9075df2019-06-27 15:41:57 +01001237}
1238
Kevin Mayce5045a2019-10-02 14:07:47 +01001239IConnectableLayer* Network::AddInstanceNormalizationLayer(const InstanceNormalizationDescriptor& desc,
1240 const char* name)
1241{
1242 return m_Graph->AddLayer<InstanceNormalizationLayer>(desc, name);
1243}
1244
Matteo Martincighbcd3c852018-09-28 14:14:12 +01001245IConnectableLayer* Network::AddL2NormalizationLayer(const L2NormalizationDescriptor& desc,
1246 const char* name)
telsoa014fcda012018-03-09 14:13:49 +00001247{
Matteo Martincighbcd3c852018-09-28 14:14:12 +01001248 return m_Graph->AddLayer<L2NormalizationLayer>(desc, name);
telsoa014fcda012018-03-09 14:13:49 +00001249}
1250
Aron Virginas-Tarf982dea2019-10-11 14:07:53 +01001251IConnectableLayer* Network::AddLogSoftmaxLayer(const LogSoftmaxDescriptor& desc,
1252 const char* name)
1253{
1254 return m_Graph->AddLayer<LogSoftmaxLayer>(desc, name);
1255}
1256
telsoa014fcda012018-03-09 14:13:49 +00001257IConnectableLayer* Network::AddConstantLayer(const ConstTensor& input, const char* name)
1258{
telsoa01c577f2c2018-08-31 09:22:23 +01001259 auto layer = m_Graph->AddLayer<ConstantLayer>(name);
1260
1261 layer->m_LayerOutput = std::make_unique<ScopedCpuTensorHandle>(input);
1262
1263 return layer;
telsoa014fcda012018-03-09 14:13:49 +00001264}
1265
telsoa01c577f2c2018-08-31 09:22:23 +01001266IConnectableLayer* Network::AddReshapeLayer(const ReshapeDescriptor& reshapeDescriptor,
1267 const char* name)
telsoa014fcda012018-03-09 14:13:49 +00001268{
1269 return m_Graph->AddLayer<ReshapeLayer>(reshapeDescriptor, name);
1270}
1271
Nattapat Chaimanowong207ef9a2018-11-02 10:57:25 +00001272IConnectableLayer* Network::AddSpaceToBatchNdLayer(const SpaceToBatchNdDescriptor& spaceToBatchNdDescriptor,
1273 const char* name)
1274{
1275 return m_Graph->AddLayer<SpaceToBatchNdLayer>(spaceToBatchNdDescriptor, name);
1276}
1277
Aron Virginas-Tar972af152019-06-11 14:14:03 +01001278IConnectableLayer* Network::AddSpaceToDepthLayer(const SpaceToDepthDescriptor& spaceToDepthDescriptor,
1279 const char* name)
1280{
1281 return m_Graph->AddLayer<SpaceToDepthLayer>(spaceToDepthDescriptor, name);
1282}
1283
telsoa014fcda012018-03-09 14:13:49 +00001284IConnectableLayer* Network::AddFloorLayer(const char* name)
1285{
1286 return m_Graph->AddLayer<FloorLayer>(name);
1287}
1288
telsoa01c577f2c2018-08-31 09:22:23 +01001289IConnectableLayer* Network::AddLstmLayer(const LstmDescriptor& descriptor,
1290 const LstmInputParams& params,
1291 const char* name)
1292{
1293 const auto layer = m_Graph->AddLayer<LstmLayer>(descriptor, name);
1294
1295 //Lstm Basic Parameters
1296 layer->m_BasicParameters.m_InputToForgetWeights =
1297 std::make_unique<ScopedCpuTensorHandle>(*(params.m_InputToForgetWeights));
1298 layer->m_BasicParameters.m_InputToCellWeights =
1299 std::make_unique<ScopedCpuTensorHandle>(*(params.m_InputToCellWeights));
1300 layer->m_BasicParameters.m_InputToOutputWeights =
1301 std::make_unique<ScopedCpuTensorHandle>(*(params.m_InputToOutputWeights));
1302 layer->m_BasicParameters.m_RecurrentToForgetWeights =
1303 std::make_unique<ScopedCpuTensorHandle>(*(params.m_RecurrentToForgetWeights));
1304 layer->m_BasicParameters.m_RecurrentToCellWeights =
1305 std::make_unique<ScopedCpuTensorHandle>(*(params.m_RecurrentToCellWeights));
1306 layer->m_BasicParameters.m_RecurrentToOutputWeights =
1307 std::make_unique<ScopedCpuTensorHandle>(*(params.m_RecurrentToOutputWeights));
1308 layer->m_BasicParameters.m_ForgetGateBias =
1309 std::make_unique<ScopedCpuTensorHandle>(*(params.m_ForgetGateBias));
1310 layer->m_BasicParameters.m_CellBias =
1311 std::make_unique<ScopedCpuTensorHandle>(*(params.m_CellBias));
1312 layer->m_BasicParameters.m_OutputGateBias =
1313 std::make_unique<ScopedCpuTensorHandle>(*(params.m_OutputGateBias));
1314
1315 //Lstm Cifg parameters
1316 if(!descriptor.m_CifgEnabled)
1317 {
1318 if(params.m_InputToInputWeights == nullptr)
1319 {
1320 throw InvalidArgumentException("AddLstmLayer: Input To Input Weights cannot be NULL");
1321 }
1322 if(params.m_RecurrentToInputWeights == nullptr)
1323 {
1324 throw InvalidArgumentException(
1325 "AddLstmLayer: Recurrent To Input Weights cannot be NULL");
1326 }
1327 if(params.m_InputGateBias == nullptr)
1328 {
1329 throw InvalidArgumentException("AddLstmLayer: Input Gate Bias cannot be NULL");
1330 }
1331 layer->m_CifgParameters.m_InputToInputWeights =
1332 std::make_unique<ScopedCpuTensorHandle>(*(params.m_InputToInputWeights));
1333 layer->m_CifgParameters.m_RecurrentToInputWeights =
1334 std::make_unique<ScopedCpuTensorHandle>(*(params.m_RecurrentToInputWeights));
1335 // In the VTS tests, cell-to-input weights may be null, even if the other CIFG params are not.
1336 if(params.m_CellToInputWeights != nullptr)
1337 {
1338 layer->m_CifgParameters.m_CellToInputWeights =
1339 std::make_unique<ScopedCpuTensorHandle>(*(params.m_CellToInputWeights));
1340 }
1341 layer->m_CifgParameters.m_InputGateBias =
1342 std::make_unique<ScopedCpuTensorHandle>(*(params.m_InputGateBias));
1343 }
1344
1345 //Lstm projection parameters
1346 if(descriptor.m_ProjectionEnabled)
1347 {
1348 if(params.m_ProjectionWeights == nullptr)
1349 {
1350 throw InvalidArgumentException("AddLstmLayer: Projection Weights cannot be NULL");
1351 }
1352 layer->m_ProjectionParameters.m_ProjectionWeights =
1353 std::make_unique<ScopedCpuTensorHandle>(*(params.m_ProjectionWeights));
1354 if(params.m_ProjectionBias != nullptr)
1355 {
1356 layer->m_ProjectionParameters.m_ProjectionBias =
1357 std::make_unique<ScopedCpuTensorHandle>(*(params.m_ProjectionBias));
1358 }
1359 }
1360
1361 //Lstm Peephole params
1362 if(descriptor.m_PeepholeEnabled)
1363 {
1364 if(params.m_CellToForgetWeights == nullptr)
1365 {
1366 throw InvalidArgumentException("AddLstmLayer: Cell To Forget Weights cannot be NULL");
1367 }
1368 if(params.m_CellToOutputWeights == nullptr)
1369 {
1370 throw InvalidArgumentException("AddLstmLayer: Cell To Output Weights cannot be NULL");
1371 }
1372 layer->m_PeepholeParameters.m_CellToForgetWeights =
1373 std::make_unique<ScopedCpuTensorHandle>(*(params.m_CellToForgetWeights));
1374 layer->m_PeepholeParameters.m_CellToOutputWeights =
1375 std::make_unique<ScopedCpuTensorHandle>(*(params.m_CellToOutputWeights));
1376 }
Jan Eilersf8c62972019-07-17 11:07:49 +01001377
1378 //Lstm Layer Normalization params
1379 if(descriptor.m_LayerNormEnabled)
1380 {
1381 if(!descriptor.m_CifgEnabled)
1382 {
1383 if(params.m_InputLayerNormWeights == nullptr)
1384 {
1385 throw InvalidArgumentException("AddLstmLayer: Input layer normalization weights cannot be NULL");
1386 }
1387 layer->m_LayerNormParameters.m_InputLayerNormWeights =
1388 std::make_unique<ScopedCpuTensorHandle>(*(params.m_InputLayerNormWeights));
1389 }
1390
1391 if(params.m_ForgetLayerNormWeights == nullptr)
1392 {
1393 throw InvalidArgumentException("AddLstmLayer: Forget layer normalization weights cannot be NULL");
1394 }
1395 if(params.m_CellLayerNormWeights == nullptr)
1396 {
1397 throw InvalidArgumentException("AddLstmLayer: Cell layer normalization weights cannot be NULL");
1398 }
1399 if(params.m_OutputLayerNormWeights == nullptr)
1400 {
1401 throw InvalidArgumentException("AddLstmLayer: Output layer normalization weights cannot be NULL");
1402 }
1403 layer->m_LayerNormParameters.m_ForgetLayerNormWeights =
1404 std::make_unique<ScopedCpuTensorHandle>(*(params.m_ForgetLayerNormWeights));
1405 layer->m_LayerNormParameters.m_CellLayerNormWeights =
1406 std::make_unique<ScopedCpuTensorHandle>(*(params.m_CellLayerNormWeights));
1407 layer->m_LayerNormParameters.m_OutputLayerNormWeights =
1408 std::make_unique<ScopedCpuTensorHandle>(*(params.m_OutputLayerNormWeights));
1409 }
telsoa01c577f2c2018-08-31 09:22:23 +01001410 return layer;
1411}
1412
Francis Murtaghe7a86a42018-08-29 12:42:10 +01001413IConnectableLayer* Network::AddDivisionLayer(const char* name)
1414{
1415 return m_Graph->AddLayer<DivisionLayer>(name);
1416}
1417
David Beck19526222018-09-12 16:00:08 +01001418IConnectableLayer* Network::AddSubtractionLayer(const char* name)
1419{
1420 return m_Graph->AddLayer<SubtractionLayer>(name);
1421}
1422
narpra0132b90462018-09-13 11:07:48 +01001423IConnectableLayer* Network::AddMeanLayer(const MeanDescriptor& meanDescriptor, const char* name)
1424{
1425 return m_Graph->AddLayer<MeanLayer>(meanDescriptor,name);
1426}
1427
Mohamed Nour Abouelseoud5662c202018-09-24 13:30:09 +01001428IConnectableLayer* Network::AddPadLayer(const PadDescriptor& padDescriptor, const char* name)
1429{
1430 return m_Graph->AddLayer<PadLayer>(padDescriptor,name);
1431}
1432
Derek Lambertia9cca6a2019-03-25 15:41:58 +00001433IConnectableLayer *Network::AddQuantizeLayer(const char *name)
1434{
1435 return m_Graph->AddLayer<QuantizeLayer>(name);
1436}
1437
Nattapat Chaimanowonge4294fd2019-03-28 09:56:53 +00001438IConnectableLayer* Network::AddDequantizeLayer(const char* name)
1439{
1440 return m_Graph->AddLayer<DequantizeLayer>(name);
1441}
1442
Conor Kennedy430b5d82018-11-14 15:28:28 +00001443IConnectableLayer* Network::AddStridedSliceLayer(const StridedSliceDescriptor& stridedSliceDescriptor,
1444 const char* name)
1445{
1446 return m_Graph->AddLayer<StridedSliceLayer>(stridedSliceDescriptor, name);
1447}
1448
Matteo Martincigh59a950c2018-12-13 12:48:25 +00001449IConnectableLayer* Network::AddGreaterLayer(const char* name)
1450{
Aron Virginas-Tar77bfb5e2019-10-16 17:45:38 +01001451 return AddComparisonLayer(ComparisonDescriptor(ComparisonOperation::Greater), name);
Matteo Martincigh59a950c2018-12-13 12:48:25 +00001452}
1453
FrancisMurtagh20995952018-12-17 12:11:36 +00001454IConnectableLayer* Network::AddEqualLayer(const char* name)
1455{
Aron Virginas-Tar77bfb5e2019-10-16 17:45:38 +01001456 return AddComparisonLayer(ComparisonDescriptor(ComparisonOperation::Equal), name);
FrancisMurtagh20995952018-12-17 12:11:36 +00001457}
1458
Mohamed Nour Abouelseouda1d3c6a2018-12-27 12:39:16 +00001459IConnectableLayer* Network::AddRsqrtLayer(const char * name)
1460{
1461 return m_Graph->AddLayer<RsqrtLayer>(name);
1462}
1463
narpra01b89b05f2019-01-16 09:53:09 +00001464IConnectableLayer* Network::AddGatherLayer(const char* name)
1465{
1466 return m_Graph->AddLayer<GatherLayer>(name);
1467}
1468
Nattapat Chaimanowong1f886302019-04-05 13:37:19 +01001469IConnectableLayer* Network::AddMergeLayer(const char* name)
1470{
1471 return m_Graph->AddLayer<MergeLayer>(name);
1472}
1473
Sadik Armaganeff363d2019-04-05 15:25:46 +01001474IConnectableLayer* Network::AddSwitchLayer(const char* name)
1475{
1476 return m_Graph->AddLayer<SwitchLayer>(name);
1477}
1478
Matteo Martincigh0e406ee2019-06-12 15:42:18 +01001479IConnectableLayer* Network::AddPreluLayer(const char* name)
1480{
1481 return m_Graph->AddLayer<PreluLayer>(name);
1482}
1483
Aron Virginas-Tar639fb042019-06-20 14:28:19 +01001484IConnectableLayer* Network::AddTransposeConvolution2dLayer(const TransposeConvolution2dDescriptor& descriptor,
1485 const ConstTensor& weights,
1486 const Optional<ConstTensor>& biases,
1487 const char* name)
1488{
1489 if (descriptor.m_BiasEnabled && !biases.has_value())
1490 {
1491 throw InvalidArgumentException("AddTransposeConvolution2dLayer: Biases cannot be empty");
1492 }
1493
1494 const auto layer = m_Graph->AddLayer<TransposeConvolution2dLayer>(descriptor, name);
1495
1496 layer->m_Weight = std::make_unique<ScopedCpuTensorHandle>(weights);
1497
1498 if (descriptor.m_BiasEnabled)
1499 {
1500 layer->m_Bias = std::make_unique<ScopedCpuTensorHandle>(biases.value());
1501 }
1502
1503 return layer;
1504}
1505
Matthew Jackson2b8c1da2019-07-04 14:59:16 +01001506IConnectableLayer* Network::AddStackLayer(const StackDescriptor& stackDescriptor,
1507 const char* name)
1508{
1509 return m_Graph->AddLayer<StackLayer>(stackDescriptor, name);
1510}
1511
Derek Lamberti013c3902019-10-21 10:46:16 +01001512
1513IConnectableLayer* Network::AddStandInLayer(const StandInDescriptor& desc,
1514 const char* name)
1515{
1516 return m_Graph->AddLayer<StandInLayer>(desc, name);
1517}
1518
James Conroyee18dc82019-07-17 11:27:46 +01001519IConnectableLayer* Network::AddQuantizedLstmLayer(const QuantizedLstmInputParams& params,
1520 const char* name)
1521{
1522 const auto layer = m_Graph->AddLayer<QuantizedLstmLayer>(name);
1523
1524 // InputToX weights
1525 layer->m_QuantizedLstmParameters.m_InputToInputWeights =
Francis Murtaghbb590b42019-08-14 09:51:36 +01001526 std::make_unique<ScopedCpuTensorHandle>(params.GetInputToInputWeights());
James Conroyee18dc82019-07-17 11:27:46 +01001527 layer->m_QuantizedLstmParameters.m_InputToForgetWeights =
Francis Murtaghbb590b42019-08-14 09:51:36 +01001528 std::make_unique<ScopedCpuTensorHandle>(params.GetInputToForgetWeights());
James Conroyee18dc82019-07-17 11:27:46 +01001529 layer->m_QuantizedLstmParameters.m_InputToCellWeights =
Francis Murtaghbb590b42019-08-14 09:51:36 +01001530 std::make_unique<ScopedCpuTensorHandle>(params.GetInputToCellWeights());
James Conroyee18dc82019-07-17 11:27:46 +01001531 layer->m_QuantizedLstmParameters.m_InputToOutputWeights =
Francis Murtaghbb590b42019-08-14 09:51:36 +01001532 std::make_unique<ScopedCpuTensorHandle>(params.GetInputToOutputWeights());
James Conroyee18dc82019-07-17 11:27:46 +01001533
1534 // RecurrentToX weights
1535 layer->m_QuantizedLstmParameters.m_RecurrentToInputWeights =
Francis Murtaghbb590b42019-08-14 09:51:36 +01001536 std::make_unique<ScopedCpuTensorHandle>(params.GetRecurrentToInputWeights());
James Conroyee18dc82019-07-17 11:27:46 +01001537 layer->m_QuantizedLstmParameters.m_RecurrentToForgetWeights =
Francis Murtaghbb590b42019-08-14 09:51:36 +01001538 std::make_unique<ScopedCpuTensorHandle>(params.GetRecurrentToForgetWeights());
James Conroyee18dc82019-07-17 11:27:46 +01001539 layer->m_QuantizedLstmParameters.m_RecurrentToCellWeights =
Francis Murtaghbb590b42019-08-14 09:51:36 +01001540 std::make_unique<ScopedCpuTensorHandle>(params.GetRecurrentToCellWeights());
James Conroyee18dc82019-07-17 11:27:46 +01001541 layer->m_QuantizedLstmParameters.m_RecurrentToOutputWeights =
Francis Murtaghbb590b42019-08-14 09:51:36 +01001542 std::make_unique<ScopedCpuTensorHandle>(params.GetRecurrentToOutputWeights());
James Conroyee18dc82019-07-17 11:27:46 +01001543
1544 // Bias
1545 layer->m_QuantizedLstmParameters.m_InputGateBias =
Francis Murtaghbb590b42019-08-14 09:51:36 +01001546 std::make_unique<ScopedCpuTensorHandle>(params.GetInputGateBias());
James Conroyee18dc82019-07-17 11:27:46 +01001547 layer->m_QuantizedLstmParameters.m_ForgetGateBias =
Francis Murtaghbb590b42019-08-14 09:51:36 +01001548 std::make_unique<ScopedCpuTensorHandle>(params.GetForgetGateBias());
James Conroyee18dc82019-07-17 11:27:46 +01001549 layer->m_QuantizedLstmParameters.m_CellBias =
Francis Murtaghbb590b42019-08-14 09:51:36 +01001550 std::make_unique<ScopedCpuTensorHandle>(params.GetCellBias());
James Conroyee18dc82019-07-17 11:27:46 +01001551 layer->m_QuantizedLstmParameters.m_OutputGateBias =
Francis Murtaghbb590b42019-08-14 09:51:36 +01001552 std::make_unique<ScopedCpuTensorHandle>(params.GetOutputGateBias());
James Conroyee18dc82019-07-17 11:27:46 +01001553
1554 return layer;
1555}
1556
Mike Kelly8c1701a2019-02-11 17:01:27 +00001557void Network::Accept(ILayerVisitor& visitor) const
1558{
1559 for (auto layer : GetGraph())
1560 {
1561 layer->Accept(visitor);
1562 };
1563}
1564
telsoa014fcda012018-03-09 14:13:49 +00001565OptimizedNetwork::OptimizedNetwork(std::unique_ptr<Graph> graph)
1566 : m_Graph(std::move(graph))
1567{
1568}
1569
1570OptimizedNetwork::~OptimizedNetwork()
1571{
1572}
1573
1574} // namespace armnn