IVGCVSW-1946: Remove armnn/src from the include paths
[platform/upstream/armnn.git] / src / armnn / Layer.cpp
1 //
2 // Copyright © 2017 Arm Ltd. All rights reserved.
3 // SPDX-License-Identifier: MIT
4 //
5 #include "Layer.hpp"
6
7 #include "Graph.hpp"
8 #include <backendsCommon/WorkloadData.hpp>
9 #include <backendsCommon/CpuTensorHandle.hpp>
10
11 #include <boost/cast.hpp>
12 #include <boost/format.hpp>
13 #include <boost/log/trivial.hpp>
14
15 #include <numeric>
16
17 namespace armnn
18 {
19
20 void InputSlot::Insert(Layer& layer)
21 {
22     BOOST_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         BOOST_ASSERT(layer.GetNumInputSlots() == 1);
33         prevSlot->Connect(layer.GetInputSlot(0));
34
35         // Sets tensor info for inserted layer.
36         const TensorInfo& tensorInfo = prevSlot->GetTensorInfo();
37         layer.GetOutputHandler().SetTensorInfo(tensorInfo);
38     }
39
40     // Connects inserted layer to this.
41     layer.GetOutputSlot(0).Connect(*this);
42 }
43
44 const InputSlot* OutputSlot::GetConnection(unsigned int index) const
45 {
46     ValidateConnectionIndex(index);
47     return m_Connections[index];
48 }
49
50 InputSlot* OutputSlot::GetConnection(unsigned int index)
51 {
52     ValidateConnectionIndex(index);
53     return m_Connections[index];
54 }
55
56 void OutputSlot::SetTensorInfo(const TensorInfo& tensorInfo)
57 {
58     GetOutputHandler().SetTensorInfo(tensorInfo);
59 }
60
61 const TensorInfo& OutputSlot::GetTensorInfo() const
62 {
63     return GetOutputHandler().GetTensorInfo();
64 }
65
66 bool OutputSlot::IsTensorInfoSet() const
67 {
68     return GetOutputHandler().IsTensorInfoSet();
69 }
70
71 bool OutputSlot::ValidateTensorShape(const TensorShape& shape) const
72 {
73     BOOST_ASSERT_MSG(IsTensorInfoSet(), "TensorInfo must be set in order to validate the shape.");
74     return shape == m_OutputHandler.GetTensorInfo().GetShape();
75 }
76
77 int OutputSlot::Connect(InputSlot& destination)
78 {
79     destination.SetConnection(this);
80     m_Connections.push_back(&destination);
81     return boost::numeric_cast<int>(m_Connections.size() - 1);
82 }
83
84 void OutputSlot::Disconnect(InputSlot& slot)
85 {
86     slot.SetConnection(nullptr);
87     m_Connections.erase(std::remove(m_Connections.begin(), m_Connections.end(), &slot), m_Connections.end());
88 }
89
90 void OutputSlot::DisconnectAll()
91 {
92     while (GetNumConnections() > 0)
93     {
94         InputSlot& connection = *GetConnection(0);
95         Disconnect(connection);
96     }
97 }
98
99 void OutputSlot::MoveAllConnections(OutputSlot& destination)
100 {
101     while (GetNumConnections() > 0)
102     {
103         InputSlot& connection = *GetConnection(0);
104         Disconnect(connection);
105         destination.Connect(connection);
106     }
107 }
108
109 void OutputSlot::ValidateConnectionIndex(unsigned int index) const
110 {
111     if (boost::numeric_cast<std::size_t>(index) >= m_Connections.size())
112     {
113         throw InvalidArgumentException(
114             boost::str(boost::format("GetConnection: Invalid index %1% provided") % index));
115     }
116 }
117
118 namespace {
119 LayerGuid GenerateLayerGuid()
120 {
121     // Note: Not thread safe.
122     static LayerGuid newGuid=0;
123     return newGuid++;
124 }
125 } // namespace
126
127 Layer::Layer(unsigned int numInputSlots,
128              unsigned int numOutputSlots,
129              LayerType type,
130              DataLayout layout,
131              const char* name)
132 : m_OutputHandlers(numOutputSlots)
133 , m_LayerName(name ? name : "")
134 , m_Type(type)
135 , m_DataLayout(layout)
136 , m_BackendId(UninitializedBackendId())
137 , m_Guid(GenerateLayerGuid())
138 {
139     m_InputSlots.reserve(numInputSlots);
140     for (unsigned int i = 0; i < numInputSlots; ++i)
141     {
142         m_InputSlots.emplace_back(*this, i);
143     }
144
145     m_OutputSlots.reserve(numOutputSlots);
146     for (unsigned int i = 0; i < numOutputSlots; ++i)
147     {
148         m_OutputSlots.emplace_back(*this, m_OutputHandlers[i]);
149     }
150 }
151
152 Layer::Layer(unsigned int numInputSlots,
153              unsigned int numOutputSlots,
154              LayerType type,
155              const char* name)
156 : Layer(numInputSlots, numOutputSlots, type, DataLayout::NCHW, name)
157 {
158 }
159
160 void Layer::CollectWorkloadInputs(WorkloadDataCollector& dataCollector, const Graph& graph) const
161 {
162     for (auto&& inputSlot : GetInputSlots())
163     {
164         // The graph must be well-formed at this point.
165         BOOST_ASSERT(inputSlot.GetConnection());
166         const OutputHandler& outputHandler = inputSlot.GetConnectedOutputSlot()->GetOutputHandler();
167         dataCollector.Push(outputHandler.GetData(), outputHandler.GetTensorInfo());
168     }
169 }
170
171 void Layer::CollectWorkloadOutputs(WorkloadDataCollector& dataCollector, const Graph& graph) const
172 {
173     for (auto&& outputHandler : m_OutputHandlers)
174     {
175         outputHandler.CollectWorkloadOutputs(dataCollector);
176     }
177 }
178
179 void Layer::CreateTensorHandles(Graph& graph, const IWorkloadFactory& factory)
180 {
181     for (auto&& outputHandler : m_OutputHandlers)
182     {
183         outputHandler.CreateTensorHandles(factory);
184     }
185 }
186
187 void Layer::ReleaseConstantData()
188 {
189     // Now free up the static data.
190     OperateOnConstantTensors([](std::unique_ptr<ScopedCpuTensorHandle>& handle)
191                                  {
192                                      handle.reset(nullptr);
193                                  });
194 }
195
196 DataType Layer::GetDataType() const
197 {
198     if (GetNumInputSlots() > 0) // Ignore the input layer.
199     {
200         return GetInputSlot(0).GetConnection()->GetTensorInfo().GetDataType();
201     }
202     return GetOutputSlot(0).GetTensorInfo().GetDataType();
203 }
204
205 void Layer::ResetPriority() const
206 {
207     m_Priority = 0;
208     m_Visiting = false;
209 }
210
211 LayerPriority Layer::GetPriority() const
212 {
213     constexpr LayerPriority inputPrio = std::numeric_limits<LayerPriority>::lowest();
214     constexpr LayerPriority outputPrio = std::numeric_limits<LayerPriority>::max();
215
216     if (GetType() == LayerType::Input)
217     {
218         m_Priority = inputPrio;
219     }
220     else if (GetType() == LayerType::Output)
221     {
222         m_Priority = outputPrio;
223     }
224     else if (m_Priority == 0)
225     {
226         if (m_Visiting)
227         {
228             throw GraphValidationException("Graph has circular dependencies: cannot walk");
229         }
230
231         auto maxPrio = [](const LayerPriority prio, const InputSlot& slot) -> LayerPriority
232             {
233                 const Layer& input = slot.GetConnectedOutputSlot()->GetOwningLayer();
234                 return std::max(prio, input.GetPriority());
235             };
236
237         m_Visiting = true;
238         LayerPriority parentPrio = std::accumulate(GetInputSlots().cbegin(), GetInputSlots().cend(), 0U, maxPrio);
239         m_Visiting = false;
240
241         if (parentPrio >= outputPrio)
242         {
243             throw GraphValidationException("Graph has too many edges");
244         }
245
246         m_Priority = parentPrio + 1U;
247     }
248
249     return m_Priority;
250 }
251
252 void Layer::VerifyLayerConnections(unsigned int expectedConnections, const CheckLocation& location) const
253 {
254     BOOST_ASSERT(GetNumInputSlots() == expectedConnections);
255
256     for (unsigned int i=0; i<expectedConnections; ++i)
257     {
258         if (GetInputSlot(i).GetConnection() == nullptr)
259         {
260             throw LayerValidationException(
261                 boost::str(
262                     boost::format(
263                         "Input connection #%1% must be connected "
264                         "for %2% layer %3% %4%")
265                         % i
266                         % GetLayerTypeAsCString(this->GetType())
267                         % GetNameStr()
268                         % location.AsString()));
269         }
270         if(! GetInputSlot(i).GetConnection()->IsTensorInfoSet())
271         {
272             throw LayerValidationException(
273                 boost::str(
274                     boost::format(
275                         "TensorInfo of Input connection #%1% must be set on connected OutputSlot for "
276                         "%2% layer %3% %4%")
277                         % i
278                         % GetLayerTypeAsCString(this->GetType())
279                         % GetNameStr()
280                         % location.AsString()));
281         }
282     }
283 }
284
285 std::vector<TensorShape> Layer::InferOutputShapes(const std::vector<TensorShape>& inputShapes) const
286 {
287     BOOST_ASSERT(GetNumInputSlots() != 0);
288     BOOST_ASSERT(GetNumOutputSlots() != 0);
289
290     // By default we return what we got, meaning the output shape(s) are the same as the input(s).
291     // This only works if the number of inputs and outputs are the same. Since we are in the Layer
292     // base class, this means the implementation needs to be overridden in the specific layers for
293     // the other cases. So the missing implementation justifies the UnimplementedException.
294
295     if (GetNumInputSlots() != GetNumOutputSlots())
296     {
297         throw UnimplementedException(
298             boost::str(
299                 boost::format(
300                     "Default implementation for InferOutputShapes can only be used for "
301                     "layers with the same number of input and output slots. This doesn't "
302                     "hold for %1% layer %2% (#inputs=%3% #outputs=%4%) %5%")
303                     % GetLayerTypeAsCString(this->GetType())
304                     % GetNameStr()
305                     % GetNumInputSlots()
306                     % GetNumOutputSlots()
307                     % CHECK_LOCATION().AsString()));
308     }
309     return inputShapes;
310 }
311
312 } // namespace armnn