• 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 #include <ProfilingService.hpp>
9 #include <armnn/utility/NumericCast.hpp>
10 #include <backendsCommon/WorkloadData.hpp>
11 #include <backendsCommon/CpuTensorHandle.hpp>
12 
13 #include <fmt/format.h>
14 
15 #include <numeric>
16 
17 namespace armnn
18 {
19 
Insert(Layer & layer)20 void InputSlot::Insert(Layer& layer)
21 {
22     ARMNN_ASSERT(layer.GetNumOutputSlots() == 1);
23 
24     OutputSlot* const prevSlot = GetConnectedOutputSlot();
25 
26     if (prevSlot != nullptr)
27     {
28         // Disconnects parent from this.
29         prevSlot->Disconnect(*this);
30 
31         // Connects inserted layer to parent.
32         ARMNN_ASSERT(layer.GetNumInputSlots() == 1);
33         int idx = prevSlot->Connect(layer.GetInputSlot(0));
34         prevSlot->SetEdgeStrategy(armnn::numeric_cast<unsigned int>(idx), EdgeStrategy::Undefined);
35 
36         // Sets tensor info for inserted layer.
37         const TensorInfo& tensorInfo = prevSlot->GetTensorInfo();
38         layer.GetOutputHandler().SetTensorInfo(tensorInfo);
39     }
40 
41     // Connects inserted layer to this.
42     layer.GetOutputSlot(0).Connect(*this);
43     layer.GetOutputSlot(0).SetEdgeStrategy(0, EdgeStrategy::Undefined);
44 }
45 
GetConnection(unsigned int index) const46 const InputSlot* OutputSlot::GetConnection(unsigned int index) const
47 {
48     ValidateConnectionIndex(index);
49     return m_Connections[index];
50 }
51 
GetConnection(unsigned int index)52 InputSlot* OutputSlot::GetConnection(unsigned int index)
53 {
54     ValidateConnectionIndex(index);
55     return m_Connections[index];
56 }
57 
SetTensorInfo(const TensorInfo & tensorInfo)58 void OutputSlot::SetTensorInfo(const TensorInfo& tensorInfo)
59 {
60     GetOutputHandler().SetTensorInfo(tensorInfo);
61 }
62 
GetTensorInfo() const63 const TensorInfo& OutputSlot::GetTensorInfo() const
64 {
65     return GetOutputHandler().GetTensorInfo();
66 }
67 
IsTensorInfoSet() const68 bool OutputSlot::IsTensorInfoSet() const
69 {
70     if (GetOwningLayer().GetShapeInferenceMethod() == ShapeInferenceMethod::InferAndValidate)
71     {
72         GetOwningLayer().ValidateTensorShapesFromInputs();
73     }
74     return GetOutputHandler().IsTensorInfoSet();
75 }
76 
ValidateTensorShape(const TensorShape & shape) const77 bool OutputSlot::ValidateTensorShape(const TensorShape& shape) const
78 {
79     ARMNN_ASSERT_MSG(IsTensorInfoSet(), "TensorInfo must be set in order to validate the shape.");
80     return shape == m_OutputHandler.GetTensorInfo().GetShape();
81 }
82 
Connect(InputSlot & destination)83 int OutputSlot::Connect(InputSlot& destination)
84 {
85     destination.SetConnection(this);
86     m_Connections.push_back(&destination);
87     m_EdgeStrategies.push_back(EdgeStrategy::Undefined);
88     return armnn::numeric_cast<int>(m_Connections.size() - 1);
89 }
90 
Disconnect(InputSlot & slot)91 void OutputSlot::Disconnect(InputSlot& slot)
92 {
93     slot.SetConnection(nullptr);
94     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);
102     m_Connections.erase(std::remove(m_Connections.begin(), m_Connections.end(), &slot), m_Connections.end());
103 
104     m_EdgeStrategies.erase(m_EdgeStrategies.begin() + idx);
105 }
106 
DisconnectAll()107 void OutputSlot::DisconnectAll()
108 {
109     while (GetNumConnections() > 0)
110     {
111         InputSlot& connection = *GetConnection(0);
112         Disconnect(connection);
113     }
114 }
115 
MoveAllConnections(OutputSlot & destination)116 void OutputSlot::MoveAllConnections(OutputSlot& destination)
117 {
118     while (GetNumConnections() > 0)
119     {
120         ARMNN_ASSERT_MSG(m_EdgeStrategies[0] == EdgeStrategy::Undefined,
121             "Cannot move connections once memory strategies have be established.");
122 
123         InputSlot& connection = *GetConnection(0);
124         Disconnect(connection);
125         destination.Connect(connection);
126         destination.GetOutputHandler().SetTensorInfo(GetOutputHandler().GetTensorInfo());
127     }
128 }
129 
CalculateIndexOnOwner() const130 unsigned int OutputSlot::CalculateIndexOnOwner() const
131 {
132     for (unsigned int i = 0; i < GetOwningLayer().GetNumOutputSlots(); i++)
133     {
134         if (GetOwningLayer().GetOutputSlot(i) == (*this))
135         {
136             return i;
137         }
138     }
139     ARMNN_ASSERT_MSG(false, "Did not find slot on owner.");
140     return 0; // Error
141 }
142 
operator ==(const OutputSlot & other) const143 bool OutputSlot::operator==(const OutputSlot& other) const
144 {
145     bool isSame = other.GetNumConnections() == GetNumConnections();
146     if (!isSame)
147     {
148         return false;
149     }
150 
151     for (unsigned int i = 0; i < GetNumConnections(); i++)
152     {
153         isSame &= other.GetConnection(i) == GetConnection(i);
154     }
155     return isSame;
156 }
157 
ValidateConnectionIndex(unsigned int index) const158 void OutputSlot::ValidateConnectionIndex(unsigned int index) const
159 {
160     if (armnn::numeric_cast<std::size_t>(index) >= m_Connections.size())
161     {
162         throw InvalidArgumentException((fmt::format("GetConnection: Invalid index {} provided", index)));
163     }
164 }
165 
GetOwningLayerGuid() const166 LayerGuid OutputSlot::GetOwningLayerGuid() const
167 {
168     return GetOwningLayer().GetGuid();
169 }
170 
SetTensorHandleFactory(const ITensorHandleFactory::FactoryId & id)171 void OutputSlot::SetTensorHandleFactory(const ITensorHandleFactory::FactoryId& id)
172 {
173     m_TensorHandleFactoryId = id;
174 }
175 
GetTensorHandleFactoryId() const176 ITensorHandleFactory::FactoryId OutputSlot::GetTensorHandleFactoryId() const
177 {
178     return m_TensorHandleFactoryId;
179 }
180 
SetEdgeStrategy(unsigned int connectionIndex,EdgeStrategy strategy)181 void OutputSlot::SetEdgeStrategy(unsigned int connectionIndex, EdgeStrategy strategy)
182 {
183     m_EdgeStrategies[connectionIndex] = strategy;
184 }
185 
GetEdgeStrategyForConnection(unsigned int connectionIdx) const186 EdgeStrategy OutputSlot::GetEdgeStrategyForConnection(unsigned int connectionIdx) const
187 {
188     return m_EdgeStrategies[connectionIdx];
189 }
190 
Layer(unsigned int numInputSlots,unsigned int numOutputSlots,LayerType type,DataLayout layout,const char * name)191 Layer::Layer(unsigned int numInputSlots,
192              unsigned int numOutputSlots,
193              LayerType type,
194              DataLayout layout,
195              const char* name)
196 : m_OutputHandlers(numOutputSlots)
197 , m_ShapeInferenceMethod(ShapeInferenceMethod::ValidateOnly)
198 , m_LayerName(name ? name : "")
199 , m_Type(type)
200 , m_BackendId()
201 , m_BackendHint(EmptyOptional())
202 , m_Guid(profiling::ProfilingService::GetNextGuid())
203 {
204     IgnoreUnused(layout);
205     m_InputSlots.reserve(numInputSlots);
206     for (unsigned int i = 0; i < numInputSlots; ++i)
207     {
208         m_InputSlots.emplace_back(*this, i);
209     }
210 
211     m_OutputSlots.reserve(numOutputSlots);
212     for (unsigned int i = 0; i < numOutputSlots; ++i)
213     {
214         m_OutputSlots.emplace_back(*this, m_OutputHandlers[i]);
215     }
216 }
217 
Layer(unsigned int numInputSlots,unsigned int numOutputSlots,LayerType type,const char * name)218 Layer::Layer(unsigned int numInputSlots,
219              unsigned int numOutputSlots,
220              LayerType type,
221              const char* name)
222 : Layer(numInputSlots, numOutputSlots, type, DataLayout::NCHW, name)
223 {
224 }
225 
CollectWorkloadInputs(WorkloadDataCollector & dataCollector) const226 void Layer::CollectWorkloadInputs(WorkloadDataCollector& dataCollector) const
227 {
228     for (auto&& inputSlot : GetInputSlots())
229     {
230         // The graph must be well-formed at this point.
231         ARMNN_ASSERT(inputSlot.GetConnection());
232         const OutputHandler& outputHandler = inputSlot.GetConnectedOutputSlot()->GetOutputHandler();
233         dataCollector.Push(outputHandler.GetData(), outputHandler.GetTensorInfo());
234     }
235 }
236 
CollectWorkloadOutputs(WorkloadDataCollector & dataCollector) const237 void Layer::CollectWorkloadOutputs(WorkloadDataCollector& dataCollector) const
238 {
239     for (auto&& outputHandler : m_OutputHandlers)
240     {
241         outputHandler.CollectWorkloadOutputs(dataCollector);
242     }
243 }
244 
SetAdditionalInfo(QueueDescriptor & descriptor) const245 void Layer::SetAdditionalInfo(QueueDescriptor& descriptor) const
246 {
247     descriptor.m_AdditionalInfoObject = m_AdditionalInfoObject.get();
248 }
249 
CreateTensorHandles(const TensorHandleFactoryRegistry & registry,const IWorkloadFactory & workloadFactory,const bool IsMemoryManaged)250 void Layer::CreateTensorHandles(const TensorHandleFactoryRegistry& registry,
251                                 const IWorkloadFactory& workloadFactory,
252                                 const bool IsMemoryManaged)
253 {
254     for (unsigned int idx=0; idx < GetNumOutputSlots(); idx++)
255     {
256 
257         OutputSlot& slot = GetOutputSlot(idx);
258         ITensorHandleFactory::FactoryId factoryId = slot.GetTensorHandleFactoryId();
259 
260         OutputHandler& handler = GetOutputHandler(idx);
261         if (factoryId == ITensorHandleFactory::LegacyFactoryId)
262         {
263             handler.CreateTensorHandles(workloadFactory, IsMemoryManaged);
264         }
265         else
266         {
267             ITensorHandleFactory* handleFactory = registry.GetFactory(factoryId);
268             ARMNN_ASSERT(handleFactory);
269             handler.CreateTensorHandles(*handleFactory, IsMemoryManaged);
270         }
271     }
272 }
273 
ReleaseConstantData()274 void Layer::ReleaseConstantData()
275 {
276     // Now free up the static data.
277     OperateOnConstantTensors([](std::unique_ptr<ScopedCpuTensorHandle>& handle)
278                                  {
279                                      handle.reset(nullptr);
280                                  });
281 }
282 
GetDataType() const283 DataType Layer::GetDataType() const
284 {
285     if (GetNumInputSlots() > 0) // Ignore the input layer.
286     {
287         return GetInputSlot(0).GetConnection()->GetTensorInfo().GetDataType();
288     }
289     return GetOutputSlot(0).GetTensorInfo().GetDataType();
290 }
291 
ResetPriority() const292 void Layer::ResetPriority() const
293 {
294     m_Priority = 0;
295     m_Visiting = false;
296 }
297 
GetPriority() const298 LayerPriority Layer::GetPriority() const
299 {
300     constexpr LayerPriority inputPrio = std::numeric_limits<LayerPriority>::lowest();
301     constexpr LayerPriority outputPrio = std::numeric_limits<LayerPriority>::max();
302 
303     if (GetType() == LayerType::Input)
304     {
305         m_Priority = inputPrio;
306     }
307     else if (GetType() == LayerType::Output)
308     {
309         m_Priority = outputPrio;
310     }
311     else if (m_Priority == 0)
312     {
313         if (m_Visiting)
314         {
315             throw GraphValidationException("Graph has circular dependencies: cannot walk");
316         }
317 
318         auto maxPrio = [](const LayerPriority prio, const InputSlot& slot) -> LayerPriority
319             {
320                 const OutputSlot *outputSlot = slot.GetConnectedOutputSlot();
321                 if (outputSlot)
322                 {
323                     const Layer& input = outputSlot->GetOwningLayer();
324                     return std::max(prio, input.GetPriority());
325                 }
326                 else
327                 {
328                     // unconnected input slot
329                     return prio;
330                 }
331             };
332 
333         m_Visiting = true;
334         LayerPriority parentPrio = std::accumulate(GetInputSlots().cbegin(), GetInputSlots().cend(), 0U, maxPrio);
335         m_Visiting = false;
336 
337         if (parentPrio >= outputPrio)
338         {
339             throw GraphValidationException("Graph has too many edges");
340         }
341 
342         m_Priority = parentPrio + 1U;
343     }
344 
345     return m_Priority;
346 }
347 
VerifyLayerConnections(unsigned int expectedConnections,const CheckLocation & location) const348 void Layer::VerifyLayerConnections(unsigned int expectedConnections, const CheckLocation& location) const
349 {
350     ARMNN_ASSERT(GetNumInputSlots() == expectedConnections);
351 
352     for (unsigned int i=0; i<expectedConnections; ++i)
353     {
354         if (GetInputSlot(i).GetConnection() == nullptr)
355         {
356             throw LayerValidationException(
357                     fmt::format("Input connection #{0} must be connected "
358                                 "for {1} layer {2} {3}",
359                                 i,
360                                 GetLayerTypeAsCString(this->GetType()),
361                                 GetNameStr(),
362                                 location.AsString()));
363         }
364     }
365 }
366 
InferOutputShapes(const std::vector<TensorShape> & inputShapes) const367 std::vector<TensorShape> Layer::InferOutputShapes(const std::vector<TensorShape>& inputShapes) const
368 {
369     ARMNN_ASSERT(GetNumInputSlots() != 0);
370     ARMNN_ASSERT(GetNumOutputSlots() != 0);
371 
372     // By default we return what we got, meaning the output shape(s) are the same as the input(s).
373     // This only works if the number of inputs and outputs are the same. Since we are in the Layer
374     // base class, this means the implementation needs to be overridden in the specific layers for
375     // the other cases. So the missing implementation justifies the UnimplementedException.
376 
377     if (GetNumInputSlots() != GetNumOutputSlots())
378     {
379         throw UnimplementedException(
380                 fmt::format("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 {0} layer {1} (#inputs={2} #outputs={3}) {4}",
383                             GetLayerTypeAsCString(this->GetType()),
384                             GetNameStr(),
385                             GetNumInputSlots(),
386                             GetNumOutputSlots(),
387                             CHECK_LOCATION().AsString()));
388     }
389     return inputShapes;
390 }
391 
ValidateAndCopyShape(const TensorShape & outputShape,const TensorShape & inferredShape,const ShapeInferenceMethod shapeInferenceMethod,const std::string & layerName,const unsigned int outputSlotIndex)392 void 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 
VerifyShapeInferenceType(const TensorShape & outputShape,ShapeInferenceMethod shapeInferenceMethod)432 void 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     }
444 }
445 
SerializeLayerParameters(ParameterStringifyFunction & fn) const446 void Layer::SerializeLayerParameters(ParameterStringifyFunction& fn) const
447 {
448     std::string guid = std::to_string(m_Guid);
449     std::string layerType = GetLayerTypeAsCString(m_Type);
450     std::string backendId = std::string(m_BackendId);
451     if (!(guid.compare("") == 0) && !guid.empty())
452     {
453         fn("Guid", guid);
454     }
455     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     std::shared_ptr<ActivationDescriptor>
468             activationDescPtr = GetAdditionalInformation<ActivationDescriptor>();
469 
470     if (activationDescPtr)
471     {
472         StringifyLayerParameters<ActivationDescriptor>::Serialize(fn, *activationDescPtr.get());
473     }
474 }
475 
476 } // namespace armnn
477