blob: 573f6a19e84cce2d4bf7f689b13a8dc927084894 [file] [log] [blame]
telsoa014fcda012018-03-09 14:13:49 +00001//
2// Copyright © 2017 Arm Ltd. All rights reserved.
David Beckecb56cd2018-09-05 12:52:57 +01003// SPDX-License-Identifier: MIT
telsoa014fcda012018-03-09 14:13:49 +00004//
Matteo Martincigh49124022019-01-11 13:25:59 +00005
telsoa014fcda012018-03-09 14:13:49 +00006#include "Network.hpp"
7#include "Graph.hpp"
8#include "Layer.hpp"
telsoa01c577f2c2018-08-31 09:22:23 +01009#include "DeviceSpec.hpp"
telsoa014fcda012018-03-09 14:13:49 +000010#include "Optimizer.hpp"
Derek Lambertiff05cc52019-04-26 13:05:17 +010011#include "SubgraphViewSelector.hpp"
Matteo Martincigh49124022019-01-11 13:25:59 +000012#include "BackendSettings.hpp"
David Beckac42efd2018-09-26 17:41:13 +010013#include "optimizations/All.hpp"
telsoa014fcda012018-03-09 14:13:49 +000014
Aron Virginas-Tarc9cc8042018-11-01 16:15:57 +000015#include <backendsCommon/CpuTensorHandle.hpp>
16#include <backendsCommon/WorkloadFactory.hpp>
David Beck263e3492018-11-09 14:46:40 +000017#include <backendsCommon/IBackendInternal.hpp>
Derek Lamberti84da38b2019-06-13 11:40:08 +010018#include <backendsCommon/TensorHandleFactoryRegistry.hpp>
David Beckac42efd2018-09-26 17:41:13 +010019
20#include <armnn/Exceptions.hpp>
telsoa014fcda012018-03-09 14:13:49 +000021#include <armnn/Utils.hpp>
telsoa01c577f2c2018-08-31 09:22:23 +010022#include <armnn/TypesUtils.hpp>
Matteo Martincighc601aa62019-10-29 15:03:22 +000023#include <armnn/BackendRegistry.hpp>
telsoa014fcda012018-03-09 14:13:49 +000024
Jan Eilers99d9d4a2019-11-06 10:02:16 +000025#include <ProfilingService.hpp>
26
telsoa014fcda012018-03-09 14:13:49 +000027#include <fcntl.h>
28#include <algorithm>
29#include <fstream>
30#include <memory>
telsoa01c577f2c2018-08-31 09:22:23 +010031#include <vector>
32#include <algorithm>
telsoa014fcda012018-03-09 14:13:49 +000033
34#include <boost/assert.hpp>
35#include <boost/format.hpp>
36#include <boost/log/trivial.hpp>
37#include <boost/numeric/conversion/converter_policies.hpp>
38#include <boost/cast.hpp>
39
40namespace armnn
41{
42
43armnn::INetwork* INetwork::CreateRaw()
44{
45 return new Network();
46}
47
48armnn::INetworkPtr INetwork::Create()
49{
50 return INetworkPtr(CreateRaw(), &INetwork::Destroy);
51}
52
53void INetwork::Destroy(INetwork* network)
54{
55 delete boost::polymorphic_downcast<Network*>(network);
56}
57
telsoa014fcda012018-03-09 14:13:49 +000058void IOptimizedNetwork::Destroy(IOptimizedNetwork* network)
59{
60 delete boost::polymorphic_downcast<OptimizedNetwork*>(network);
61}
62
63Status OptimizedNetwork::PrintGraph()
64{
65 m_Graph->Print();
66 return Status::Success;
67}
68
surmeh01bceff2f2018-03-29 16:29:27 +010069Status OptimizedNetwork::SerializeToDot(std::ostream& stream) const
70{
71 return m_Graph->SerializeToDot(stream);
72}
73
Matteo Martincigh49124022019-01-11 13:25:59 +000074
Matteo Martincigh49124022019-01-11 13:25:59 +000075
76void ReportError(const std::string& errorMessage,
77 Optional<std::vector<std::string>&> errorMessages)
78{
79 std::stringstream fullErrorMessage;
80 fullErrorMessage << "ERROR: " << errorMessage;
81 BOOST_LOG_TRIVIAL(warning) << fullErrorMessage.str();
82 if (errorMessages)
83 {
84 errorMessages.value().push_back(fullErrorMessage.str());
85 }
86}
87
88void ReportWarning(const std::string& warningMessage,
89 Optional<std::vector<std::string>&> warningMessages)
90{
91 std::stringstream fullWarningMessage;
92 fullWarningMessage << "WARNING: " << warningMessage;
93 BOOST_LOG_TRIVIAL(warning) << fullWarningMessage.str();
94 if (warningMessages)
95 {
96 warningMessages.value().push_back(fullWarningMessage.str());
97 }
98}
99
jimfly016b0b53d2018-10-08 14:43:01 +0100100bool CheckScaleSetOnQuantizedType(Layer* layer, Optional<std::vector<std::string>&> errMessages)
101{
102 bool noErrors = true;
103 unsigned int numOutputs = layer->GetNumOutputSlots();
104 for (unsigned int i = 0; i < numOutputs; i++) {
David Monahanb8554702019-04-25 16:03:38 +0100105 OutputSlot& outputSlot = layer->GetOutputSlot(i);
106 TensorInfo info = outputSlot.GetTensorInfo();
jimfly016b0b53d2018-10-08 14:43:01 +0100107 if (DataType::QuantisedAsymm8 == info.GetDataType()) {
108 if (0.f == info.GetQuantizationScale()) {
109 noErrors = false;
110 std::stringstream ss;
Matteo Martincigh49124022019-01-11 13:25:59 +0000111 ss << "output " << i << " of layer " << GetLayerTypeAsCString(layer->GetType())
jimfly016b0b53d2018-10-08 14:43:01 +0100112 << " (" << layer->GetNameStr() << ") is of type"
113 << " Quantized 8 bit but its scale parameter has not been set";
Matteo Martincigh49124022019-01-11 13:25:59 +0000114 ReportError(ss.str(), errMessages);
jimfly016b0b53d2018-10-08 14:43:01 +0100115 }
David Monahanb8554702019-04-25 16:03:38 +0100116 // Softmax under QuantisedAsymm8 must always be scale (1.0f/256.0f) and offset 0
117 if ((info.GetQuantizationScale() != (1.0f / 256.0f) ||
118 info.GetQuantizationOffset() != 0) &&
119 layer->GetType() == armnn::LayerType::Softmax)
120 {
121 std::stringstream ss;
122 ss << "Quantization parameters for Softmax layer (Scale: " <<
123 info.GetQuantizationScale() << " and Offset: " << info.GetQuantizationOffset() <<
124 ") are incorrect and have been updated to Scale: 0.00390625 and Offset: 0";
125 BOOST_LOG_TRIVIAL(warning) << ss.str();
126 info.SetQuantizationScale((1.0f /256.0f));
127 info.SetQuantizationOffset(0);
128 outputSlot.SetTensorInfo(info);
129 }
jimfly016b0b53d2018-10-08 14:43:01 +0100130 }
131 }
132 return noErrors;
133}
134
Matteo Martincigh49124022019-01-11 13:25:59 +0000135OptimizationResult AssignBackends(OptimizedNetwork* optNetObjPtr,
136 BackendSettings& backendSettings,
137 Graph::Iterator& firstLayer,
138 Graph::Iterator& lastLayer,
139 Optional<std::vector<std::string>&> errMessages)
telsoa014fcda012018-03-09 14:13:49 +0000140{
Matteo Martincigh49124022019-01-11 13:25:59 +0000141 OptimizationResult result;
telsoa014fcda012018-03-09 14:13:49 +0000142
Matteo Martincigh49124022019-01-11 13:25:59 +0000143 // Helper lambda to compose meaningful error message before returning with error
144 auto ReturnWithError = [&](const Layer* layer)
telsoa01c577f2c2018-08-31 09:22:23 +0100145 {
jimfly016b0b53d2018-10-08 14:43:01 +0100146 std::stringstream failureMsg;
Matteo Martincigh49124022019-01-11 13:25:59 +0000147 failureMsg << "Layer of type " << GetLayerTypeAsCString(layer->GetType())
148 << " is not supported on any preferred backend " << backendSettings.m_PreferredBackends;
149 ReportError(failureMsg.str(), errMessages);
150
151 result.m_Error = true;
152 return result;
telsoa01c577f2c2018-08-31 09:22:23 +0100153 };
154
Matteo Martincigh49124022019-01-11 13:25:59 +0000155 auto availablePreferredBackends = backendSettings.GetAvailablePreferredBackends();
156 if (availablePreferredBackends.empty())
telsoa01c577f2c2018-08-31 09:22:23 +0100157 {
Matteo Martincigh49124022019-01-11 13:25:59 +0000158 std::stringstream failureMsg;
159 failureMsg << "No preferred backends are available";
160 ReportError(failureMsg.str(), errMessages);
161
162 result.m_Error = true;
163 return result;
164 }
165
166 for (auto it = firstLayer; it != lastLayer; ++it)
167 {
168 auto layer = *it;
telsoa01c577f2c2018-08-31 09:22:23 +0100169 DataType dataType = layer->GetDataType();
170 std::string reasonIfUnsupported;
171 bool found = false;
jimfly016b0b53d2018-10-08 14:43:01 +0100172 if (!CheckScaleSetOnQuantizedType(layer, errMessages))
173 {
174 // don't bomb immediately, find all the quantized outputs
175 // which haven't had a scale set and report them all back.
Matteo Martincigh49124022019-01-11 13:25:59 +0000176 result.m_Error = true;
jimfly016b0b53d2018-10-08 14:43:01 +0100177 }
Matteo Martincigh49124022019-01-11 13:25:59 +0000178
David Beckf0b48452018-10-19 15:20:56 +0100179 for (const auto& backend : availablePreferredBackends)
telsoa01c577f2c2018-08-31 09:22:23 +0100180 {
181 // need to set the compute device on the layer
182 // before we can check if it is supported
David Beck33f0ae02018-10-18 15:13:56 +0100183 layer->SetBackendId(backend);
telsoa01c577f2c2018-08-31 09:22:23 +0100184 if (!IWorkloadFactory::IsLayerSupported(*layer, dataType, reasonIfUnsupported))
185 {
186 if (dataType == DataType::Float16)
187 {
188 if (IWorkloadFactory::IsLayerSupported(*layer, DataType::Float32, reasonIfUnsupported)
189 && layer->GetType() != LayerType::ConvertFp32ToFp16
190 && layer->GetType() != LayerType::ConvertFp16ToFp32)
191 {
192 // Insert FP16 -> FP32 conversion layer before current layer
193 std::vector<ConvertFp16ToFp32Layer*> convertFp16ToFp32Layers =
194 InsertConvertFp16ToFp32LayersBefore(optNetObjPtr->GetGraph(), *layer);
195
196 // Insert FP32 -> FP16 conversion layer after current layer
197 std::vector<ConvertFp32ToFp16Layer*> convertFp32ToFp16Layers =
198 InsertConvertFp32ToFp16LayersAfter(optNetObjPtr->GetGraph(), *layer);
199
200 // Assign a supported backend to the newly introduced conversion layers
David Beckf0b48452018-10-19 15:20:56 +0100201 auto AssignFirstSupportedBackend = [&](Layer* layer, BackendId preferredBackend)
telsoa01c577f2c2018-08-31 09:22:23 +0100202 {
203 bool supportedBackendFound = false;
204 std::string reasonIfUnsupported;
205
206 // Try preferred backend first
David Beck33f0ae02018-10-18 15:13:56 +0100207 layer->SetBackendId(preferredBackend);
David Beck29c75de2018-10-23 13:35:58 +0100208 if (IWorkloadFactory::IsLayerSupported(*layer,
209 EmptyOptional(),
210 reasonIfUnsupported))
telsoa01c577f2c2018-08-31 09:22:23 +0100211 {
212 supportedBackendFound = true;
213 }
214 else
215 {
David Beckf0b48452018-10-19 15:20:56 +0100216 for (const auto& backend : availablePreferredBackends)
telsoa01c577f2c2018-08-31 09:22:23 +0100217 {
218 // Skip preferred backend (we already determined that it is not supported)
219 if (backend == preferredBackend)
220 {
221 continue;
222 }
223
David Beck33f0ae02018-10-18 15:13:56 +0100224 layer->SetBackendId(backend);
David Beck29c75de2018-10-23 13:35:58 +0100225 if (IWorkloadFactory::IsLayerSupported(*layer,
226 EmptyOptional(),
227 reasonIfUnsupported))
telsoa01c577f2c2018-08-31 09:22:23 +0100228 {
229 supportedBackendFound = true;
230 break;
231 }
232 }
233 }
234
235 return supportedBackendFound;
236 };
237
238 for (ConvertFp16ToFp32Layer* convertLayer : convertFp16ToFp32Layers)
239 {
240 if (!AssignFirstSupportedBackend(convertLayer, backend))
241 {
242 return ReturnWithError(convertLayer);
243 }
244 }
245
246 for (ConvertFp32ToFp16Layer* convertLayer : convertFp32ToFp16Layers)
247 {
248 if (!AssignFirstSupportedBackend(convertLayer, backend))
249 {
250 return ReturnWithError(convertLayer);
251 }
252 }
253
254 found = true;
255 break;
256 }
257 }
jimfly016b0b53d2018-10-08 14:43:01 +0100258 std::stringstream warningMsg;
Matteo Martincigh49124022019-01-11 13:25:59 +0000259 warningMsg << "Layer of type " << GetLayerTypeAsCString(layer->GetType())
David Beck33f0ae02018-10-18 15:13:56 +0100260 << " is not supported on requested backend " << layer->GetBackendId().Get()
jimfly016b0b53d2018-10-08 14:43:01 +0100261 << " for data type " << GetDataTypeName(dataType)
262 << " (reason: " << reasonIfUnsupported
263 << "), falling back to the next backend.";
Matteo Martincigh49124022019-01-11 13:25:59 +0000264 ReportWarning(warningMsg.str(), errMessages);
telsoa01c577f2c2018-08-31 09:22:23 +0100265 }
266 else
267 {
268 found = true;
Matteo Martincigh49124022019-01-11 13:25:59 +0000269 backendSettings.m_SelectedBackends.insert(backend);
telsoa01c577f2c2018-08-31 09:22:23 +0100270 break;
271 }
272 }
273
274 // If the layer is unsupported by any devices, log and return a null network.
Matteo Martincigh49124022019-01-11 13:25:59 +0000275 if (!found)
276 {
telsoa01c577f2c2018-08-31 09:22:23 +0100277 // NOTE: if the layer is not an operation queue type AND we have not got CpuRef as a
278 // fallback we should set the compute device on the layer to CpuRef (these are not
279 // available as accelerated operations, or are only available under certain
280 // conditions, currently they comprise MemCopy, Constant, Permute)
281 armnn::LayerType layerType = layer->GetType();
Matteo Martincigh49124022019-01-11 13:25:59 +0000282 if (!backendSettings.IsCpuRefUsed() && (layerType == armnn::LayerType::MemCopy ||
283 layerType == armnn::LayerType::Constant ||
284 layerType == armnn::LayerType::Permute))
telsoa01c577f2c2018-08-31 09:22:23 +0100285 {
Matteo Martincigh49124022019-01-11 13:25:59 +0000286 BackendId cpuBackendId(armnn::Compute::CpuRef);
287 layer->SetBackendId(cpuBackendId);
288 backendSettings.m_SelectedBackends.insert(cpuBackendId);
telsoa01c577f2c2018-08-31 09:22:23 +0100289 }
290 else
291 {
292 return ReturnWithError(layer);
293 }
294 }
295 }
Matteo Martincigh49124022019-01-11 13:25:59 +0000296
297 return result;
298}
299
Matteo Martincighadddddb2019-01-24 14:06:23 +0000300OptimizationResult AssignBackends(OptimizedNetwork* optNetObjPtr,
301 BackendSettings& backendSettings,
Derek Lambertiff05cc52019-04-26 13:05:17 +0100302 SubgraphView& subgraph,
Matteo Martincighadddddb2019-01-24 14:06:23 +0000303 Optional<std::vector<std::string>&> errMessages)
Matteo Martincigh49124022019-01-11 13:25:59 +0000304{
Derek Lambertiff05cc52019-04-26 13:05:17 +0100305 Graph::Iterator firstLayer = subgraph.begin();
306 Graph::Iterator lastLayer = subgraph.end();
Matteo Martincighadddddb2019-01-24 14:06:23 +0000307 return AssignBackends(optNetObjPtr,
308 backendSettings,
309 firstLayer,
310 lastLayer,
311 errMessages);
312}
313
Derek Lamberti84da38b2019-06-13 11:40:08 +0100314BackendsMap CreateSupportedBackends(TensorHandleFactoryRegistry& handleFactoryRegistry,
315 BackendSettings& backendSettings)
316{
317 BackendsMap backends;
318 auto const& backendRegistry = BackendRegistryInstance();
319 for (auto&& selectedBackend : backendSettings.m_SupportedBackends)
320 {
321 auto backendFactory = backendRegistry.GetFactory(selectedBackend);
322 auto backendObjPtr = backendFactory();
323 BOOST_ASSERT(backendObjPtr);
324
325 backendObjPtr->RegisterTensorHandleFactories(handleFactoryRegistry);
326
327 backends[backendObjPtr->GetId()] = std::move(backendObjPtr);
328 }
329
330 return backends;
331}
332
Matteo Martincighadddddb2019-01-24 14:06:23 +0000333OptimizationResult ApplyBackendOptimizations(OptimizedNetwork* optNetObjPtr,
334 BackendSettings& backendSettings,
Derek Lamberti84da38b2019-06-13 11:40:08 +0100335 BackendsMap& backends,
Matteo Martincighadddddb2019-01-24 14:06:23 +0000336 Optional<std::vector<std::string>&> errMessages)
337{
338 BOOST_ASSERT(optNetObjPtr);
Matteo Martincigh49124022019-01-11 13:25:59 +0000339
340 OptimizationResult result;
341
Matteo Martincighadddddb2019-01-24 14:06:23 +0000342 // Get the optimized graph
343 Graph& optGraph = optNetObjPtr->GetGraph();
Matteo Martincigh49124022019-01-11 13:25:59 +0000344
Matteo Martincighadddddb2019-01-24 14:06:23 +0000345 // Run backend specific optimizations
Matteo Martincighadddddb2019-01-24 14:06:23 +0000346 for (auto&& selectedBackend : backendSettings.m_SelectedBackends)
Matteo Martincigh49124022019-01-11 13:25:59 +0000347 {
Derek Lamberti84da38b2019-06-13 11:40:08 +0100348 auto backendObjPtr = backends.find(selectedBackend)->second.get();
Matteo Martincighadddddb2019-01-24 14:06:23 +0000349 BOOST_ASSERT(backendObjPtr);
350
351 // Select sub-graphs based on backend
Derek Lambertiff05cc52019-04-26 13:05:17 +0100352 SubgraphViewSelector::Subgraphs subgraphs =
Rob Hughes65c32262019-07-23 15:33:39 +0100353 SubgraphViewSelector::SelectSubgraphs(optGraph,
Matteo Martincigh602af092019-05-01 10:31:27 +0100354 // Select layers assigned to the requested backend
355 [&backendObjPtr](const Layer& layer)
356 {
357 return layer.GetType() != LayerType::Input &&
358 layer.GetType() != LayerType::Output &&
359 layer.GetBackendId() == backendObjPtr->GetId();
360 });
Derek Lambertiff05cc52019-04-26 13:05:17 +0100361 if (subgraphs.empty())
Matteo Martincigh49124022019-01-11 13:25:59 +0000362 {
Matteo Martincighadddddb2019-01-24 14:06:23 +0000363 // No sub-graphs found, try with next selected backend
364 continue;
Matteo Martincigh49124022019-01-11 13:25:59 +0000365 }
Matteo Martincighadddddb2019-01-24 14:06:23 +0000366
367 // Try to optimize each sub-graph
Derek Lambertiff05cc52019-04-26 13:05:17 +0100368 for (auto& subgraph : subgraphs)
Matteo Martincigh49124022019-01-11 13:25:59 +0000369 {
Matteo Martincighadddddb2019-01-24 14:06:23 +0000370 // Try to optimize the current sub-graph
Matteo Martincigh84924332019-05-09 12:46:16 +0100371 OptimizationViews optimizationViews = backendObjPtr->OptimizeSubgraphView(*subgraph);
372 BOOST_ASSERT(optimizationViews.Validate(*subgraph));
Matteo Martincighadddddb2019-01-24 14:06:23 +0000373
374 // Optimization attempted, check the resulting optimized sub-graph
Matteo Martincigh84924332019-05-09 12:46:16 +0100375 for (auto& substitution : optimizationViews.GetSubstitutions())
Matteo Martincighadddddb2019-01-24 14:06:23 +0000376 {
377 // Sub-graph optimized, substitute the sub-graph with the new optimized one in the main optimized graph
Matteo Martincigh84924332019-05-09 12:46:16 +0100378 SubgraphView& replacementSubgraph = substitution.m_ReplacementSubgraph;
379 SubgraphView& substitutableSubgraph = substitution.m_SubstitutableSubgraph;
380 optGraph.SubstituteSubgraph(substitutableSubgraph, replacementSubgraph);
Matteo Martincighadddddb2019-01-24 14:06:23 +0000381
382 // Assign the current backend to the optimized sub-graph
Matteo Martincigh84924332019-05-09 12:46:16 +0100383 std::for_each(replacementSubgraph.begin(), replacementSubgraph.end(), [&selectedBackend](Layer* l)
Derek Lambertic2fe5fb2019-05-08 10:23:08 +0100384 {
385 BOOST_ASSERT(l);
386 l->SetBackendId(selectedBackend);
387 });
Matteo Martincighadddddb2019-01-24 14:06:23 +0000388 }
Derek Lambertic2fe5fb2019-05-08 10:23:08 +0100389
Matteo Martincigh84924332019-05-09 12:46:16 +0100390 if (!optimizationViews.GetFailedSubgraphs().empty())
Matteo Martincighadddddb2019-01-24 14:06:23 +0000391 {
Matteo Martincighadddddb2019-01-24 14:06:23 +0000392 std::stringstream warningMsg;
Derek Lambertic2fe5fb2019-05-08 10:23:08 +0100393 warningMsg << "Some sub-graph(s) failed to optimized on " << backendObjPtr->GetId() << " backend.";
Matteo Martincighadddddb2019-01-24 14:06:23 +0000394 ReportWarning(warningMsg.str(), errMessages);
395
396 // Failed to optimize the given sub-graph, re-assign the sub-graph layers to other available backends
Derek Lambertic2fe5fb2019-05-08 10:23:08 +0100397 BackendSettings settingsCopy(backendSettings);
Matteo Martincighadddddb2019-01-24 14:06:23 +0000398 if (!backendObjPtr->GetId().IsCpuRef())
399 {
400 // Add the current backend to the list of backends to ignore
Derek Lambertic2fe5fb2019-05-08 10:23:08 +0100401 settingsCopy.m_IgnoredBackends.insert(backendObjPtr->GetId());
Matteo Martincighadddddb2019-01-24 14:06:23 +0000402 }
Derek Lambertic2fe5fb2019-05-08 10:23:08 +0100403
404 int count=0;
Matteo Martincigh84924332019-05-09 12:46:16 +0100405 for (auto& failedSubgraph : optimizationViews.GetFailedSubgraphs())
Matteo Martincighadddddb2019-01-24 14:06:23 +0000406 {
Derek Lambertic2fe5fb2019-05-08 10:23:08 +0100407 // An error occurred: the optimization was attempted but not performed, try different backends
408 std::stringstream subgraphMsg;
409 subgraphMsg << "Re-assigning backends to " << failedSubgraph.GetLayers().size()
410 << " layers inside sub-graph " << count++;
Matteo Martincigh328d92b2019-07-04 17:52:55 +0100411 ReportWarning(subgraphMsg.str(), errMessages);
Derek Lambertic2fe5fb2019-05-08 10:23:08 +0100412
413 OptimizationResult reassignmentResult = AssignBackends(optNetObjPtr,
414 settingsCopy,
415 *subgraph,
416 errMessages);
417 if (reassignmentResult.m_Error)
418 {
419 // Failed to re-assign one of the remaining backends to each layer of the sub-graph
420 result.m_Error = true;
421 return result;
422 }
Matteo Martincighadddddb2019-01-24 14:06:23 +0000423 }
Matteo Martincigh49124022019-01-11 13:25:59 +0000424 }
425 }
426 }
427
428 return result;
429}
430
Derek Lamberti84da38b2019-06-13 11:40:08 +0100431bool RequiresCopy(ITensorHandleFactory::FactoryId src,
432 ITensorHandleFactory::FactoryId dst,
433 TensorHandleFactoryRegistry& registry)
434{
435 if (src != dst)
436 {
437 ITensorHandleFactory* srcFactory = registry.GetFactory(src);
438 ITensorHandleFactory* dstFactory = registry.GetFactory(dst);
439
Matteo Martincigha6539ed2019-08-27 13:43:32 +0100440 if (srcFactory && dstFactory &&
441 (srcFactory->GetExportFlags() & dstFactory->GetImportFlags()) != 0)
Derek Lamberti84da38b2019-06-13 11:40:08 +0100442 {
443 return false;
444 }
445 return true;
446 }
447 return false;
448}
449
450// Find the handle factory for the input layer which results in fewest required copies.
451ITensorHandleFactory::FactoryId CalculateSlotOptionForInput(BackendsMap& backends,
452 OutputSlot& slot,
453 TensorHandleFactoryRegistry& registry)
454{
455 Layer& layer = slot.GetOwningLayer();
456 BOOST_ASSERT(layer.GetType() == LayerType::Input);
457
458 // Explicitly select the tensorhandle factory for InputLayer because the rules for it are slightly different. It
459 // doesn't matter which backend it is assigned to because they all use the same implementation, which
460 // requires Map/Unmap support. This means that, so long as the handle type supports map/unmap semantics, we can
461 // select a factory with maximum compatibility with the layers connected to the InputLayer.
462
463 // First ensure the from backends can support the TensorHandeAPI
464 auto frmBackend = backends.find(layer.GetBackendId());
465 if (frmBackend == backends.end() ||
466 !frmBackend->second->SupportsTensorAllocatorAPI())
467 {
468 return ITensorHandleFactory::LegacyFactoryId;
469 }
470
471 // Go through all connections to the output slot and determine the TensorHandleFactory which results in the
472 // fewest copies.
473 std::map<ITensorHandleFactory::FactoryId, int> factoryScores;
474 int topScore = 0;
475 ITensorHandleFactory::FactoryId topChoice = ITensorHandleFactory::LegacyFactoryId;
476
477 for (auto&& connection : slot.GetConnections())
478 {
479 const Layer& connectedLayer = connection->GetOwningLayer();
480
481 auto toBackend = backends.find(connectedLayer.GetBackendId());
482 BOOST_ASSERT_MSG(toBackend != backends.end(), "Backend id not found for the connected layer");
483
484 if (!toBackend->second.get()->SupportsTensorAllocatorAPI())
485 {
486 // The destination backend does not support the tensor allocator API, move to the next one
487 continue;
488 }
489
490 auto dstPrefs = toBackend->second.get()->GetHandleFactoryPreferences();
491 for (auto&& dst : dstPrefs)
492 {
Derek Lambertif674aa02019-08-01 15:56:25 +0100493 // Input layers use the mem copy workload or import, so the selected factory must
494 // support either the map/unmap API or Import API
Derek Lamberti84da38b2019-06-13 11:40:08 +0100495 ITensorHandleFactory* factory = registry.GetFactory(dst);
Derek Lambertif674aa02019-08-01 15:56:25 +0100496 if (!factory->SupportsMapUnmap() &&
497 !CheckFlag(factory->GetImportFlags(), MemorySource::Malloc)) // Just support cpu mem imports for now
Derek Lamberti84da38b2019-06-13 11:40:08 +0100498 {
Derek Lambertif674aa02019-08-01 15:56:25 +0100499 // The current tensor handle factory does not support the map/unmap or import
500 // strategy, move to the next one
Derek Lamberti84da38b2019-06-13 11:40:08 +0100501 continue;
502 }
503
504 auto it = factoryScores.find(dst);
505 if (it == factoryScores.end())
506 {
507 // Add new score to the table
508 factoryScores[dst] = 0;
509 if (topChoice == ITensorHandleFactory::LegacyFactoryId)
510 {
511 topChoice = dst;
512 }
513 }
514 else
515 {
516 // Increase the score
517 factoryScores[dst]++;
518
519 // Track the best option
520 if (factoryScores[dst] > topScore)
521 {
522 topScore = factoryScores[dst];
523 topChoice = dst;
524 }
525 }
526 }
527 }
528
529 return topChoice;
530}
531
532// Find the handle factory for the output layer which results in fewest required copies.
533ITensorHandleFactory::FactoryId CalculateSlotOptionForOutput(BackendsMap& backends,
534 OutputSlot& slot,
535 TensorHandleFactoryRegistry& registry)
536{
537 return ITensorHandleFactory::DeferredFactoryId;
538}
539
540// For all handle factories supported on the source backend, we wish to find the one which requires the fewest copies
541// when considering all connections.
542ITensorHandleFactory::FactoryId CalculateSlotOption(BackendsMap& backends,
543 OutputSlot& outputSlot,
544 TensorHandleFactoryRegistry& registry)
545{
546 // First ensure the from backends can support the TensorHandeAPI
547 Layer& layer = outputSlot.GetOwningLayer();
548 auto frmBackend = backends.find(layer.GetBackendId());
549 if (frmBackend == backends.end() ||
550 !frmBackend->second->SupportsTensorAllocatorAPI())
551 {
552 return ITensorHandleFactory::LegacyFactoryId;
553 }
554
555 // Connections to Output Layers requires support for map/unmap on the TensorHandle.
556 bool requiresMapUnmap = false;
557 for (auto&& connection : outputSlot.GetConnections())
558 {
559 const Layer& connectedLayer = connection->GetOwningLayer();
560 if (connectedLayer.GetType() == LayerType::Output)
561 {
562 requiresMapUnmap = true;
563 }
564 }
565
566 IBackendInternal* srcBackend = frmBackend->second.get();
567 auto srcPrefs = srcBackend->GetHandleFactoryPreferences();
568
569 // Initialize the scores
570 std::map<ITensorHandleFactory::FactoryId, int> factoryScores;
571 for (auto&& pref : srcPrefs)
572 {
573 if (requiresMapUnmap) // Only consider factories that support map/unmap if required
574 {
575 ITensorHandleFactory* factory = registry.GetFactory(pref);
576 if (!factory->SupportsMapUnmap())
577 {
578 // The current tensor handle factory does not support the map/unmap strategy, move to the next one
579 continue;
580 }
581 }
582
583 auto it = factoryScores.find(pref);
584 if (it == factoryScores.end())
585 {
586 // Add new score to the table
587 factoryScores[pref] = 0;
588 }
589 }
590
591 // Score each handle factory based on how many times it requires copies on the slot connections
592 for (auto&& connection : outputSlot.GetConnections())
593 {
594 const Layer& connectedLayer = connection->GetOwningLayer();
595
596 auto toBackend = backends.find(connectedLayer.GetBackendId());
597 BOOST_ASSERT_MSG(toBackend != backends.end(), "Backend id not found for the connected layer");
598
599 auto dstPrefs = toBackend->second.get()->GetHandleFactoryPreferences();
600 for (auto&& src : srcPrefs)
601 {
602 if (factoryScores.find(src) == factoryScores.end()) // Don't consider excluded factories
603 {
604 continue;
605 }
606
607 for (auto&& dst : dstPrefs)
608 {
609 if (RequiresCopy(src, dst, registry))
610 {
611 // Copy avoided, increase the score
612 factoryScores[src]++;
613 break;
614 }
615 }
616 }
617 }
618
619 // Find the lowest score
620 int minScore = std::numeric_limits<int>::max();
621 for (auto it : factoryScores)
622 {
623 minScore = std::min(minScore, it.second);
624 }
625
626 // Collect factories matching the best(lowest) score
627 std::vector<ITensorHandleFactory::FactoryId> optimalFactories;
628 for (auto it : factoryScores)
629 {
630 if (it.second == minScore)
631 {
632 optimalFactories.push_back(it.first);
633 }
634 }
635
636 // For all compatible Factories matching the best score, find the preferred one for the current layer.
637 for (auto&& srcPref : srcPrefs)
638 {
639 for (auto&& comp : optimalFactories)
640 {
641 if (comp == srcPref)
642 {
643 return comp;
644 }
645 }
646 }
647
648 return ITensorHandleFactory::LegacyFactoryId;
649}
650
Derek Lambertif674aa02019-08-01 15:56:25 +0100651EdgeStrategy CalculateEdgeStrategy(BackendsMap& backends,
652 ITensorHandleFactory::FactoryId srcFactoryId,
653 const Layer& layer,
654 const Layer& connectedLayer,
655 TensorHandleFactoryRegistry& registry)
Derek Lamberti84da38b2019-06-13 11:40:08 +0100656{
657 auto toBackend = backends.find(connectedLayer.GetBackendId());
658 BOOST_ASSERT_MSG(toBackend != backends.end(), "Backend id not found for the connected layer");
659
660 auto dstPrefs = toBackend->second.get()->GetHandleFactoryPreferences();
661
662 // Legacy API check for backward compatibility
663 if (srcFactoryId == ITensorHandleFactory::LegacyFactoryId || dstPrefs.empty())
664 {
665 if (layer.GetBackendId() != connectedLayer.GetBackendId())
666 {
Derek Lambertif674aa02019-08-01 15:56:25 +0100667 return EdgeStrategy::CopyToTarget;
Derek Lamberti84da38b2019-06-13 11:40:08 +0100668 }
669 else
670 {
Derek Lambertif674aa02019-08-01 15:56:25 +0100671 return EdgeStrategy::DirectCompatibility;
Derek Lamberti84da38b2019-06-13 11:40:08 +0100672 }
673 }
674
675 // TensorHandleFactory API present, so perform more sophisticated strategies.
Derek Lambertif674aa02019-08-01 15:56:25 +0100676 // Dst Output layers don't require copy because they use import or map/unmap
Derek Lamberti84da38b2019-06-13 11:40:08 +0100677 if (connectedLayer.GetType() == LayerType::Output)
678 {
Derek Lambertif674aa02019-08-01 15:56:25 +0100679 return EdgeStrategy::DirectCompatibility;
Derek Lamberti84da38b2019-06-13 11:40:08 +0100680 }
681
682 // Search for direct match in prefs
683 for (auto&& pref : dstPrefs)
684 {
685 if (pref == srcFactoryId)
686 {
Derek Lambertif674aa02019-08-01 15:56:25 +0100687 return EdgeStrategy::DirectCompatibility;
Derek Lamberti84da38b2019-06-13 11:40:08 +0100688 }
689 }
690
691 // Search for export/import options
692 ITensorHandleFactory* srcFactory = registry.GetFactory(srcFactoryId);
Derek Lambertif674aa02019-08-01 15:56:25 +0100693 if (srcFactory->GetExportFlags() != 0)
Derek Lamberti84da38b2019-06-13 11:40:08 +0100694 {
695 for (auto&& pref : dstPrefs)
696 {
697 ITensorHandleFactory* dstFactory = registry.GetFactory(pref);
James Conroyffab16f2019-11-07 14:37:09 +0000698
699 // Handles some cases where dstFactory is null when Neon memory import is disabled
700 if (!dstFactory) {
701 return EdgeStrategy::CopyToTarget;
702 }
703
Derek Lambertif674aa02019-08-01 15:56:25 +0100704 if ((dstFactory->GetImportFlags() & srcFactory->GetExportFlags()) != 0)
Derek Lamberti84da38b2019-06-13 11:40:08 +0100705 {
Derek Lambertif674aa02019-08-01 15:56:25 +0100706 return EdgeStrategy::ExportToTarget;
Derek Lamberti84da38b2019-06-13 11:40:08 +0100707 }
708 }
709 }
710
711 // Search for copy options via map/unmap
712 if (srcFactory->SupportsMapUnmap())
713 {
714 for (auto&& pref : dstPrefs)
715 {
716 ITensorHandleFactory* dstFactory = registry.GetFactory(pref);
717 if (dstFactory->SupportsMapUnmap())
718 {
Derek Lambertif674aa02019-08-01 15:56:25 +0100719 return EdgeStrategy::CopyToTarget;
Derek Lamberti84da38b2019-06-13 11:40:08 +0100720 }
721 }
722 }
723
Derek Lambertif674aa02019-08-01 15:56:25 +0100724 return EdgeStrategy::Undefined;
Derek Lamberti84da38b2019-06-13 11:40:08 +0100725}
726
727// Select the TensorHandleFactories and the corresponding memory strategy
728OptimizationResult SelectTensorHandleStrategy(Graph& optGraph,
729 BackendsMap& backends,
730 TensorHandleFactoryRegistry& registry,
731 Optional<std::vector<std::string>&> errMessages)
732{
733 OptimizationResult result;
734
735 optGraph.ForEachLayer([&backends, &registry, &result, &errMessages](Layer* layer)
736 {
737 BOOST_ASSERT(layer);
738
739 // Lets make sure the backend is in our list of supported backends. Something went wrong during backend
740 // assignment if this check fails
741 BOOST_ASSERT(backends.find(layer->GetBackendId()) != backends.end());
742
743 // Check each output separately
744 for (unsigned int slotIdx = 0; slotIdx < layer->GetNumOutputSlots(); slotIdx++)
745 {
746 OutputSlot& outputSlot = layer->GetOutputSlot(slotIdx);
747
748 ITensorHandleFactory::FactoryId slotOption = ITensorHandleFactory::LegacyFactoryId;
749
750 // Calculate the factory to use which results in the fewest copies being made.
751 switch(layer->GetType())
752 {
753 case LayerType::Input:
754 slotOption = CalculateSlotOptionForInput(backends, outputSlot, registry);
755 break;
756 case LayerType::Output:
757 slotOption = CalculateSlotOptionForOutput(backends, outputSlot, registry);
758 break;
759 default:
760 slotOption = CalculateSlotOption(backends, outputSlot, registry);
761 break;
762 }
763 outputSlot.SetTensorHandleFactory(slotOption);
764
Derek Lambertif674aa02019-08-01 15:56:25 +0100765 // Now determine the "best" edge strategy for each connection given the slotOption.
Derek Lamberti84da38b2019-06-13 11:40:08 +0100766 unsigned int connectionIdx = 0;
767 for (auto&& connection : outputSlot.GetConnections())
768 {
769 const Layer& connectedLayer = connection->GetOwningLayer();
770
Derek Lambertif674aa02019-08-01 15:56:25 +0100771 EdgeStrategy strategy = CalculateEdgeStrategy(backends, slotOption, *layer, connectedLayer, registry);
Derek Lamberti84da38b2019-06-13 11:40:08 +0100772
Derek Lambertif674aa02019-08-01 15:56:25 +0100773 if (strategy == EdgeStrategy::Undefined)
Derek Lamberti84da38b2019-06-13 11:40:08 +0100774 {
775 result.m_Error = true;
776 if (errMessages)
777 {
778 errMessages.value().emplace_back("Could not find valid strategy required for compatibility"
779 " between backends.");
780 }
781 return;
782 }
783
Derek Lambertif674aa02019-08-01 15:56:25 +0100784 outputSlot.SetEdgeStrategy(connectionIdx, strategy);
Derek Lamberti84da38b2019-06-13 11:40:08 +0100785
786 connectionIdx++;
787 }
788 }
789 });
790
791 return result;
792}
793
Matteo Martincigh49124022019-01-11 13:25:59 +0000794IOptimizedNetworkPtr Optimize(const INetwork& inNetwork,
795 const std::vector<BackendId>& backendPreferences,
796 const IDeviceSpec& deviceSpec,
797 const OptimizerOptions& options,
Rob Hughes23214432019-11-05 11:27:36 +0000798 Optional<std::vector<std::string>&> messages)
Matteo Martincigh49124022019-01-11 13:25:59 +0000799{
800 if (backendPreferences.empty())
801 {
802 throw armnn::InvalidArgumentException("Invoked Optimize with no backends specified");
803 }
804
805 const Network& network = *boost::polymorphic_downcast<const Network*>(&inNetwork);
806 std::unique_ptr<Graph> graph = std::make_unique<Graph>(network.GetGraph());
807
808 auto optNet = IOptimizedNetworkPtr(new OptimizedNetwork(std::move(graph)), &IOptimizedNetwork::Destroy);
809
810 OptimizedNetwork* optNetObjPtr = boost::polymorphic_downcast<OptimizedNetwork*>(optNet.get());
811
Matteo Martincighadddddb2019-01-24 14:06:23 +0000812 // Get the optimized graph
813 Graph& optGraph = optNetObjPtr->GetGraph();
814
Matteo Martincigh49124022019-01-11 13:25:59 +0000815 // Perform optimisation passes
816 using namespace optimizations;
Matteo Martincighadddddb2019-01-24 14:06:23 +0000817 Optimizer::Pass(optGraph, MakeOptimizations(SquashEqualPermuteSiblings(),
818 SquashEqualReshapeSiblings(),
819 OptimizeInversePermutes(),
820 MovePermuteUp(),
821 PermuteAsReshape(),
Nina Drozd861985f2019-04-18 14:48:51 +0100822 OptimizeConsecutiveReshapes(),
Rob Hughes3a7d3a72019-09-24 16:59:56 +0100823 FoldPadIntoConvolution2d(),
824 PermuteAndBatchToSpaceAsDepthToSpace()));
Matteo Martincigh49124022019-01-11 13:25:59 +0000825
Matteo Martincighadddddb2019-01-24 14:06:23 +0000826 // Infer the tensor infos for all output slots. Throws an exception on failure
827 optGraph.InferTensorInfos();
Matteo Martincigh49124022019-01-11 13:25:59 +0000828
829 // If Fp32 to Fp16 optimization is set convert Fp32 network to Fp16
830 if (options.m_ReduceFp32ToFp16)
831 {
Matteo Martincighadddddb2019-01-24 14:06:23 +0000832 Optimizer::Pass(optGraph, MakeOptimizations(Fp32NetworkToFp16Converter()));
Matteo Martincigh49124022019-01-11 13:25:59 +0000833 }
834
835 // Initialize backend settings
836 BackendSettings backendSettings(backendPreferences, deviceSpec);
837 if (backendSettings.GetAvailablePreferredBackends().empty())
838 {
839 std::stringstream failureMsg;
840 failureMsg << "None of the preferred backends " << backendPreferences
841 << " are supported. Current platform provides " << backendSettings.m_SupportedBackends;
Rob Hughes23214432019-11-05 11:27:36 +0000842 ReportError(failureMsg.str(), messages);
Matteo Martincigh49124022019-01-11 13:25:59 +0000843 return IOptimizedNetworkPtr(nullptr, &IOptimizedNetwork::Destroy);
844 }
845
Derek Lamberti84da38b2019-06-13 11:40:08 +0100846 // Create a map to temporarily hold initialized backend objects
847 TensorHandleFactoryRegistry tensorHandleFactoryRegistry;
848 BackendsMap backends = CreateSupportedBackends(tensorHandleFactoryRegistry, backendSettings);
849
Matteo Martincigh49124022019-01-11 13:25:59 +0000850 // Assign an available backend to each layer
Matteo Martincighadddddb2019-01-24 14:06:23 +0000851 Graph::Iterator firstLayer = optGraph.begin();
852 Graph::Iterator lastLayer = optGraph.end();
Derek Lamberti84da38b2019-06-13 11:40:08 +0100853 OptimizationResult assignBackendsResult = AssignBackends(optNetObjPtr,
854 backendSettings,
855 firstLayer,
856 lastLayer,
Rob Hughes23214432019-11-05 11:27:36 +0000857 messages);
Derek Lamberti84da38b2019-06-13 11:40:08 +0100858 if (assignBackendsResult.m_Error)
Matteo Martincigh49124022019-01-11 13:25:59 +0000859 {
860 // Failed to assign a backend to each layer
jimfly016b0b53d2018-10-08 14:43:01 +0100861 return IOptimizedNetworkPtr(nullptr, &IOptimizedNetwork::Destroy);
862 }
telsoa01c577f2c2018-08-31 09:22:23 +0100863
Matteo Martincighadddddb2019-01-24 14:06:23 +0000864 Optimizer::Pass(optGraph, MakeOptimizations(OptimizeInverseConversionsFp16(),
865 OptimizeInverseConversionsFp32()));
telsoa01c577f2c2018-08-31 09:22:23 +0100866
Matteo Martincighadddddb2019-01-24 14:06:23 +0000867 // Apply the backend-specific optimizations
868 OptimizationResult backendOptimizationResult = ApplyBackendOptimizations(optNetObjPtr,
869 backendSettings,
Derek Lamberti84da38b2019-06-13 11:40:08 +0100870 backends,
Rob Hughes23214432019-11-05 11:27:36 +0000871 messages);
Matteo Martincighadddddb2019-01-24 14:06:23 +0000872 if (backendOptimizationResult.m_Error)
Matteo Martincigh49124022019-01-11 13:25:59 +0000873 {
Matteo Martincighadddddb2019-01-24 14:06:23 +0000874 // Failed to apply the backend-specific optimizations
875 return IOptimizedNetworkPtr(nullptr, &IOptimizedNetwork::Destroy);
Matteo Martincigh49124022019-01-11 13:25:59 +0000876 }
877
Matteo Martincighadddddb2019-01-24 14:06:23 +0000878 // If the debug flag is set, then insert a DebugLayer after each layer
879 // Doing this after applying the backend optimizations as they might have changed some layers
880 if (options.m_Debug)
881 {
882 Optimizer::Pass(optGraph, MakeOptimizations(InsertDebugLayer()));
883 }
884
Derek Lamberti84da38b2019-06-13 11:40:08 +0100885 // Calculate the compatibility strategies for tensor handles
886 OptimizationResult strategyResult = SelectTensorHandleStrategy(optGraph,
887 backends,
888 tensorHandleFactoryRegistry,
Rob Hughes23214432019-11-05 11:27:36 +0000889 messages);
Derek Lamberti84da38b2019-06-13 11:40:08 +0100890 if (strategyResult.m_Error)
891 {
892 // Failed to apply the backend-specific optimizations
893 return IOptimizedNetworkPtr(nullptr, &IOptimizedNetwork::Destroy);
894 }
895
896 // Based on the tensor handle strategy determined above, insert copy layers where required.
Derek Lambertif674aa02019-08-01 15:56:25 +0100897 optGraph.AddCompatibilityLayers(backends, tensorHandleFactoryRegistry);
telsoa01c577f2c2018-08-31 09:22:23 +0100898
899 // Convert constants
Matteo Martincighadddddb2019-01-24 14:06:23 +0000900 Optimizer::Pass(optGraph, MakeOptimizations(ConvertConstantsFloatToHalf()));
901 Optimizer::Pass(optGraph, MakeOptimizations(ConvertConstantsHalfToFloat()));
telsoa01c577f2c2018-08-31 09:22:23 +0100902
Derek Lamberti84da38b2019-06-13 11:40:08 +0100903 // Run backend specific optimizations (deprecated)
Matteo Martincigh49124022019-01-11 13:25:59 +0000904 for (auto&& chosenBackend : backendSettings.m_SelectedBackends)
David Beck263e3492018-11-09 14:46:40 +0000905 {
906 auto factoryFun = BackendRegistryInstance().GetFactory(chosenBackend);
907 auto backendPtr = factoryFun();
908 BOOST_ASSERT(backendPtr.get() != nullptr);
909
Matteo Martincighed735042019-05-22 09:42:43 +0100910 ARMNN_NO_DEPRECATE_WARN_BEGIN
David Beck263e3492018-11-09 14:46:40 +0000911 auto backendSpecificOptimizations = backendPtr->GetOptimizations();
Matteo Martincighed735042019-05-22 09:42:43 +0100912 ARMNN_NO_DEPRECATE_WARN_END
913
David Beck263e3492018-11-09 14:46:40 +0000914 if (!backendSpecificOptimizations.empty())
915 {
916 Optimizer::Pass(optNetObjPtr->GetGraph(), backendSpecificOptimizations);
917 }
918 }
919
telsoa01c577f2c2018-08-31 09:22:23 +0100920 return optNet;
telsoa014fcda012018-03-09 14:13:49 +0000921}
922
923Network::Network()
Jan Eilers99d9d4a2019-11-06 10:02:16 +0000924: m_Graph(std::make_unique<Graph>()),
925 m_Guid(profiling::ProfilingService::Instance().NextGuid())
telsoa014fcda012018-03-09 14:13:49 +0000926{
927}
928
929Network::~Network()
930{
931}
932
Jan Eilers99d9d4a2019-11-06 10:02:16 +0000933Status Network::PrintGraph()
934{
935 m_Graph->Print();
936 return Status::Success;
937}
938
telsoa014fcda012018-03-09 14:13:49 +0000939IConnectableLayer* Network::AddInputLayer(LayerBindingId id, const char* name)
940{
941 return m_Graph->AddLayer<InputLayer>(id, name);
942}
943
Éanna Ó Catháin4e1e1362018-11-12 11:36:34 +0000944IConnectableLayer* Network::AddBatchToSpaceNdLayer(const BatchToSpaceNdDescriptor& batchToSpaceNdDescriptor,
945 const char* name)
946{
947 return m_Graph->AddLayer<BatchToSpaceNdLayer>(batchToSpaceNdDescriptor, name);
948}
949
Aron Virginas-Tar77bfb5e2019-10-16 17:45:38 +0100950IConnectableLayer* Network::AddComparisonLayer(const ComparisonDescriptor& comparisonDescriptor,
951 const char* name)
952{
953 return m_Graph->AddLayer<ComparisonLayer>(comparisonDescriptor, name);
954}
955
telsoa014fcda012018-03-09 14:13:49 +0000956IConnectableLayer* Network::AddFullyConnectedLayerImpl(const FullyConnectedDescriptor& fullyConnectedDescriptor,
telsoa01c577f2c2018-08-31 09:22:23 +0100957 const ConstTensor& weights,
Aron Virginas-Tarad402702019-02-22 17:03:44 +0000958 const Optional<ConstTensor>& biases,
telsoa01c577f2c2018-08-31 09:22:23 +0100959 const char* name)
telsoa014fcda012018-03-09 14:13:49 +0000960{
Aron Virginas-Tarad402702019-02-22 17:03:44 +0000961 if (fullyConnectedDescriptor.m_BiasEnabled && !biases.has_value())
telsoa014fcda012018-03-09 14:13:49 +0000962 {
Aron Virginas-Tarad402702019-02-22 17:03:44 +0000963 throw InvalidArgumentException("AddFullyConnectedLayer: biases cannot be empty");
telsoa014fcda012018-03-09 14:13:49 +0000964 }
965
966 const auto layer = m_Graph->AddLayer<FullyConnectedLayer>(fullyConnectedDescriptor, name);
967
968 layer->m_Weight = std::make_unique<ScopedCpuTensorHandle>(weights);
969
970 if (fullyConnectedDescriptor.m_BiasEnabled)
971 {
Aron Virginas-Tarad402702019-02-22 17:03:44 +0000972 layer->m_Bias = std::make_unique<ScopedCpuTensorHandle>(biases.value());
telsoa014fcda012018-03-09 14:13:49 +0000973 }
974
975 return layer;
976}
977
978IConnectableLayer* Network::AddFullyConnectedLayer(const FullyConnectedDescriptor& fullyConnectedDescriptor,
telsoa01c577f2c2018-08-31 09:22:23 +0100979 const ConstTensor& weights,
Aron Virginas-Tarad402702019-02-22 17:03:44 +0000980 const Optional<ConstTensor>& biases,
telsoa01c577f2c2018-08-31 09:22:23 +0100981 const char* name)
telsoa014fcda012018-03-09 14:13:49 +0000982{
Aron Virginas-Tarad402702019-02-22 17:03:44 +0000983 return AddFullyConnectedLayerImpl(fullyConnectedDescriptor, weights, biases, name);
telsoa014fcda012018-03-09 14:13:49 +0000984}
985
Aron Virginas-Tarad402702019-02-22 17:03:44 +0000986IConnectableLayer* Network::AddFullyConnectedLayer(const FullyConnectedDescriptor& fullyConnectedDescriptor,
987 const ConstTensor& weights,
988 const char* name)
989{
Matteo Martincighfc598e12019-05-14 10:36:13 +0100990 Optional<ConstTensor> biases;
Aron Virginas-Tarad402702019-02-22 17:03:44 +0000991 return AddFullyConnectedLayerImpl(fullyConnectedDescriptor, weights, biases, name);
992}
993
telsoa014fcda012018-03-09 14:13:49 +0000994IConnectableLayer* Network::AddFullyConnectedLayer(const FullyConnectedDescriptor& fullyConnectedDescriptor,
telsoa01c577f2c2018-08-31 09:22:23 +0100995 const ConstTensor& weights,
996 const ConstTensor& biases,
997 const char* name)
telsoa014fcda012018-03-09 14:13:49 +0000998{
Aron Virginas-Tarad402702019-02-22 17:03:44 +0000999 Optional<ConstTensor> optionalBiases(biases);
1000 return AddFullyConnectedLayerImpl(fullyConnectedDescriptor, weights, optionalBiases, name);
telsoa014fcda012018-03-09 14:13:49 +00001001}
1002
Jim Flynne242f2d2019-05-22 14:24:13 +01001003IConnectableLayer* Network::AddConcatLayer(const ConcatDescriptor& concatDescriptor,
Jim Flynn906f9462019-05-10 13:55:21 +01001004 const char* name)
1005{
Jim Flynne242f2d2019-05-22 14:24:13 +01001006 return m_Graph->AddLayer<ConcatLayer>(concatDescriptor, name);
Jim Flynn906f9462019-05-10 13:55:21 +01001007}
1008
telsoa014fcda012018-03-09 14:13:49 +00001009IConnectableLayer* Network::AddConvolution2dLayerImpl(const Convolution2dDescriptor& convolution2dDescriptor,
telsoa01c577f2c2018-08-31 09:22:23 +01001010 const ConstTensor& weights,
Aron Virginas-Tarad402702019-02-22 17:03:44 +00001011 const Optional<ConstTensor>& biases,
telsoa01c577f2c2018-08-31 09:22:23 +01001012 const char* name)
telsoa014fcda012018-03-09 14:13:49 +00001013{
Aron Virginas-Tarad402702019-02-22 17:03:44 +00001014 if (convolution2dDescriptor.m_BiasEnabled && !biases.has_value())
telsoa014fcda012018-03-09 14:13:49 +00001015 {
Aron Virginas-Tarad402702019-02-22 17:03:44 +00001016 throw InvalidArgumentException("AddConvolution2dLayer: biases cannot be empty");
telsoa014fcda012018-03-09 14:13:49 +00001017 }
1018
1019 const auto layer = m_Graph->AddLayer<Convolution2dLayer>(convolution2dDescriptor, name);
1020
1021 layer->m_Weight = std::make_unique<ScopedCpuTensorHandle>(weights);
1022
1023 if (convolution2dDescriptor.m_BiasEnabled)
1024 {
Aron Virginas-Tarad402702019-02-22 17:03:44 +00001025 layer->m_Bias = std::make_unique<ScopedCpuTensorHandle>(biases.value());
telsoa014fcda012018-03-09 14:13:49 +00001026 }
1027
1028 return layer;
1029}
1030
1031IConnectableLayer* Network::AddConvolution2dLayer(const Convolution2dDescriptor& convolution2dDescriptor,
telsoa01c577f2c2018-08-31 09:22:23 +01001032 const ConstTensor& weights,
Aron Virginas-Tarad402702019-02-22 17:03:44 +00001033 const Optional<ConstTensor>& biases,
telsoa01c577f2c2018-08-31 09:22:23 +01001034 const char* name)
telsoa014fcda012018-03-09 14:13:49 +00001035{
Aron Virginas-Tarad402702019-02-22 17:03:44 +00001036 return AddConvolution2dLayerImpl(convolution2dDescriptor, weights, biases, name);
telsoa014fcda012018-03-09 14:13:49 +00001037}
Aron Virginas-Tarad402702019-02-22 17:03:44 +00001038
Aron Virginas-Tarad402702019-02-22 17:03:44 +00001039IConnectableLayer* Network::AddConvolution2dLayer(const Convolution2dDescriptor& convolution2dDescriptor,
1040 const ConstTensor& weights,
1041 const char* name)
1042{
Matteo Martincighfc598e12019-05-14 10:36:13 +01001043 Optional<ConstTensor> biases;
Aron Virginas-Tarad402702019-02-22 17:03:44 +00001044 return AddConvolution2dLayerImpl(convolution2dDescriptor, weights, biases, name);
1045}
1046
telsoa014fcda012018-03-09 14:13:49 +00001047IConnectableLayer* Network::AddConvolution2dLayer(const Convolution2dDescriptor& convolution2dDescriptor,
telsoa01c577f2c2018-08-31 09:22:23 +01001048 const ConstTensor& weights,
1049 const ConstTensor& biases,
1050 const char* name)
telsoa014fcda012018-03-09 14:13:49 +00001051{
Aron Virginas-Tarad402702019-02-22 17:03:44 +00001052 Optional<ConstTensor> optionalBiases(biases);
1053 return AddConvolution2dLayerImpl(convolution2dDescriptor, weights, optionalBiases, name);
telsoa014fcda012018-03-09 14:13:49 +00001054}
1055
1056IConnectableLayer* Network::AddDepthwiseConvolution2dLayerImpl(
1057 const DepthwiseConvolution2dDescriptor& convolution2dDescriptor,
1058 const ConstTensor& weights,
Aron Virginas-Tarad402702019-02-22 17:03:44 +00001059 const Optional<ConstTensor>& biases,
telsoa014fcda012018-03-09 14:13:49 +00001060 const char* name)
1061{
Aron Virginas-Tarad402702019-02-22 17:03:44 +00001062 if (convolution2dDescriptor.m_BiasEnabled && !biases.has_value())
telsoa014fcda012018-03-09 14:13:49 +00001063 {
Aron Virginas-Tarad402702019-02-22 17:03:44 +00001064 throw InvalidArgumentException("AddDepthwiseConvolution2dLayer: biases cannot be empty");
telsoa014fcda012018-03-09 14:13:49 +00001065 }
1066
Matteo Martincigh3d6898c2019-01-15 16:11:44 +00001067 const auto layer = m_Graph->AddLayer<DepthwiseConvolution2dLayer>(convolution2dDescriptor, name);
telsoa014fcda012018-03-09 14:13:49 +00001068
1069 layer->m_Weight = std::make_unique<ScopedCpuTensorHandle>(weights);
1070
1071 if (convolution2dDescriptor.m_BiasEnabled)
1072 {
Aron Virginas-Tarad402702019-02-22 17:03:44 +00001073 layer->m_Bias = std::make_unique<ScopedCpuTensorHandle>(biases.value());
telsoa014fcda012018-03-09 14:13:49 +00001074 }
1075
1076 return layer;
1077}
1078
Aron Virginas-Tardd6247f2019-09-19 14:31:17 +01001079IConnectableLayer* Network::AddDepthToSpaceLayer(const DepthToSpaceDescriptor& depthToSpaceDescriptor,
1080 const char* name)
1081{
1082 return m_Graph->AddLayer<DepthToSpaceLayer>(depthToSpaceDescriptor, name);
1083}
1084
telsoa014fcda012018-03-09 14:13:49 +00001085IConnectableLayer* Network::AddDepthwiseConvolution2dLayer(
Aron Virginas-Tarad402702019-02-22 17:03:44 +00001086 const DepthwiseConvolution2dDescriptor& convolution2dDescriptor,
1087 const ConstTensor& weights,
1088 const Optional<ConstTensor>& biases,
1089 const char* name)
1090{
1091 return AddDepthwiseConvolution2dLayerImpl(convolution2dDescriptor, weights, biases, name);
1092}
1093
Aron Virginas-Tarad402702019-02-22 17:03:44 +00001094IConnectableLayer* Network::AddDepthwiseConvolution2dLayer(
telsoa014fcda012018-03-09 14:13:49 +00001095 const DepthwiseConvolution2dDescriptor& convolution2dDescriptor,
1096 const ConstTensor& weights,
1097 const char* name)
1098{
Matteo Martincighfc598e12019-05-14 10:36:13 +01001099 Optional<ConstTensor> biases;
Aron Virginas-Tarad402702019-02-22 17:03:44 +00001100 return AddDepthwiseConvolution2dLayerImpl(convolution2dDescriptor, weights, biases, name);
telsoa014fcda012018-03-09 14:13:49 +00001101}
Aron Virginas-Tarad402702019-02-22 17:03:44 +00001102
telsoa014fcda012018-03-09 14:13:49 +00001103IConnectableLayer* Network::AddDepthwiseConvolution2dLayer(
1104 const DepthwiseConvolution2dDescriptor& convolution2dDescriptor,
1105 const ConstTensor& weights,
1106 const ConstTensor& biases,
1107 const char* name)
1108{
Aron Virginas-Tarad402702019-02-22 17:03:44 +00001109 Optional<ConstTensor> optionalBiases(biases);
1110 return AddDepthwiseConvolution2dLayerImpl(convolution2dDescriptor, weights, optionalBiases, name);
telsoa014fcda012018-03-09 14:13:49 +00001111}
1112
Narumol Prangnawarat94dd5d82019-01-23 18:06:26 +00001113IConnectableLayer* Network::AddDetectionPostProcessLayer(const armnn::DetectionPostProcessDescriptor& descriptor,
Narumol Prangnawarat6d302bf2019-02-04 11:46:26 +00001114 const ConstTensor& anchors, const char* name)
Narumol Prangnawarat94dd5d82019-01-23 18:06:26 +00001115{
Narumol Prangnawarat6d302bf2019-02-04 11:46:26 +00001116 const auto layer = m_Graph->AddLayer<DetectionPostProcessLayer>(descriptor, name);
1117
1118 layer->m_Anchors = std::make_unique<ScopedCpuTensorHandle>(anchors);
1119
1120 return layer;
Narumol Prangnawarat94dd5d82019-01-23 18:06:26 +00001121}
1122
telsoa014fcda012018-03-09 14:13:49 +00001123IConnectableLayer* Network::AddPermuteLayer(const PermuteDescriptor& permuteDescriptor,
1124 const char* name)
1125{
1126 return m_Graph->AddLayer<PermuteLayer>(permuteDescriptor, name);
1127}
1128
1129IConnectableLayer* Network::AddPooling2dLayer(const Pooling2dDescriptor& pooling2dDescriptor,
1130 const char* name)
1131{
1132 return m_Graph->AddLayer<Pooling2dLayer>(pooling2dDescriptor, name);
1133}
1134
1135IConnectableLayer* Network::AddActivationLayer(const ActivationDescriptor& activationDescriptor,
1136 const char* name)
1137{
1138 return m_Graph->AddLayer<ActivationLayer>(activationDescriptor, name);
1139}
1140
Nikhil Rajee391d52019-09-05 17:50:44 +01001141IConnectableLayer* Network::AddArgMinMaxLayer(const ArgMinMaxDescriptor& argMinMaxDescriptor,
1142 const char* name)
1143{
1144 return m_Graph->AddLayer<ArgMinMaxLayer>(argMinMaxDescriptor, name);
1145}
1146
telsoa01c577f2c2018-08-31 09:22:23 +01001147IConnectableLayer* Network::AddNormalizationLayer(const NormalizationDescriptor&
1148normalizationDescriptor,
telsoa014fcda012018-03-09 14:13:49 +00001149 const char* name)
1150{
1151 return m_Graph->AddLayer<NormalizationLayer>(normalizationDescriptor, name);
1152}
1153
Aron Virginas-Tar636ab402019-09-16 14:27:45 +01001154IConnectableLayer* Network::AddSliceLayer(const SliceDescriptor& sliceDescriptor, const char* name)
1155{
1156 return m_Graph->AddLayer<SliceLayer>(sliceDescriptor, name);
1157}
1158
telsoa014fcda012018-03-09 14:13:49 +00001159IConnectableLayer* Network::AddSoftmaxLayer(const SoftmaxDescriptor& softmaxDescriptor,
1160 const char* name)
1161{
1162 return m_Graph->AddLayer<SoftmaxLayer>(softmaxDescriptor, name);
1163}
1164
1165IConnectableLayer* Network::AddSplitterLayer(const ViewsDescriptor& splitterDescriptor,
1166 const char* name)
1167{
1168 return m_Graph->AddLayer<SplitterLayer>(splitterDescriptor, name);
1169}
1170
Nattapat Chaimanowong5a4304a2018-11-28 10:44:37 +00001171IConnectableLayer* Network::AddMaximumLayer(const char* name)
1172{
1173 return m_Graph->AddLayer<MaximumLayer>(name);
1174}
1175
Éanna Ó Catháin20e58802018-12-04 10:29:06 +00001176IConnectableLayer* Network::AddMinimumLayer(const char* name)
1177{
1178 return m_Graph->AddLayer<MinimumLayer>(name);
1179}
1180
Jim Flynne242f2d2019-05-22 14:24:13 +01001181IConnectableLayer* Network::AddMergerLayer(const MergerDescriptor& mergerDescriptor,
Jim Flynn906f9462019-05-10 13:55:21 +01001182 const char* name)
telsoa014fcda012018-03-09 14:13:49 +00001183{
Jim Flynne242f2d2019-05-22 14:24:13 +01001184 return AddConcatLayer(mergerDescriptor, name);
telsoa014fcda012018-03-09 14:13:49 +00001185}
1186
Kevin May868eb142019-09-04 17:29:31 +01001187IConnectableLayer* Network::AddAbsLayer(const char * name)
1188{
1189 return m_Graph->AddLayer<AbsLayer>(name);
1190}
1191
telsoa014fcda012018-03-09 14:13:49 +00001192IConnectableLayer* Network::AddAdditionLayer(const char* name)
1193{
1194 return m_Graph->AddLayer<AdditionLayer>(name);
1195}
1196
1197IConnectableLayer* Network::AddMultiplicationLayer(const char* name)
1198{
1199 return m_Graph->AddLayer<MultiplicationLayer>(name);
1200}
1201
1202IConnectableLayer* Network::AddOutputLayer(LayerBindingId id, const char* name)
1203{
1204 return m_Graph->AddLayer<OutputLayer>(id, name);
1205}
1206
1207IConnectableLayer* Network::AddBatchNormalizationLayer(const BatchNormalizationDescriptor& desc,
1208 const ConstTensor& mean,
1209 const ConstTensor& variance,
1210 const ConstTensor& beta,
1211 const ConstTensor& gamma,
1212 const char* name)
1213{
1214 const auto layer = m_Graph->AddLayer<BatchNormalizationLayer>(desc, name);
1215
1216 layer->m_Mean = std::make_unique<ScopedCpuTensorHandle>(mean);
1217 layer->m_Variance = std::make_unique<ScopedCpuTensorHandle>(variance);
1218 layer->m_Beta = std::make_unique<ScopedCpuTensorHandle>(beta);
1219 layer->m_Gamma = std::make_unique<ScopedCpuTensorHandle>(gamma);
1220
1221 return layer;
1222}
1223
Aron Virginas-Tar169d2f12019-07-01 19:01:44 +01001224IConnectableLayer* Network::AddResizeBilinearLayer(const ResizeBilinearDescriptor& descriptor,
1225 const char* name)
telsoa014fcda012018-03-09 14:13:49 +00001226{
Aron Virginas-Tar169d2f12019-07-01 19:01:44 +01001227 ResizeDescriptor resizeDescriptor;
1228 resizeDescriptor.m_Method = ResizeMethod::Bilinear;
1229 resizeDescriptor.m_DataLayout = descriptor.m_DataLayout;
1230 resizeDescriptor.m_TargetWidth = descriptor.m_TargetWidth;
1231 resizeDescriptor.m_TargetHeight = descriptor.m_TargetHeight;
1232
1233 return m_Graph->AddLayer<ResizeLayer>(resizeDescriptor, name);
telsoa014fcda012018-03-09 14:13:49 +00001234}
1235
Teresa Charlina9075df2019-06-27 15:41:57 +01001236IConnectableLayer* Network::AddResizeLayer(const ResizeDescriptor&
1237resizeDescriptor, const char* name)
1238{
Aron Virginas-Tar169d2f12019-07-01 19:01:44 +01001239 return m_Graph->AddLayer<ResizeLayer>(resizeDescriptor, name);
Teresa Charlina9075df2019-06-27 15:41:57 +01001240}
1241
Kevin Mayce5045a2019-10-02 14:07:47 +01001242IConnectableLayer* Network::AddInstanceNormalizationLayer(const InstanceNormalizationDescriptor& desc,
1243 const char* name)
1244{
1245 return m_Graph->AddLayer<InstanceNormalizationLayer>(desc, name);
1246}
1247
Matteo Martincighbcd3c852018-09-28 14:14:12 +01001248IConnectableLayer* Network::AddL2NormalizationLayer(const L2NormalizationDescriptor& desc,
1249 const char* name)
telsoa014fcda012018-03-09 14:13:49 +00001250{
Matteo Martincighbcd3c852018-09-28 14:14:12 +01001251 return m_Graph->AddLayer<L2NormalizationLayer>(desc, name);
telsoa014fcda012018-03-09 14:13:49 +00001252}
1253
Aron Virginas-Tarf982dea2019-10-11 14:07:53 +01001254IConnectableLayer* Network::AddLogSoftmaxLayer(const LogSoftmaxDescriptor& desc,
1255 const char* name)
1256{
1257 return m_Graph->AddLayer<LogSoftmaxLayer>(desc, name);
1258}
1259
telsoa014fcda012018-03-09 14:13:49 +00001260IConnectableLayer* Network::AddConstantLayer(const ConstTensor& input, const char* name)
1261{
telsoa01c577f2c2018-08-31 09:22:23 +01001262 auto layer = m_Graph->AddLayer<ConstantLayer>(name);
1263
1264 layer->m_LayerOutput = std::make_unique<ScopedCpuTensorHandle>(input);
1265
1266 return layer;
telsoa014fcda012018-03-09 14:13:49 +00001267}
1268
telsoa01c577f2c2018-08-31 09:22:23 +01001269IConnectableLayer* Network::AddReshapeLayer(const ReshapeDescriptor& reshapeDescriptor,
1270 const char* name)
telsoa014fcda012018-03-09 14:13:49 +00001271{
1272 return m_Graph->AddLayer<ReshapeLayer>(reshapeDescriptor, name);
1273}
1274
Nattapat Chaimanowong207ef9a2018-11-02 10:57:25 +00001275IConnectableLayer* Network::AddSpaceToBatchNdLayer(const SpaceToBatchNdDescriptor& spaceToBatchNdDescriptor,
1276 const char* name)
1277{
1278 return m_Graph->AddLayer<SpaceToBatchNdLayer>(spaceToBatchNdDescriptor, name);
1279}
1280
Aron Virginas-Tar972af152019-06-11 14:14:03 +01001281IConnectableLayer* Network::AddSpaceToDepthLayer(const SpaceToDepthDescriptor& spaceToDepthDescriptor,
1282 const char* name)
1283{
1284 return m_Graph->AddLayer<SpaceToDepthLayer>(spaceToDepthDescriptor, name);
1285}
1286
telsoa014fcda012018-03-09 14:13:49 +00001287IConnectableLayer* Network::AddFloorLayer(const char* name)
1288{
1289 return m_Graph->AddLayer<FloorLayer>(name);
1290}
1291
telsoa01c577f2c2018-08-31 09:22:23 +01001292IConnectableLayer* Network::AddLstmLayer(const LstmDescriptor& descriptor,
1293 const LstmInputParams& params,
1294 const char* name)
1295{
1296 const auto layer = m_Graph->AddLayer<LstmLayer>(descriptor, name);
1297
1298 //Lstm Basic Parameters
1299 layer->m_BasicParameters.m_InputToForgetWeights =
1300 std::make_unique<ScopedCpuTensorHandle>(*(params.m_InputToForgetWeights));
1301 layer->m_BasicParameters.m_InputToCellWeights =
1302 std::make_unique<ScopedCpuTensorHandle>(*(params.m_InputToCellWeights));
1303 layer->m_BasicParameters.m_InputToOutputWeights =
1304 std::make_unique<ScopedCpuTensorHandle>(*(params.m_InputToOutputWeights));
1305 layer->m_BasicParameters.m_RecurrentToForgetWeights =
1306 std::make_unique<ScopedCpuTensorHandle>(*(params.m_RecurrentToForgetWeights));
1307 layer->m_BasicParameters.m_RecurrentToCellWeights =
1308 std::make_unique<ScopedCpuTensorHandle>(*(params.m_RecurrentToCellWeights));
1309 layer->m_BasicParameters.m_RecurrentToOutputWeights =
1310 std::make_unique<ScopedCpuTensorHandle>(*(params.m_RecurrentToOutputWeights));
1311 layer->m_BasicParameters.m_ForgetGateBias =
1312 std::make_unique<ScopedCpuTensorHandle>(*(params.m_ForgetGateBias));
1313 layer->m_BasicParameters.m_CellBias =
1314 std::make_unique<ScopedCpuTensorHandle>(*(params.m_CellBias));
1315 layer->m_BasicParameters.m_OutputGateBias =
1316 std::make_unique<ScopedCpuTensorHandle>(*(params.m_OutputGateBias));
1317
1318 //Lstm Cifg parameters
1319 if(!descriptor.m_CifgEnabled)
1320 {
1321 if(params.m_InputToInputWeights == nullptr)
1322 {
1323 throw InvalidArgumentException("AddLstmLayer: Input To Input Weights cannot be NULL");
1324 }
1325 if(params.m_RecurrentToInputWeights == nullptr)
1326 {
1327 throw InvalidArgumentException(
1328 "AddLstmLayer: Recurrent To Input Weights cannot be NULL");
1329 }
1330 if(params.m_InputGateBias == nullptr)
1331 {
1332 throw InvalidArgumentException("AddLstmLayer: Input Gate Bias cannot be NULL");
1333 }
1334 layer->m_CifgParameters.m_InputToInputWeights =
1335 std::make_unique<ScopedCpuTensorHandle>(*(params.m_InputToInputWeights));
1336 layer->m_CifgParameters.m_RecurrentToInputWeights =
1337 std::make_unique<ScopedCpuTensorHandle>(*(params.m_RecurrentToInputWeights));
1338 // In the VTS tests, cell-to-input weights may be null, even if the other CIFG params are not.
1339 if(params.m_CellToInputWeights != nullptr)
1340 {
1341 layer->m_CifgParameters.m_CellToInputWeights =
1342 std::make_unique<ScopedCpuTensorHandle>(*(params.m_CellToInputWeights));
1343 }
1344 layer->m_CifgParameters.m_InputGateBias =
1345 std::make_unique<ScopedCpuTensorHandle>(*(params.m_InputGateBias));
1346 }
1347
1348 //Lstm projection parameters
1349 if(descriptor.m_ProjectionEnabled)
1350 {
1351 if(params.m_ProjectionWeights == nullptr)
1352 {
1353 throw InvalidArgumentException("AddLstmLayer: Projection Weights cannot be NULL");
1354 }
1355 layer->m_ProjectionParameters.m_ProjectionWeights =
1356 std::make_unique<ScopedCpuTensorHandle>(*(params.m_ProjectionWeights));
1357 if(params.m_ProjectionBias != nullptr)
1358 {
1359 layer->m_ProjectionParameters.m_ProjectionBias =
1360 std::make_unique<ScopedCpuTensorHandle>(*(params.m_ProjectionBias));
1361 }
1362 }
1363
1364 //Lstm Peephole params
1365 if(descriptor.m_PeepholeEnabled)
1366 {
1367 if(params.m_CellToForgetWeights == nullptr)
1368 {
1369 throw InvalidArgumentException("AddLstmLayer: Cell To Forget Weights cannot be NULL");
1370 }
1371 if(params.m_CellToOutputWeights == nullptr)
1372 {
1373 throw InvalidArgumentException("AddLstmLayer: Cell To Output Weights cannot be NULL");
1374 }
1375 layer->m_PeepholeParameters.m_CellToForgetWeights =
1376 std::make_unique<ScopedCpuTensorHandle>(*(params.m_CellToForgetWeights));
1377 layer->m_PeepholeParameters.m_CellToOutputWeights =
1378 std::make_unique<ScopedCpuTensorHandle>(*(params.m_CellToOutputWeights));
1379 }
Jan Eilersf8c62972019-07-17 11:07:49 +01001380
1381 //Lstm Layer Normalization params
1382 if(descriptor.m_LayerNormEnabled)
1383 {
1384 if(!descriptor.m_CifgEnabled)
1385 {
1386 if(params.m_InputLayerNormWeights == nullptr)
1387 {
1388 throw InvalidArgumentException("AddLstmLayer: Input layer normalization weights cannot be NULL");
1389 }
1390 layer->m_LayerNormParameters.m_InputLayerNormWeights =
1391 std::make_unique<ScopedCpuTensorHandle>(*(params.m_InputLayerNormWeights));
1392 }
1393
1394 if(params.m_ForgetLayerNormWeights == nullptr)
1395 {
1396 throw InvalidArgumentException("AddLstmLayer: Forget layer normalization weights cannot be NULL");
1397 }
1398 if(params.m_CellLayerNormWeights == nullptr)
1399 {
1400 throw InvalidArgumentException("AddLstmLayer: Cell layer normalization weights cannot be NULL");
1401 }
1402 if(params.m_OutputLayerNormWeights == nullptr)
1403 {
1404 throw InvalidArgumentException("AddLstmLayer: Output layer normalization weights cannot be NULL");
1405 }
1406 layer->m_LayerNormParameters.m_ForgetLayerNormWeights =
1407 std::make_unique<ScopedCpuTensorHandle>(*(params.m_ForgetLayerNormWeights));
1408 layer->m_LayerNormParameters.m_CellLayerNormWeights =
1409 std::make_unique<ScopedCpuTensorHandle>(*(params.m_CellLayerNormWeights));
1410 layer->m_LayerNormParameters.m_OutputLayerNormWeights =
1411 std::make_unique<ScopedCpuTensorHandle>(*(params.m_OutputLayerNormWeights));
1412 }
telsoa01c577f2c2018-08-31 09:22:23 +01001413 return layer;
1414}
1415
Francis Murtaghe7a86a42018-08-29 12:42:10 +01001416IConnectableLayer* Network::AddDivisionLayer(const char* name)
1417{
1418 return m_Graph->AddLayer<DivisionLayer>(name);
1419}
1420
David Beck19526222018-09-12 16:00:08 +01001421IConnectableLayer* Network::AddSubtractionLayer(const char* name)
1422{
1423 return m_Graph->AddLayer<SubtractionLayer>(name);
1424}
1425
narpra0132b90462018-09-13 11:07:48 +01001426IConnectableLayer* Network::AddMeanLayer(const MeanDescriptor& meanDescriptor, const char* name)
1427{
1428 return m_Graph->AddLayer<MeanLayer>(meanDescriptor,name);
1429}
1430
Mohamed Nour Abouelseoud5662c202018-09-24 13:30:09 +01001431IConnectableLayer* Network::AddPadLayer(const PadDescriptor& padDescriptor, const char* name)
1432{
1433 return m_Graph->AddLayer<PadLayer>(padDescriptor,name);
1434}
1435
Derek Lambertia9cca6a2019-03-25 15:41:58 +00001436IConnectableLayer *Network::AddQuantizeLayer(const char *name)
1437{
1438 return m_Graph->AddLayer<QuantizeLayer>(name);
1439}
1440
Nattapat Chaimanowonge4294fd2019-03-28 09:56:53 +00001441IConnectableLayer* Network::AddDequantizeLayer(const char* name)
1442{
1443 return m_Graph->AddLayer<DequantizeLayer>(name);
1444}
1445
Conor Kennedy430b5d82018-11-14 15:28:28 +00001446IConnectableLayer* Network::AddStridedSliceLayer(const StridedSliceDescriptor& stridedSliceDescriptor,
1447 const char* name)
1448{
1449 return m_Graph->AddLayer<StridedSliceLayer>(stridedSliceDescriptor, name);
1450}
1451
Matteo Martincigh59a950c2018-12-13 12:48:25 +00001452IConnectableLayer* Network::AddGreaterLayer(const char* name)
1453{
Aron Virginas-Tar77bfb5e2019-10-16 17:45:38 +01001454 return AddComparisonLayer(ComparisonDescriptor(ComparisonOperation::Greater), name);
Matteo Martincigh59a950c2018-12-13 12:48:25 +00001455}
1456
FrancisMurtagh20995952018-12-17 12:11:36 +00001457IConnectableLayer* Network::AddEqualLayer(const char* name)
1458{
Aron Virginas-Tar77bfb5e2019-10-16 17:45:38 +01001459 return AddComparisonLayer(ComparisonDescriptor(ComparisonOperation::Equal), name);
FrancisMurtagh20995952018-12-17 12:11:36 +00001460}
1461
Mohamed Nour Abouelseouda1d3c6a2018-12-27 12:39:16 +00001462IConnectableLayer* Network::AddRsqrtLayer(const char * name)
1463{
1464 return m_Graph->AddLayer<RsqrtLayer>(name);
1465}
1466
narpra01b89b05f2019-01-16 09:53:09 +00001467IConnectableLayer* Network::AddGatherLayer(const char* name)
1468{
1469 return m_Graph->AddLayer<GatherLayer>(name);
1470}
1471
Nattapat Chaimanowong1f886302019-04-05 13:37:19 +01001472IConnectableLayer* Network::AddMergeLayer(const char* name)
1473{
1474 return m_Graph->AddLayer<MergeLayer>(name);
1475}
1476
Sadik Armaganeff363d2019-04-05 15:25:46 +01001477IConnectableLayer* Network::AddSwitchLayer(const char* name)
1478{
1479 return m_Graph->AddLayer<SwitchLayer>(name);
1480}
1481
Matteo Martincigh0e406ee2019-06-12 15:42:18 +01001482IConnectableLayer* Network::AddPreluLayer(const char* name)
1483{
1484 return m_Graph->AddLayer<PreluLayer>(name);
1485}
1486
Aron Virginas-Tar639fb042019-06-20 14:28:19 +01001487IConnectableLayer* Network::AddTransposeConvolution2dLayer(const TransposeConvolution2dDescriptor& descriptor,
1488 const ConstTensor& weights,
1489 const Optional<ConstTensor>& biases,
1490 const char* name)
1491{
1492 if (descriptor.m_BiasEnabled && !biases.has_value())
1493 {
1494 throw InvalidArgumentException("AddTransposeConvolution2dLayer: Biases cannot be empty");
1495 }
1496
1497 const auto layer = m_Graph->AddLayer<TransposeConvolution2dLayer>(descriptor, name);
1498
1499 layer->m_Weight = std::make_unique<ScopedCpuTensorHandle>(weights);
1500
1501 if (descriptor.m_BiasEnabled)
1502 {
1503 layer->m_Bias = std::make_unique<ScopedCpuTensorHandle>(biases.value());
1504 }
1505
1506 return layer;
1507}
1508
Matthew Jackson2b8c1da2019-07-04 14:59:16 +01001509IConnectableLayer* Network::AddStackLayer(const StackDescriptor& stackDescriptor,
1510 const char* name)
1511{
1512 return m_Graph->AddLayer<StackLayer>(stackDescriptor, name);
1513}
1514
Derek Lamberti013c3902019-10-21 10:46:16 +01001515
1516IConnectableLayer* Network::AddStandInLayer(const StandInDescriptor& desc,
1517 const char* name)
1518{
1519 return m_Graph->AddLayer<StandInLayer>(desc, name);
1520}
1521
James Conroyee18dc82019-07-17 11:27:46 +01001522IConnectableLayer* Network::AddQuantizedLstmLayer(const QuantizedLstmInputParams& params,
1523 const char* name)
1524{
1525 const auto layer = m_Graph->AddLayer<QuantizedLstmLayer>(name);
1526
1527 // InputToX weights
1528 layer->m_QuantizedLstmParameters.m_InputToInputWeights =
Francis Murtaghbb590b42019-08-14 09:51:36 +01001529 std::make_unique<ScopedCpuTensorHandle>(params.GetInputToInputWeights());
James Conroyee18dc82019-07-17 11:27:46 +01001530 layer->m_QuantizedLstmParameters.m_InputToForgetWeights =
Francis Murtaghbb590b42019-08-14 09:51:36 +01001531 std::make_unique<ScopedCpuTensorHandle>(params.GetInputToForgetWeights());
James Conroyee18dc82019-07-17 11:27:46 +01001532 layer->m_QuantizedLstmParameters.m_InputToCellWeights =
Francis Murtaghbb590b42019-08-14 09:51:36 +01001533 std::make_unique<ScopedCpuTensorHandle>(params.GetInputToCellWeights());
James Conroyee18dc82019-07-17 11:27:46 +01001534 layer->m_QuantizedLstmParameters.m_InputToOutputWeights =
Francis Murtaghbb590b42019-08-14 09:51:36 +01001535 std::make_unique<ScopedCpuTensorHandle>(params.GetInputToOutputWeights());
James Conroyee18dc82019-07-17 11:27:46 +01001536
1537 // RecurrentToX weights
1538 layer->m_QuantizedLstmParameters.m_RecurrentToInputWeights =
Francis Murtaghbb590b42019-08-14 09:51:36 +01001539 std::make_unique<ScopedCpuTensorHandle>(params.GetRecurrentToInputWeights());
James Conroyee18dc82019-07-17 11:27:46 +01001540 layer->m_QuantizedLstmParameters.m_RecurrentToForgetWeights =
Francis Murtaghbb590b42019-08-14 09:51:36 +01001541 std::make_unique<ScopedCpuTensorHandle>(params.GetRecurrentToForgetWeights());
James Conroyee18dc82019-07-17 11:27:46 +01001542 layer->m_QuantizedLstmParameters.m_RecurrentToCellWeights =
Francis Murtaghbb590b42019-08-14 09:51:36 +01001543 std::make_unique<ScopedCpuTensorHandle>(params.GetRecurrentToCellWeights());
James Conroyee18dc82019-07-17 11:27:46 +01001544 layer->m_QuantizedLstmParameters.m_RecurrentToOutputWeights =
Francis Murtaghbb590b42019-08-14 09:51:36 +01001545 std::make_unique<ScopedCpuTensorHandle>(params.GetRecurrentToOutputWeights());
James Conroyee18dc82019-07-17 11:27:46 +01001546
1547 // Bias
1548 layer->m_QuantizedLstmParameters.m_InputGateBias =
Francis Murtaghbb590b42019-08-14 09:51:36 +01001549 std::make_unique<ScopedCpuTensorHandle>(params.GetInputGateBias());
James Conroyee18dc82019-07-17 11:27:46 +01001550 layer->m_QuantizedLstmParameters.m_ForgetGateBias =
Francis Murtaghbb590b42019-08-14 09:51:36 +01001551 std::make_unique<ScopedCpuTensorHandle>(params.GetForgetGateBias());
James Conroyee18dc82019-07-17 11:27:46 +01001552 layer->m_QuantizedLstmParameters.m_CellBias =
Francis Murtaghbb590b42019-08-14 09:51:36 +01001553 std::make_unique<ScopedCpuTensorHandle>(params.GetCellBias());
James Conroyee18dc82019-07-17 11:27:46 +01001554 layer->m_QuantizedLstmParameters.m_OutputGateBias =
Francis Murtaghbb590b42019-08-14 09:51:36 +01001555 std::make_unique<ScopedCpuTensorHandle>(params.GetOutputGateBias());
James Conroyee18dc82019-07-17 11:27:46 +01001556
1557 return layer;
1558}
1559
Mike Kelly8c1701a2019-02-11 17:01:27 +00001560void Network::Accept(ILayerVisitor& visitor) const
1561{
1562 for (auto layer : GetGraph())
1563 {
1564 layer->Accept(visitor);
1565 };
1566}
1567
telsoa014fcda012018-03-09 14:13:49 +00001568OptimizedNetwork::OptimizedNetwork(std::unique_ptr<Graph> graph)
Jan Eilers99d9d4a2019-11-06 10:02:16 +00001569 : m_Graph(std::move(graph)),
1570 m_Guid(profiling::ProfilingService::Instance().NextGuid())
telsoa014fcda012018-03-09 14:13:49 +00001571{
1572}
1573
1574OptimizedNetwork::~OptimizedNetwork()
1575{
1576}
1577
1578} // namespace armnn