blob: c8d5a1690d2c0dbce78c04e842c35b0d12e9b798 [file] [log] [blame]
Laurent Carlier749294b2020-06-01 09:03:17 +01001//
Finn Williamsf24effa2020-07-03 10:12:03 +01002// Copyright © 2017 Arm Ltd and Contributors. All rights reserved.
David Beckecb56cd2018-09-05 12:52:57 +01003// SPDX-License-Identifier: MIT
telsoa014fcda012018-03-09 14:13:49 +00004//
5#include "Layer.hpp"
6
7#include "Graph.hpp"
janeil013fec1ea2019-11-07 09:47:20 +00008#include <ProfilingService.hpp>
Matthew Sloyan0663d662020-09-14 11:47:26 +01009#include <armnn/utility/NumericCast.hpp>
Aron Virginas-Tarc9cc8042018-11-01 16:15:57 +000010#include <backendsCommon/WorkloadData.hpp>
11#include <backendsCommon/CpuTensorHandle.hpp>
telsoa014fcda012018-03-09 14:13:49 +000012
telsoa014fcda012018-03-09 14:13:49 +000013#include <boost/format.hpp>
telsoa014fcda012018-03-09 14:13:49 +000014
15#include <numeric>
16
17namespace armnn
18{
19
20void InputSlot::Insert(Layer& layer)
21{
Narumol Prangnawaratac2770a2020-04-01 16:51:23 +010022 ARMNN_ASSERT(layer.GetNumOutputSlots() == 1);
telsoa014fcda012018-03-09 14:13:49 +000023
24 OutputSlot* const prevSlot = GetConnectedOutputSlot();
25
26 if (prevSlot != nullptr)
27 {
telsoa01c577f2c2018-08-31 09:22:23 +010028 // Disconnects parent from this.
telsoa014fcda012018-03-09 14:13:49 +000029 prevSlot->Disconnect(*this);
30
telsoa01c577f2c2018-08-31 09:22:23 +010031 // Connects inserted layer to parent.
Narumol Prangnawaratac2770a2020-04-01 16:51:23 +010032 ARMNN_ASSERT(layer.GetNumInputSlots() == 1);
Derek Lamberti84da38b2019-06-13 11:40:08 +010033 int idx = prevSlot->Connect(layer.GetInputSlot(0));
Matthew Sloyan0663d662020-09-14 11:47:26 +010034 prevSlot->SetEdgeStrategy(armnn::numeric_cast<unsigned int>(idx), EdgeStrategy::Undefined);
telsoa014fcda012018-03-09 14:13:49 +000035
telsoa01c577f2c2018-08-31 09:22:23 +010036 // Sets tensor info for inserted layer.
telsoa014fcda012018-03-09 14:13:49 +000037 const TensorInfo& tensorInfo = prevSlot->GetTensorInfo();
38 layer.GetOutputHandler().SetTensorInfo(tensorInfo);
39 }
40
telsoa01c577f2c2018-08-31 09:22:23 +010041 // Connects inserted layer to this.
telsoa014fcda012018-03-09 14:13:49 +000042 layer.GetOutputSlot(0).Connect(*this);
Derek Lambertif674aa02019-08-01 15:56:25 +010043 layer.GetOutputSlot(0).SetEdgeStrategy(0, EdgeStrategy::Undefined);
telsoa014fcda012018-03-09 14:13:49 +000044}
45
46const InputSlot* OutputSlot::GetConnection(unsigned int index) const
47{
48 ValidateConnectionIndex(index);
49 return m_Connections[index];
50}
51
52InputSlot* OutputSlot::GetConnection(unsigned int index)
53{
54 ValidateConnectionIndex(index);
55 return m_Connections[index];
56}
57
58void OutputSlot::SetTensorInfo(const TensorInfo& tensorInfo)
59{
60 GetOutputHandler().SetTensorInfo(tensorInfo);
61}
62
63const TensorInfo& OutputSlot::GetTensorInfo() const
64{
65 return GetOutputHandler().GetTensorInfo();
66}
67
68bool OutputSlot::IsTensorInfoSet() const
69{
Finn Williamsf24effa2020-07-03 10:12:03 +010070 if (GetOwningLayer().GetShapeInferenceMethod() == ShapeInferenceMethod::InferAndValidate)
71 {
72 GetOwningLayer().ValidateTensorShapesFromInputs();
73 }
telsoa014fcda012018-03-09 14:13:49 +000074 return GetOutputHandler().IsTensorInfoSet();
75}
76
77bool OutputSlot::ValidateTensorShape(const TensorShape& shape) const
78{
Narumol Prangnawaratac2770a2020-04-01 16:51:23 +010079 ARMNN_ASSERT_MSG(IsTensorInfoSet(), "TensorInfo must be set in order to validate the shape.");
telsoa014fcda012018-03-09 14:13:49 +000080 return shape == m_OutputHandler.GetTensorInfo().GetShape();
81}
82
83int OutputSlot::Connect(InputSlot& destination)
84{
85 destination.SetConnection(this);
86 m_Connections.push_back(&destination);
Derek Lambertif674aa02019-08-01 15:56:25 +010087 m_EdgeStrategies.push_back(EdgeStrategy::Undefined);
Matthew Sloyan0663d662020-09-14 11:47:26 +010088 return armnn::numeric_cast<int>(m_Connections.size() - 1);
telsoa014fcda012018-03-09 14:13:49 +000089}
90
91void OutputSlot::Disconnect(InputSlot& slot)
92{
93 slot.SetConnection(nullptr);
Derek Lamberti84da38b2019-06-13 11:40:08 +010094 auto it = std::find(m_Connections.begin(), m_Connections.end(), &slot);
95
96 if (it == m_Connections.end())
97 {
98 return;
99 }
100
101 auto idx = std::distance(m_Connections.begin(), it);
telsoa014fcda012018-03-09 14:13:49 +0000102 m_Connections.erase(std::remove(m_Connections.begin(), m_Connections.end(), &slot), m_Connections.end());
Derek Lamberti84da38b2019-06-13 11:40:08 +0100103
Derek Lambertif674aa02019-08-01 15:56:25 +0100104 m_EdgeStrategies.erase(m_EdgeStrategies.begin() + idx);
telsoa014fcda012018-03-09 14:13:49 +0000105}
106
107void OutputSlot::DisconnectAll()
108{
109 while (GetNumConnections() > 0)
110 {
111 InputSlot& connection = *GetConnection(0);
112 Disconnect(connection);
113 }
114}
115
116void OutputSlot::MoveAllConnections(OutputSlot& destination)
117{
118 while (GetNumConnections() > 0)
119 {
Narumol Prangnawaratac2770a2020-04-01 16:51:23 +0100120 ARMNN_ASSERT_MSG(m_EdgeStrategies[0] == EdgeStrategy::Undefined,
Derek Lamberti84da38b2019-06-13 11:40:08 +0100121 "Cannot move connections once memory strategies have be established.");
122
telsoa014fcda012018-03-09 14:13:49 +0000123 InputSlot& connection = *GetConnection(0);
124 Disconnect(connection);
125 destination.Connect(connection);
Finn Williams3e6676d2020-05-14 13:41:48 +0100126 destination.GetOutputHandler().SetTensorInfo(GetOutputHandler().GetTensorInfo());
telsoa014fcda012018-03-09 14:13:49 +0000127 }
128}
129
Derek Lamberti27d83072019-02-05 16:00:08 +0000130unsigned int OutputSlot::CalculateIndexOnOwner() const
131{
Matteo Martincigh9c5d33a2019-02-07 17:52:41 +0000132 for (unsigned int i = 0; i < GetOwningLayer().GetNumOutputSlots(); i++)
Derek Lamberti27d83072019-02-05 16:00:08 +0000133 {
134 if (GetOwningLayer().GetOutputSlot(i) == (*this))
135 {
136 return i;
137 }
138 }
Narumol Prangnawaratac2770a2020-04-01 16:51:23 +0100139 ARMNN_ASSERT_MSG(false, "Did not find slot on owner.");
Derek Lamberti27d83072019-02-05 16:00:08 +0000140 return 0; // Error
141}
142
143bool OutputSlot::operator==(const OutputSlot& other) const
144{
145 bool isSame = other.GetNumConnections() == GetNumConnections();
146 if (!isSame)
147 {
148 return false;
149 }
150
Matteo Martincigh9c5d33a2019-02-07 17:52:41 +0000151 for (unsigned int i = 0; i < GetNumConnections(); i++)
Derek Lamberti27d83072019-02-05 16:00:08 +0000152 {
153 isSame &= other.GetConnection(i) == GetConnection(i);
154 }
155 return isSame;
156}
157
telsoa014fcda012018-03-09 14:13:49 +0000158void OutputSlot::ValidateConnectionIndex(unsigned int index) const
159{
Matthew Sloyan0663d662020-09-14 11:47:26 +0100160 if (armnn::numeric_cast<std::size_t>(index) >= m_Connections.size())
telsoa014fcda012018-03-09 14:13:49 +0000161 {
162 throw InvalidArgumentException(
163 boost::str(boost::format("GetConnection: Invalid index %1% provided") % index));
164 }
165}
166
Mike Kelly8c1701a2019-02-11 17:01:27 +0000167LayerGuid OutputSlot::GetOwningLayerGuid() const
168{
169 return GetOwningLayer().GetGuid();
170}
171
Derek Lamberti84da38b2019-06-13 11:40:08 +0100172void OutputSlot::SetTensorHandleFactory(const ITensorHandleFactory::FactoryId& id)
173{
174 m_TensorHandleFactoryId = id;
175}
176
177ITensorHandleFactory::FactoryId OutputSlot::GetTensorHandleFactoryId() const
178{
179 return m_TensorHandleFactoryId;
180}
181
Derek Lambertif674aa02019-08-01 15:56:25 +0100182void OutputSlot::SetEdgeStrategy(unsigned int connectionIndex, EdgeStrategy strategy)
Derek Lamberti84da38b2019-06-13 11:40:08 +0100183{
Derek Lambertif674aa02019-08-01 15:56:25 +0100184 m_EdgeStrategies[connectionIndex] = strategy;
Derek Lamberti84da38b2019-06-13 11:40:08 +0100185}
186
Derek Lambertif674aa02019-08-01 15:56:25 +0100187EdgeStrategy OutputSlot::GetEdgeStrategyForConnection(unsigned int connectionIdx) const
Derek Lamberti84da38b2019-06-13 11:40:08 +0100188{
Derek Lambertif674aa02019-08-01 15:56:25 +0100189 return m_EdgeStrategies[connectionIdx];
Derek Lamberti84da38b2019-06-13 11:40:08 +0100190}
191
Derek Lamberti0cff1632018-09-18 16:02:25 +0100192Layer::Layer(unsigned int numInputSlots,
193 unsigned int numOutputSlots,
194 LayerType type,
195 DataLayout layout,
196 const char* name)
telsoa014fcda012018-03-09 14:13:49 +0000197: m_OutputHandlers(numOutputSlots)
Finn Williamsf24effa2020-07-03 10:12:03 +0100198, m_ShapeInferenceMethod(ShapeInferenceMethod::ValidateOnly)
telsoa014fcda012018-03-09 14:13:49 +0000199, m_LayerName(name ? name : "")
200, m_Type(type)
Matteo Martincigh992d6dc2019-01-10 17:34:20 +0000201, m_BackendId()
Derek Lamberti4a9e24b2020-01-03 16:53:38 +0000202, m_BackendHint(EmptyOptional())
Sadik Armagan3184c902020-03-18 10:57:30 +0000203, m_Guid(profiling::ProfilingService::GetNextGuid())
telsoa014fcda012018-03-09 14:13:49 +0000204{
Jan Eilers8eb25602020-03-09 12:13:48 +0000205 IgnoreUnused(layout);
telsoa014fcda012018-03-09 14:13:49 +0000206 m_InputSlots.reserve(numInputSlots);
207 for (unsigned int i = 0; i < numInputSlots; ++i)
208 {
209 m_InputSlots.emplace_back(*this, i);
210 }
211
212 m_OutputSlots.reserve(numOutputSlots);
213 for (unsigned int i = 0; i < numOutputSlots; ++i)
214 {
215 m_OutputSlots.emplace_back(*this, m_OutputHandlers[i]);
216 }
217}
218
Derek Lamberti0cff1632018-09-18 16:02:25 +0100219Layer::Layer(unsigned int numInputSlots,
220 unsigned int numOutputSlots,
221 LayerType type,
222 const char* name)
223: Layer(numInputSlots, numOutputSlots, type, DataLayout::NCHW, name)
224{
225}
226
Derek Lamberti94a88d22019-12-10 21:12:59 +0000227void Layer::CollectWorkloadInputs(WorkloadDataCollector& dataCollector) const
telsoa014fcda012018-03-09 14:13:49 +0000228{
229 for (auto&& inputSlot : GetInputSlots())
230 {
telsoa01c577f2c2018-08-31 09:22:23 +0100231 // The graph must be well-formed at this point.
Narumol Prangnawaratac2770a2020-04-01 16:51:23 +0100232 ARMNN_ASSERT(inputSlot.GetConnection());
telsoa014fcda012018-03-09 14:13:49 +0000233 const OutputHandler& outputHandler = inputSlot.GetConnectedOutputSlot()->GetOutputHandler();
234 dataCollector.Push(outputHandler.GetData(), outputHandler.GetTensorInfo());
235 }
236}
237
Derek Lamberti94a88d22019-12-10 21:12:59 +0000238void Layer::CollectWorkloadOutputs(WorkloadDataCollector& dataCollector) const
telsoa014fcda012018-03-09 14:13:49 +0000239{
240 for (auto&& outputHandler : m_OutputHandlers)
241 {
242 outputHandler.CollectWorkloadOutputs(dataCollector);
243 }
244}
245
David Monahan3fb7e102019-08-20 11:25:29 +0100246void Layer::CreateTensorHandles(const TensorHandleFactoryRegistry& registry,
247 const IWorkloadFactory& workloadFactory,
248 const bool IsMemoryManaged)
telsoa014fcda012018-03-09 14:13:49 +0000249{
Derek Lamberti84da38b2019-06-13 11:40:08 +0100250 for (unsigned int idx=0; idx < GetNumOutputSlots(); idx++)
telsoa014fcda012018-03-09 14:13:49 +0000251 {
Derek Lamberti84da38b2019-06-13 11:40:08 +0100252
253 OutputSlot& slot = GetOutputSlot(idx);
254 ITensorHandleFactory::FactoryId factoryId = slot.GetTensorHandleFactoryId();
255
256 OutputHandler& handler = GetOutputHandler(idx);
257 if (factoryId == ITensorHandleFactory::LegacyFactoryId)
258 {
David Monahan3fb7e102019-08-20 11:25:29 +0100259 handler.CreateTensorHandles(workloadFactory, IsMemoryManaged);
Derek Lamberti84da38b2019-06-13 11:40:08 +0100260 }
261 else
262 {
263 ITensorHandleFactory* handleFactory = registry.GetFactory(factoryId);
Narumol Prangnawaratac2770a2020-04-01 16:51:23 +0100264 ARMNN_ASSERT(handleFactory);
David Monahan3fb7e102019-08-20 11:25:29 +0100265 handler.CreateTensorHandles(*handleFactory, IsMemoryManaged);
Derek Lamberti84da38b2019-06-13 11:40:08 +0100266 }
telsoa014fcda012018-03-09 14:13:49 +0000267 }
268}
269
telsoa01c577f2c2018-08-31 09:22:23 +0100270void Layer::ReleaseConstantData()
271{
272 // Now free up the static data.
273 OperateOnConstantTensors([](std::unique_ptr<ScopedCpuTensorHandle>& handle)
274 {
275 handle.reset(nullptr);
276 });
277}
278
telsoa014fcda012018-03-09 14:13:49 +0000279DataType Layer::GetDataType() const
280{
telsoa01c577f2c2018-08-31 09:22:23 +0100281 if (GetNumInputSlots() > 0) // Ignore the input layer.
telsoa014fcda012018-03-09 14:13:49 +0000282 {
283 return GetInputSlot(0).GetConnection()->GetTensorInfo().GetDataType();
284 }
telsoa01c577f2c2018-08-31 09:22:23 +0100285 return GetOutputSlot(0).GetTensorInfo().GetDataType();
telsoa014fcda012018-03-09 14:13:49 +0000286}
287
288void Layer::ResetPriority() const
289{
290 m_Priority = 0;
291 m_Visiting = false;
292}
293
294LayerPriority Layer::GetPriority() const
295{
296 constexpr LayerPriority inputPrio = std::numeric_limits<LayerPriority>::lowest();
297 constexpr LayerPriority outputPrio = std::numeric_limits<LayerPriority>::max();
298
299 if (GetType() == LayerType::Input)
300 {
301 m_Priority = inputPrio;
302 }
303 else if (GetType() == LayerType::Output)
304 {
305 m_Priority = outputPrio;
306 }
307 else if (m_Priority == 0)
308 {
309 if (m_Visiting)
310 {
311 throw GraphValidationException("Graph has circular dependencies: cannot walk");
312 }
313
314 auto maxPrio = [](const LayerPriority prio, const InputSlot& slot) -> LayerPriority
315 {
Matthew Benthamd78b891d2019-04-30 10:17:40 +0100316 const OutputSlot *outputSlot = slot.GetConnectedOutputSlot();
317 if (outputSlot)
318 {
319 const Layer& input = outputSlot->GetOwningLayer();
320 return std::max(prio, input.GetPriority());
321 }
322 else
323 {
324 // unconnected input slot
325 return prio;
326 }
telsoa014fcda012018-03-09 14:13:49 +0000327 };
328
329 m_Visiting = true;
330 LayerPriority parentPrio = std::accumulate(GetInputSlots().cbegin(), GetInputSlots().cend(), 0U, maxPrio);
331 m_Visiting = false;
332
333 if (parentPrio >= outputPrio)
334 {
335 throw GraphValidationException("Graph has too many edges");
336 }
337
338 m_Priority = parentPrio + 1U;
339 }
340
341 return m_Priority;
342}
343
telsoa01c577f2c2018-08-31 09:22:23 +0100344void Layer::VerifyLayerConnections(unsigned int expectedConnections, const CheckLocation& location) const
345{
Narumol Prangnawaratac2770a2020-04-01 16:51:23 +0100346 ARMNN_ASSERT(GetNumInputSlots() == expectedConnections);
telsoa01c577f2c2018-08-31 09:22:23 +0100347
348 for (unsigned int i=0; i<expectedConnections; ++i)
349 {
350 if (GetInputSlot(i).GetConnection() == nullptr)
351 {
352 throw LayerValidationException(
353 boost::str(
354 boost::format(
355 "Input connection #%1% must be connected "
356 "for %2% layer %3% %4%")
357 % i
358 % GetLayerTypeAsCString(this->GetType())
359 % GetNameStr()
360 % location.AsString()));
361 }
telsoa01c577f2c2018-08-31 09:22:23 +0100362 }
363}
364
365std::vector<TensorShape> Layer::InferOutputShapes(const std::vector<TensorShape>& inputShapes) const
366{
Narumol Prangnawaratac2770a2020-04-01 16:51:23 +0100367 ARMNN_ASSERT(GetNumInputSlots() != 0);
368 ARMNN_ASSERT(GetNumOutputSlots() != 0);
telsoa01c577f2c2018-08-31 09:22:23 +0100369
370 // By default we return what we got, meaning the output shape(s) are the same as the input(s).
371 // This only works if the number of inputs and outputs are the same. Since we are in the Layer
372 // base class, this means the implementation needs to be overridden in the specific layers for
373 // the other cases. So the missing implementation justifies the UnimplementedException.
374
375 if (GetNumInputSlots() != GetNumOutputSlots())
376 {
377 throw UnimplementedException(
378 boost::str(
379 boost::format(
380 "Default implementation for InferOutputShapes can only be used for "
381 "layers with the same number of input and output slots. This doesn't "
382 "hold for %1% layer %2% (#inputs=%3% #outputs=%4%) %5%")
383 % GetLayerTypeAsCString(this->GetType())
384 % GetNameStr()
385 % GetNumInputSlots()
386 % GetNumOutputSlots()
387 % CHECK_LOCATION().AsString()));
388 }
389 return inputShapes;
390}
Andre Ghattas23ae2ea2019-08-07 12:18:38 +0100391
Finn Williams87d0bda2020-07-03 10:12:03 +0100392void Layer::ValidateAndCopyShape(const TensorShape& outputShape,
393 const TensorShape& inferredShape,
394 const ShapeInferenceMethod shapeInferenceMethod,
395 const std::string& layerName,
396 const unsigned int outputSlotIndex)
397{
398 if (shapeInferenceMethod == ShapeInferenceMethod::ValidateOnly)
399 {
400 ConditionalThrowIfNotEqual<LayerValidationException>(
401 layerName + ": TensorShape set on OutputSlot[0] does not match the inferred shape.",
402 outputShape,
403 inferredShape);
404 return;
405 }
406
407 if (outputShape.GetDimensionality() == Dimensionality::Specified)
408 {
409 for (unsigned int i = 0; i < outputShape.GetNumDimensions(); ++i)
410 {
411 if (outputShape.GetDimensionSpecificity(i) && outputShape[i] != inferredShape[i])
412 {
413 std::stringstream ss;
414 ss << layerName << ": TensorShape set on OutputSlot[" << outputSlotIndex <<
415 "] does not match the inferred shape at dimension index [";
416 ss << i << "] " << outputShape << " != " << inferredShape;
417 throw LayerValidationException(ss.str());
418 }
419 }
420 }
421
422 TensorInfo info = GetOutputSlot(outputSlotIndex).GetTensorInfo();
423
424 armnn::TensorInfo inferredTensorInfo(inferredShape,
425 info.GetDataType(),
426 info.GetQuantizationScale(),
427 info.GetQuantizationOffset());
428
429 GetOutputSlot(outputSlotIndex).SetTensorInfo(inferredTensorInfo);
430}
431
432void Layer::VerifyShapeInferenceType(const TensorShape& outputShape, ShapeInferenceMethod shapeInferenceMethod)
433{
434 if (shapeInferenceMethod == ShapeInferenceMethod::ValidateOnly)
435 {
436 ConditionalThrow<LayerValidationException>(
437 outputShape.GetDimensionality() != Dimensionality::NotSpecified,
438 "Dimensionality can not be NotSpecified while using ShapeInferenceMethod::ValidateOnly");
439
440 ConditionalThrow<LayerValidationException>(
441 outputShape.AreAllDimensionsSpecified(),
442 "Unspecified dimension while using ShapeInferenceMethod::ValidateOnly");
443 }
Finn Williams87d0bda2020-07-03 10:12:03 +0100444}
445
Andre Ghattas23ae2ea2019-08-07 12:18:38 +0100446void Layer::SerializeLayerParameters(ParameterStringifyFunction& fn) const
447{
Rob Hughesb17220d2020-08-28 11:48:35 +0100448 std::string guid = std::to_string(m_Guid);
Andre Ghattas23ae2ea2019-08-07 12:18:38 +0100449 std::string layerType = GetLayerTypeAsCString(m_Type);
450 std::string backendId = std::string(m_BackendId);
Rob Hughesb17220d2020-08-28 11:48:35 +0100451 if (!(guid.compare("") == 0) && !guid.empty())
452 {
453 fn("Guid", guid);
454 }
Andre Ghattas23ae2ea2019-08-07 12:18:38 +0100455 if(!(m_LayerName.compare("") == 0) && !m_LayerName.empty())
456 {
457 fn("LayerName",m_LayerName);
458 }
459 if(!(layerType.compare("") == 0) && !layerType.empty())
460 {
461 fn("LayerType",layerType);
462 }
463 if(!(backendId.compare("") == 0) && !backendId.empty())
464 {
465 fn("BackendID",backendId);
466 }
467}
468
telsoa014fcda012018-03-09 14:13:49 +0000469} // namespace armnn