• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 //
2 // Copyright © 2017 Arm Ltd and Contributors. All rights reserved.
3 // SPDX-License-Identifier: MIT
4 //
5 #include "Layer.hpp"
6 
7 #include "Graph.hpp"
8 
9 #include <armnn/backends/TensorHandle.hpp>
10 #include <armnn/backends/WorkloadData.hpp>
11 
12 #include <armnn/utility/NumericCast.hpp>
13 
14 #include <armnnUtils/TensorUtils.hpp>
15 
16 #include <client/include/IProfilingService.hpp>
17 
18 #include <fmt/format.h>
19 
20 #include <numeric>
21 
22 namespace armnn
23 {
24 
25 // Instantiate the static member variable
26 NullDescriptor Layer::m_NullDescriptor;
27 
AssertNumberOfInputSlots(Layer & layer)28 void AssertNumberOfInputSlots(Layer& layer)
29 {
30     switch (layer.GetType())
31     {
32         case LayerType::Convolution2d:
33         case LayerType::DepthwiseConvolution2d:
34         case LayerType::FullyConnected:
35         {
36             ARMNN_ASSERT(layer.GetNumInputSlots() == 2 ||
37                          layer.GetNumInputSlots() == 3);
38             break;
39         }
40         default:
41         {
42             ARMNN_ASSERT(layer.GetNumInputSlots() == 1);
43             break;
44         }
45     }
46 }
47 
Insert(Layer & layer)48 void InputSlot::Insert(Layer& layer)
49 {
50     ARMNN_ASSERT(layer.GetNumOutputSlots() == 1);
51 
52     OutputSlot* const prevSlot = GetConnectedOutputSlot();
53 
54     if (prevSlot != nullptr)
55     {
56         // Disconnects parent from this.
57         prevSlot->Disconnect(*this);
58 
59         AssertNumberOfInputSlots(layer);
60 
61         // Connects inserted layer to parent.
62         int idx = prevSlot->Connect(layer.GetInputSlot(0));
63         prevSlot->SetEdgeStrategy(armnn::numeric_cast<unsigned int>(idx), EdgeStrategy::Undefined);
64 
65         // Sets tensor info for inserted layer.
66         const TensorInfo& tensorInfo = prevSlot->GetTensorInfo();
67         layer.GetOutputHandler().SetTensorInfo(tensorInfo);
68     }
69 
70     // Connects inserted layer to this.
71     layer.GetOutputSlot(0).Connect(*this);
72     layer.GetOutputSlot(0).SetEdgeStrategy(0, EdgeStrategy::Undefined);
73 }
74 
GetConnection(unsigned int index) const75 const InputSlot* OutputSlot::GetConnection(unsigned int index) const
76 {
77     ValidateConnectionIndex(index);
78     return m_Connections[index];
79 }
80 
GetConnection(unsigned int index)81 InputSlot* OutputSlot::GetConnection(unsigned int index)
82 {
83     ValidateConnectionIndex(index);
84     return m_Connections[index];
85 }
86 
SetTensorInfo(const TensorInfo & tensorInfo)87 void OutputSlot::SetTensorInfo(const TensorInfo& tensorInfo)
88 {
89     GetOutputHandler().SetTensorInfo(tensorInfo);
90 }
91 
GetTensorInfo() const92 const TensorInfo& OutputSlot::GetTensorInfo() const
93 {
94     return GetOutputHandler().GetTensorInfo();
95 }
96 
IsTensorInfoSet() const97 bool OutputSlot::IsTensorInfoSet() const
98 {
99     if (GetOwningLayer().GetShapeInferenceMethod() == ShapeInferenceMethod::InferAndValidate)
100     {
101         GetOwningLayer().ValidateTensorShapesFromInputs();
102     }
103     return GetOutputHandler().IsTensorInfoSet();
104 }
105 
ValidateTensorShape(const TensorShape & shape) const106 bool OutputSlot::ValidateTensorShape(const TensorShape& shape) const
107 {
108     ARMNN_ASSERT_MSG(IsTensorInfoSet(), "TensorInfo must be set in order to validate the shape.");
109     return shape == m_OutputHandler.GetTensorInfo().GetShape();
110 }
111 
Connect(InputSlot & destination)112 int OutputSlot::Connect(InputSlot& destination)
113 {
114     destination.SetConnection(this);
115     m_Connections.push_back(&destination);
116     m_EdgeStrategies.push_back(EdgeStrategy::Undefined);
117     return armnn::numeric_cast<int>(m_Connections.size() - 1);
118 }
119 
Disconnect(InputSlot & slot)120 void OutputSlot::Disconnect(InputSlot& slot)
121 {
122     slot.SetConnection(nullptr);
123     auto it = std::find(m_Connections.begin(), m_Connections.end(), &slot);
124 
125     if (it == m_Connections.end())
126     {
127         return;
128     }
129 
130     auto idx = std::distance(m_Connections.begin(), it);
131     m_Connections.erase(std::remove(m_Connections.begin(), m_Connections.end(), &slot), m_Connections.end());
132 
133     m_EdgeStrategies.erase(m_EdgeStrategies.begin() + idx);
134 }
135 
DisconnectAll()136 void OutputSlot::DisconnectAll()
137 {
138     while (GetNumConnections() > 0)
139     {
140         InputSlot& connection = *GetConnection(0);
141         Disconnect(connection);
142     }
143 }
144 
MoveAllConnections(OutputSlot & destination)145 void OutputSlot::MoveAllConnections(OutputSlot& destination)
146 {
147     while (GetNumConnections() > 0)
148     {
149         ARMNN_ASSERT_MSG(m_EdgeStrategies[0] == EdgeStrategy::Undefined,
150             "Cannot move connections once memory strategies have be established.");
151 
152         InputSlot& connection = *GetConnection(0);
153         Disconnect(connection);
154         destination.Connect(connection);
155         destination.GetOutputHandler().SetTensorInfo(GetOutputHandler().GetTensorInfo());
156     }
157 }
158 
CalculateIndexOnOwner() const159 unsigned int OutputSlot::CalculateIndexOnOwner() const
160 {
161     for (unsigned int i = 0; i < GetOwningLayer().GetNumOutputSlots(); i++)
162     {
163         if (GetOwningLayer().GetOutputSlot(i) == (*this))
164         {
165             return i;
166         }
167     }
168     ARMNN_ASSERT_MSG(false, "Did not find slot on owner.");
169     return 0; // Error
170 }
171 
operator ==(const OutputSlot & other) const172 bool OutputSlot::operator==(const OutputSlot& other) const
173 {
174     bool isSame = other.GetNumConnections() == GetNumConnections();
175     if (!isSame)
176     {
177         return false;
178     }
179 
180     for (unsigned int i = 0; i < GetNumConnections(); i++)
181     {
182         isSame &= other.GetConnection(i) == GetConnection(i);
183     }
184     return isSame;
185 }
186 
ValidateConnectionIndex(unsigned int index) const187 void OutputSlot::ValidateConnectionIndex(unsigned int index) const
188 {
189     if (armnn::numeric_cast<std::size_t>(index) >= m_Connections.size())
190     {
191         throw InvalidArgumentException((fmt::format("GetConnection: Invalid index {} provided", index)));
192     }
193 }
194 
GetOwningLayerGuid() const195 LayerGuid OutputSlot::GetOwningLayerGuid() const
196 {
197     return GetOwningLayer().GetGuid();
198 }
199 
SetTensorHandleFactory(const ITensorHandleFactory::FactoryId & id)200 void OutputSlot::SetTensorHandleFactory(const ITensorHandleFactory::FactoryId& id)
201 {
202     m_TensorHandleFactoryId = id;
203 }
204 
GetTensorHandleFactoryId() const205 ITensorHandleFactory::FactoryId OutputSlot::GetTensorHandleFactoryId() const
206 {
207     return m_TensorHandleFactoryId;
208 }
209 
SetEdgeStrategy(unsigned int connectionIndex,EdgeStrategy strategy)210 void OutputSlot::SetEdgeStrategy(unsigned int connectionIndex, EdgeStrategy strategy)
211 {
212     m_EdgeStrategies[connectionIndex] = strategy;
213 }
214 
GetEdgeStrategyForConnection(unsigned int connectionIdx) const215 EdgeStrategy OutputSlot::GetEdgeStrategyForConnection(unsigned int connectionIdx) const
216 {
217     return m_EdgeStrategies[connectionIdx];
218 }
219 
Layer(unsigned int numInputSlots,unsigned int numOutputSlots,LayerType type,DataLayout layout,const char * name)220 Layer::Layer(unsigned int numInputSlots,
221              unsigned int numOutputSlots,
222              LayerType type,
223              DataLayout layout,
224              const char* name)
225 : m_OutputHandlers(numOutputSlots)
226 , m_ShapeInferenceMethod(ShapeInferenceMethod::ValidateOnly)
227 , m_LayerName(name ? name : "")
228 , m_Type(type)
229 , m_BackendId()
230 , m_BackendHint(EmptyOptional())
231 , m_Guid(arm::pipe::IProfilingService::GetNextGuid())
232 {
233     IgnoreUnused(layout);
234     m_InputSlots.reserve(numInputSlots);
235     for (unsigned int i = 0; i < numInputSlots; ++i)
236     {
237         m_InputSlots.emplace_back(*this, i);
238     }
239 
240     m_OutputSlots.reserve(numOutputSlots);
241     for (unsigned int i = 0; i < numOutputSlots; ++i)
242     {
243         m_OutputSlots.emplace_back(*this, m_OutputHandlers[i]);
244     }
245 }
246 
Layer(unsigned int numInputSlots,unsigned int numOutputSlots,LayerType type,const char * name)247 Layer::Layer(unsigned int numInputSlots,
248              unsigned int numOutputSlots,
249              LayerType type,
250              const char* name)
251 : Layer(numInputSlots, numOutputSlots, type, DataLayout::NCHW, name)
252 {
253 }
254 
CollectWorkloadInputs(WorkloadDataCollector & dataCollector) const255 void Layer::CollectWorkloadInputs(WorkloadDataCollector& dataCollector) const
256 {
257     for (auto&& inputSlot : GetInputSlots())
258     {
259         // The graph must be well-formed at this point.
260         ARMNN_ASSERT(inputSlot.GetConnection());
261         const OutputHandler& outputHandler = inputSlot.GetConnectedOutputSlot()->GetOutputHandler();
262         dataCollector.Push(outputHandler.GetData(), outputHandler.GetTensorInfo());
263     }
264 }
265 
CollectWorkloadOutputs(WorkloadDataCollector & dataCollector) const266 void Layer::CollectWorkloadOutputs(WorkloadDataCollector& dataCollector) const
267 {
268     for (auto&& outputHandler : m_OutputHandlers)
269     {
270         outputHandler.CollectWorkloadOutputs(dataCollector);
271     }
272 }
273 
SetAdditionalInfo(QueueDescriptor & descriptor) const274 void Layer::SetAdditionalInfo(QueueDescriptor& descriptor) const
275 {
276     descriptor.m_AdditionalInfoObject = m_AdditionalInfoObject.get();
277 }
278 
CreateTensorHandles(const TensorHandleFactoryRegistry & registry,const IWorkloadFactory & workloadFactory,const bool IsMemoryManaged)279 void Layer::CreateTensorHandles(const TensorHandleFactoryRegistry& registry,
280                                 const IWorkloadFactory& workloadFactory,
281                                 const bool IsMemoryManaged)
282 {
283     for (unsigned int idx=0; idx < GetNumOutputSlots(); idx++)
284     {
285 
286         OutputSlot& slot = GetOutputSlot(idx);
287         ITensorHandleFactory::FactoryId factoryId = slot.GetTensorHandleFactoryId();
288 
289         OutputHandler& handler = GetOutputHandler(idx);
290         if (factoryId == ITensorHandleFactory::LegacyFactoryId)
291         {
292             handler.CreateTensorHandles(workloadFactory, IsMemoryManaged);
293         }
294         else
295         {
296             ITensorHandleFactory* handleFactory;
297             handleFactory = registry.GetFactory(factoryId);
298             ARMNN_ASSERT(handleFactory);
299             handler.CreateTensorHandles(*handleFactory, IsMemoryManaged);
300         }
301     }
302 }
303 
ReleaseConstantData()304 void Layer::ReleaseConstantData()
305 {
306     // Now free up the static data.
307     OperateOnConstantTensors([](std::shared_ptr<ConstTensorHandle>& handle)
308                                  {
309                                      handle.reset();
310                                  });
311 }
312 
GetDataType() const313 DataType Layer::GetDataType() const
314 {
315     if (GetNumInputSlots() > 0) // Ignore the input layer.
316     {
317         return GetInputSlot(0).GetConnection()->GetTensorInfo().GetDataType();
318     }
319     return GetOutputSlot(0).GetTensorInfo().GetDataType();
320 }
321 
ResetPriority() const322 void Layer::ResetPriority() const
323 {
324     m_Priority = 0;
325     m_Visiting = false;
326 }
327 
GetPriority() const328 LayerPriority Layer::GetPriority() const
329 {
330     constexpr LayerPriority inputPrio = std::numeric_limits<LayerPriority>::lowest();
331     constexpr LayerPriority outputPrio = std::numeric_limits<LayerPriority>::max();
332 
333     if (GetType() == LayerType::Input)
334     {
335         m_Priority = inputPrio;
336     }
337     else if (GetType() == LayerType::Output)
338     {
339         m_Priority = outputPrio;
340     }
341     else if (m_Priority == 0)
342     {
343         if (m_Visiting)
344         {
345             throw GraphValidationException("Graph has circular dependencies: cannot walk");
346         }
347 
348         auto maxPrio = [](const LayerPriority prio, const InputSlot& slot) -> LayerPriority
349             {
350                 const OutputSlot *outputSlot = slot.GetConnectedOutputSlot();
351                 if (outputSlot)
352                 {
353                     const Layer& input = outputSlot->GetOwningLayer();
354                     return std::max(prio, input.GetPriority());
355                 }
356                 else
357                 {
358                     // unconnected input slot
359                     return prio;
360                 }
361             };
362 
363         m_Visiting = true;
364         LayerPriority parentPrio = std::accumulate(GetInputSlots().cbegin(), GetInputSlots().cend(), 0U, maxPrio);
365         m_Visiting = false;
366 
367         if (parentPrio >= outputPrio)
368         {
369             throw GraphValidationException("Graph has too many edges");
370         }
371 
372         m_Priority = parentPrio + 1U;
373     }
374 
375     return m_Priority;
376 }
377 
VerifyLayerConnections(unsigned int expectedConnections,const CheckLocation & location) const378 void Layer::VerifyLayerConnections(unsigned int expectedConnections, const CheckLocation& location) const
379 {
380     ARMNN_ASSERT(GetNumInputSlots() == expectedConnections);
381 
382     for (unsigned int i=0; i<expectedConnections; ++i)
383     {
384         if (GetInputSlot(i).GetConnection() == nullptr)
385         {
386             throw LayerValidationException(
387                     fmt::format("Input connection #{0} must be connected "
388                                 "for {1} layer {2} {3}",
389                                 i,
390                                 GetLayerTypeAsCString(this->GetType()),
391                                 GetNameStr(),
392                                 location.AsString()));
393         }
394     }
395 }
396 
InferOutputShapes(const std::vector<TensorShape> & inputShapes) const397 std::vector<TensorShape> Layer::InferOutputShapes(const std::vector<TensorShape>& inputShapes) const
398 {
399     ARMNN_ASSERT(GetNumInputSlots() != 0);
400     ARMNN_ASSERT(GetNumOutputSlots() != 0);
401 
402     // By default we return what we got, meaning the output shape(s) are the same as the input(s).
403     // This only works if the number of inputs and outputs are the same. Since we are in the Layer
404     // base class, this means the implementation needs to be overridden in the specific layers for
405     // the other cases. So the missing implementation justifies the UnimplementedException.
406 
407     if (GetNumInputSlots() != GetNumOutputSlots())
408     {
409         throw UnimplementedException(
410                 fmt::format("Default implementation for InferOutputShapes can only be used for "
411                             "layers with the same number of input and output slots. This doesn't "
412                             "hold for {0} layer {1} (#inputs={2} #outputs={3}) {4}",
413                             GetLayerTypeAsCString(this->GetType()),
414                             GetNameStr(),
415                             GetNumInputSlots(),
416                             GetNumOutputSlots(),
417                             CHECK_LOCATION().AsString()));
418     }
419     return inputShapes;
420 }
421 
ValidateAndCopyShape(const TensorShape & outputShape,const TensorShape & inferredShape,const ShapeInferenceMethod shapeInferenceMethod,const std::string & layerName,const unsigned int outputSlotIndex)422 void Layer::ValidateAndCopyShape(const TensorShape& outputShape,
423                                  const TensorShape& inferredShape,
424                                  const ShapeInferenceMethod shapeInferenceMethod,
425                                  const std::string& layerName,
426                                  const unsigned int outputSlotIndex)
427 {
428     if (shapeInferenceMethod == ShapeInferenceMethod::ValidateOnly)
429     {
430         if (m_AllowExpandedDims)
431         {
432             std::vector<unsigned int> outputDims = armnnUtils::SqueezeDims(outputShape);
433             std::vector<unsigned int> inferredDims = armnnUtils::SqueezeDims(inferredShape);
434 
435             if (outputDims.size() != inferredDims.size())
436             {
437                 std::stringstream ss;
438                 ss << layerName << ": TensorShape set on OutputSlot[" << outputSlotIndex <<
439                    "] does not match the inferred shape. ";
440                 ss << outputShape << " != " << inferredShape;
441                 throw LayerValidationException(ss.str());
442             }
443             for (unsigned int i = 0; i < outputDims.size(); ++i)
444             {
445                 if (outputDims[i] != inferredDims[i])
446                 {
447                     std::stringstream ss;
448                     ss << layerName << ": TensorShape set on OutputSlot[" << outputSlotIndex <<
449                        "] does not match the inferred shape at dimension index [";
450                     ss << i << "] " << outputShape << " != " << inferredShape;
451                     throw LayerValidationException(ss.str());
452                 }
453             }
454             return;
455         }
456         else
457         {
458             ConditionalThrowIfNotEqual<LayerValidationException>(
459                     layerName + ": TensorShape set on OutputSlot[0] does not match the inferred shape.",
460                     outputShape,
461                     inferredShape);
462             return;
463         }
464     }
465 
466     if (outputShape.GetDimensionality() == Dimensionality::Specified)
467     {
468         for (unsigned int i = 0; i < outputShape.GetNumDimensions(); ++i)
469         {
470             if (outputShape.GetDimensionSpecificity(i) && outputShape[i] != inferredShape[i])
471             {
472                 std::stringstream ss;
473                 ss << layerName << ": TensorShape set on OutputSlot[" << outputSlotIndex <<
474                 "] does not match the inferred shape at dimension index [";
475                 ss << i << "] " << outputShape << " != " << inferredShape;
476                 throw LayerValidationException(ss.str());
477             }
478         }
479     }
480 
481     TensorInfo info = GetOutputSlot(outputSlotIndex).GetTensorInfo();
482 
483     armnn::TensorInfo inferredTensorInfo(inferredShape,
484                                          info.GetDataType(),
485                                          info.GetQuantizationScale(),
486                                          info.GetQuantizationOffset());
487 
488     GetOutputSlot(outputSlotIndex).SetTensorInfo(inferredTensorInfo);
489 }
490 
VerifyShapeInferenceType(const TensorShape & outputShape,ShapeInferenceMethod shapeInferenceMethod)491 void Layer::VerifyShapeInferenceType(const TensorShape& outputShape, ShapeInferenceMethod shapeInferenceMethod)
492 {
493     if (shapeInferenceMethod == ShapeInferenceMethod::ValidateOnly)
494     {
495         ConditionalThrow<LayerValidationException>(
496                 outputShape.GetDimensionality() != Dimensionality::NotSpecified,
497                 "Dimensionality can not be NotSpecified while using ShapeInferenceMethod::ValidateOnly");
498 
499         ConditionalThrow<LayerValidationException>(
500                 outputShape.AreAllDimensionsSpecified(),
501                 "Unspecified dimension while using ShapeInferenceMethod::ValidateOnly");
502     }
503 }
504 
SerializeLayerParameters(ParameterStringifyFunction & fn) const505 void Layer::SerializeLayerParameters(ParameterStringifyFunction& fn) const
506 {
507     std::string guid = std::to_string(m_Guid);
508     std::string layerType = GetLayerTypeAsCString(m_Type);
509     std::string backendId = std::string(m_BackendId);
510     if (!(guid.compare("") == 0) && !guid.empty())
511     {
512         fn("Guid", guid);
513     }
514     if(!(m_LayerName.compare("") == 0) && !m_LayerName.empty())
515     {
516         fn("LayerName",m_LayerName);
517     }
518     if(!(layerType.compare("") == 0) && !layerType.empty())
519     {
520         fn("LayerType",layerType);
521     }
522     if(!(backendId.compare("") == 0) && !backendId.empty())
523     {
524         fn("BackendID",backendId);
525     }
526     std::shared_ptr<ActivationDescriptor>
527             activationDescPtr = GetAdditionalInformation<ActivationDescriptor>();
528 
529     if (activationDescPtr)
530     {
531         StringifyLayerParameters<ActivationDescriptor>::Serialize(fn, *activationDescPtr.get());
532     }
533 }
534 
535 // default implementation of ExecuteStrategy
ExecuteStrategy(IStrategy & strategy) const536 void Layer::ExecuteStrategy(IStrategy& strategy) const
537 {
538     strategy.ExecuteStrategy(this, BaseDescriptor(), {}, GetName());
539 }
540 
GetConstantTensorsByRef()541 Layer::ConstantTensors Layer::GetConstantTensorsByRef()
542 {
543     const Layer *constThis = const_cast<const Layer*>(this);
544     ConstantTensors res;
545 
546     ImmutableConstantTensors immutableData = constThis->GetConstantTensorsByRef();
547     for (auto i : immutableData)
548     {
549         res.push_back(const_cast<std::shared_ptr<ConstTensorHandle>&>(i.get()));
550     }
551     return res;
552 }
553 
GetOwningIConnectableLayer() const554 const IConnectableLayer& OutputSlot::GetOwningIConnectableLayer() const
555 {
556     return m_OwningLayer;
557 }
558 
GetOwningIConnectableLayer()559 IConnectableLayer& OutputSlot::GetOwningIConnectableLayer()
560 {
561     return m_OwningLayer;
562 }
563 
GetOwningIConnectableLayer() const564 const IConnectableLayer& InputSlot::GetOwningIConnectableLayer() const
565 {
566     return m_OwningLayer;
567 }
568 
GetOwningIConnectableLayer()569 IConnectableLayer& InputSlot::GetOwningIConnectableLayer()
570 {
571     return m_OwningLayer;
572 }
573 
574 } // namespace armnn
575