Release 18.03
[platform/upstream/armnn.git] / src / armnn / Layer.cpp
1 //
2 // Copyright © 2017 Arm Ltd. All rights reserved.
3 // See LICENSE file in the project root for full license information.
4 //
5 #include "Layer.hpp"
6
7 #include "Graph.hpp"
8 #include "backends/WorkloadData.hpp"
9
10 #include <boost/cast.hpp>
11 #include <boost/format.hpp>
12 #include <boost/log/trivial.hpp>
13
14 #include <numeric>
15
16 namespace armnn
17 {
18
19 void InputSlot::Insert(Layer& layer)
20 {
21     BOOST_ASSERT(layer.GetNumOutputSlots() == 1);
22
23     OutputSlot* const prevSlot = GetConnectedOutputSlot();
24
25     if (prevSlot != nullptr)
26     {
27         // Disconnect parent from this
28         prevSlot->Disconnect(*this);
29
30         // Connect inserted layer to parent
31         BOOST_ASSERT(layer.GetNumInputSlots() == 1);
32         prevSlot->Connect(layer.GetInputSlot(0));
33
34         // Set tensor info for inserted layer
35         const TensorInfo& tensorInfo = prevSlot->GetTensorInfo();
36         layer.GetOutputHandler().SetTensorInfo(tensorInfo);
37     }
38
39     // Connect inserted layer to this
40     layer.GetOutputSlot(0).Connect(*this);
41 }
42
43 const InputSlot* OutputSlot::GetConnection(unsigned int index) const
44 {
45     ValidateConnectionIndex(index);
46     return m_Connections[index];
47 }
48
49 InputSlot* OutputSlot::GetConnection(unsigned int index)
50 {
51     ValidateConnectionIndex(index);
52     return m_Connections[index];
53 }
54
55 void OutputSlot::SetTensorInfo(const TensorInfo& tensorInfo)
56 {
57     GetOutputHandler().SetTensorInfo(tensorInfo);
58 }
59
60 const TensorInfo& OutputSlot::GetTensorInfo() const
61 {
62     return GetOutputHandler().GetTensorInfo();
63 }
64
65 bool OutputSlot::IsTensorInfoSet() const
66 {
67     return GetOutputHandler().IsTensorInfoSet();
68 }
69
70 bool OutputSlot::ValidateTensorShape(const TensorShape& shape) const
71 {
72     BOOST_ASSERT_MSG(IsTensorInfoSet(), "TensorInfo must be set in order to validate the shape.");
73     return shape == m_OutputHandler.GetTensorInfo().GetShape();
74 }
75
76 int OutputSlot::Connect(InputSlot& destination)
77 {
78     destination.SetConnection(this);
79     m_Connections.push_back(&destination);
80     return boost::numeric_cast<int>(m_Connections.size() - 1);
81 }
82
83 void OutputSlot::Disconnect(InputSlot& slot)
84 {
85     slot.SetConnection(nullptr);
86     m_Connections.erase(std::remove(m_Connections.begin(), m_Connections.end(), &slot), m_Connections.end());
87 }
88
89 void OutputSlot::DisconnectAll()
90 {
91     while (GetNumConnections() > 0)
92     {
93         InputSlot& connection = *GetConnection(0);
94         Disconnect(connection);
95     }
96 }
97
98 void OutputSlot::MoveAllConnections(OutputSlot& destination)
99 {
100     while (GetNumConnections() > 0)
101     {
102         InputSlot& connection = *GetConnection(0);
103         Disconnect(connection);
104         destination.Connect(connection);
105     }
106 }
107
108 void OutputSlot::ValidateConnectionIndex(unsigned int index) const
109 {
110     if (boost::numeric_cast<std::size_t>(index) >= m_Connections.size())
111     {
112         throw InvalidArgumentException(
113             boost::str(boost::format("GetConnection: Invalid index %1% provided") % index));
114     }
115 }
116
117 namespace {
118 LayerGuid GenerateLayerGuid()
119 {
120     //Note: Not thread safe.
121     static LayerGuid newGuid=0;
122     return newGuid++;
123 }
124 } //namespace
125
126 Layer::Layer(unsigned int numInputSlots, unsigned int numOutputSlots, LayerType type, const char* name)
127 : m_OutputHandlers(numOutputSlots)
128 , m_LayerName(name ? name : "")
129 , m_Type(type)
130 , m_ComputeDevice(Compute::Undefined)
131 , m_Guid(GenerateLayerGuid())
132 {
133     m_InputSlots.reserve(numInputSlots);
134     for (unsigned int i = 0; i < numInputSlots; ++i)
135     {
136         m_InputSlots.emplace_back(*this, i);
137     }
138
139     m_OutputSlots.reserve(numOutputSlots);
140     for (unsigned int i = 0; i < numOutputSlots; ++i)
141     {
142         m_OutputSlots.emplace_back(*this, m_OutputHandlers[i]);
143     }
144 }
145
146 void Layer::CollectWorkloadInputs(WorkloadDataCollector& dataCollector, const Graph& graph) const
147 {
148     for (auto&& inputSlot : GetInputSlots())
149     {
150         // The graph must be well-formed at this point
151         BOOST_ASSERT(inputSlot.GetConnection());
152         const OutputHandler& outputHandler = inputSlot.GetConnectedOutputSlot()->GetOutputHandler();
153         dataCollector.Push(outputHandler.GetData(), outputHandler.GetTensorInfo());
154     }
155 }
156
157 void Layer::CollectWorkloadOutputs(WorkloadDataCollector& dataCollector, const Graph& graph) const
158 {
159     for (auto&& outputHandler : m_OutputHandlers)
160     {
161         outputHandler.CollectWorkloadOutputs(dataCollector);
162     }
163 }
164
165 void Layer::CreateTensorHandles(Graph& graph, const IWorkloadFactory& factory)
166 {
167     for (auto&& outputHandler : m_OutputHandlers)
168     {
169         outputHandler.CreateTensorHandles(factory);
170     }
171 }
172
173 DataType Layer::GetDataType() const
174 {
175     if (GetNumInputSlots() > 0) // Ignore the input layer
176     {
177         return GetInputSlot(0).GetConnection()->GetTensorInfo().GetDataType();
178     }
179     return DataType::Float32;
180 }
181
182 void Layer::ResetPriority() const
183 {
184     m_Priority = 0;
185     m_Visiting = false;
186 }
187
188 LayerPriority Layer::GetPriority() const
189 {
190     constexpr LayerPriority inputPrio = std::numeric_limits<LayerPriority>::lowest();
191     constexpr LayerPriority outputPrio = std::numeric_limits<LayerPriority>::max();
192
193     if (GetType() == LayerType::Input)
194     {
195         m_Priority = inputPrio;
196     }
197     else if (GetType() == LayerType::Output)
198     {
199         m_Priority = outputPrio;
200     }
201     else if (m_Priority == 0)
202     {
203         if (m_Visiting)
204         {
205             throw GraphValidationException("Graph has circular dependencies: cannot walk");
206         }
207
208         auto maxPrio = [](const LayerPriority prio, const InputSlot& slot) -> LayerPriority
209             {
210                 const Layer& input = slot.GetConnectedOutputSlot()->GetOwningLayer();
211                 return std::max(prio, input.GetPriority());
212             };
213
214         m_Visiting = true;
215         LayerPriority parentPrio = std::accumulate(GetInputSlots().cbegin(), GetInputSlots().cend(), 0U, maxPrio);
216         m_Visiting = false;
217
218         if (parentPrio >= outputPrio)
219         {
220             throw GraphValidationException("Graph has too many edges");
221         }
222
223         m_Priority = parentPrio + 1U;
224     }
225
226     return m_Priority;
227 }
228
229 } // namespace armnn