blob: d13a27792407caebbbce43b0978b3854f1fb0a47 [file] [log] [blame]
surmeh01bceff2f2018-03-29 16:29:27 +01001//
Teresa Charlin52664732020-06-29 16:27:03 +01002// Copyright © 2017 Arm Ltd and Contributors. All rights reserved.
David Beckecb56cd2018-09-05 12:52:57 +01003// SPDX-License-Identifier: MIT
surmeh01bceff2f2018-03-29 16:29:27 +01004//
Ferran Balaguer51dd62f2019-01-11 19:29:18 +00005
surmeh01bceff2f2018-03-29 16:29:27 +01006#include "TfParser.hpp"
7
surmeh01bceff2f2018-03-29 16:29:27 +01008#include <armnn/TypesUtils.hpp>
surmeh01bceff2f2018-03-29 16:29:27 +01009#include <armnn/Descriptors.hpp>
10
Matteo Martincighe011d202019-11-28 11:35:47 +000011#include <armnnUtils/Permute.hpp>
12#include <armnnUtils/DataLayoutIndexed.hpp>
Mike Kelly08759e22020-03-02 11:41:31 +000013#include <armnnUtils/Transpose.hpp>
Jan Eilers8eb25602020-03-09 12:13:48 +000014#include <armnn/utility/IgnoreUnused.hpp>
Matthew Sloyan589e3e82020-09-11 16:17:48 +010015#include <armnn/utility/NumericCast.hpp>
Jan Eilersbb446e52020-04-02 13:56:54 +010016#include <armnn/utility/PolymorphicDowncast.hpp>
Matteo Martincighe011d202019-11-28 11:35:47 +000017
surmeh01bceff2f2018-03-29 16:29:27 +010018#include <GraphTopologicalSort.hpp>
Sadik Armagan479045b2018-10-01 11:51:37 +010019#include <ParserHelper.hpp>
surmeh01bceff2f2018-03-29 16:29:27 +010020
21#include <google/protobuf/io/zero_copy_stream_impl.h>
22#include <google/protobuf/text_format.h>
23
Derek Lambertibaa177f2019-12-10 22:00:43 +000024#include <tensorflow/core/framework/graph.pb.h>
surmeh01bceff2f2018-03-29 16:29:27 +010025
James Ward58dec6b2020-09-11 17:32:44 +010026#include <fmt/core.h>
Jan Eilersba3ef182020-09-25 08:36:44 +010027#include <fmt/format.h>
surmeh01bceff2f2018-03-29 16:29:27 +010028#include <numeric>
surmeh01bceff2f2018-03-29 16:29:27 +010029
Matteo Martincigh46315822018-11-28 16:22:36 +000030using namespace armnnUtils;
surmeh01bceff2f2018-03-29 16:29:27 +010031using namespace armnn;
32
33namespace armnnTfParser
34{
Kevin May7d96b162021-02-03 17:38:41 +000035
36ITfParser::ITfParser() : pTfParserImpl(new ITfParser::TfParserImpl()){}
37
38ITfParser::~ITfParser() = default;
39
40ITfParser *ITfParser::CreateRaw()
41{
42 return new ITfParser();
43}
44
45ITfParserPtr ITfParser::Create()
46{
47 return ITfParserPtr(CreateRaw(), &ITfParser::Destroy);
48}
49
50void ITfParser::Destroy(ITfParser *parser)
51{
52 delete parser;
53}
54
55armnn::INetworkPtr ITfParser::CreateNetworkFromTextFile(const char* graphFile,
56 const std::map<std::string, armnn::TensorShape>& inputShapes,
57 const std::vector<std::string>& requestedOutputs)
58{
59 return pTfParserImpl->CreateNetworkFromTextFile(graphFile, inputShapes, requestedOutputs);
60}
61
62armnn::INetworkPtr ITfParser::CreateNetworkFromBinaryFile(const char* graphFile,
63 const std::map<std::string, armnn::TensorShape>& inputShapes,
64 const std::vector<std::string>& requestedOutputs)
65{
66 return pTfParserImpl->CreateNetworkFromBinaryFile(graphFile, inputShapes, requestedOutputs);
67}
68
69armnn::INetworkPtr ITfParser::CreateNetworkFromString(const char* protoText,
70 const std::map<std::string, armnn::TensorShape>& inputShapes,
71 const std::vector<std::string>& requestedOutputs)
72{
73 return pTfParserImpl->CreateNetworkFromString(protoText, inputShapes, requestedOutputs);
74}
75
76BindingPointInfo ITfParser::GetNetworkInputBindingInfo(const std::string& name) const
77{
78 return pTfParserImpl->GetNetworkInputBindingInfo(name);
79}
80
81BindingPointInfo ITfParser::GetNetworkOutputBindingInfo(const std::string& name) const
82{
83 return pTfParserImpl->GetNetworkOutputBindingInfo(name);
84}
surmeh01bceff2f2018-03-29 16:29:27 +010085namespace
86{
87
88const PermutationVector NHWCToArmNN = { 0, 2, 3, 1 };
89const PermutationVector ArmNNToNHWC = { 0, 3, 1, 2 };
90
surmeh01bceff2f2018-03-29 16:29:27 +010091
92template <typename Callable>
93void ReadMandatoryNodeAttributeImpl(const tensorflow::NodeDef& nodeDef,
94 const std::string& attribName,
95 tensorflow::AttrValue::ValueCase expectedValueCase,
96 Callable callable)
97{
98 auto iter = nodeDef.attr().find(attribName);
99 if (iter != nodeDef.attr().end())
100 {
101 const auto& attrValue = iter->second;
102 if (attrValue.value_case() == expectedValueCase)
103 {
104 callable(attrValue);
105 }
106 else
107 {
telsoa01c577f2c2018-08-31 09:22:23 +0100108 throw ParseException(
James Ward58dec6b2020-09-11 17:32:44 +0100109 fmt::format("Attribute {} of node {} expected to have {} as tensorflow::AttrValue::ValueCase, "
110 "but found {} instead {}",
111 attribName,
112 nodeDef.name(),
113 static_cast<int>(expectedValueCase),
114 static_cast<int>(attrValue.value_case()),
115 CHECK_LOCATION().AsString()));
surmeh01bceff2f2018-03-29 16:29:27 +0100116 }
117 }
118 else
119 {
telsoa01c577f2c2018-08-31 09:22:23 +0100120 throw ParseException(
James Ward58dec6b2020-09-11 17:32:44 +0100121 fmt::format("Could not find required attribute {} in node {} {}",
122 attribName,
123 nodeDef.name(),
124 CHECK_LOCATION().AsString()));
surmeh01bceff2f2018-03-29 16:29:27 +0100125 }
126}
127
128template <typename Callable>
129void ReadOptionalNodeAttributeImpl(const tensorflow::NodeDef& nodeDef,
130 const std::string& attribName,
131 tensorflow::AttrValue::ValueCase expectedValueCase,
132 Callable callable)
133{
134 auto iter = nodeDef.attr().find(attribName);
135 if (iter != nodeDef.attr().end())
136 {
137 const auto& attrValue = iter->second;
138 if (attrValue.value_case() == expectedValueCase)
139 {
140 callable(attrValue);
141 }
142 else
143 {
telsoa01c577f2c2018-08-31 09:22:23 +0100144 throw ParseException(
James Ward58dec6b2020-09-11 17:32:44 +0100145 fmt::format("Attribute {} of node {} expected to have {} as tensorflow::AttrValue::ValueCase, "
146 "but found {} instead {}",
147 attribName,
148 nodeDef.name(),
149 static_cast<int>(expectedValueCase),
150 static_cast<int>(attrValue.value_case()),
151 CHECK_LOCATION().AsString()));
surmeh01bceff2f2018-03-29 16:29:27 +0100152 }
153 }
154}
155
156float ReadMandatoryNodeFloatAttribute(const tensorflow::NodeDef& nodeDef, const std::string& name)
157{
158 float attribValue = 0.0f;
159 ReadMandatoryNodeAttributeImpl(nodeDef, name, tensorflow::AttrValue::kF,
160 [&attribValue](const tensorflow::AttrValue& attrValue)
161 {
162 attribValue = attrValue.f();
163 });
164 return attribValue;
165}
166
Conor Kennedyc2130a02018-12-05 11:05:54 +0000167int32_t ReadMandatoryNodeInt32Attribute(const tensorflow::NodeDef& nodeDef, const std::string& name)
168{
169 int32_t attribValue = 0u;
170 ReadMandatoryNodeAttributeImpl(nodeDef, name, tensorflow::AttrValue::kI,
171 [&attribValue](const tensorflow::AttrValue& attrValue)
172 {
173 attribValue = static_cast<int32_t>(attrValue.i());
174 });
175 return attribValue;
176}
177
Ferran Balaguer51dd62f2019-01-11 19:29:18 +0000178bool ReadMandatoryNodeBoolAttribute(const tensorflow::NodeDef& nodeDef, const std::string& name)
179{
180 bool attribValue = false;
181 ReadMandatoryNodeAttributeImpl(nodeDef, name, tensorflow::AttrValue::kB,
182 [&attribValue](const tensorflow::AttrValue& attrValue)
183 {
184 attribValue = static_cast<bool>(attrValue.b());
185 });
186 return attribValue;
187}
188
surmeh01bceff2f2018-03-29 16:29:27 +0100189uint32_t ReadMandatoryNodeUint32Attribute(const tensorflow::NodeDef& nodeDef, const std::string& name)
190{
191 uint32_t attribValue = 0u;
192 ReadMandatoryNodeAttributeImpl(nodeDef, name, tensorflow::AttrValue::kI,
193 [&attribValue](const tensorflow::AttrValue& attrValue)
194 {
195 attribValue = static_cast<uint32_t>(attrValue.i());
196 });
197 return attribValue;
198}
199
200std::string ReadMandatoryNodeStringAttribute(const tensorflow::NodeDef& nodeDef, const std::string& name)
201{
202 std::string attribValue = "";
203 ReadMandatoryNodeAttributeImpl(nodeDef, name, tensorflow::AttrValue::kS,
204 [&attribValue](const tensorflow::AttrValue& attrValue)
205 {
206 attribValue = attrValue.s();
207 });
208 return attribValue;
209}
210
211std::vector<uint32_t> ReadMandatoryNodeUint32ListAttribute(const tensorflow::NodeDef& nodeDef,
212 const std::string& name)
213{
214 std::vector<uint32_t> attriList;
215 ReadMandatoryNodeAttributeImpl(nodeDef, name, tensorflow::AttrValue::kList,
216 [&attriList](const tensorflow::AttrValue& attrValue)
217 {
218 for (int attriNum = 0; attriNum < attrValue.list().i_size(); ++attriNum)
219 {
220 attriList.push_back(static_cast<uint32_t>(attrValue.list().i(attriNum)));
221 }
222 });
223
224 return attriList;
225}
226
227std::vector<uint32_t> ReadOptionalNodeUint32ListAttribute(const tensorflow::NodeDef& nodeDef,
228 const std::string& name)
229{
230 std::vector<uint32_t> attriList;
231 ReadOptionalNodeAttributeImpl(nodeDef, name, tensorflow::AttrValue::kList,
232 [&attriList](const tensorflow::AttrValue& attrValue)
233 {
234 for (int attriNum = 0; attriNum < attrValue.list().i_size(); ++attriNum)
235 {
236 attriList.push_back(static_cast<uint32_t>(attrValue.list().i(attriNum)));
237 }
238 });
239
240 return attriList;
241}
242
Aron Virginas-Tar2e259272019-11-27 13:29:51 +0000243std::string ReadOptionalNodeStringAttribute(const tensorflow::NodeDef& nodeDef,
244 const std::string& name,
245 const std::string& defaultValue = "")
246{
247 std::string attribValue = defaultValue;
248 ReadOptionalNodeAttributeImpl(nodeDef, name, tensorflow::AttrValue::kS,
249 [&attribValue](const tensorflow::AttrValue& attrValue)
250 {
251 attribValue = attrValue.s();
252 });
253 return attribValue;
254}
255
surmeh01bceff2f2018-03-29 16:29:27 +0100256bool ReadOptionalNodeBoolAttribute(const tensorflow::NodeDef& nodeDef,
257 const std::string& name,
258 bool defaultValue = false)
259{
260 bool attribValue = defaultValue;
261 ReadOptionalNodeAttributeImpl(nodeDef, name, tensorflow::AttrValue::kB,
262 [&attribValue](const tensorflow::AttrValue& attrValue)
263 {
264 attribValue = attrValue.b();
265 });
266 return attribValue;
267}
268
269tensorflow::DataType ReadMandatoryNodeTypeAttribute(const tensorflow::NodeDef& nodeDef, const std::string& name)
270{
271 tensorflow::DataType attribValue = tensorflow::DT_INVALID;
272 ReadMandatoryNodeAttributeImpl(nodeDef, name, tensorflow::AttrValue::kType,
273 [&attribValue](const tensorflow::AttrValue& attrValue)
274 {
275 attribValue = attrValue.type();
276 });
277 return attribValue;
278}
279
280TensorInfo PrepareReshape(const TensorInfo& input, const std::vector<int32_t>& targetDims)
281{
282 std::vector<unsigned int> outDims(targetDims.begin(), targetDims.end());
283 const auto stretchDim = std::find(targetDims.begin(), targetDims.end(), -1);
284
285 if (stretchDim != targetDims.end())
286 {
287 if (std::find(std::next(stretchDim), targetDims.end(), -1) != targetDims.end())
288 {
telsoa01c577f2c2018-08-31 09:22:23 +0100289 throw ParseException(
James Ward58dec6b2020-09-11 17:32:44 +0100290 fmt::format("At most one component of shape can be -1 {}",
291 CHECK_LOCATION().AsString()));
surmeh01bceff2f2018-03-29 16:29:27 +0100292 }
293
telsoa01c577f2c2018-08-31 09:22:23 +0100294 auto targetNumElements =
Matthew Sloyan589e3e82020-09-11 16:17:48 +0100295 armnn::numeric_cast<unsigned int>(
telsoa01c577f2c2018-08-31 09:22:23 +0100296 std::accumulate(targetDims.begin(), targetDims.end(), -1, std::multiplies<int32_t>()));
surmeh01bceff2f2018-03-29 16:29:27 +0100297 auto stretchIndex = static_cast<size_t>(std::distance(targetDims.begin(), stretchDim));
298 outDims[stretchIndex] = input.GetNumElements() / targetNumElements;
299 }
300
301 TensorInfo reshapeInfo = input;
302 reshapeInfo.SetShape(TensorShape{ static_cast<unsigned int>(outDims.size()), outDims.data() });
303
304 return reshapeInfo;
305}
306
telsoa01c577f2c2018-08-31 09:22:23 +0100307// We need the input0Slot to guide the reshape for input1Slot.
saoste01bbd40612018-08-28 15:41:51 +0100308IOutputSlot* AddBroadcastReshapeLayer(IOutputSlot* input0Slot, IOutputSlot* input1Slot, bool isNHWC,
309 INetwork& m_Network, const tensorflow::NodeDef& nodeDef)
surmeh01bceff2f2018-03-29 16:29:27 +0100310{
311 const TensorInfo& input1Info = input1Slot->GetTensorInfo();
312 const TensorInfo inputTensorInfo = input0Slot->GetTensorInfo();
313 const unsigned int matchDim = inputTensorInfo.GetNumDimensions() - (isNHWC ? 1 : 3);
314 std::array<unsigned int, MaxNumOfTensorDimensions> reshapedDimensions;
315 std::fill_n(reshapedDimensions.begin(), inputTensorInfo.GetNumDimensions(), 1);
316 reshapedDimensions[matchDim] = input1Info.GetShape()[0];
317
318 armnn::TensorInfo reshapedInfo = input1Info;
319 reshapedInfo.SetShape(TensorShape{ inputTensorInfo.GetNumDimensions(), reshapedDimensions.data() });
320
321 const std::string reshapeLayerName = "reshape_for-" + nodeDef.name();
322 ReshapeDescriptor reshapeDesc;
323 reshapeDesc.m_TargetShape = reshapedInfo.GetShape();
324 IConnectableLayer* const reshapeLayer = m_Network.AddReshapeLayer(reshapeDesc, reshapeLayerName.c_str());
325
326 input1Slot->Connect(reshapeLayer->GetInputSlot(0));
327 reshapeLayer->GetOutputSlot(0).SetTensorInfo(reshapedInfo);
328
329 input1Slot = &reshapeLayer->GetOutputSlot(0);
330
331 return input1Slot;
332}
333
334OutputId ParseOutputId(const std::string & name)
335{
336 unsigned int outputNum = 0;
337 size_t colonPos = name.find_last_of(":");
338 if (colonPos != std::string::npos)
339 {
340 int n = std::stoi(name.substr(colonPos+1));
341 if (n<0 || n>100)
342 {
telsoa01c577f2c2018-08-31 09:22:23 +0100343 throw ParseException(
James Ward58dec6b2020-09-11 17:32:44 +0100344 fmt::format("Output tensor id is out of range for {} {}",
345 name,
346 CHECK_LOCATION().AsString()));
surmeh01bceff2f2018-03-29 16:29:27 +0100347 }
348 outputNum = static_cast<unsigned int>(n);
349 }
350 return OutputId(name.substr(0,colonPos),outputNum);
351}
352
telsoa01c577f2c2018-08-31 09:22:23 +0100353#define CHECK_DATA_FORMAT(NODE_DEF, FORMAT, NODE_TYPE) \
354 if( FORMAT != "NHWC" && FORMAT != "NCHW" ) \
355 { \
356 throw ParseException( \
James Ward58dec6b2020-09-11 17:32:44 +0100357 fmt::format("Unsupported data format {} passed for {} node {}. " \
358 "Only NHWC and NCHW supported {}", \
359 FORMAT, \
360 NODE_TYPE, \
361 NODE_DEF.name(), \
362 CHECK_LOCATION().AsString())); \
telsoa01c577f2c2018-08-31 09:22:23 +0100363 }
364
365#define CHECK_PADDING_TYPE(NODE_DEF, PADDING) \
366 if(PADDING != "SAME" && PADDING != "VALID" ) \
367 { \
368 throw ParseException( \
James Ward58dec6b2020-09-11 17:32:44 +0100369 fmt::format("Only 'SAME' and 'VALID' padding supported. Got {} for {} {}", \
370 PADDING, \
371 NODE_DEF.name(), \
372 CHECK_LOCATION().AsString())); \
telsoa01c577f2c2018-08-31 09:22:23 +0100373 } \
374
surmeh01bceff2f2018-03-29 16:29:27 +0100375} // namespace
376
Kevin May7d96b162021-02-03 17:38:41 +0000377const std::map<std::string, ITfParser::TfParserImpl::OperationParsingFunction>
378 ITfParser::TfParserImpl::ms_OperationNameToParsingFunctions = {
379 { "Const", &TfParserImpl::ParseConst },
380 { "Add", &TfParserImpl::ParseAdd },
381 { "AddN", &TfParserImpl::ParseAddN },
382 { "BiasAdd", &TfParserImpl::ParseBiasAdd },
383 { "Identity", &TfParserImpl::ParseIdentity },
384 { "Conv2D", &TfParserImpl::ParseConv2D },
385 { "DepthwiseConv2dNative", &TfParserImpl::ParseDepthwiseConv2D },
386 { "ExpandDims", &TfParserImpl::ParseExpandDims },
387 { "FusedBatchNorm", &TfParserImpl::ParseFusedBatchNorm },
388 { "Gather", &TfParserImpl::ParseGather},
389 { "Greater", &TfParserImpl::ParseGreater},
390 { "ConcatV2", &TfParserImpl::ParseConcat },
391 { "LRN", &TfParserImpl::ParseLrn },
392 { "MatMul", &TfParserImpl::ParseMatMul },
393 { "Mean", &TfParserImpl::ParseMean },
394 { "Mul", &TfParserImpl::ParseMul },
395 { "Placeholder", &TfParserImpl::ParsePlaceholder },
396 { "RealDiv", &TfParserImpl::ParseRealDiv },
397 { "Relu", &TfParserImpl::ParseRelu },
398 { "Relu6", &TfParserImpl::ParseRelu6 },
399 { "Reshape", &TfParserImpl::ParseReshape },
400 { "ResizeBilinear", &TfParserImpl::ParseResizeBilinear },
401 { "Rsqrt", &TfParserImpl::ParseRsqrt },
402 { "Shape", &TfParserImpl::ParseShape },
403 { "Squeeze", &TfParserImpl::ParseSqueeze },
404 { "Sigmoid", &TfParserImpl::ParseSigmoid },
405 { "Softmax", &TfParserImpl::ParseSoftmax },
406 { "Softplus", &TfParserImpl::ParseSoftplus },
407 { "Split", &TfParserImpl::ParseSplit },
408 { "StridedSlice", &TfParserImpl::ParseStridedSlice },
409 { "Tanh", &TfParserImpl::ParseTanh },
410 { "MaxPool", &TfParserImpl::ParseMaxPool },
411 { "AvgPool", &TfParserImpl::ParseAvgPool },
412 { "Maximum", &TfParserImpl::ParseMaximum },
413 { "Minimum", &TfParserImpl::ParseMinimum },
414 { "Equal", &TfParserImpl::ParseEqual },
415 { "Pad", &TfParserImpl::ParsePad },
416 { "Sub", &TfParserImpl::ParseSub },
417 { "Pack" , &TfParserImpl::ParseStack },
418 { "Stack", &TfParserImpl::ParseStack },
419 { "Transpose", &TfParserImpl::ParseTranspose },
narpra016f37f832018-12-21 18:30:00 +0000420};
421
Kevin May7d96b162021-02-03 17:38:41 +0000422const std::list<std::string> ITfParser::TfParserImpl::m_ControlInputs = {
narpra016f37f832018-12-21 18:30:00 +0000423 "Assert"
surmeh01bceff2f2018-03-29 16:29:27 +0100424};
425
Sadik Armagan60bb9d82021-01-11 15:15:01 +0000426void CalcPadding(uint32_t inputSize,
427 uint32_t filterSize,
428 uint32_t stride,
429 uint32_t dilation,
430 uint32_t& paddingFront,
431 uint32_t& paddingBack,
surmeh01bceff2f2018-03-29 16:29:27 +0100432 bool samePadding)
433{
Sadik Armagan60bb9d82021-01-11 15:15:01 +0000434 paddingFront = 0;
435 paddingBack = 0;
436 if (samePadding)
437 {
438 uint32_t outputSize = (inputSize + stride - 1) / stride;
439 uint32_t dilatedSize = filterSize + (dilation - 1) * (filterSize - 1);
440 uint32_t temp = (outputSize - 1) * stride + dilatedSize;
441 if (temp > inputSize)
442 {
443 paddingFront = (temp - inputSize) / 2;
444 paddingBack = (temp - inputSize) - paddingFront;
445 }
446 }
surmeh01bceff2f2018-03-29 16:29:27 +0100447}
448
449/// An Abstract base class which represents a single tensorflow operation (node)
450/// that has been (potentially partially) converted to Armnn.
451/// It may not yet have been fully converted into actual Armnn layers.
452class ParsedTfOperation
453{
454public:
Kevin May7d96b162021-02-03 17:38:41 +0000455 ParsedTfOperation(ITfParser::TfParserImpl* parser, const tensorflow::NodeDef& node)
surmeh01bceff2f2018-03-29 16:29:27 +0100456 : m_Parser(parser)
457 , m_Node(node)
458 {
459 }
460
461 virtual ~ParsedTfOperation() {};
462
463 const tensorflow::NodeDef& GetNode() const { return m_Node; }
464
465 /// Gets the ArmNN IOutputSlot corresponding to the given output index of the Tensorflow operation.
466 /// This may result in the creation of Armnn layers if this was deferred (e.g. see ParsedConstTfOperation).
467 virtual IOutputSlot& ResolveArmnnOutputSlot(unsigned int tfOutputIndex) = 0;
468
469 /// If this operation is an Identity then this will follow return the 'parent' operation (recursively).
470 virtual ParsedTfOperation* ResolveIdentityOperations()
471 {
472 return this;
473 }
474
475protected:
Kevin May7d96b162021-02-03 17:38:41 +0000476 ITfParser::TfParserImpl* m_Parser;
surmeh01bceff2f2018-03-29 16:29:27 +0100477 const tensorflow::NodeDef& m_Node;
478};
479
480/// An ParsedTfOperation where the Armnn equivalent is a single layer,
481/// with output slots that correspond directly to the Tf node outputs.
482class SingleLayerParsedTfOperation : public ParsedTfOperation
483{
484public:
Kevin May7d96b162021-02-03 17:38:41 +0000485 SingleLayerParsedTfOperation(ITfParser::TfParserImpl* parser,
486 const tensorflow::NodeDef& node,
487 IConnectableLayer* layer)
surmeh01bceff2f2018-03-29 16:29:27 +0100488 : ParsedTfOperation(parser, node)
489 , m_Layer(layer)
490 {
491 }
492
493 IOutputSlot& ResolveArmnnOutputSlot(unsigned int tfOutputIndex) override
494 {
Narumol Prangnawaratac2770a2020-04-01 16:51:23 +0100495 ARMNN_ASSERT(m_Layer);
telsoa01c577f2c2018-08-31 09:22:23 +0100496 // Assumes one-to-one mapping between Tf and armnn output slots.
surmeh01bceff2f2018-03-29 16:29:27 +0100497 unsigned int armnnOutputSlotIdx = tfOutputIndex;
498 if (armnnOutputSlotIdx >= m_Layer->GetNumOutputSlots())
499 {
500 throw ParseException(
James Ward58dec6b2020-09-11 17:32:44 +0100501 fmt::format("The requested output slot #{} "
502 "for {} does not exist {}",
503 armnnOutputSlotIdx,
504 m_Layer->GetName(),
505 CHECK_LOCATION().AsString()));
surmeh01bceff2f2018-03-29 16:29:27 +0100506 }
507 return m_Layer->GetOutputSlot(armnnOutputSlotIdx);
508 }
509
510protected:
511 IConnectableLayer* m_Layer;
512};
513
telsoa01c577f2c2018-08-31 09:22:23 +0100514/// A SingleLayerParsedTfOperation for deferred layer creation.
surmeh01bceff2f2018-03-29 16:29:27 +0100515class DeferredSingleLayerParsedTfOperation : public SingleLayerParsedTfOperation
516{
517public:
Kevin May7d96b162021-02-03 17:38:41 +0000518 DeferredSingleLayerParsedTfOperation(ITfParser::TfParserImpl* parser, const tensorflow::NodeDef& node)
surmeh01bceff2f2018-03-29 16:29:27 +0100519 : SingleLayerParsedTfOperation(parser, node, nullptr)
520 {
521 }
522
523 IOutputSlot& ResolveArmnnOutputSlot(unsigned int tfOutputIndex) override
524 {
525 if (!m_Layer)
526 {
527 CreateLayerDeferred();
528 }
529 return SingleLayerParsedTfOperation::ResolveArmnnOutputSlot(tfOutputIndex);
530 }
531
532private:
533 virtual void CreateLayerDeferred() = 0;
534};
535
536
Kevin May7d96b162021-02-03 17:38:41 +0000537ITfParser::TfParserImpl::TfParserImpl()
surmeh01bceff2f2018-03-29 16:29:27 +0100538 : m_Network(nullptr, nullptr)
539{
540}
541
542
Kevin May7d96b162021-02-03 17:38:41 +0000543const tensorflow::NodeDef* ITfParser::TfParserImpl::ResolveIdentityNode(const tensorflow::NodeDef* nodeDef)
surmeh01bceff2f2018-03-29 16:29:27 +0100544{
545 if (nodeDef->op() != "Identity")
546 {
547 return nodeDef;
548 }
549
550 if (nodeDef->input_size() != 1)
551 {
telsoa01c577f2c2018-08-31 09:22:23 +0100552 throw ParseException(
James Ward58dec6b2020-09-11 17:32:44 +0100553 fmt::format("Identity node should have a single input! {} has {} inputs {}",
554 nodeDef->name(),
555 nodeDef->input_size(),
556 CHECK_LOCATION().AsString()));
surmeh01bceff2f2018-03-29 16:29:27 +0100557 }
558
559 auto it = m_NodesByName.find(nodeDef->input(0));
560 if (it != m_NodesByName.end())
561 {
562 const tensorflow::NodeDef* inputNode = it->second;
563 return ResolveIdentityNode(inputNode);
564 }
565 else
566 {
telsoa01c577f2c2018-08-31 09:22:23 +0100567 throw ParseException(
James Ward58dec6b2020-09-11 17:32:44 +0100568 fmt::format("Cannot find what the Identity node {} is linked to! {}",
569 nodeDef->name(),
570 CHECK_LOCATION().AsString()));
surmeh01bceff2f2018-03-29 16:29:27 +0100571 }
572}
573
574std::vector<OutputOfConstNodeDef>
Kevin May7d96b162021-02-03 17:38:41 +0000575ITfParser::TfParserImpl::GetTfInputNodes(const tensorflow::NodeDef& nodeDef) const
surmeh01bceff2f2018-03-29 16:29:27 +0100576{
577 std::vector<OutputOfConstNodeDef> ret;
578
surmeh013537c2c2018-05-18 16:31:43 +0100579 if (nodeDef.op() == "Const")
580 {
581 // For some reason const node can have "Control Inputs". We ignore them for now.
582 return ret;
583 }
584
Matthew Sloyan589e3e82020-09-11 16:17:48 +0100585 ret.reserve(armnn::numeric_cast<size_t>(nodeDef.input_size()));
surmeh01bceff2f2018-03-29 16:29:27 +0100586 for (int j = 0; j < nodeDef.input_size(); ++j)
587 {
588 OutputId outputId = ParseOutputId(nodeDef.input(j));
surmeh013537c2c2018-05-18 16:31:43 +0100589
590 if (nodeDef.input(j)[0] == '^') // I couldn't find a better test for control inputs.
591 {
narpra016f37f832018-12-21 18:30:00 +0000592 // We currently allow Control Input from TensorFlow graph but we ignore them from ArmNN graph.
593 continue;
surmeh013537c2c2018-05-18 16:31:43 +0100594 }
595
surmeh01bceff2f2018-03-29 16:29:27 +0100596 auto inputIt = m_NodesByName.find(outputId.m_IndexedValue);
597 if (inputIt == m_NodesByName.end())
598 {
599 throw ParseException(
James Ward58dec6b2020-09-11 17:32:44 +0100600 fmt::format("Can't find node '{}', which is listed as an input of '{}' {}",
601 nodeDef.input(j),
602 nodeDef.name(),
603 CHECK_LOCATION().AsString()));
surmeh01bceff2f2018-03-29 16:29:27 +0100604 }
605 ret.push_back(OutputOfConstNodeDef(inputIt->second,outputId.m_Index));
606 }
607
608 return ret;
609}
610
611std::vector<OutputOfParsedTfOperation>
Kevin May7d96b162021-02-03 17:38:41 +0000612ITfParser::TfParserImpl::GetInputParsedTfOperationsChecked(const tensorflow::NodeDef& nodeDef,
surmeh01bceff2f2018-03-29 16:29:27 +0100613 std::size_t expectedNumInputs)
614{
telsoa01c577f2c2018-08-31 09:22:23 +0100615 // Fetches the tensorflow nodes connected as inputs and validate the size.
surmeh01bceff2f2018-03-29 16:29:27 +0100616 std::vector<OutputOfConstNodeDef> nodes = GetTfInputNodes(nodeDef);
617 const std::size_t numInputs = nodes.size();
618 if (numInputs != expectedNumInputs)
619 {
telsoa01c577f2c2018-08-31 09:22:23 +0100620 throw ParseException(
James Ward58dec6b2020-09-11 17:32:44 +0100621 fmt::format("Unexpected number of inputs for node {}. Expected {}, found {} {}",
622 nodeDef.name(),
623 expectedNumInputs,
624 numInputs,
625 CHECK_LOCATION().AsString()));
surmeh01bceff2f2018-03-29 16:29:27 +0100626 }
telsoa01c577f2c2018-08-31 09:22:23 +0100627 // Fetches the corresponding ParsedTfOperation operations
surmeh01bceff2f2018-03-29 16:29:27 +0100628 std::vector<OutputOfParsedTfOperation> result;
629 for (auto&& node : nodes)
630 {
631 auto it = m_ParsedTfOperations.find(node.m_IndexedValue->name());
632 if (it == m_ParsedTfOperations.end())
633 {
telsoa01c577f2c2018-08-31 09:22:23 +0100634 throw ParseException(
James Ward58dec6b2020-09-11 17:32:44 +0100635 fmt::format("Node with name '{}' has not been parsed {}",
636 node.m_IndexedValue->name(),
637 CHECK_LOCATION().AsString()));
surmeh01bceff2f2018-03-29 16:29:27 +0100638 }
639 ParsedTfOperation* parsedOp = it->second.get();
640 // Transparently 'skip' any Identity operations. This simplifies the logic inside the ParseXXX() functions.
641 parsedOp = parsedOp->ResolveIdentityOperations();
642 result.push_back(OutputOfParsedTfOperation(parsedOp,node.m_Index));
643 }
644 return result;
645}
646
Kevin May7d96b162021-02-03 17:38:41 +0000647IConnectableLayer* ITfParser::TfParserImpl::CreateAdditionLayer(
Ferran Balaguerfbdad032018-12-28 18:15:24 +0000648 const tensorflow::NodeDef& nodeDef,
649 IOutputSlot* input0Slot,
650 IOutputSlot* input1Slot,
651 const std::string& layerName)
652{
653 const TensorInfo& input0Info = input0Slot->GetTensorInfo();
654 const TensorInfo& input1Info = input1Slot->GetTensorInfo();
655
656 const unsigned int input0Dim = input0Info.GetNumDimensions();
657 const unsigned int input1Dim = input1Info.GetNumDimensions();
658 if (input0Dim != input1Dim)
659 {
660 // broadcasting where input0 and input1 have different number of dimensions
661 // is only supported for 1D and 4D tensors pair
662 if (input0Dim == 1 && input1Dim == 4)
663 {
664 input0Slot = AddBroadcastReshapeLayer(input1Slot, input0Slot, true, *m_Network, nodeDef);
665 }
666 else if (input0Dim == 4 && input1Dim == 1)
667 {
668 input1Slot = AddBroadcastReshapeLayer(input0Slot, input1Slot, true, *m_Network, nodeDef);
669 }
670 else
671 {
672 throw ParseException(
James Ward58dec6b2020-09-11 17:32:44 +0100673 fmt::format("Unsupported broadcast configuration for {} operation {} {}",
674 layerName,
675 nodeDef.name(),
676 CHECK_LOCATION().AsString()));
Ferran Balaguerfbdad032018-12-28 18:15:24 +0000677 }
678 }
679 IConnectableLayer* const layer = m_Network->AddAdditionLayer(layerName.c_str());
680
681 input0Slot->Connect(layer->GetInputSlot(0));
682 input1Slot->Connect(layer->GetInputSlot(1));
683
684 // Ensure the output tensor has the correct dimensions even if a broadcast has been done
685 TensorInfo outputInfo = input0Slot->GetTensorInfo();
686 std::vector<unsigned int> outputShape;
687
688 const TensorShape& input0Shape = input0Slot->GetTensorInfo().GetShape();
689 const TensorShape& input1Shape = input1Slot->GetTensorInfo().GetShape();
690
691 for (unsigned int i = 0; i < input0Shape.GetNumDimensions(); i++)
692 {
693 outputShape.push_back(std::max(input0Shape[i], input1Shape[i]));
694 }
695
696 outputInfo.SetShape(TensorShape(input0Shape.GetNumDimensions(), outputShape.data()));
697 layer->GetOutputSlot(0).SetTensorInfo(outputInfo);
698
699 return layer;
700}
701
Kevin May7d96b162021-02-03 17:38:41 +0000702IConnectableLayer* ITfParser::TfParserImpl::CreateAdditionLayer(
Ferran Balaguerfbdad032018-12-28 18:15:24 +0000703 const tensorflow::NodeDef& nodeDef,
704 IConnectableLayer* layerOne,
705 IConnectableLayer* layerTwo,
706 unsigned int numberOfAddition,
707 unsigned long numberOfLayersToConnect,
708 bool isOdd)
709{
710 IOutputSlot* input0Slot = &layerOne->GetOutputSlot(0);
711 IOutputSlot* input1Slot = &layerTwo->GetOutputSlot(0);
712 std::string layerName(nodeDef.name());
713 if (isOdd || numberOfLayersToConnect != 2)
714 {
715 // we are not connecting the final layer
716 layerName.append("_addN_").append(std::to_string(numberOfAddition));
717 }
718 return CreateAdditionLayer(nodeDef, input0Slot, input1Slot, layerName);
719}
720
Kevin May7d96b162021-02-03 17:38:41 +0000721IConnectableLayer* ITfParser::TfParserImpl::CreateAdditionLayer(
Ferran Balaguerfbdad032018-12-28 18:15:24 +0000722 const tensorflow::NodeDef& nodeDef,
723 const OutputOfParsedTfOperation& opOne,
724 const OutputOfParsedTfOperation& opTwo,
725 unsigned int numberOfAddition)
726{
727 IOutputSlot* input0Slot = &opOne.m_IndexedValue->ResolveArmnnOutputSlot(opOne.m_Index);
728 IOutputSlot* input1Slot = &opTwo.m_IndexedValue->ResolveArmnnOutputSlot(opTwo.m_Index);
729 std::string layerName(nodeDef.name());
730 layerName.append("_addN_").append(std::to_string(numberOfAddition));
731 return CreateAdditionLayer(nodeDef, input0Slot, input1Slot, layerName);
732}
733
Kevin May7d96b162021-02-03 17:38:41 +0000734IConnectableLayer* ITfParser::TfParserImpl::CreateAdditionLayer(
Ferran Balaguerfbdad032018-12-28 18:15:24 +0000735 const tensorflow::NodeDef& nodeDef,
736 const OutputOfParsedTfOperation& op,
737 IConnectableLayer* layer)
738{
739 IOutputSlot* input0Slot = &op.m_IndexedValue->ResolveArmnnOutputSlot(op.m_Index);
740 IOutputSlot* input1Slot = &layer->GetOutputSlot(0);
741 return CreateAdditionLayer(nodeDef, input0Slot, input1Slot, nodeDef.name());
742}
743
Kevin May7d96b162021-02-03 17:38:41 +0000744ParsedTfOperationPtr ITfParser::TfParserImpl::ParseAddN(const tensorflow::NodeDef& nodeDef,
745 const tensorflow::GraphDef& graphDef)
Ferran Balaguerfbdad032018-12-28 18:15:24 +0000746{
Jan Eilers8eb25602020-03-09 12:13:48 +0000747 IgnoreUnused(graphDef);
Ferran Balaguerfbdad032018-12-28 18:15:24 +0000748 uint32_t numberOfInputs = ReadMandatoryNodeUint32Attribute(nodeDef, "N");
749 if (numberOfInputs < 2)
750 {
751 // should never happen
752 throw ParseException(
James Ward58dec6b2020-09-11 17:32:44 +0100753 fmt::format("AddN Node with name '{}' has less than two ({}) inputs {}",
754 nodeDef.name(),
755 std::to_string(numberOfInputs),
756 CHECK_LOCATION().AsString()));
Ferran Balaguerfbdad032018-12-28 18:15:24 +0000757 }
758 else if (numberOfInputs == 2)
759 {
760 //this is the same as a simple Add operation
761 return AddAdditionLayer(nodeDef, false);
762 }
763 else
764 {
765 // build a binary tree of Add layers and return the final Add as the return from the function
766 // if we have an odd number of inputs then the final Add will consist of a layer connecting to an
767 // OutputOfParsedTfOperation, otherwise it will be two layers being added together
768 std::vector<OutputOfParsedTfOperation> inputs = GetInputParsedTfOperationsChecked(nodeDef, numberOfInputs);
769 unsigned int numberOfAdditions = 0;
770 std::vector<IConnectableLayer*> layers;
771 // NOTE: at this point we will have a minimum of three inputs
772 for (unsigned int i = 0; i < numberOfInputs; ++i)
773 {
774 // every time i is odd we have two inputs to process.
775 bool onSecondItem = i % 2;
776 if (onSecondItem)
777 {
778 ++numberOfAdditions;
779 IConnectableLayer* newLayer = CreateAdditionLayer(
780 nodeDef, inputs[ i - 1], inputs[i], numberOfAdditions);
781 layers.push_back(newLayer);
782 }
783 }
784
785 std::vector<IConnectableLayer*> layersToConnect(layers);
786 unsigned long numberOfLayersToConnect = layersToConnect.size();
787 bool isOdd = numberOfInputs % 2;
788
789 while (numberOfLayersToConnect > 1)
790 {
791 layers.clear();
792 for (unsigned long i = 0; i < numberOfLayersToConnect; ++i) {
793 bool onSecondItem = i % 2;
794 if (onSecondItem) {
795 ++numberOfAdditions;
796 IConnectableLayer* newLayer = CreateAdditionLayer(
797 nodeDef,
798 layersToConnect[i - 1],
799 layersToConnect[i],
800 numberOfAdditions,
801 numberOfLayersToConnect,
802 isOdd);
803 layers.push_back(newLayer);
804 }
805 }
806 //OK... need to go again... maybe
807 layersToConnect = layers;
808 numberOfLayersToConnect = layersToConnect.size();
809 }
810 IConnectableLayer* finalLayer = layersToConnect[0];
811 // if we had an odd number of inputs we need to connect the final layer to the
812 // last OutputOfParsedTfOperation in order to create the last Add layer we will
813 // be handing back.
814 if (isOdd)
815 {
816 // connect the final layer to the last op
817 finalLayer = CreateAdditionLayer(nodeDef, inputs[numberOfInputs - 1], finalLayer);
818 }
819 return std::make_unique<SingleLayerParsedTfOperation>(this, nodeDef, finalLayer);
820 }
821}
822
Kevin May7d96b162021-02-03 17:38:41 +0000823ParsedTfOperationPtr ITfParser::TfParserImpl::ParseAdd(const tensorflow::NodeDef& nodeDef,
824 const tensorflow::GraphDef& graphDef)
surmeh01bceff2f2018-03-29 16:29:27 +0100825{
Jan Eilers8eb25602020-03-09 12:13:48 +0000826 IgnoreUnused(graphDef);
surmeh01bceff2f2018-03-29 16:29:27 +0100827 std::vector<OutputOfParsedTfOperation> inputs = GetInputParsedTfOperationsChecked(nodeDef, 2);
828
telsoa01c577f2c2018-08-31 09:22:23 +0100829 // If one of the inputs is a MatMul and the other is a const, then we handle both nodes
830 // together as FullyConnected.
surmeh01bceff2f2018-03-29 16:29:27 +0100831 if (inputs[0].m_IndexedValue->GetNode().op() == "MatMul" &&
832 HasParsedConstTensor<float>(inputs[1].m_IndexedValue->GetNode().name()))
833 {
834 IConnectableLayer* layer =
835 AddFullyConnectedLayer(inputs[0].m_IndexedValue->GetNode(),
836 &nodeDef,nodeDef.name().c_str());
837 return std::make_unique<SingleLayerParsedTfOperation>(this, nodeDef, layer);
838 }
839 else if (HasParsedConstTensor<float>(inputs[0].m_IndexedValue->GetNode().name()) &&
840 inputs[1].m_IndexedValue->GetNode().op() == "MatMul")
841 {
842 IConnectableLayer* layer =
843 AddFullyConnectedLayer(inputs[1].m_IndexedValue->GetNode(),
844 &nodeDef,nodeDef.name().c_str());
845 return std::make_unique<SingleLayerParsedTfOperation>(this, nodeDef, layer);
846 }
847 else
848 {
telsoa01c577f2c2018-08-31 09:22:23 +0100849 // Otherwise it's just a regular addition.
surmeh01bceff2f2018-03-29 16:29:27 +0100850 return AddAdditionLayer(nodeDef);
851 }
852}
853
Kevin May7d96b162021-02-03 17:38:41 +0000854ParsedTfOperationPtr ITfParser::TfParserImpl::ParseBiasAdd(const tensorflow::NodeDef& nodeDef,
855 const tensorflow::GraphDef& graphDef)
surmeh01bceff2f2018-03-29 16:29:27 +0100856{
Jan Eilers8eb25602020-03-09 12:13:48 +0000857 IgnoreUnused(graphDef);
surmeh01bceff2f2018-03-29 16:29:27 +0100858 return AddAdditionLayer(nodeDef, true);
859}
860
861/// An ParsedTfOperation which forwards to another (used for Identity nodes).
862class ParsedIdentityTfOperation : public ParsedTfOperation
863{
864public:
Kevin May7d96b162021-02-03 17:38:41 +0000865 ParsedIdentityTfOperation(ITfParser::TfParserImpl* parser,
866 const tensorflow::NodeDef& node,
867 ParsedTfOperation* representative)
surmeh01bceff2f2018-03-29 16:29:27 +0100868 : ParsedTfOperation(parser, node)
869 , m_Representative(representative)
870 {
871 }
872
873 virtual IOutputSlot& ResolveArmnnOutputSlot(unsigned int tfOutputIndex) override
874 {
Narumol Prangnawaratac2770a2020-04-01 16:51:23 +0100875 ARMNN_ASSERT(m_Representative);
surmeh01bceff2f2018-03-29 16:29:27 +0100876 return m_Representative->ResolveArmnnOutputSlot(tfOutputIndex);
877 }
878
879 virtual ParsedTfOperation* ResolveIdentityOperations() override
880 {
881 return m_Representative->ResolveIdentityOperations();
882 }
883
884private:
885 ParsedTfOperation* m_Representative;
886};
887
Kevin May7d96b162021-02-03 17:38:41 +0000888ParsedTfOperationPtr ITfParser::TfParserImpl::ParseIdentity(const tensorflow::NodeDef& nodeDef,
889 const tensorflow::GraphDef& graphDef)
surmeh01bceff2f2018-03-29 16:29:27 +0100890{
Jan Eilers8eb25602020-03-09 12:13:48 +0000891 IgnoreUnused(graphDef);
surmeh01bceff2f2018-03-29 16:29:27 +0100892 std::vector<OutputOfParsedTfOperation> inputs = GetInputParsedTfOperationsChecked(nodeDef, 1);
893 // Any requests for the output slots of this node should be forwarded to the node connected as input.
894 return std::make_unique<ParsedIdentityTfOperation>(this, nodeDef, inputs[0].m_IndexedValue);
895}
896
897/// An ParsedTfOperation for a Const node.
898/// Creation of the armnn ConstLayer is deferred until it is actually needed, because Const nodes are mostly used
899/// for weight inputs to MatMul/Conv2D nodes and in these cases armnn doesn't need a ConstLayer.
900template <typename T>
901class ParsedConstTfOperation : public DeferredSingleLayerParsedTfOperation
902{
903public:
Kevin May7d96b162021-02-03 17:38:41 +0000904 ParsedConstTfOperation(ITfParser::TfParserImpl* parser, const tensorflow::NodeDef& node,
surmeh01bceff2f2018-03-29 16:29:27 +0100905 const T* tensorData, const TensorInfo& tensorInfo)
906 : DeferredSingleLayerParsedTfOperation(parser, node),
907 m_Storage(tensorData, tensorData + tensorInfo.GetNumElements()),
908 m_TensorInfo(tensorInfo)
909 {
Narumol Prangnawaratac2770a2020-04-01 16:51:23 +0100910 ARMNN_ASSERT(GetDataTypeSize(tensorInfo.GetDataType()) == sizeof(T));
surmeh01bceff2f2018-03-29 16:29:27 +0100911 }
912
913 void CreateLayerDeferred() override
914 {
Narumol Prangnawaratac2770a2020-04-01 16:51:23 +0100915 ARMNN_ASSERT(m_Layer == nullptr);
Kevin May7d96b162021-02-03 17:38:41 +0000916 m_Layer = m_Parser->m_Network->AddConstantLayer(ConstTensor(m_TensorInfo, m_Storage),
917 m_Node.name().c_str());
surmeh01bceff2f2018-03-29 16:29:27 +0100918 m_Layer->GetOutputSlot(0).SetTensorInfo(m_TensorInfo);
919 }
920
Matteo Martincigh482ca852018-12-12 09:20:55 +0000921 ConstTensor GetConstTensor(std::vector<T>& outputTensorData) const
surmeh01bceff2f2018-03-29 16:29:27 +0100922 {
surmeh01bceff2f2018-03-29 16:29:27 +0100923 outputTensorData.resize(m_TensorInfo.GetNumElements());
924
Matteo Martincigh482ca852018-12-12 09:20:55 +0000925 memcpy(outputTensorData.data(), m_Storage.data(), m_TensorInfo.GetNumBytes());
926
telsoa01c577f2c2018-08-31 09:22:23 +0100927 // Updates the result to point to the user provided storage.
Matteo Martincigh482ca852018-12-12 09:20:55 +0000928 ConstTensor constTensor(m_TensorInfo, outputTensorData);
surmeh01bceff2f2018-03-29 16:29:27 +0100929 return constTensor;
930 }
931
Matteo Martincigh46315822018-11-28 16:22:36 +0000932 const T* GetStorage() const
933 {
934 return m_Storage.data();
935 }
936
937 const TensorInfo& GetTensorInfo() const
938 {
939 return m_TensorInfo;
940 }
941
surmeh01bceff2f2018-03-29 16:29:27 +0100942private:
943 ///< Manages the lifetime of the tensor data.
944 std::vector<T> m_Storage;
945 ///< Describes the layout of the tensor and points to the data in m_Storage.
946 TensorInfo m_TensorInfo;
947};
948
telsoa01c577f2c2018-08-31 09:22:23 +0100949DataType ConvertTfTensorDataType(const tensorflow::DataType tfDataType,
950 const tensorflow::NodeDef& nodeDef)
surmeh01bceff2f2018-03-29 16:29:27 +0100951{
952 switch (tfDataType)
953 {
954 case tensorflow::DT_FLOAT:
955 return DataType::Float32;
956 break;
957 case tensorflow::DT_INT32:
958 return DataType::Signed32;
959 break;
960 default:
telsoa01c577f2c2018-08-31 09:22:23 +0100961 throw ParseException(
James Ward58dec6b2020-09-11 17:32:44 +0100962 fmt::format("Unknown DataType {} for node {} {}",
963 tensorflow::DataType_Name(tfDataType),
964 nodeDef.name(),
965 CHECK_LOCATION().AsString()));
surmeh01bceff2f2018-03-29 16:29:27 +0100966 }
967}
968
969struct ParseTfTensorValueList
970{
971 template<typename DataType>
972 static void Parse(
973 const tensorflow::TensorProto& tfTensor,
974 unsigned int dstElements,
975 std::vector<int8_t>& outputData);
976
977 template <typename DataType>
978 static void ReadData(const void* srcData, unsigned int numSrcElements,
979 std::vector<int8_t>& dstData, unsigned int numDstElements)
980 {
telsoa01c577f2c2018-08-31 09:22:23 +0100981 // If there are no entries in the list, perform no action.
surmeh01bceff2f2018-03-29 16:29:27 +0100982 if (numSrcElements == 0)
983 {
984 return;
985 }
986
telsoa01c577f2c2018-08-31 09:22:23 +0100987 // If no size was provided, use the length of the value list.
surmeh01bceff2f2018-03-29 16:29:27 +0100988 if (numDstElements == 0)
989 {
990 numDstElements = numSrcElements;
991 }
992
telsoa01c577f2c2018-08-31 09:22:23 +0100993 // Allocates memory.
surmeh01bceff2f2018-03-29 16:29:27 +0100994 dstData.resize(std::max(numSrcElements, numDstElements) * sizeof(DataType));
995
996 const DataType* srcTensor = reinterpret_cast<const DataType*>(srcData);
997 DataType* dstTensor = reinterpret_cast<DataType*>(dstData.data());
998
telsoa01c577f2c2018-08-31 09:22:23 +0100999 // Copies the value list entries into the destination.
surmeh01bceff2f2018-03-29 16:29:27 +01001000 std::copy(srcTensor, srcTensor + numSrcElements, dstTensor);
1001
1002 if (numDstElements > numSrcElements)
1003 {
telsoa01c577f2c2018-08-31 09:22:23 +01001004 // Uses the last element in the list to fill the remaining entries.
surmeh01bceff2f2018-03-29 16:29:27 +01001005 std::fill(dstTensor + numSrcElements, dstTensor + numDstElements, srcTensor[numSrcElements - 1]);
1006 }
1007 }
1008
1009};
1010
1011template <>
1012void ParseTfTensorValueList::Parse<float>(const tensorflow::TensorProto& tfTensor,
1013 unsigned int dstElements, std::vector<int8_t>& outputData)
1014{
1015 ReadData<float>(tfTensor.float_val().data(), static_cast<unsigned int>(tfTensor.float_val_size()),
1016 outputData, dstElements);
1017}
1018
1019template <>
1020void ParseTfTensorValueList::Parse<int32_t>(const tensorflow::TensorProto& tfTensor,
1021 unsigned int dstElements, std::vector<int8_t>& outputData)
1022{
1023 ReadData<int32_t>(tfTensor.int_val().data(), static_cast<unsigned int>(tfTensor.int_val_size()),
1024 outputData, dstElements);
1025}
1026
1027template <template<typename> class OperatorType, typename T = int8_t>
1028struct MakeTfOperation
1029{
1030 template<typename DataType, class... Args>
Kevin May7d96b162021-02-03 17:38:41 +00001031 inline static std::unique_ptr<OperatorType<DataType>> Parse(ITfParser::TfParserImpl* parser,
1032 const tensorflow::NodeDef& node,
1033 Args&&... args)
surmeh01bceff2f2018-03-29 16:29:27 +01001034 {
1035 return std::make_unique<OperatorType<DataType>>(parser, node, std::forward<Args>(args)...);
1036 }
1037};
1038
1039template <>
1040struct MakeTfOperation<ParsedConstTfOperation>
1041{
1042 template<typename DataType, class... Args>
Kevin May7d96b162021-02-03 17:38:41 +00001043 inline static std::unique_ptr<ParsedConstTfOperation<DataType>> Parse(ITfParser::TfParserImpl* parser,
surmeh01bceff2f2018-03-29 16:29:27 +01001044 const tensorflow::NodeDef& node, const std::vector<int8_t>& tensorData, const TensorInfo& tensorInfo)
1045 {
1046 return std::make_unique<ParsedConstTfOperation<DataType>>(parser, node,
1047 reinterpret_cast<const DataType*>(tensorData.data()), tensorInfo);
1048 }
1049};
1050
1051template <class FuncType>
1052struct InvokeParseFunction
1053{
1054 template<class ResType, class... Args>
1055 inline static ResType Result(DataType dataType, Args&&... args)
1056 {
1057 if (dataType == DataType::Float32)
1058 {
1059 return FuncType::template Parse<float>(std::forward<Args>(args)...);
1060 }
1061 else if (dataType == DataType::Signed32)
1062 {
1063 return FuncType::template Parse<int32_t>(std::forward<Args>(args)...);
1064 }
1065
1066 return ResType();
1067 }
1068
1069 template<class... Args>
1070 inline static void Result(DataType dataType, Args&&... args)
1071 {
1072 if (dataType == DataType::Float32)
1073 {
1074 FuncType::template Parse<float>(std::forward<Args>(args)...);
1075 }
1076 else if (dataType == DataType::Signed32)
1077 {
1078 FuncType::template Parse<int32_t>(std::forward<Args>(args)...);
1079 }
1080 }
1081};
1082
Kevin May7d96b162021-02-03 17:38:41 +00001083ParsedTfOperationPtr ITfParser::TfParserImpl::ParseConst(const tensorflow::NodeDef& nodeDef,
1084 const tensorflow::GraphDef& graphDef)
surmeh01bceff2f2018-03-29 16:29:27 +01001085{
Jan Eilers8eb25602020-03-09 12:13:48 +00001086 IgnoreUnused(graphDef);
Narumol Prangnawaratac2770a2020-04-01 16:51:23 +01001087 ARMNN_ASSERT(nodeDef.op() == "Const");
surmeh01bceff2f2018-03-29 16:29:27 +01001088
1089 if (nodeDef.attr().count("value") == 0)
1090 {
telsoa01c577f2c2018-08-31 09:22:23 +01001091 throw ParseException(
James Ward58dec6b2020-09-11 17:32:44 +01001092 fmt::format("Value not found for Const node - {} {}",
1093 nodeDef.name(),
1094 CHECK_LOCATION().AsString()));
surmeh01bceff2f2018-03-29 16:29:27 +01001095 }
1096
1097 const tensorflow::TensorProto& tfTensor = nodeDef.attr().at("value").tensor();
1098 const tensorflow::TensorShapeProto& tfTensorShape = tfTensor.tensor_shape();
1099 const tensorflow::DataType tfDataType = ReadMandatoryNodeTypeAttribute(nodeDef, "dtype");
1100
1101 const auto GetDimensionSize = [](auto& d) { return d.size(); };
1102
1103 std::vector<unsigned int> dimensionSizes;
1104 std::transform(tfTensorShape.dim().begin(), tfTensorShape.dim().end(),
1105 std::back_inserter(dimensionSizes), GetDimensionSize);
1106
telsoa01c577f2c2018-08-31 09:22:23 +01001107 // Calculates number of elements.
1108 const DataType dataType = ConvertTfTensorDataType(tfDataType, nodeDef);
surmeh01bceff2f2018-03-29 16:29:27 +01001109 unsigned int numElements = 0U;
1110
1111 if (!dimensionSizes.empty())
1112 {
1113 numElements = std::accumulate(dimensionSizes.begin(), dimensionSizes.end(),
1114 1U, std::multiplies<unsigned int>());
1115 }
1116
1117 std::vector<int8_t> tensorData;
1118
telsoa01c577f2c2018-08-31 09:22:23 +01001119 // Get tensor data from the list of values attribute.
surmeh01bceff2f2018-03-29 16:29:27 +01001120 if (tfTensor.tensor_content().empty())
1121 {
1122 InvokeParseFunction<ParseTfTensorValueList>::Result<void>(dataType, tfTensor, numElements, tensorData);
1123
1124 // If the tensor shape is not defined, but there is a value list, then interpret the data as a 1D
telsoa01c577f2c2018-08-31 09:22:23 +01001125 // tensor of the provided number of elements.
surmeh01bceff2f2018-03-29 16:29:27 +01001126 if (numElements == 0)
1127 {
telsoa01c577f2c2018-08-31 09:22:23 +01001128 const unsigned int tfNumElements =
1129 static_cast<unsigned int>(tensorData.size()) / GetDataTypeSize(dataType);
surmeh01bceff2f2018-03-29 16:29:27 +01001130 dimensionSizes.push_back(tfNumElements);
1131 }
1132 }
telsoa01c577f2c2018-08-31 09:22:23 +01001133 // Gets tensor data from tensor content attribute.
surmeh01bceff2f2018-03-29 16:29:27 +01001134 else
1135 {
1136 tensorData.assign(tfTensor.tensor_content().begin(), tfTensor.tensor_content().end());
1137
telsoa01c577f2c2018-08-31 09:22:23 +01001138 // Checks if a tensor shape is defined for the tensor content.
surmeh01bceff2f2018-03-29 16:29:27 +01001139 if (numElements == 0)
1140 {
telsoa01c577f2c2018-08-31 09:22:23 +01001141 throw ParseException(
James Ward58dec6b2020-09-11 17:32:44 +01001142 fmt::format("No tensor shape found for Const node - {} {}",
1143 nodeDef.name(),
1144 CHECK_LOCATION().AsString()));
surmeh01bceff2f2018-03-29 16:29:27 +01001145 }
1146 }
1147
telsoa01c577f2c2018-08-31 09:22:23 +01001148 // Const node requires at least a list of values or a content attribute.
surmeh01bceff2f2018-03-29 16:29:27 +01001149 if (tensorData.empty())
1150 {
telsoa01c577f2c2018-08-31 09:22:23 +01001151 throw ParseException(
James Ward58dec6b2020-09-11 17:32:44 +01001152 fmt::format("No tensor data found for Const node - {} {}",
1153 nodeDef.name(),
1154 CHECK_LOCATION().AsString()));
surmeh01bceff2f2018-03-29 16:29:27 +01001155 }
1156
telsoa01c577f2c2018-08-31 09:22:23 +01001157 const TensorInfo tensorInfo(static_cast<unsigned int>(dimensionSizes.size()),
1158 dimensionSizes.data(),
1159 dataType);
surmeh01bceff2f2018-03-29 16:29:27 +01001160
1161 // If we have a list of values, then the length of the list must be
telsoa01c577f2c2018-08-31 09:22:23 +01001162 // less than or equal to the number of elements implied by the shape argument.
surmeh01bceff2f2018-03-29 16:29:27 +01001163 if (tensorData.size() > tensorInfo.GetNumBytes())
1164 {
telsoa01c577f2c2018-08-31 09:22:23 +01001165 throw ParseException(
James Ward58dec6b2020-09-11 17:32:44 +01001166 fmt::format("Number of elements ({}) should be less than or equal "
1167 "to the number of elements implied by the shape argument ({}) for Const node - {} {}",
1168 (tensorData.size() / GetDataTypeSize(dataType)),
1169 tensorInfo.GetNumElements(),
1170 nodeDef.name(),
1171 CHECK_LOCATION().AsString()));
surmeh01bceff2f2018-03-29 16:29:27 +01001172 }
1173
1174 return InvokeParseFunction<MakeTfOperation<ParsedConstTfOperation>>::Result<ParsedTfOperationPtr>(
1175 dataType, this, nodeDef, tensorData, tensorInfo);
1176}
1177
1178template<typename Type>
Kevin May7d96b162021-02-03 17:38:41 +00001179bool ITfParser::TfParserImpl::HasParsedConstTensor(const std::string & nodeName) const
surmeh01bceff2f2018-03-29 16:29:27 +01001180{
1181 auto it = m_ParsedTfOperations.find(nodeName);
jimfly01f6ba7472018-12-04 10:09:52 +00001182 if (it == m_ParsedTfOperations.end())
surmeh01bceff2f2018-03-29 16:29:27 +01001183 {
1184 return false;
1185 }
jimfly01f6ba7472018-12-04 10:09:52 +00001186 return dynamic_cast<ParsedConstTfOperation<Type>*>(it->second.get()) != nullptr;
1187}
1188
1189template<typename Type>
Kevin May7d96b162021-02-03 17:38:41 +00001190bool ITfParser::TfParserImpl::HasParsedConstTensor(ParsedTfOperation* parsedTfOpPtr) const
jimfly01f6ba7472018-12-04 10:09:52 +00001191{
1192 return dynamic_cast<ParsedConstTfOperation<Type>*>(parsedTfOpPtr) != nullptr;
surmeh01bceff2f2018-03-29 16:29:27 +01001193}
1194
Kevin May7d96b162021-02-03 17:38:41 +00001195unsigned int ITfParser::TfParserImpl::GetConstInputIndex(const std::vector<OutputOfParsedTfOperation>& inputs)
Saoirse Stewart91c0eff2019-02-27 11:07:57 +00001196{
1197 for (unsigned int i = 0; i < inputs.size(); i++)
1198 {
1199 if (HasParsedConstTensor<int32_t>(inputs[i].m_IndexedValue->GetNode().name()))
1200 {
1201 return i;
1202 }
1203 }
1204 throw ParseException(
James Ward58dec6b2020-09-11 17:32:44 +01001205 fmt::format("ArmNN only supports operators with constant axis. {}",
1206 CHECK_LOCATION().AsString()));
Saoirse Stewart91c0eff2019-02-27 11:07:57 +00001207
1208}
1209
Kevin May7d96b162021-02-03 17:38:41 +00001210ParsedTfOperationPtr ITfParser::TfParserImpl::ParseConv2D(const tensorflow::NodeDef& nodeDef,
surmeh01bceff2f2018-03-29 16:29:27 +01001211 const tensorflow::GraphDef& graphDef)
1212{
Jan Eilers8eb25602020-03-09 12:13:48 +00001213 IgnoreUnused(graphDef);
surmeh01bceff2f2018-03-29 16:29:27 +01001214 std::vector<OutputOfParsedTfOperation> inputs = GetInputParsedTfOperationsChecked(nodeDef, 2);
1215 IOutputSlot& inputSlot = inputs[0].m_IndexedValue->ResolveArmnnOutputSlot(inputs[0].m_Index);
1216 TensorInfo inputTensorInfo = inputSlot.GetTensorInfo();
1217
1218 if (!HasParsedConstTensor<float>(inputs[1].m_IndexedValue->GetNode().name()))
1219 {
telsoa01c577f2c2018-08-31 09:22:23 +01001220 throw ParseException(
James Ward58dec6b2020-09-11 17:32:44 +01001221 fmt::format("ArmNN only supports Convolution layers with constant weights for {}, input {} {}",
1222 nodeDef.name(),
1223 inputs[1].m_IndexedValue->GetNode().name(),
1224 CHECK_LOCATION().AsString()));
surmeh01bceff2f2018-03-29 16:29:27 +01001225 }
1226 ParsedConstTfOperation<float>* weightNode =
Jan Eilersbb446e52020-04-02 13:56:54 +01001227 PolymorphicDowncast<ParsedConstTfOperation<float> *>(inputs[1].m_IndexedValue);
surmeh01bceff2f2018-03-29 16:29:27 +01001228
1229 std::string paddingString = ReadMandatoryNodeStringAttribute(nodeDef, "padding");
1230 std::string dataFormat = ReadMandatoryNodeStringAttribute(nodeDef, "data_format");
1231 std::vector<uint32_t> strides = ReadMandatoryNodeUint32ListAttribute(nodeDef, "strides");
1232
surmeh01bceff2f2018-03-29 16:29:27 +01001233 Convolution2dDescriptor desc;
1234 desc.m_BiasEnabled = false;
1235
telsoa01c577f2c2018-08-31 09:22:23 +01001236 CHECK_DATA_FORMAT(nodeDef, dataFormat, "Conv2D");
1237
Matteo Martincigh46315822018-11-28 16:22:36 +00001238 DataLayout dataLayout = dataFormat == "NHWC" ? DataLayout::NHWC : DataLayout::NCHW;
surmeh01bceff2f2018-03-29 16:29:27 +01001239
Matteo Martincigh46315822018-11-28 16:22:36 +00001240 desc.m_DataLayout = dataLayout;
surmeh01bceff2f2018-03-29 16:29:27 +01001241
Matteo Martincigh46315822018-11-28 16:22:36 +00001242 DataLayoutIndexed dataLayoutIndexed(dataLayout);
surmeh01bceff2f2018-03-29 16:29:27 +01001243
Matteo Martincigh46315822018-11-28 16:22:36 +00001244 desc.m_StrideX = strides[dataLayoutIndexed.GetWidthIndex()];
1245 desc.m_StrideY = strides[dataLayoutIndexed.GetHeightIndex()];
surmeh01bceff2f2018-03-29 16:29:27 +01001246
Sadik Armagan60bb9d82021-01-11 15:15:01 +00001247 std::vector<uint32_t> dilations = ReadOptionalNodeUint32ListAttribute(nodeDef, "dilations");
1248 if (!dilations.empty())
1249 {
1250 desc.m_DilationX = dilations[dataLayoutIndexed.GetWidthIndex()];
1251 desc.m_DilationY = dilations[dataLayoutIndexed.GetHeightIndex()];
1252 }
1253
Matteo Martincigh46315822018-11-28 16:22:36 +00001254 uint32_t inputHeight = inputTensorInfo.GetShape()[dataLayoutIndexed.GetHeightIndex()];
1255 uint32_t inputWidth = inputTensorInfo.GetShape()[dataLayoutIndexed.GetWidthIndex()];
1256
1257 // Mappings from TensorFlow filter tensors to the ArmNN filter tensors.
1258 // Tensorflow weights are [H, W, In, Out].
1259 // ArmNN weights have to be [Out, H, W, In] when the data layout is NHWC,
1260 // and [Out, In, H, W] when the data layout is NCHW.
1261 PermutationVector permutationVector =
1262 dataLayout == DataLayout::NHWC ?
1263 std::initializer_list<unsigned int>{ 1, 2, 3, 0 } : // NHWC: [H, W, In, Out] -> [Out, H, W, In]
1264 std::initializer_list<unsigned int>{ 2, 3, 1, 0 }; // NCHW: [H, W, In, Out] -> [Out, In, H, W]
1265
1266 // Swizzle the tensor using the given permutation vector.
1267 const TensorInfo& weightTensorInfo = weightNode->GetTensorInfo();
1268 const TensorInfo weightTensorSwizzledInfo = armnnUtils::Permuted(weightTensorInfo, permutationVector);
1269
1270 // Swizzles the content of the tensor's permanent storage into a local storage.
1271 std::vector<float> weightTensorSwizzledData(weightTensorInfo.GetNumElements());
1272 armnnUtils::Permute(weightTensorSwizzledInfo.GetShape(), permutationVector,
Matteo Martincighd5b9e642019-01-04 18:01:21 +00001273 weightNode->GetStorage(), weightTensorSwizzledData.data(), sizeof(float));
Matteo Martincigh46315822018-11-28 16:22:36 +00001274
1275 // Create a weight tensor with the newly swizzled data.
1276 ConstTensor weightTensor(weightTensorSwizzledInfo, weightTensorSwizzledData);
1277
1278 uint32_t weightHeight = weightTensor.GetShape()[dataLayoutIndexed.GetHeightIndex()];
1279 uint32_t weightWidth = weightTensor.GetShape()[dataLayoutIndexed.GetWidthIndex()];
surmeh01bceff2f2018-03-29 16:29:27 +01001280
1281 bool padding = false;
1282 TensorInfo outputInfo;
Matteo Martincigh46315822018-11-28 16:22:36 +00001283 unsigned int outputHeight = 0;
1284 unsigned int outputWidth = 0;
telsoa01c577f2c2018-08-31 09:22:23 +01001285
1286 CHECK_PADDING_TYPE(nodeDef, paddingString);
1287
surmeh01bceff2f2018-03-29 16:29:27 +01001288 if (paddingString == "SAME")
1289 {
1290 padding = true;
surmeh01bceff2f2018-03-29 16:29:27 +01001291 }
1292 else if (paddingString == "VALID")
1293 {
1294 padding = false;
Matteo Martincigh46315822018-11-28 16:22:36 +00001295 }
1296
Sadik Armagan60bb9d82021-01-11 15:15:01 +00001297 CalcPadding(inputHeight, weightHeight, desc.m_StrideY, desc.m_DilationY, desc.m_PadTop, desc.m_PadBottom, padding);
1298 CalcPadding(inputWidth, weightWidth, desc.m_StrideX, desc.m_DilationX, desc.m_PadLeft, desc.m_PadRight, padding);
1299
1300 // Calculate output height and width
1301 unsigned int dilatedFilterWidth = weightWidth + (desc.m_DilationX - 1) * (weightWidth - 1);
1302 unsigned int readWidth = (inputWidth + desc.m_PadLeft + desc.m_PadRight) - dilatedFilterWidth;
1303 outputWidth = 1 + (readWidth / desc.m_StrideX);
1304
1305 unsigned int dilatedFilterHeight = weightHeight + (desc.m_DilationY - 1) * (weightHeight - 1);
1306 unsigned int readHeight = (inputHeight + desc.m_PadTop + desc.m_PadBottom) - dilatedFilterHeight;
1307 outputHeight = 1 + (readHeight / desc.m_StrideY);
1308
Matteo Martincigh46315822018-11-28 16:22:36 +00001309 switch (dataLayout)
1310 {
1311 case DataLayout::NHWC:
1312 outputInfo = TensorInfo({ inputTensorInfo.GetShape()[0],
1313 outputHeight,
1314 outputWidth,
1315 weightTensor.GetShape()[0] },
1316 DataType::Float32);
1317 break;
1318 case DataLayout::NCHW:
1319 default:
surmeh01bceff2f2018-03-29 16:29:27 +01001320 outputInfo = TensorInfo({ inputTensorInfo.GetShape()[0],
1321 weightTensor.GetShape()[0],
Matteo Martincigh46315822018-11-28 16:22:36 +00001322 outputHeight,
1323 outputWidth },
1324 DataType::Float32);
1325 break;
surmeh01bceff2f2018-03-29 16:29:27 +01001326 }
surmeh01bceff2f2018-03-29 16:29:27 +01001327
Matteo Martincighfc598e12019-05-14 10:36:13 +01001328 IConnectableLayer* layer = m_Network->AddConvolution2dLayer(desc,
1329 weightTensor,
1330 EmptyOptional(),
1331 nodeDef.name().c_str());
surmeh01bceff2f2018-03-29 16:29:27 +01001332 layer->GetOutputSlot(0).SetTensorInfo(outputInfo);
Matteo Martincigh46315822018-11-28 16:22:36 +00001333 inputSlot.Connect(layer->GetInputSlot(0));
surmeh01bceff2f2018-03-29 16:29:27 +01001334
1335 return std::make_unique<SingleLayerParsedTfOperation>(this, nodeDef, layer);
1336}
1337
Kevin May7d96b162021-02-03 17:38:41 +00001338ParsedTfOperationPtr ITfParser::TfParserImpl::ParseDepthwiseConv2D(const tensorflow::NodeDef& nodeDef,
1339 const tensorflow::GraphDef& graphDef)
surmeh01bceff2f2018-03-29 16:29:27 +01001340{
Jan Eilers8eb25602020-03-09 12:13:48 +00001341 IgnoreUnused(graphDef);
surmeh01bceff2f2018-03-29 16:29:27 +01001342 std::vector<OutputOfParsedTfOperation> inputs = GetInputParsedTfOperationsChecked(nodeDef, 2);
1343 IOutputSlot& inputSlot = inputs[0].m_IndexedValue->ResolveArmnnOutputSlot(inputs[0].m_Index);
1344 TensorInfo inputTensorInfo = inputSlot.GetTensorInfo();
1345
1346 if (!HasParsedConstTensor<float>(inputs[1].m_IndexedValue->GetNode().name()))
1347 {
telsoa01c577f2c2018-08-31 09:22:23 +01001348 throw ParseException(
James Ward58dec6b2020-09-11 17:32:44 +01001349 fmt::format("ArmNN only supports Depthwise Convolution layer with constant weights. "
1350 "Non const input found {} for node {} {}",
1351 inputs[1].m_IndexedValue->GetNode().name(),
1352 nodeDef.name(),
1353 CHECK_LOCATION().AsString()));
surmeh01bceff2f2018-03-29 16:29:27 +01001354 }
Ferran Balaguer6a669d72018-12-11 10:29:05 +00001355
surmeh01bceff2f2018-03-29 16:29:27 +01001356 ParsedConstTfOperation<float>* weightNode =
Jan Eilersbb446e52020-04-02 13:56:54 +01001357 PolymorphicDowncast<ParsedConstTfOperation<float> *>(inputs[1].m_IndexedValue);
surmeh01bceff2f2018-03-29 16:29:27 +01001358
surmeh01bceff2f2018-03-29 16:29:27 +01001359 std::string paddingString = ReadMandatoryNodeStringAttribute(nodeDef, "padding");
1360 std::string dataFormat = ReadMandatoryNodeStringAttribute(nodeDef, "data_format");
1361 std::vector<uint32_t> strides = ReadMandatoryNodeUint32ListAttribute(nodeDef, "strides");
1362
1363 DepthwiseConvolution2dDescriptor desc;
1364 desc.m_BiasEnabled = false;
1365
telsoa01c577f2c2018-08-31 09:22:23 +01001366 CHECK_DATA_FORMAT(nodeDef, dataFormat, "DepthwiseConv2dNative");
1367
Ferran Balaguer6a669d72018-12-11 10:29:05 +00001368 DataLayout dataLayout = dataFormat == "NHWC" ? DataLayout::NHWC : DataLayout::NCHW;
surmeh01bceff2f2018-03-29 16:29:27 +01001369
Ferran Balaguer6a669d72018-12-11 10:29:05 +00001370 desc.m_DataLayout = dataLayout;
surmeh01bceff2f2018-03-29 16:29:27 +01001371
Ferran Balaguer6a669d72018-12-11 10:29:05 +00001372 DataLayoutIndexed dataLayoutIndexed(dataLayout);
surmeh01bceff2f2018-03-29 16:29:27 +01001373
Ferran Balaguer6a669d72018-12-11 10:29:05 +00001374 desc.m_StrideX = strides[dataLayoutIndexed.GetWidthIndex()];
1375 desc.m_StrideY = strides[dataLayoutIndexed.GetHeightIndex()];
Sadik Armagan60bb9d82021-01-11 15:15:01 +00001376 std::vector<uint32_t> dilations = ReadOptionalNodeUint32ListAttribute(nodeDef, "dilations");
1377 if (!dilations.empty())
1378 {
1379 desc.m_DilationX = dilations[dataLayoutIndexed.GetWidthIndex()];
1380 desc.m_DilationY = dilations[dataLayoutIndexed.GetHeightIndex()];
1381 }
surmeh01bceff2f2018-03-29 16:29:27 +01001382
Ferran Balaguer6a669d72018-12-11 10:29:05 +00001383 uint32_t inputHeight = inputTensorInfo.GetShape()[dataLayoutIndexed.GetHeightIndex()];
1384 uint32_t inputWidth = inputTensorInfo.GetShape()[dataLayoutIndexed.GetWidthIndex()];
1385
1386 // Mappings from TensorFlow filter tensors to the ArmNN filter tensors.
Matteo Martincigh747ef822018-12-18 09:26:39 +00001387 // Tensorflow weights come in the format [H, W, I, M].
1388 // ArmNN weights have to be [M, I, H, W].
1389 PermutationVector permutationVector{ 2, 3, 1, 0 }; // [H, W, I, M] -> [M, I, H, W]
Ferran Balaguer6a669d72018-12-11 10:29:05 +00001390
1391 // Swizzle the tensor using the given permutation vector.
1392 const TensorInfo& weightTensorInfo = weightNode->GetTensorInfo();
1393 const TensorInfo weightTensorSwizzledInfo = armnnUtils::Permuted(weightTensorInfo, permutationVector);
1394
1395 // Swizzles the content of the tensor's permanent storage into a local storage.
1396 std::vector<float> weightTensorSwizzledData(weightTensorInfo.GetNumElements());
1397 armnnUtils::Permute(weightTensorSwizzledInfo.GetShape(), permutationVector,
Matteo Martincighd5b9e642019-01-04 18:01:21 +00001398 weightNode->GetStorage(), weightTensorSwizzledData.data(), sizeof(float));
Ferran Balaguer6a669d72018-12-11 10:29:05 +00001399
1400 // Create a weight tensor with the newly swizzled data.
1401 ConstTensor weightTensor(weightTensorSwizzledInfo, weightTensorSwizzledData);
1402
Matteo Martincigh747ef822018-12-18 09:26:39 +00001403 uint32_t weightHeight = weightTensor.GetShape()[2];
1404 uint32_t weightWidth = weightTensor.GetShape()[3];
surmeh01bceff2f2018-03-29 16:29:27 +01001405
1406 bool padding = false;
1407 TensorInfo outputInfo;
Ferran Balaguer6a669d72018-12-11 10:29:05 +00001408 unsigned int outputHeight = 0;
1409 unsigned int outputWidth = 0;
telsoa01c577f2c2018-08-31 09:22:23 +01001410
1411 CHECK_PADDING_TYPE(nodeDef, paddingString);
1412
surmeh01bceff2f2018-03-29 16:29:27 +01001413 if (paddingString == "SAME")
1414 {
1415 padding = true;
surmeh01bceff2f2018-03-29 16:29:27 +01001416 }
1417 else if (paddingString == "VALID")
1418 {
1419 padding = false;
Ferran Balaguer6a669d72018-12-11 10:29:05 +00001420 }
1421
Sadik Armagan60bb9d82021-01-11 15:15:01 +00001422 CalcPadding(inputHeight, weightHeight, desc.m_StrideY, desc.m_DilationY, desc.m_PadTop, desc.m_PadBottom, padding);
1423 CalcPadding(inputWidth, weightWidth, desc.m_StrideX, desc.m_DilationX, desc.m_PadLeft, desc.m_PadRight, padding);
1424
1425 // Calculate output height and width
1426 unsigned int dilatedFilterWidth = weightWidth + (desc.m_DilationX - 1) * (weightWidth - 1);
1427 unsigned int readWidth = (inputWidth + desc.m_PadLeft + desc.m_PadRight) - dilatedFilterWidth;
1428 outputWidth = 1 + (readWidth / desc.m_StrideX);
1429
1430 unsigned int dilatedFilterHeight = weightHeight + (desc.m_DilationY - 1) * (weightHeight - 1);
1431 unsigned int readHeight = (inputHeight + desc.m_PadTop + desc.m_PadBottom) - dilatedFilterHeight;
1432 outputHeight = 1 + (readHeight / desc.m_StrideY);
1433
Ferran Balaguer6a669d72018-12-11 10:29:05 +00001434 switch (dataLayout)
1435 {
1436 case DataLayout::NHWC:
1437 outputInfo = TensorInfo({ inputTensorInfo.GetShape()[0],
1438 outputHeight,
1439 outputWidth,
Matteo Martincigh747ef822018-12-18 09:26:39 +00001440 weightTensor.GetShape()[0] * weightTensor.GetShape()[1]},
Ferran Balaguer6a669d72018-12-11 10:29:05 +00001441 DataType::Float32);
1442 break;
1443 case DataLayout::NCHW:
1444 default:
1445 outputInfo = TensorInfo({ inputTensorInfo.GetShape()[0],
1446 weightTensor.GetShape()[0] * weightTensor.GetShape()[1],
1447 outputHeight,
1448 outputWidth },
1449 DataType::Float32);
1450 break;
surmeh01bceff2f2018-03-29 16:29:27 +01001451 }
surmeh01bceff2f2018-03-29 16:29:27 +01001452
Matteo Martincighfc598e12019-05-14 10:36:13 +01001453 IConnectableLayer* layer = m_Network->AddDepthwiseConvolution2dLayer(desc,
1454 weightTensor,
1455 EmptyOptional(),
1456 nodeDef.name().c_str());
surmeh01bceff2f2018-03-29 16:29:27 +01001457 layer->GetOutputSlot(0).SetTensorInfo(outputInfo);
Ferran Balaguer6a669d72018-12-11 10:29:05 +00001458 inputSlot.Connect(layer->GetInputSlot(0));
surmeh01bceff2f2018-03-29 16:29:27 +01001459
1460 return std::make_unique<SingleLayerParsedTfOperation>(this, nodeDef, layer);
1461}
1462
Jan Eilers1f3b49b2020-09-08 08:57:40 +01001463TensorInfo OutputShapeOfExpandDims(const tensorflow::NodeDef& nodeDef,
1464 TensorInfo inputTensorInfo,
1465 std::int32_t expandDim)
Conor Kennedyc2130a02018-12-05 11:05:54 +00001466{
Narumol Prangnawaratac2770a2020-04-01 16:51:23 +01001467 ARMNN_ASSERT(nodeDef.op() == "ExpandDims");
Conor Kennedyc2130a02018-12-05 11:05:54 +00001468
1469 if (inputTensorInfo.GetNumDimensions() > 4) {
1470 throw ParseException(
James Ward58dec6b2020-09-11 17:32:44 +01001471 fmt::format("Unsupported number of dimensions: {} for input shape for ExpandDims {} {}",
1472 inputTensorInfo.GetNumDimensions(),
1473 nodeDef.name(),
1474 CHECK_LOCATION().AsString()));
Conor Kennedyc2130a02018-12-05 11:05:54 +00001475 }
1476
Matthew Sloyan589e3e82020-09-11 16:17:48 +01001477 std::int32_t inputDimSize = armnn::numeric_cast<int32_t>(inputTensorInfo.GetNumDimensions());
Conor Kennedyc2130a02018-12-05 11:05:54 +00001478 std::vector<uint32_t> outputDims;
1479
1480 // expandDim operation requires: -1-input.dims() <= dim <= input.dims()
1481 if (expandDim >= -1 - inputDimSize && expandDim <= inputDimSize)
1482 {
1483 // add current input shape to outputDims
1484 for (unsigned int i = 0; i < inputTensorInfo.GetNumDimensions(); ++i) {
1485 auto currentDimension = inputTensorInfo.GetShape()[i];
1486 outputDims.push_back(currentDimension);
1487 }
1488
1489 // insert a dimension of 1 at index 'expandDim' of inputs shape
1490 if (expandDim >= 0)
1491 {
1492 auto getPosition = std::next(outputDims.begin() + 0, expandDim);
1493 outputDims.insert(getPosition, 1);
1494 }
1495
1496 // if negative number for 'expandDim' then count backwards from the last element
1497 // and insert 1 dimension at index 'expandDim'
1498 if (expandDim < 0)
1499 {
Matthew Sloyan589e3e82020-09-11 16:17:48 +01001500 int outputDimSize = armnn::numeric_cast<int>(outputDims.size() + 1);
Conor Kennedyc2130a02018-12-05 11:05:54 +00001501 auto getPosition = std::next(outputDims.begin() + outputDimSize, expandDim);
1502 outputDims.insert(getPosition, 1);
1503 }
1504 }
1505 else
1506 {
1507 throw InvalidArgumentException(
James Ward58dec6b2020-09-11 17:32:44 +01001508 fmt::format("Cannot expand dimension {} in input tensor with {} dimension {}",
1509 expandDim,
1510 inputDimSize,
1511 CHECK_LOCATION().AsString()));
Conor Kennedyc2130a02018-12-05 11:05:54 +00001512 }
1513
1514 if (outputDims.size() > 4)
1515 {
1516 throw ParseException(
James Ward58dec6b2020-09-11 17:32:44 +01001517 fmt::format("Unsupported number of dimensions: {} for output shape for ExpandDims {} {}",
1518 outputDims.size(),
1519 nodeDef.name(),
1520 CHECK_LOCATION().AsString()));
Conor Kennedyc2130a02018-12-05 11:05:54 +00001521 }
1522
1523 TensorShape outShape = TensorShape(static_cast<unsigned int>(outputDims.size()),
1524 outputDims.data());
1525
1526 TensorInfo outTensorInfo = inputTensorInfo;
1527 outTensorInfo.SetShape(outShape);
1528
1529 return outTensorInfo;
1530}
1531
Kevin May7d96b162021-02-03 17:38:41 +00001532ParsedTfOperationPtr ITfParser::TfParserImpl::ParseExpandDims(const tensorflow::NodeDef& nodeDef,
1533 const tensorflow::GraphDef& graphDef)
Conor Kennedyc2130a02018-12-05 11:05:54 +00001534{
Jan Eilers8eb25602020-03-09 12:13:48 +00001535 IgnoreUnused(graphDef);
Conor Kennedyc2130a02018-12-05 11:05:54 +00001536
Jan Eilers1f3b49b2020-09-08 08:57:40 +01001537 // Number of inputs can either
1538 // be 1 - that indicates that the axis parameter is passed as an attribute of the operation
1539 // or 2 - which means that the axis parameter is passed as a second input
1540 std::vector<OutputOfConstNodeDef> nodes = GetTfInputNodes(nodeDef);
1541 const std::size_t numInputs = nodes.size();
1542 std::vector<OutputOfParsedTfOperation> inputs;
1543 std::int32_t expandDim; // axis or dim parameter. Describes which dimension to expand.
1544 if (numInputs == 1)
1545 {
1546 inputs = GetInputParsedTfOperationsChecked(nodeDef, 1);
1547 expandDim = ReadMandatoryNodeInt32Attribute(nodeDef, "Tdim");
1548 }
1549 else
1550 {
1551 inputs = GetInputParsedTfOperationsChecked(nodeDef, 2);
1552
1553 // make sure data type is int32
1554 IOutputSlot& prevLayerOutputSlot = inputs[1].m_IndexedValue->ResolveArmnnOutputSlot(inputs[1].m_Index);
1555 TensorInfo inputTensorInfo = prevLayerOutputSlot.GetTensorInfo();
1556
1557 if (inputTensorInfo.GetDataType()!=armnn::DataType::Signed32)
1558 {
1559 throw ParseException(
James Ward58dec6b2020-09-11 17:32:44 +01001560 fmt::format("The axis parameter of ExpandDims operation given as second input is not of type int32."
1561 " Input {0} Node {1} {2}",
1562 inputs[1].m_IndexedValue->GetNode().name(),
1563 nodeDef.name(),
1564 CHECK_LOCATION().AsString()));
Jan Eilers1f3b49b2020-09-08 08:57:40 +01001565 }
1566
1567 // ensure the second input is a constant value
1568 if (!HasParsedConstTensor<int32_t>(inputs[1].m_IndexedValue->GetNode().name()))
1569 {
1570 throw ParseException(
James Ward58dec6b2020-09-11 17:32:44 +01001571 fmt::format("ArmNN only supports ExpandDims layers with constant axis/dim parameter. "
1572 "Input {0} Node {1} {2}",
1573 inputs[1].m_IndexedValue->GetNode().name(),
1574 nodeDef.name(),
1575 CHECK_LOCATION().AsString()));
Jan Eilers1f3b49b2020-09-08 08:57:40 +01001576 }
1577
1578 // make sure the second input is scalar or contains only a single value
1579 // (we don't support expand dims for multiple axis but we don't care what shape the
1580 // given tensor has as long as there is only a single value in it
1581 // e.g. a tensor like this [[[1]]] is completely fine)
1582 if (inputTensorInfo.GetNumElements() != 1)
1583 {
1584 throw ParseException(
James Ward58dec6b2020-09-11 17:32:44 +01001585 fmt::format("The axis parameter of ExpandDims operation given as second input is not "
1586 "allowed to hold more than one value. "
1587 "Input {0} Node {1} {2}",
1588 inputs[1].m_IndexedValue->GetNode().name(),
1589 nodeDef.name(),
1590 CHECK_LOCATION().AsString()));
Jan Eilers1f3b49b2020-09-08 08:57:40 +01001591 }
1592
1593 ParsedConstTfOperation<int32_t>* expandDimsNode =
1594 PolymorphicDowncast<ParsedConstTfOperation<int32_t>*>(inputs[1].m_IndexedValue);
1595
1596 memcpy(&expandDim, expandDimsNode->GetStorage(), sizeof(expandDim));
1597 }
1598
1599 // First input is the vector that should be expanded by another dimension
Conor Kennedyc2130a02018-12-05 11:05:54 +00001600 IOutputSlot& prevLayerOutputSlot = inputs[0].m_IndexedValue->ResolveArmnnOutputSlot(inputs[0].m_Index);
1601 TensorInfo inputTensorInfo = prevLayerOutputSlot.GetTensorInfo();
1602
1603 TensorInfo outputInfo;
Jan Eilers1f3b49b2020-09-08 08:57:40 +01001604 outputInfo = OutputShapeOfExpandDims(nodeDef, inputTensorInfo, expandDim);
Conor Kennedyc2130a02018-12-05 11:05:54 +00001605
1606 ReshapeDescriptor reshapeDesc;
1607 reshapeDesc.m_TargetShape = outputInfo.GetShape();
1608 IConnectableLayer* layer = m_Network->AddReshapeLayer(reshapeDesc, nodeDef.name().c_str());
1609 prevLayerOutputSlot.Connect(layer->GetInputSlot(0));
1610 layer->GetOutputSlot(0).SetTensorInfo(outputInfo);
1611
1612 return std::make_unique<SingleLayerParsedTfOperation>(this, nodeDef, layer);
1613}
1614
Kevin May7d96b162021-02-03 17:38:41 +00001615ParsedTfOperationPtr ITfParser::TfParserImpl::ParseFusedBatchNorm(const tensorflow::NodeDef& nodeDef,
1616 const tensorflow::GraphDef& graphDef)
surmeh01bceff2f2018-03-29 16:29:27 +01001617{
Jan Eilers8eb25602020-03-09 12:13:48 +00001618 IgnoreUnused(graphDef);
surmeh01bceff2f2018-03-29 16:29:27 +01001619 std::vector<OutputOfParsedTfOperation> inputs = GetInputParsedTfOperationsChecked(nodeDef, 5);
1620
1621 if (!HasParsedConstTensor<float>(inputs[1].m_IndexedValue->GetNode().name()))
1622 {
telsoa01c577f2c2018-08-31 09:22:23 +01001623 throw ParseException(
James Ward58dec6b2020-09-11 17:32:44 +01001624 fmt::format("ArmNN only supports FusedBatchNormalization layers with constant scale. "
1625 "Input {}. Node {} {}",
1626 inputs[1].m_IndexedValue->GetNode().name(),
1627 nodeDef.name(),
1628 CHECK_LOCATION().AsString()));
surmeh01bceff2f2018-03-29 16:29:27 +01001629 }
1630 ParsedConstTfOperation<float>* scaleNode =
Jan Eilersbb446e52020-04-02 13:56:54 +01001631 PolymorphicDowncast<ParsedConstTfOperation<float> *>(inputs[1].m_IndexedValue);
surmeh01bceff2f2018-03-29 16:29:27 +01001632
1633 if (!HasParsedConstTensor<float>(inputs[2].m_IndexedValue->GetNode().name()))
1634 {
telsoa01c577f2c2018-08-31 09:22:23 +01001635 throw ParseException(
James Ward58dec6b2020-09-11 17:32:44 +01001636 fmt::format("ArmNN only supports FusedBatchNormalization layers with constant offset. "
1637 "Input {}. Node {} {}",
1638 inputs[2].m_IndexedValue->GetNode().name(),
1639 nodeDef.name(),
1640 CHECK_LOCATION().AsString()));
surmeh01bceff2f2018-03-29 16:29:27 +01001641 }
1642 ParsedConstTfOperation<float>* offsetNode =
Jan Eilersbb446e52020-04-02 13:56:54 +01001643 PolymorphicDowncast<ParsedConstTfOperation<float> *>(inputs[2].m_IndexedValue);
surmeh01bceff2f2018-03-29 16:29:27 +01001644
1645 if (!HasParsedConstTensor<float>(inputs[3].m_IndexedValue->GetNode().name()))
1646 {
telsoa01c577f2c2018-08-31 09:22:23 +01001647 throw ParseException(
James Ward58dec6b2020-09-11 17:32:44 +01001648 fmt::format("ArmNN only supports FusedBatchNormalization layers with constant mean. "
1649 "Input {}. Node {} {}",
1650 inputs[3].m_IndexedValue->GetNode().name(),
1651 nodeDef.name(),
1652 CHECK_LOCATION().AsString()));
surmeh01bceff2f2018-03-29 16:29:27 +01001653 }
1654 ParsedConstTfOperation<float>* meanNode =
Jan Eilersbb446e52020-04-02 13:56:54 +01001655 PolymorphicDowncast<ParsedConstTfOperation<float> *>(inputs[3].m_IndexedValue);
surmeh01bceff2f2018-03-29 16:29:27 +01001656
1657 if (!HasParsedConstTensor<float>(inputs[4].m_IndexedValue->GetNode().name()))
1658 {
telsoa01c577f2c2018-08-31 09:22:23 +01001659 throw ParseException(
James Ward58dec6b2020-09-11 17:32:44 +01001660 fmt::format("ArmNN only supports FusedBatchNormalization layers with constant variance. "
1661 "Input {}. Node {} {}",
1662 inputs[4].m_IndexedValue->GetNode().name(),
1663 nodeDef.name(),
1664 CHECK_LOCATION().AsString()));
surmeh01bceff2f2018-03-29 16:29:27 +01001665 }
1666 ParsedConstTfOperation<float>* varianceNode =
Jan Eilersbb446e52020-04-02 13:56:54 +01001667 PolymorphicDowncast<ParsedConstTfOperation<float> *>(inputs[4].m_IndexedValue);
surmeh01bceff2f2018-03-29 16:29:27 +01001668
Aron Virginas-Tar2e259272019-11-27 13:29:51 +00001669 const std::string dataFormat = ReadOptionalNodeStringAttribute(nodeDef, "data_format", "NHWC");
Matteo Martincigh075c7502018-12-05 13:10:45 +00001670 CHECK_DATA_FORMAT(nodeDef, dataFormat, "FusedBatchNorm");
1671
telsoa01c577f2c2018-08-31 09:22:23 +01001672 // The descriptor only has the epsilon attribute.
surmeh01bceff2f2018-03-29 16:29:27 +01001673 BatchNormalizationDescriptor desc;
1674 desc.m_Eps = ReadMandatoryNodeFloatAttribute(nodeDef, "epsilon");
Matteo Martincigh075c7502018-12-05 13:10:45 +00001675 desc.m_DataLayout = dataFormat == "NHWC" ? DataLayout::NHWC : DataLayout::NCHW;
surmeh01bceff2f2018-03-29 16:29:27 +01001676
telsoa01c577f2c2018-08-31 09:22:23 +01001677 // Data for the parsed tensor args (scale, offset, mean, variance) must be stored
1678 // locally until the layer is added.
surmeh01bceff2f2018-03-29 16:29:27 +01001679 std::vector<float> scaleTensorData;
Matteo Martincigh482ca852018-12-12 09:20:55 +00001680 ConstTensor scaleTensor = scaleNode->GetConstTensor(scaleTensorData);
surmeh01bceff2f2018-03-29 16:29:27 +01001681
1682 std::vector<float> offsetTensorData;
Matteo Martincigh482ca852018-12-12 09:20:55 +00001683 ConstTensor offsetTensor = offsetNode->GetConstTensor(offsetTensorData);
surmeh01bceff2f2018-03-29 16:29:27 +01001684
1685 std::vector<float> meanTensorData;
Matteo Martincigh482ca852018-12-12 09:20:55 +00001686 ConstTensor meanTensor = meanNode->GetConstTensor(meanTensorData);
surmeh01bceff2f2018-03-29 16:29:27 +01001687
1688 std::vector<float> varianceTensorData;
Matteo Martincigh482ca852018-12-12 09:20:55 +00001689 ConstTensor varianceTensor = varianceNode->GetConstTensor(varianceTensorData);
surmeh01bceff2f2018-03-29 16:29:27 +01001690
1691 IConnectableLayer* layer = m_Network->AddBatchNormalizationLayer(desc,
1692 meanTensor,
1693 varianceTensor,
1694 offsetTensor,
1695 scaleTensor,
1696 nodeDef.name().c_str());
1697
1698 IOutputSlot& inputSlot = inputs[0].m_IndexedValue->ResolveArmnnOutputSlot(inputs[0].m_Index);
1699
Matteo Martincigh075c7502018-12-05 13:10:45 +00001700 layer->GetOutputSlot(0).SetTensorInfo(inputSlot.GetTensorInfo());
1701 inputSlot.Connect(layer->GetInputSlot(0));
surmeh01bceff2f2018-03-29 16:29:27 +01001702
1703 return std::make_unique<SingleLayerParsedTfOperation>(this, nodeDef, layer);
1704}
1705
Kevin May7d96b162021-02-03 17:38:41 +00001706bool ITfParser::TfParserImpl::IsSupportedLeakyReluPattern(const tensorflow::NodeDef& mulNodeDef,
1707 size_t alphaLayerIndex,
1708 const OutputOfParsedTfOperation& otherOp,
1709 armnn::IOutputSlot** outputOfLeakyRelu,
1710 armnn::ActivationDescriptor & desc)
telsoa01c577f2c2018-08-31 09:22:23 +01001711{
1712 const tensorflow::NodeDef& otherNodeDef = otherOp.m_IndexedValue->GetNode();
1713
1714 // Verifying all these assumptions hold:
1715 //
1716 // 1, the mulNodeDef is an elementwise multiplication node "Mul"
1717 // 2, the alphaLayerIndex selects a constant node from the inputs of the "Mul" node
1718 // 3, the inputLayerIndex selects a layer which has the same name as otherNodeDef
1719 //
1720
1721 if (mulNodeDef.op() == "Mul")
1722 {
1723 size_t otherLayerIndex = (alphaLayerIndex == 0 ? 1 : 0);
1724 std::vector<OutputOfParsedTfOperation> inputs = GetInputParsedTfOperationsChecked(mulNodeDef, 2);
1725
Narumol Prangnawaratac2770a2020-04-01 16:51:23 +01001726 ARMNN_ASSERT(inputs.size() == 2);
1727 ARMNN_ASSERT((otherLayerIndex == 0 || alphaLayerIndex == 0));
1728 ARMNN_ASSERT((otherLayerIndex == 1 || alphaLayerIndex == 1));
1729 ARMNN_ASSERT(((otherLayerIndex + alphaLayerIndex) == 1));
telsoa01c577f2c2018-08-31 09:22:23 +01001730
1731 if (inputs[otherLayerIndex].m_IndexedValue->GetNode().name() == otherNodeDef.name())
1732 {
1733 if (HasParsedConstTensor<float>(inputs[alphaLayerIndex].m_IndexedValue->GetNode().name()))
1734 {
1735 ParsedConstTfOperation<float>* alpha =
Jan Eilersbb446e52020-04-02 13:56:54 +01001736 PolymorphicDowncast<ParsedConstTfOperation<float> *>(
telsoa01c577f2c2018-08-31 09:22:23 +01001737 inputs[alphaLayerIndex].m_IndexedValue);
1738
1739 std::vector<float> const_data;
Matteo Martincigh482ca852018-12-12 09:20:55 +00001740 ConstTensor const_tensor = alpha->GetConstTensor(const_data);
telsoa01c577f2c2018-08-31 09:22:23 +01001741
1742 if (const_data.size() == 1)
1743 {
1744 desc.m_Function = ActivationFunction::LeakyReLu;
1745 desc.m_A = const_data[0];
1746
1747 *outputOfLeakyRelu = &(otherOp.m_IndexedValue->ResolveArmnnOutputSlot(otherOp.m_Index));
1748 return true;
1749 }
1750 }
1751 }
1752 }
1753 return false;
1754}
1755
Kevin May7d96b162021-02-03 17:38:41 +00001756ParsedTfOperationPtr ITfParser::TfParserImpl::ParseMaximum(const tensorflow::NodeDef& nodeDef,
1757 const tensorflow::GraphDef& graphDef)
telsoa01c577f2c2018-08-31 09:22:23 +01001758{
Jan Eilers8eb25602020-03-09 12:13:48 +00001759 IgnoreUnused(graphDef);
telsoa01c577f2c2018-08-31 09:22:23 +01001760 std::vector<OutputOfParsedTfOperation> inputs = GetInputParsedTfOperationsChecked(nodeDef, 2);
Sadik Armagan975c09a2018-12-04 10:02:08 +00001761 if (inputs.size() != 2)
1762 {
1763 throw ParseException(
James Ward58dec6b2020-09-11 17:32:44 +01001764 fmt::format("Maximum expects two inputs!. Got {} for Node {} {}",
1765 inputs.size(),
1766 nodeDef.name(),
1767 CHECK_LOCATION().AsString()));
Sadik Armagan975c09a2018-12-04 10:02:08 +00001768 }
1769
telsoa01c577f2c2018-08-31 09:22:23 +01001770 auto inputNode0 = inputs[0].m_IndexedValue->GetNode();
1771 auto inputNode1 = inputs[1].m_IndexedValue->GetNode();
1772 IOutputSlot* outputOfLeakyRelu = nullptr;
1773
1774 ActivationDescriptor desc;
1775
Sadik Armagan975c09a2018-12-04 10:02:08 +00001776 // A max node may be part of a LeakyRelu, with one input as a multiplication with a scalar constant,
1777 // i.e. one of the four possible scenarios:
1778 // 1, max(mul(a, x), x)
1779 // 2, max(mul(x, a), x)
1780 // 3, max(x, mul(a, x))
1781 // 4, max(x, mul(x, a))
1782 // These are handled by an activation layer.
telsoa01c577f2c2018-08-31 09:22:23 +01001783
1784 if (IsSupportedLeakyReluPattern(inputNode0, 0, inputs[1], &outputOfLeakyRelu, desc) ||
1785 IsSupportedLeakyReluPattern(inputNode0, 1, inputs[1], &outputOfLeakyRelu, desc) ||
1786 IsSupportedLeakyReluPattern(inputNode1, 0, inputs[0], &outputOfLeakyRelu, desc) ||
1787 IsSupportedLeakyReluPattern(inputNode1, 1, inputs[0], &outputOfLeakyRelu, desc))
1788 {
Narumol Prangnawaratac2770a2020-04-01 16:51:23 +01001789 ARMNN_ASSERT(outputOfLeakyRelu != nullptr);
telsoa01c577f2c2018-08-31 09:22:23 +01001790
1791 IConnectableLayer* const layer = m_Network->AddActivationLayer(desc, nodeDef.name().c_str());
1792 outputOfLeakyRelu->Connect(layer->GetInputSlot(0));
1793 layer->GetOutputSlot(0).SetTensorInfo(outputOfLeakyRelu->GetTensorInfo());
1794 return std::make_unique<SingleLayerParsedTfOperation>(this, nodeDef, layer);
1795 }
1796 else
1797 {
Sadik Armagan975c09a2018-12-04 10:02:08 +00001798 // Anything else is just a maximum layer.
1799
1800 return AddMaximumLayer(nodeDef);
telsoa01c577f2c2018-08-31 09:22:23 +01001801 }
1802}
1803
Kevin May7d96b162021-02-03 17:38:41 +00001804std::pair<armnn::IOutputSlot*, armnn::IOutputSlot*> ITfParser::TfParserImpl::ProcessElementwiseInputSlots(
jimfly0184c70e62018-12-19 13:14:46 +00001805 const tensorflow::NodeDef& nodeDef, const std::string& layerName)
Nattapat Chaimanowong24df8222018-12-04 13:47:02 +00001806{
1807 std::vector<OutputOfParsedTfOperation> inputs = GetInputParsedTfOperationsChecked(nodeDef, 2);
1808
1809 IOutputSlot* input0Slot = &inputs[0].m_IndexedValue->ResolveArmnnOutputSlot(inputs[0].m_Index);
1810 IOutputSlot* input1Slot = &inputs[1].m_IndexedValue->ResolveArmnnOutputSlot(inputs[1].m_Index);
1811 const unsigned int input0Dim = input0Slot->GetTensorInfo().GetNumDimensions();
1812 const unsigned int input1Dim = input1Slot->GetTensorInfo().GetNumDimensions();
1813
1814 if (input0Dim != input1Dim)
1815 {
1816 // broadcasting where input0 and input1 have different number of dimensions
1817 // is only supported for 1D and 4D tensors pair
1818 if (input0Dim == 1 && input1Dim == 4)
1819 {
1820 input0Slot = AddBroadcastReshapeLayer(input1Slot, input0Slot, true, *m_Network, nodeDef);
1821 }
1822 else if (input0Dim == 4 && input1Dim == 1)
1823 {
1824 input1Slot = AddBroadcastReshapeLayer(input0Slot, input1Slot, true, *m_Network, nodeDef);
1825 }
1826 else
1827 {
1828 throw ParseException(
James Ward58dec6b2020-09-11 17:32:44 +01001829 fmt::format("Unsupported broadcast configuration for {} operation {} {}",
1830 layerName,
1831 nodeDef.name(),
1832 CHECK_LOCATION().AsString()));
Nattapat Chaimanowong24df8222018-12-04 13:47:02 +00001833 }
1834 }
jimfly0184c70e62018-12-19 13:14:46 +00001835 return {input0Slot, input1Slot};
1836}
Nattapat Chaimanowong24df8222018-12-04 13:47:02 +00001837
Kevin May7d96b162021-02-03 17:38:41 +00001838ParsedTfOperationPtr ITfParser::TfParserImpl::ProcessComparisonLayer(
kevmay012b4d88e2019-01-24 14:05:09 +00001839 IOutputSlot* input0Slot,
1840 IOutputSlot* input1Slot,
1841 IConnectableLayer* const layer,
1842 const tensorflow::NodeDef& nodeDef)
1843{
1844 input0Slot->Connect(layer->GetInputSlot(0));
1845 input1Slot->Connect(layer->GetInputSlot(1));
1846
1847 TensorInfo outputInfo = input0Slot->GetTensorInfo();
1848 outputInfo.SetDataType(DataType::Boolean);
1849 std::vector<unsigned int> outputShape;
1850
1851 const TensorShape& input0Shape = input0Slot->GetTensorInfo().GetShape();
1852 const TensorShape& input1Shape = input1Slot->GetTensorInfo().GetShape();
1853
1854 for (unsigned int i = 0; i < input0Shape.GetNumDimensions(); i++)
1855 {
1856 outputShape.push_back(std::max(input0Shape[i], input1Shape[i]));
1857 }
1858
1859 outputInfo.SetShape(TensorShape(input0Shape.GetNumDimensions(), outputShape.data()));
1860 layer->GetOutputSlot(0).SetTensorInfo(outputInfo);
1861
1862 return std::make_unique<SingleLayerParsedTfOperation>(this, nodeDef, layer);
1863}
1864
Kevin May7d96b162021-02-03 17:38:41 +00001865ParsedTfOperationPtr ITfParser::TfParserImpl::ProcessElementwiseLayer(
jimfly0184c70e62018-12-19 13:14:46 +00001866 IOutputSlot* input0Slot,
1867 IOutputSlot* input1Slot,
1868 IConnectableLayer* const layer,
1869 const tensorflow::NodeDef& nodeDef)
1870{
Nattapat Chaimanowong24df8222018-12-04 13:47:02 +00001871 input0Slot->Connect(layer->GetInputSlot(0));
1872 input1Slot->Connect(layer->GetInputSlot(1));
1873
1874 TensorInfo outputInfo = input0Slot->GetTensorInfo();
1875 std::vector<unsigned int> outputShape;
1876
1877 const TensorShape& input0Shape = input0Slot->GetTensorInfo().GetShape();
1878 const TensorShape& input1Shape = input1Slot->GetTensorInfo().GetShape();
1879
1880 for (unsigned int i = 0; i < input0Shape.GetNumDimensions(); i++)
1881 {
1882 outputShape.push_back(std::max(input0Shape[i], input1Shape[i]));
1883 }
1884
1885 outputInfo.SetShape(TensorShape(input0Shape.GetNumDimensions(), outputShape.data()));
1886 layer->GetOutputSlot(0).SetTensorInfo(outputInfo);
1887
1888 return std::make_unique<SingleLayerParsedTfOperation>(this, nodeDef, layer);
1889}
1890
Kevin May7d96b162021-02-03 17:38:41 +00001891ParsedTfOperationPtr ITfParser::TfParserImpl::ParseGather(const tensorflow::NodeDef& nodeDef,
1892 const tensorflow::GraphDef& graphDef)
FrancisMurtagh94412af2019-01-24 10:53:39 +00001893{
Jan Eilers8eb25602020-03-09 12:13:48 +00001894 IgnoreUnused(graphDef);
FrancisMurtagh94412af2019-01-24 10:53:39 +00001895 std::vector<OutputOfParsedTfOperation> inputs = GetInputParsedTfOperationsChecked(nodeDef, 2);
1896 IOutputSlot& params = inputs[0].m_IndexedValue->ResolveArmnnOutputSlot(inputs[0].m_Index);
1897 IOutputSlot& indices = inputs[1].m_IndexedValue->ResolveArmnnOutputSlot(inputs[1].m_Index);
Teresa Charlin52664732020-06-29 16:27:03 +01001898 GatherDescriptor descriptor;
1899 descriptor.m_Axis = ReadMandatoryNodeInt32Attribute(nodeDef, "axis");
FrancisMurtagh94412af2019-01-24 10:53:39 +00001900
1901 // Infer shape of output tensor
1902 unsigned int paramsDim = params.GetTensorInfo().GetNumDimensions();
1903 unsigned int indicesDim = indices.GetTensorInfo().GetNumDimensions();
1904 unsigned int outputDim = paramsDim - 1 + indicesDim;
1905
1906 std::vector<unsigned int> dimSizes;
1907
1908 for (unsigned int i = 0; i < indicesDim; ++i)
1909 {
1910 dimSizes.push_back(indices.GetTensorInfo().GetShape()[i]);
1911 }
1912 for (unsigned int i = 1; i < paramsDim; ++i)
1913 {
1914 dimSizes.push_back(params.GetTensorInfo().GetShape()[i]);
1915 }
1916
1917 const TensorShape& inferredShape = TensorShape(outputDim, dimSizes.data());
1918
1919 const TensorInfo inferredOutputInfo(inferredShape, params.GetTensorInfo().GetDataType());
1920
Teresa Charlin52664732020-06-29 16:27:03 +01001921 IConnectableLayer* const layer = m_Network->AddGatherLayer(descriptor, nodeDef.name().c_str());
FrancisMurtagh94412af2019-01-24 10:53:39 +00001922 layer->GetOutputSlot(0).SetTensorInfo(inferredOutputInfo);
1923
1924 params.Connect(layer->GetInputSlot(0));
1925 indices.Connect(layer->GetInputSlot(1));
1926
1927 return std::make_unique<SingleLayerParsedTfOperation>(this, nodeDef, layer);
1928}
1929
Kevin May7d96b162021-02-03 17:38:41 +00001930ParsedTfOperationPtr ITfParser::TfParserImpl::ParseGreater(const tensorflow::NodeDef& nodeDef,
1931 const tensorflow::GraphDef& graphDef)
jimfly01a06bf312018-12-18 16:24:51 +00001932{
Jan Eilers8eb25602020-03-09 12:13:48 +00001933 IgnoreUnused(graphDef);
jimfly01a06bf312018-12-18 16:24:51 +00001934 std::pair<armnn::IOutputSlot*, armnn::IOutputSlot*> inputLayers = ProcessElementwiseInputSlots(nodeDef, "Greater");
1935 IOutputSlot* input0Slot = inputLayers.first;
1936 IOutputSlot* input1Slot = inputLayers.second;
1937
Aron Virginas-Tar77bfb5e2019-10-16 17:45:38 +01001938 ComparisonDescriptor descriptor(ComparisonOperation::Greater);
1939 IConnectableLayer* const layer = m_Network->AddComparisonLayer(descriptor, nodeDef.name().c_str());
jimfly01a06bf312018-12-18 16:24:51 +00001940
kevmay012b4d88e2019-01-24 14:05:09 +00001941 return ProcessComparisonLayer(input0Slot, input1Slot, layer, nodeDef);
jimfly01a06bf312018-12-18 16:24:51 +00001942}
1943
Kevin May7d96b162021-02-03 17:38:41 +00001944ParsedTfOperationPtr ITfParser::TfParserImpl::ParseEqual(const tensorflow::NodeDef& nodeDef,
1945 const tensorflow::GraphDef& graphDef)
jimfly0184c70e62018-12-19 13:14:46 +00001946{
Jan Eilers8eb25602020-03-09 12:13:48 +00001947 IgnoreUnused(graphDef);
jimfly0184c70e62018-12-19 13:14:46 +00001948 std::pair<armnn::IOutputSlot*, armnn::IOutputSlot*> inputLayers = ProcessElementwiseInputSlots(nodeDef, "Equal");
1949 IOutputSlot* input0Slot = inputLayers.first;
1950 IOutputSlot* input1Slot = inputLayers.second;
1951
Aron Virginas-Tar77bfb5e2019-10-16 17:45:38 +01001952 ComparisonDescriptor descriptor(ComparisonOperation::Equal);
1953 IConnectableLayer* const layer = m_Network->AddComparisonLayer(descriptor, nodeDef.name().c_str());
jimfly0184c70e62018-12-19 13:14:46 +00001954
kevmay012b4d88e2019-01-24 14:05:09 +00001955 return ProcessComparisonLayer(input0Slot, input1Slot, layer, nodeDef);
jimfly0184c70e62018-12-19 13:14:46 +00001956}
1957
Kevin May7d96b162021-02-03 17:38:41 +00001958ParsedTfOperationPtr ITfParser::TfParserImpl::ParseMinimum(const tensorflow::NodeDef& nodeDef,
1959 const tensorflow::GraphDef& graphDef)
jimfly0184c70e62018-12-19 13:14:46 +00001960{
Jan Eilers8eb25602020-03-09 12:13:48 +00001961 IgnoreUnused(graphDef);
jimfly0184c70e62018-12-19 13:14:46 +00001962 std::pair<armnn::IOutputSlot*, armnn::IOutputSlot*> inputLayers = ProcessElementwiseInputSlots(nodeDef, "Minimum");
1963 IOutputSlot* input0Slot = inputLayers.first;
1964 IOutputSlot* input1Slot = inputLayers.second;
1965
1966 IConnectableLayer* const layer = m_Network->AddMinimumLayer(nodeDef.name().c_str());
1967
1968 return ProcessElementwiseLayer(input0Slot, input1Slot, layer, nodeDef);
1969}
1970
Kevin May7d96b162021-02-03 17:38:41 +00001971ParsedTfOperationPtr ITfParser::TfParserImpl::ParseSub(const tensorflow::NodeDef& nodeDef,
1972 const tensorflow::GraphDef& graphDef)
jimfly0123be07e2018-12-04 17:47:22 +00001973{
Jan Eilers8eb25602020-03-09 12:13:48 +00001974 IgnoreUnused(graphDef);
jimfly0123be07e2018-12-04 17:47:22 +00001975 std::vector<OutputOfParsedTfOperation> inputs = GetInputParsedTfOperationsChecked(nodeDef, 2);
1976
1977 IOutputSlot* input0Slot = &inputs[0].m_IndexedValue->ResolveArmnnOutputSlot(inputs[0].m_Index);
1978 IOutputSlot* input1Slot = &inputs[1].m_IndexedValue->ResolveArmnnOutputSlot(inputs[1].m_Index);
1979
1980 const TensorInfo& input0Info = input0Slot->GetTensorInfo();
1981 const TensorInfo& input1Info = input1Slot->GetTensorInfo();
1982
1983 if (input0Info.GetNumDimensions() == 1)
1984 {
1985 const bool isNHWC = true;
1986 input0Slot = AddBroadcastReshapeLayer(input1Slot, input0Slot, isNHWC, *m_Network, nodeDef);
1987 }
1988
1989 if (input1Info.GetNumDimensions() == 1)
1990 {
1991 const bool isNHWC = true;
1992 input1Slot = AddBroadcastReshapeLayer(input0Slot, input1Slot, isNHWC, *m_Network, nodeDef);
1993 }
1994
1995 IConnectableLayer* const layer = m_Network->AddSubtractionLayer(nodeDef.name().c_str());
1996
1997 input0Slot->Connect(layer->GetInputSlot(0));
1998 input1Slot->Connect(layer->GetInputSlot(1));
1999
2000 if (input0Info.GetNumDimensions() == 1)
2001 {
2002 layer->GetOutputSlot(0).SetTensorInfo(input1Slot->GetTensorInfo());
2003 }
2004 else
2005 {
2006 layer->GetOutputSlot(0).SetTensorInfo(input0Slot->GetTensorInfo());
2007 }
2008
2009 return std::make_unique<SingleLayerParsedTfOperation>(this, nodeDef, layer);
2010}
2011
Kevin May7d96b162021-02-03 17:38:41 +00002012ParsedTfOperationPtr ITfParser::TfParserImpl::ParseStack(const tensorflow::NodeDef& nodeDef,
2013 const tensorflow::GraphDef& graphDef)
Sadik Armagan48d70932020-02-18 15:18:27 +00002014{
Jan Eilers8eb25602020-03-09 12:13:48 +00002015 IgnoreUnused(graphDef);
Sadik Armagan48d70932020-02-18 15:18:27 +00002016 std::vector<OutputOfConstNodeDef> nodes = GetTfInputNodes(nodeDef);
2017
2018 unsigned int numInputs = static_cast<unsigned int>(nodes.size());
2019 if (numInputs < 1)
2020 {
2021 throw ParseException(
James Ward58dec6b2020-09-11 17:32:44 +01002022 fmt::format("Pack/Stack expects at least one input. Got {} for Node {} {}",
2023 numInputs,
2024 nodeDef.name(),
2025 CHECK_LOCATION().AsString()));
Sadik Armagan48d70932020-02-18 15:18:27 +00002026 }
2027
2028 std::vector<OutputOfParsedTfOperation> inputs = GetInputParsedTfOperationsChecked(nodeDef, numInputs);
2029 // Use the tensor shape of the first input as the "correct" input shape in the descriptor
2030 IOutputSlot* input0Slot = &inputs[0].m_IndexedValue->ResolveArmnnOutputSlot(inputs[0].m_Index);
2031 const TensorInfo& inputTensorInfo = input0Slot->GetTensorInfo();
2032 auto numDimensions = inputTensorInfo.GetShape().GetNumDimensions();
2033
2034 // validate axis
2035 int32_t axis = ReadMandatoryNodeInt32Attribute(nodeDef, "axis");
2036 const int sNumDimensions = (static_cast<int>(numDimensions) + 1);
2037 if (!(axis < sNumDimensions && axis >= -sNumDimensions))
2038 {
2039 throw ParseException(
James Ward58dec6b2020-09-11 17:32:44 +01002040 fmt::format("Axis index is not in range. Got {} for Node {} {}",
2041 axis,
2042 nodeDef.name(),
2043 CHECK_LOCATION().AsString()));
Sadik Armagan48d70932020-02-18 15:18:27 +00002044 }
2045
2046 if (axis < 0)
2047 {
2048 axis = static_cast<int32_t>(numDimensions) + axis + 1;
2049 }
2050
2051 StackDescriptor stackDescriptor;
2052 stackDescriptor.m_Axis = static_cast<uint32_t>(axis);
2053 stackDescriptor.m_NumInputs = static_cast<uint32_t>(numInputs);
2054 stackDescriptor.m_InputShape = inputTensorInfo.GetShape();
2055
2056 const unsigned int supportedNumDims = 4;
2057 for (unsigned int viewIndex = 0; viewIndex < numInputs; ++viewIndex)
2058 {
2059 IOutputSlot& inputSlot = inputs[viewIndex].m_IndexedValue->ResolveArmnnOutputSlot(inputs[viewIndex].m_Index);
2060 TensorInfo inputTensorInfo = inputSlot.GetTensorInfo();
2061
2062 // Double check dimensions of the tensors
2063 if (inputTensorInfo.GetNumDimensions() >= supportedNumDims)
2064 {
2065 throw armnn::ParseException(
James Ward58dec6b2020-09-11 17:32:44 +01002066 fmt::format("The number of dimensions: {} for input tensors of the "
2067 "Pack/Stack op. Number of dimensions should be less than {} {}",
2068 inputTensorInfo.GetNumDimensions(),
2069 supportedNumDims,
2070 CHECK_LOCATION().AsString()));
Sadik Armagan48d70932020-02-18 15:18:27 +00002071 }
2072 }
2073
2074 std::vector<unsigned int> outputDimensions;
2075 for (unsigned int i = 0; i < stackDescriptor.m_InputShape.GetNumDimensions(); ++i)
2076 {
2077 outputDimensions.push_back(stackDescriptor.m_InputShape[i]);
2078 }
2079 outputDimensions.insert(outputDimensions.begin() + axis, numInputs);
2080
2081 // add Stack Layer
2082 IConnectableLayer* const layer = m_Network->AddStackLayer(stackDescriptor, nodeDef.name().c_str());
2083
2084 for (unsigned int viewIndex = 0; viewIndex < numInputs; ++viewIndex)
2085 {
2086 IOutputSlot& inputSlot = inputs[viewIndex].m_IndexedValue->ResolveArmnnOutputSlot(inputs[viewIndex].m_Index);
2087 inputSlot.Connect(layer->GetInputSlot(viewIndex));
2088 }
2089
2090 layer->GetOutputSlot(0).SetTensorInfo(
2091 armnn::TensorInfo(static_cast<uint32_t>(outputDimensions.size()),
2092 outputDimensions.data(),
2093 inputTensorInfo.GetDataType()));
2094
2095 return std::make_unique<SingleLayerParsedTfOperation>(this, nodeDef, layer);
2096}
2097
Kevin May7d96b162021-02-03 17:38:41 +00002098ParsedTfOperationPtr ITfParser::TfParserImpl::ParseTranspose(const tensorflow::NodeDef& nodeDef,
2099 const tensorflow::GraphDef& graphDef)
Sang-Hoon Parkdd3f71b2020-02-18 11:27:35 +00002100{
Jan Eilers8eb25602020-03-09 12:13:48 +00002101 IgnoreUnused(graphDef);
Sang-Hoon Parkdd3f71b2020-02-18 11:27:35 +00002102
2103 auto inputs = GetInputParsedTfOperationsChecked(nodeDef, 2);
2104 const auto inputCount = inputs.size();
2105
2106 if (inputCount != 2)
2107 {
2108 throw ParseException(
James Ward58dec6b2020-09-11 17:32:44 +01002109 fmt::format("The number of given input is {}. It should be two for Transpose op."
2110 "Node {} {}",
2111 inputCount,
2112 nodeDef.name(),
2113 CHECK_LOCATION().AsString()));
Sang-Hoon Parkdd3f71b2020-02-18 11:27:35 +00002114 }
2115
2116 auto* input0Slot = &inputs[0].m_IndexedValue->ResolveArmnnOutputSlot(inputs[0].m_Index);
2117
2118 const auto constInput = inputs[GetConstInputIndex(inputs)];
2119 auto* permuteVectorInput =
Jan Eilersbb446e52020-04-02 13:56:54 +01002120 PolymorphicDowncast<ParsedConstTfOperation<int32_t>*>(constInput.m_IndexedValue);
Sang-Hoon Parkdd3f71b2020-02-18 11:27:35 +00002121 const auto& permuteVectorInfo = permuteVectorInput->GetTensorInfo();
2122
2123 std::vector<int32_t> permuteVectorData;
2124 permuteVectorInput->GetConstTensor(permuteVectorData);
2125
Mike Kelly08759e22020-03-02 11:41:31 +00002126 std::vector<unsigned int> armnnPermuteVectorData(permuteVectorData.begin(), permuteVectorData.end());
Sang-Hoon Parkdd3f71b2020-02-18 11:27:35 +00002127
2128 const auto permutationVector = PermutationVector(armnnPermuteVectorData.data(), permuteVectorInfo.GetNumElements());
Mike Kelly08759e22020-03-02 11:41:31 +00002129 const auto desc = TransposeDescriptor(permutationVector);
Sang-Hoon Parkdd3f71b2020-02-18 11:27:35 +00002130
Mike Kelly08759e22020-03-02 11:41:31 +00002131 auto* layer = m_Network->AddTransposeLayer(desc, nodeDef.name().c_str());
Narumol Prangnawaratac2770a2020-04-01 16:51:23 +01002132 ARMNN_ASSERT(layer);
Sang-Hoon Parkdd3f71b2020-02-18 11:27:35 +00002133
2134 input0Slot->Connect(layer->GetInputSlot(0));
2135
2136 const auto& input0Info = input0Slot->GetTensorInfo();
2137 armnn::TensorInfo outputInfo {input0Info};
Mike Kelly08759e22020-03-02 11:41:31 +00002138 outputInfo.SetShape(armnnUtils::TransposeTensorShape(input0Info.GetShape(), desc.m_DimMappings));
Sang-Hoon Parkdd3f71b2020-02-18 11:27:35 +00002139 layer->GetOutputSlot(0).SetTensorInfo(outputInfo);
2140
2141 return std::make_unique<SingleLayerParsedTfOperation>(this, nodeDef, layer);
2142}
2143
jimfly01f6ba7472018-12-04 10:09:52 +00002144unsigned int CheckPaddingTensor(const ConstTensor& paddingTensor,
2145 const TensorInfo& inputTensorInfo,
2146 const std::string& nodeName)
2147{
2148 unsigned int rank = paddingTensor.GetShape()[0];
2149 unsigned int expectedRank = inputTensorInfo.GetNumDimensions();
2150 if (rank != expectedRank)
2151 {
2152 throw ParseException(
James Ward58dec6b2020-09-11 17:32:44 +01002153 fmt::format("Expected the padding tensor to be of rank {} not {} on Node {} {}.",
2154 expectedRank,
2155 rank,
2156 nodeName,
2157 CHECK_LOCATION().AsString()));
jimfly01f6ba7472018-12-04 10:09:52 +00002158 }
2159 unsigned int second = paddingTensor.GetShape()[1];
2160 if (second != 2)
2161 {
2162 throw ParseException(
James Ward58dec6b2020-09-11 17:32:44 +01002163 fmt::format("Expected the padding tensor to be of dimensions "
2164 "[{1}, 2] not [{1}, {2}] on Node {3} {4}.",
2165 rank,
2166 second,
2167 nodeName,
2168 CHECK_LOCATION().AsString()));
jimfly01f6ba7472018-12-04 10:09:52 +00002169 }
2170 return rank;
2171}
2172
2173TensorInfo CalculatePaddedOutputTensorInfo(const TensorInfo& inputTensorInfo,
2174 const std::vector<std::pair<unsigned int, unsigned int>>& padList)
2175{
2176 unsigned int numDims = inputTensorInfo.GetNumDimensions();
2177 std::vector<unsigned int> outDims;
2178 for (unsigned int i = 0; i < numDims; ++i)
2179 {
2180 unsigned int dimSize = inputTensorInfo.GetShape()[i];
2181 const std::pair<unsigned int, unsigned int>& dimPadding = padList[i];
2182 dimSize += dimPadding.first;
2183 dimSize += dimPadding.second;
2184 outDims.push_back(dimSize);
2185 }
2186 TensorInfo paddedTensorInfo = inputTensorInfo;
2187 unsigned int outDimsSize = static_cast<unsigned int>(outDims.size());
2188 paddedTensorInfo.SetShape(TensorShape{ outDimsSize, outDims.data() });
2189 return paddedTensorInfo;
2190}
2191
Kevin May7d96b162021-02-03 17:38:41 +00002192ParsedTfOperationPtr ITfParser::TfParserImpl::ParsePad(const tensorflow::NodeDef& nodeDef,
2193 const tensorflow::GraphDef& graphDef)
jimfly01f6ba7472018-12-04 10:09:52 +00002194{
Jan Eilers8eb25602020-03-09 12:13:48 +00002195 IgnoreUnused(graphDef);
jimfly01f6ba7472018-12-04 10:09:52 +00002196 // input consists of:
2197 // input[0] the tensor which will be padded
2198 // input[1] the tensor holding the padding values
2199 std::vector<OutputOfParsedTfOperation> inputs = GetInputParsedTfOperationsChecked(nodeDef, 2);
2200 IOutputSlot& previousLayerOutputSlot = inputs[0].m_IndexedValue->ResolveArmnnOutputSlot(inputs[0].m_Index);
2201 TensorInfo inputTensorInfo = previousLayerOutputSlot.GetTensorInfo();
2202 if (!HasParsedConstTensor<int32_t>(inputs[1].m_IndexedValue))
2203 {
2204 throw ParseException(
James Ward58dec6b2020-09-11 17:32:44 +01002205 fmt::format("ArmNN only supports Pad with constant padding. "
2206 "Input {}. Node {} {}",
2207 inputs[1].m_IndexedValue->GetNode().name(),
2208 nodeDef.name(),
2209 CHECK_LOCATION().AsString()));
jimfly01f6ba7472018-12-04 10:09:52 +00002210
2211 }
2212 ParsedConstTfOperation<int32_t>* paddingTensorOp =
Jan Eilersbb446e52020-04-02 13:56:54 +01002213 PolymorphicDowncast<ParsedConstTfOperation<int32_t>*>(inputs[1].m_IndexedValue);
jimfly01f6ba7472018-12-04 10:09:52 +00002214
2215 std::vector<int32_t> paddingTensorData;
Matteo Martincigh482ca852018-12-12 09:20:55 +00002216 ConstTensor paddingTensor = paddingTensorOp->GetConstTensor(paddingTensorData);
jimfly01f6ba7472018-12-04 10:09:52 +00002217 // paddings is an integer tensor with shape [n, 2], where n is the rank of tensor
2218 // and should match the rank of the input tensor that is being padded.
2219 // For each dimension D of input, paddings[D, 0] indicates how many values to add
2220 // before the contents of tensor in that dimension, and paddings[D, 1] indicates how
2221 // many values to add after the contents of tensor in that dimension
2222 // This needs to be translated into a padList for ACL
2223 std::vector<std::pair<unsigned int, unsigned int>> padList;
2224 unsigned int rank = CheckPaddingTensor(paddingTensor, inputTensorInfo, nodeDef.name());
2225 for (unsigned int i = 0; i < rank; ++i)
2226 {
2227 std::pair<unsigned int, unsigned int> paddingForDim;
2228 for (unsigned int j = 0; j < 2; j++)
2229 {
2230 unsigned int index = (i * 2) + j;
2231 int paddingAmount = paddingTensorData[index];
2232 // make sure we can cast to an unsigned value
2233 if (paddingAmount < 0)
2234 {
2235 throw ParseException(
James Ward58dec6b2020-09-11 17:32:44 +01002236 fmt::format("Negative amount {} specified at [{}, {}] of padding tensor on Node {} {}.",
2237 paddingAmount,
2238 i,
2239 j,
2240 nodeDef.name(),
2241 CHECK_LOCATION().AsString()));
jimfly01f6ba7472018-12-04 10:09:52 +00002242 }
2243 if (j == 0)
2244 {
2245 paddingForDim.first = static_cast<unsigned int>(paddingAmount);
2246 }
2247 else
2248 {
2249 paddingForDim.second = static_cast<unsigned int>(paddingAmount);
2250 }
2251 }
2252 padList.push_back(paddingForDim);
2253 }
2254 PadDescriptor padDescriptor(padList);
2255 IConnectableLayer* layer = m_Network->AddPadLayer(padDescriptor, nodeDef.name().c_str());
2256 previousLayerOutputSlot.Connect(layer->GetInputSlot(0));
2257 // Use the padding to calculate the new output tensor shape
2258 TensorInfo outputTensorInfo = CalculatePaddedOutputTensorInfo(inputTensorInfo, padList);
2259 layer->GetOutputSlot(0).SetTensorInfo(outputTensorInfo);
2260 return std::make_unique<SingleLayerParsedTfOperation>(this, nodeDef, layer);
2261}
2262
Kevin May7d96b162021-02-03 17:38:41 +00002263ParsedTfOperationPtr ITfParser::TfParserImpl::ParseConcat(const tensorflow::NodeDef& nodeDef,
2264 const tensorflow::GraphDef& graphDef)
surmeh01bceff2f2018-03-29 16:29:27 +01002265{
Jan Eilers8eb25602020-03-09 12:13:48 +00002266 IgnoreUnused(graphDef);
surmeh01bceff2f2018-03-29 16:29:27 +01002267 std::vector<OutputOfConstNodeDef> nodes = GetTfInputNodes(nodeDef);
Matteo Martincighf9afc792018-12-06 12:03:17 +00002268
telsoa01c577f2c2018-08-31 09:22:23 +01002269 // In tensorflow, we have the last input of the Concat layer as the axis for concatenation.
surmeh01bceff2f2018-03-29 16:29:27 +01002270 unsigned int numInputs = static_cast<unsigned int>(nodes.size());
surmeh01bceff2f2018-03-29 16:29:27 +01002271
surmeh01bceff2f2018-03-29 16:29:27 +01002272 std::vector<OutputOfParsedTfOperation> inputs = GetInputParsedTfOperationsChecked(nodeDef, numInputs);
2273
Saoirse Stewart91c0eff2019-02-27 11:07:57 +00002274 // Constant tensor index
2275 unsigned int index = GetConstInputIndex(inputs);
Matteo Martincighf9afc792018-12-06 12:03:17 +00002276 // Get the axis tensor data
Saoirse Stewart91c0eff2019-02-27 11:07:57 +00002277 ParsedConstTfOperation<int32_t>* shapeNode =
Jan Eilersbb446e52020-04-02 13:56:54 +01002278 PolymorphicDowncast<ParsedConstTfOperation<int32_t>*>(inputs[index].m_IndexedValue);
Saoirse Stewart91c0eff2019-02-27 11:07:57 +00002279
surmeh01bceff2f2018-03-29 16:29:27 +01002280 std::vector<int32_t> axisTensorData;
Matteo Martincigh482ca852018-12-12 09:20:55 +00002281 shapeNode->GetConstTensor(axisTensorData);
surmeh01bceff2f2018-03-29 16:29:27 +01002282
telsoa01c577f2c2018-08-31 09:22:23 +01002283 // This concatDim indicates the data format: 3 is the NHWC, 1 is the NCHW.
Matteo Martincighf9afc792018-12-06 12:03:17 +00002284 const unsigned int concatDim = static_cast<unsigned int>(axisTensorData[0]);
surmeh01bceff2f2018-03-29 16:29:27 +01002285
telsoa01c577f2c2018-08-31 09:22:23 +01002286 // Armnn supports concatenation along the channel dimension for data formats NHWC and NCHW.
Matteo Martincighf9afc792018-12-06 12:03:17 +00002287 if (concatDim == 0 || concatDim == 2)
surmeh01bceff2f2018-03-29 16:29:27 +01002288 {
telsoa01c577f2c2018-08-31 09:22:23 +01002289 throw ParseException(
James Ward58dec6b2020-09-11 17:32:44 +01002290 fmt::format("Dimension {} for concatenation is not supported by Armnn. "
2291 "Node {} {}",
2292 concatDim,
2293 nodeDef.name(),
2294 CHECK_LOCATION().AsString()));
surmeh01bceff2f2018-03-29 16:29:27 +01002295 }
2296
Matthew Jacksondba634f2019-08-15 15:14:18 +01002297 const unsigned int supportedNumDims = 4;
Matteo Martincighf9afc792018-12-06 12:03:17 +00002298 unsigned int numConcatViews = numInputs - 1;
Matthew Jacksondba634f2019-08-15 15:14:18 +01002299 OriginsDescriptor concatDescriptor(static_cast<uint32_t>(numConcatViews), supportedNumDims);
Matteo Martincighf9afc792018-12-06 12:03:17 +00002300 concatDescriptor.SetConcatAxis(concatDim);
Matthew Jacksondba634f2019-08-15 15:14:18 +01002301 TensorShape mergeDims(supportedNumDims);
Matteo Martincighf9afc792018-12-06 12:03:17 +00002302 unsigned int mergeDim = 0;
2303 for (unsigned int viewIndex = 0; viewIndex < numConcatViews; ++viewIndex)
surmeh01bceff2f2018-03-29 16:29:27 +01002304 {
telsoa01c577f2c2018-08-31 09:22:23 +01002305 // Need to double check whether it should be
Matteo Martincighf9afc792018-12-06 12:03:17 +00002306 IOutputSlot& inputSlot = inputs[viewIndex].m_IndexedValue->ResolveArmnnOutputSlot(inputs[viewIndex].m_Index);
surmeh01bceff2f2018-03-29 16:29:27 +01002307 TensorInfo inputTensorInfo = inputSlot.GetTensorInfo();
2308
Matteo Martincighf9afc792018-12-06 12:03:17 +00002309 // Double check dimensions of the tensors
Matthew Jacksondba634f2019-08-15 15:14:18 +01002310 if (inputTensorInfo.GetNumDimensions() != supportedNumDims)
Matteo Martincighf9afc792018-12-06 12:03:17 +00002311 {
2312 throw armnn::ParseException(
James Ward58dec6b2020-09-11 17:32:44 +01002313 fmt::format("The number of dimensions: {} for input tensors of the "
2314 "concatenation op should be {} {}",
2315 inputTensorInfo.GetNumDimensions(),
2316 supportedNumDims,
2317 CHECK_LOCATION().AsString()));
Matteo Martincighf9afc792018-12-06 12:03:17 +00002318 }
2319
2320 // Copy the input tensor shape to mergeDimSizes and initialize the view origin coordinates for the current input
2321 mergeDims = inputTensorInfo.GetShape();
2322 unsigned int* viewOrigin = const_cast<unsigned int*>(concatDescriptor.GetViewOrigin(viewIndex));
Matthew Jacksondba634f2019-08-15 15:14:18 +01002323 std::fill(viewOrigin, viewOrigin + supportedNumDims, 0);
Matteo Martincighf9afc792018-12-06 12:03:17 +00002324
2325 // Update the view origin coordinates and the merge dimension value
2326 concatDescriptor.SetViewOriginCoord(viewIndex, concatDim, mergeDim);
2327 mergeDim += mergeDims[concatDim];
surmeh01bceff2f2018-03-29 16:29:27 +01002328 }
2329
Matteo Martincighf9afc792018-12-06 12:03:17 +00002330 // Update the output shape
2331 mergeDims[concatDim] = mergeDim;
Jim Flynn906f9462019-05-10 13:55:21 +01002332 armnn::IConnectableLayer *layer = m_Network->AddConcatLayer(concatDescriptor, nodeDef.name().c_str());
surmeh01bceff2f2018-03-29 16:29:27 +01002333
Matteo Martincighf9afc792018-12-06 12:03:17 +00002334 layer->GetOutputSlot(0).SetTensorInfo(armnn::TensorInfo(mergeDims, DataType::Float32));
surmeh01bceff2f2018-03-29 16:29:27 +01002335
Matteo Martincighf9afc792018-12-06 12:03:17 +00002336 for (unsigned int viewIndex = 0; viewIndex < numConcatViews; ++viewIndex)
surmeh01bceff2f2018-03-29 16:29:27 +01002337 {
Matteo Martincighf9afc792018-12-06 12:03:17 +00002338 IOutputSlot& inputSlot = inputs[viewIndex].m_IndexedValue->ResolveArmnnOutputSlot(inputs[viewIndex].m_Index);
2339 inputSlot.Connect(layer->GetInputSlot(viewIndex));
surmeh01bceff2f2018-03-29 16:29:27 +01002340 }
2341
2342 return std::make_unique<SingleLayerParsedTfOperation>(this, nodeDef, layer);
2343}
2344
Kevin May7d96b162021-02-03 17:38:41 +00002345ParsedTfOperationPtr ITfParser::TfParserImpl::ParseShape(const tensorflow::NodeDef& nodeDef,
surmeh01bceff2f2018-03-29 16:29:27 +01002346 const tensorflow::GraphDef& graphDef)
2347{
Jan Eilers8eb25602020-03-09 12:13:48 +00002348 IgnoreUnused(graphDef);
telsoa01c577f2c2018-08-31 09:22:23 +01002349 // Note: the Shape layer is handled in a special way, because:
2350 // 1. ARMNN doesn't support int32 tensors which it outputs.
2351 // 2. ARMNN works with statically shaped tensors which are known at parse time.
surmeh01bceff2f2018-03-29 16:29:27 +01002352 // 3. because of 1. and 2. we treat the output of Shape as a temporary const int32
telsoa01c577f2c2018-08-31 09:22:23 +01002353 // tensor which may be used as an input to other ops, most likely a Reshape.
surmeh01bceff2f2018-03-29 16:29:27 +01002354
2355 const tensorflow::DataType tfDataType = ReadMandatoryNodeTypeAttribute(nodeDef, "out_type");
2356 if (tfDataType != tensorflow::DT_INT32)
2357 {
telsoa01c577f2c2018-08-31 09:22:23 +01002358 throw ParseException(
James Ward58dec6b2020-09-11 17:32:44 +01002359 fmt::format("Armnn only supports DT_INT32 as out_type. Got {} for Node {} {}",
2360 tensorflow::DataType_Name(tfDataType),
2361 nodeDef.name(),
2362 CHECK_LOCATION().AsString()));
surmeh01bceff2f2018-03-29 16:29:27 +01002363 }
2364
2365 const std::vector<OutputOfParsedTfOperation> inputs = GetInputParsedTfOperationsChecked(nodeDef, 1);
2366 IOutputSlot& prevLayerOutputSlot = inputs[0].m_IndexedValue->ResolveArmnnOutputSlot(inputs[0].m_Index);
2367 const TensorInfo& prevLayerTensorInfo = prevLayerOutputSlot.GetTensorInfo();
2368 unsigned int prevLayerDimensions = prevLayerTensorInfo.GetNumDimensions();
2369
2370 std::vector<int32_t> shapeTensorData;
2371 shapeTensorData.reserve(prevLayerDimensions);
2372
2373 for (unsigned int i=0; i<prevLayerDimensions; ++i)
2374 {
2375 shapeTensorData.push_back(static_cast<int32_t>(prevLayerTensorInfo.GetShape()[i]));
2376 }
2377
2378 TensorInfo shapeTensorInfo(1, &prevLayerDimensions, DataType::Signed32);
2379
2380 return std::make_unique<ParsedConstTfOperation<int32_t>>(this,
2381 nodeDef,
2382 &shapeTensorData[0],
2383 shapeTensorInfo);
2384}
2385
Kevin May7d96b162021-02-03 17:38:41 +00002386ParsedTfOperationPtr ITfParser::TfParserImpl::ParseReshape(const tensorflow::NodeDef& nodeDef,
surmeh01bceff2f2018-03-29 16:29:27 +01002387 const tensorflow::GraphDef& graphDef)
2388{
Jan Eilers8eb25602020-03-09 12:13:48 +00002389 IgnoreUnused(graphDef);
surmeh01bceff2f2018-03-29 16:29:27 +01002390 std::vector<OutputOfParsedTfOperation> inputs = GetInputParsedTfOperationsChecked(nodeDef, 2);
2391 ParsedTfOperation* inputNode = inputs[0].m_IndexedValue;
2392
2393 if (!HasParsedConstTensor<int32_t>(inputs[1].m_IndexedValue->GetNode().name()))
2394 {
telsoa01c577f2c2018-08-31 09:22:23 +01002395 throw ParseException(
James Ward58dec6b2020-09-11 17:32:44 +01002396 fmt::format("ArmNN only supports Reshape layers with constant shapes. "
2397 "Input {} Node {} {}",
2398 inputs[1].m_IndexedValue->GetNode().name(),
2399 nodeDef.name(),
2400 CHECK_LOCATION().AsString()));
surmeh01bceff2f2018-03-29 16:29:27 +01002401 }
2402 ParsedConstTfOperation<int32_t>* shapeNode =
Jan Eilersbb446e52020-04-02 13:56:54 +01002403 PolymorphicDowncast<ParsedConstTfOperation<int32_t>*>(inputs[1].m_IndexedValue);
surmeh01bceff2f2018-03-29 16:29:27 +01002404
2405 armnn::IOutputSlot& prevLayerOutputSlot = inputNode->ResolveArmnnOutputSlot(inputs[0].m_Index);
2406 TensorInfo inputTensorInfo = prevLayerOutputSlot.GetTensorInfo();
2407
2408 std::vector<int32_t> shapeTensorData;
Matteo Martincigh482ca852018-12-12 09:20:55 +00002409 ConstTensor shapeTensor = shapeNode->GetConstTensor(shapeTensorData);
surmeh01bceff2f2018-03-29 16:29:27 +01002410 const TensorInfo outputTensorInfo = PrepareReshape(inputTensorInfo, shapeTensorData);
2411
2412 TensorShape targetShape = outputTensorInfo.GetShape();
2413 ReshapeDescriptor reshapeDesc;
2414 reshapeDesc.m_TargetShape = targetShape;
2415
2416 IConnectableLayer* layer = m_Network->AddReshapeLayer(reshapeDesc, nodeDef.name().c_str());
2417 prevLayerOutputSlot.Connect(layer->GetInputSlot(0));
2418 layer->GetOutputSlot(0).SetTensorInfo(outputTensorInfo);
2419
2420 return std::make_unique<SingleLayerParsedTfOperation>(this, nodeDef, layer);
2421}
2422
Kevin May7d96b162021-02-03 17:38:41 +00002423ParsedTfOperationPtr ITfParser::TfParserImpl::ParseResizeBilinear(const tensorflow::NodeDef& nodeDef,
surmeh01bceff2f2018-03-29 16:29:27 +01002424 const tensorflow::GraphDef& graphDef)
2425{
Jan Eilers8eb25602020-03-09 12:13:48 +00002426 IgnoreUnused(graphDef);
surmeh01bceff2f2018-03-29 16:29:27 +01002427 std::vector<OutputOfParsedTfOperation> inputs = GetInputParsedTfOperationsChecked(nodeDef, 2);
2428
2429 if (!HasParsedConstTensor<int32_t>(inputs[1].m_IndexedValue->GetNode().name()))
2430 {
telsoa01c577f2c2018-08-31 09:22:23 +01002431 throw ParseException(
James Ward58dec6b2020-09-11 17:32:44 +01002432 fmt::format("ArmNN only supports ResizeBilinear layers with constant sizes. "
2433 "Input {}. Node {} {}",
2434 inputs[1].m_IndexedValue->GetNode().name(),
2435 nodeDef.name(),
2436 CHECK_LOCATION().AsString()));
surmeh01bceff2f2018-03-29 16:29:27 +01002437 }
2438 ParsedConstTfOperation<int32_t>* sizeNode =
Jan Eilersbb446e52020-04-02 13:56:54 +01002439 PolymorphicDowncast<ParsedConstTfOperation<int32_t>*>(inputs[1].m_IndexedValue);
surmeh01bceff2f2018-03-29 16:29:27 +01002440
telsoa01c577f2c2018-08-31 09:22:23 +01002441 // Checks the align_corners attribute is not set.
surmeh01bceff2f2018-03-29 16:29:27 +01002442 if (ReadOptionalNodeBoolAttribute(nodeDef, "align_corners", false))
2443 {
telsoa01c577f2c2018-08-31 09:22:23 +01002444 throw ParseException(
James Ward58dec6b2020-09-11 17:32:44 +01002445 fmt::format("ArmNN only supports ResizeBilinear layers with align_corners set to false. "
2446 "Node {} {}",
2447 nodeDef.name(),
2448 CHECK_LOCATION().AsString()));
surmeh01bceff2f2018-03-29 16:29:27 +01002449 }
2450
telsoa01c577f2c2018-08-31 09:22:23 +01002451 // Data for the parsed tensor args (size) must be stored locally.
surmeh01bceff2f2018-03-29 16:29:27 +01002452 std::vector<int32_t> sizeTensorData;
Matteo Martincigh482ca852018-12-12 09:20:55 +00002453 ConstTensor sizeTensor = sizeNode->GetConstTensor(sizeTensorData);
surmeh01bceff2f2018-03-29 16:29:27 +01002454
telsoa01c577f2c2018-08-31 09:22:23 +01002455 // The descriptor only has target height and width attributes, which we get from the size tensor.
Aron Virginas-Tar169d2f12019-07-01 19:01:44 +01002456 ResizeDescriptor desc;
2457 desc.m_Method = armnn::ResizeMethod::Bilinear;
surmeh01bceff2f2018-03-29 16:29:27 +01002458 desc.m_TargetHeight = static_cast<uint32_t> (sizeTensorData[0]);
Aron Virginas-Tar169d2f12019-07-01 19:01:44 +01002459 desc.m_TargetWidth = static_cast<uint32_t> (sizeTensorData[1]);
2460 desc.m_DataLayout = armnn::DataLayout::NHWC;
surmeh01bceff2f2018-03-29 16:29:27 +01002461
Aron Virginas-Tar169d2f12019-07-01 19:01:44 +01002462 IConnectableLayer* layer = m_Network->AddResizeLayer(desc, nodeDef.name().c_str());
surmeh01bceff2f2018-03-29 16:29:27 +01002463
2464 IOutputSlot& inputSlot = inputs[0].m_IndexedValue->ResolveArmnnOutputSlot(inputs[0].m_Index);
2465 TensorInfo inputTensorInfo = inputSlot.GetTensorInfo();
telsoa01c577f2c2018-08-31 09:22:23 +01002466 // The input shape is always in BHWC format, this will be swizzled below; for now,
2467 // get the batch and channels to make up the ArmNN output shape with the target size.
surmeh01bceff2f2018-03-29 16:29:27 +01002468 unsigned int outBatch = inputTensorInfo.GetShape()[0];
2469 unsigned int outChannels = inputTensorInfo.GetShape()[3];
2470 unsigned int outHeight = desc.m_TargetHeight;
2471 unsigned int outWidth = desc.m_TargetWidth;
jimfly018a121502018-12-06 16:19:52 +00002472 TensorShape outShape({outBatch, outHeight, outWidth, outChannels });
telsoa01c577f2c2018-08-31 09:22:23 +01002473 // The output DataType is always Float32, regardless of the input DataType.
surmeh01bceff2f2018-03-29 16:29:27 +01002474 const TensorInfo outputTensorInfo(outShape, armnn::DataType::Float32);
2475 layer->GetOutputSlot(0).SetTensorInfo(outputTensorInfo);
2476
jimfly018a121502018-12-06 16:19:52 +00002477 inputSlot.Connect(layer->GetInputSlot(0));
surmeh01bceff2f2018-03-29 16:29:27 +01002478
2479 return std::make_unique<SingleLayerParsedTfOperation>(this, nodeDef, layer);
2480}
2481
2482TensorInfo OutputShapeOfSqueeze(const tensorflow::NodeDef& nodeDef, TensorInfo inputTensorInfo)
2483{
Narumol Prangnawaratac2770a2020-04-01 16:51:23 +01002484 ARMNN_ASSERT(nodeDef.op() == "Squeeze");
surmeh01bceff2f2018-03-29 16:29:27 +01002485 tensorflow::DataType tfDataType = ReadMandatoryNodeTypeAttribute(nodeDef, "T");
2486
2487 DataType type;
2488 if (tfDataType == tensorflow::DT_FLOAT)
2489 {
2490 type = DataType::Float32;
2491 }
2492 else if (tfDataType == tensorflow::DT_INT32)
2493 {
2494 type = DataType::Signed32;
2495 }
2496 else
2497 {
telsoa01c577f2c2018-08-31 09:22:23 +01002498 throw ParseException(
James Ward58dec6b2020-09-11 17:32:44 +01002499 fmt::format("Unsupported DataType {} for Squeeze operation {} {}",
2500 tensorflow::DataType_Name(tfDataType),
2501 nodeDef.name(),
2502 CHECK_LOCATION().AsString()));
telsoa01c577f2c2018-08-31 09:22:23 +01002503 }
2504
2505
2506 if (inputTensorInfo.GetNumDimensions() > 4)
2507 {
2508 throw ParseException(
James Ward58dec6b2020-09-11 17:32:44 +01002509 fmt::format("Unsupported number of dimensions: {} for input shape for Squeeze {} {}",
2510 inputTensorInfo.GetNumDimensions(),
2511 nodeDef.name(),
2512 CHECK_LOCATION().AsString()));
surmeh01bceff2f2018-03-29 16:29:27 +01002513 }
2514
2515 std::vector<uint32_t> squeezeDims = ReadOptionalNodeUint32ListAttribute(nodeDef, "squeeze_dims");
telsoa01c577f2c2018-08-31 09:22:23 +01002516 static const uint32_t dimensionSequence[] = { 0, 1, 2, 3 };
2517
surmeh01bceff2f2018-03-29 16:29:27 +01002518 if (squeezeDims.empty())
2519 {
telsoa01c577f2c2018-08-31 09:22:23 +01002520 squeezeDims.assign(dimensionSequence,
2521 dimensionSequence+inputTensorInfo.GetNumDimensions());
surmeh01bceff2f2018-03-29 16:29:27 +01002522 }
2523
2524 std::vector<uint32_t> outputDims;
2525 for(unsigned int i = 0; i < inputTensorInfo.GetNumDimensions(); i++)
2526 {
telsoa01c577f2c2018-08-31 09:22:23 +01002527 bool skipSqueeze = (std::find(squeezeDims.begin(), squeezeDims.end(), i) == squeezeDims.end());
2528 auto currentDimension = inputTensorInfo.GetShape()[i];
2529 if (skipSqueeze || currentDimension != 1)
surmeh01bceff2f2018-03-29 16:29:27 +01002530 {
telsoa01c577f2c2018-08-31 09:22:23 +01002531 outputDims.push_back(currentDimension);
surmeh01bceff2f2018-03-29 16:29:27 +01002532 }
2533 }
2534
2535 if (outputDims.size() > 4)
2536 {
telsoa01c577f2c2018-08-31 09:22:23 +01002537 throw ParseException(
James Ward58dec6b2020-09-11 17:32:44 +01002538 fmt::format("Unsupported number of dimensions: {} for output shape for Squeeze {} {}",
2539 outputDims.size(),
2540 nodeDef.name(),
2541 CHECK_LOCATION().AsString()));
surmeh01bceff2f2018-03-29 16:29:27 +01002542 }
2543
telsoa01c577f2c2018-08-31 09:22:23 +01002544 TensorShape outShape = TensorShape(static_cast<unsigned int>(outputDims.size()),
2545 outputDims.data());
2546
2547 TensorInfo outTensorInfo = inputTensorInfo;
2548 outTensorInfo.SetShape(outShape);
2549 outTensorInfo.SetDataType(type);
surmeh01bceff2f2018-03-29 16:29:27 +01002550
2551 return outTensorInfo;
2552}
2553
Kevin May7d96b162021-02-03 17:38:41 +00002554ParsedTfOperationPtr ITfParser::TfParserImpl::ParseSqueeze(const tensorflow::NodeDef& nodeDef,
2555 const tensorflow::GraphDef& graphDef)
surmeh01bceff2f2018-03-29 16:29:27 +01002556{
Jan Eilers8eb25602020-03-09 12:13:48 +00002557 IgnoreUnused(graphDef);
surmeh01bceff2f2018-03-29 16:29:27 +01002558 std::vector<OutputOfParsedTfOperation> inputs = GetInputParsedTfOperationsChecked(nodeDef, 1);
2559
2560 IOutputSlot& prevLayerOutputSlot = inputs[0].m_IndexedValue->ResolveArmnnOutputSlot(inputs[0].m_Index);
2561 TensorInfo inputTensorInfo = prevLayerOutputSlot.GetTensorInfo();
2562
2563 TensorInfo outputInfo;
2564 outputInfo = OutputShapeOfSqueeze(nodeDef, inputTensorInfo);
2565
2566 ReshapeDescriptor reshapeDesc;
2567 reshapeDesc.m_TargetShape = outputInfo.GetShape();
2568 IConnectableLayer* layer = m_Network->AddReshapeLayer(reshapeDesc, nodeDef.name().c_str());
2569 prevLayerOutputSlot.Connect(layer->GetInputSlot(0));
2570 layer->GetOutputSlot(0).SetTensorInfo(outputInfo);
2571
2572 return std::make_unique<SingleLayerParsedTfOperation>(this, nodeDef, layer);
2573}
2574
Kevin May7d96b162021-02-03 17:38:41 +00002575ParsedTfOperationPtr ITfParser::TfParserImpl::ParseLrn(const tensorflow::NodeDef& nodeDef,
2576 const tensorflow::GraphDef& graphDef)
surmeh01bceff2f2018-03-29 16:29:27 +01002577{
Jan Eilers8eb25602020-03-09 12:13:48 +00002578 IgnoreUnused(graphDef);
surmeh01bceff2f2018-03-29 16:29:27 +01002579 std::vector<OutputOfParsedTfOperation> inputs = GetInputParsedTfOperationsChecked(nodeDef, 1);
2580
2581 NormalizationDescriptor normalizationDescriptor;
2582 normalizationDescriptor.m_NormMethodType = NormalizationAlgorithmMethod::LocalBrightness;
2583 normalizationDescriptor.m_NormChannelType = NormalizationAlgorithmChannel::Across;
2584 normalizationDescriptor.m_Alpha = ReadMandatoryNodeFloatAttribute(nodeDef, "alpha");
2585 normalizationDescriptor.m_Beta = ReadMandatoryNodeFloatAttribute(nodeDef, "beta");
2586 normalizationDescriptor.m_K = ReadMandatoryNodeFloatAttribute(nodeDef, "bias");
2587 normalizationDescriptor.m_NormSize = ReadMandatoryNodeUint32Attribute(nodeDef, "depth_radius");
ruoyan018174f362018-12-04 18:24:08 +00002588 normalizationDescriptor.m_DataLayout = armnn::DataLayout::NHWC;
surmeh01bceff2f2018-03-29 16:29:27 +01002589
2590 // The window size must be an odd value. For a window size of (2 * n + 1), TensorFlow defines depth_radius = n.
2591 normalizationDescriptor.m_NormSize = normalizationDescriptor.m_NormSize * 2 + 1;
2592
2593 IOutputSlot& prevLayerOutputSlot = inputs[0].m_IndexedValue->ResolveArmnnOutputSlot(inputs[0].m_Index);
surmeh01bceff2f2018-03-29 16:29:27 +01002594 IConnectableLayer* layer = m_Network->AddNormalizationLayer(normalizationDescriptor,
2595 nodeDef.name().c_str());
ruoyan018174f362018-12-04 18:24:08 +00002596 prevLayerOutputSlot.Connect(layer->GetInputSlot(0));
2597 layer->GetOutputSlot(0).SetTensorInfo(prevLayerOutputSlot.GetTensorInfo());
surmeh01bceff2f2018-03-29 16:29:27 +01002598
2599 return std::make_unique<SingleLayerParsedTfOperation>(this, nodeDef, layer);
2600}
2601
2602/// An ParsedTfOperation for a MatMul node.
telsoa01c577f2c2018-08-31 09:22:23 +01002603/// Creation of the armnn FullyConnected layer is deferred until it is actually needed, because
2604/// MatMul nodes are often used for the first part of a biased FullyConnected (MatMul followed
2605/// by Add) and in these cases armnn doesn't need a separate layer for the MatMul.
2606///
surmeh01bceff2f2018-03-29 16:29:27 +01002607class ParsedMatMulTfOperation : public DeferredSingleLayerParsedTfOperation
2608{
2609public:
Kevin May7d96b162021-02-03 17:38:41 +00002610 ParsedMatMulTfOperation(ITfParser::TfParserImpl* parser, const tensorflow::NodeDef& node)
surmeh01bceff2f2018-03-29 16:29:27 +01002611 : DeferredSingleLayerParsedTfOperation(parser, node)
2612 {
2613 }
2614
2615 void CreateLayerDeferred() override
2616 {
Narumol Prangnawaratac2770a2020-04-01 16:51:23 +01002617 ARMNN_ASSERT(m_Layer == nullptr);
surmeh01bceff2f2018-03-29 16:29:27 +01002618 m_Layer = m_Parser->AddFullyConnectedLayer(m_Node, nullptr, m_Node.name().c_str());
2619 }
2620};
2621
Kevin May7d96b162021-02-03 17:38:41 +00002622ParsedTfOperationPtr ITfParser::TfParserImpl::ParseMatMul(const tensorflow::NodeDef& nodeDef,
2623 const tensorflow::GraphDef& graphDef)
surmeh01bceff2f2018-03-29 16:29:27 +01002624{
Jan Eilers8eb25602020-03-09 12:13:48 +00002625 IgnoreUnused(graphDef);
Derek Lambertibaa177f2019-12-10 22:00:43 +00002626
telsoa01c577f2c2018-08-31 09:22:23 +01002627 // Defers the creation of the layer (see ParsedMatMulTfOperation).
surmeh01bceff2f2018-03-29 16:29:27 +01002628 return std::make_unique<ParsedMatMulTfOperation>(this, nodeDef);
2629}
2630
Kevin May7d96b162021-02-03 17:38:41 +00002631ParsedTfOperationPtr ITfParser::TfParserImpl::ParseMean(const tensorflow::NodeDef& nodeDef,
2632 const tensorflow::GraphDef& graphDef)
Ferran Balaguer51dd62f2019-01-11 19:29:18 +00002633{
Jan Eilers8eb25602020-03-09 12:13:48 +00002634 IgnoreUnused(graphDef);
Ferran Balaguer51dd62f2019-01-11 19:29:18 +00002635 std::vector<OutputOfParsedTfOperation> inputs = GetInputParsedTfOperationsChecked(nodeDef, 2);
2636 IOutputSlot& inputSlot = inputs[0].m_IndexedValue->ResolveArmnnOutputSlot(inputs[0].m_Index);
2637 TensorInfo inputTensorInfo = inputSlot.GetTensorInfo();
2638
2639 if (inputs.size() != 2)
2640 {
2641 throw ParseException(
James Ward58dec6b2020-09-11 17:32:44 +01002642 fmt::format("Mean expects two inputs!. Got {} for Node {} {}",
2643 inputs.size(),
2644 nodeDef.name(),
2645 CHECK_LOCATION().AsString()));
Ferran Balaguer51dd62f2019-01-11 19:29:18 +00002646 }
2647
2648 bool keepDims = ReadMandatoryNodeBoolAttribute(nodeDef, "keep_dims");
2649
2650 ParsedConstTfOperation<int32_t>* axisNode =
Jan Eilersbb446e52020-04-02 13:56:54 +01002651 PolymorphicDowncast<ParsedConstTfOperation<int32_t>*>(inputs[1].m_IndexedValue);
Ferran Balaguer51dd62f2019-01-11 19:29:18 +00002652
2653 const TensorInfo& axisTensorInfo = axisNode->GetTensorInfo();
2654
2655 ConstTensor axisTensor(axisTensorInfo, axisNode->GetStorage());
2656 const int* axisData = static_cast<const int*>(axisTensor.GetMemoryArea());
2657
2658 TensorInfo outputTensorInfo;
2659 MeanDescriptor meanDescriptor;
2660 meanDescriptor.m_KeepDims = keepDims;
2661
2662 // Negative axis values are supported so that the process requires
2663 // to convert them into the corresponding positive ones.
2664 // Duplicate values are also removed.
2665 std::vector<int> rawAxisVector(axisData, axisData + axisTensorInfo.GetNumElements());
2666 std::set<unsigned int> positiveAxisSet;
2667 int rank = static_cast<int>(inputTensorInfo.GetNumDimensions());
2668
2669 std::transform(rawAxisVector.begin(), rawAxisVector.end(),
2670 std::inserter(positiveAxisSet, positiveAxisSet.begin()),
2671 [rank](int i) -> unsigned int { return static_cast<unsigned int>((i + rank) % rank); });
2672
Derek Lambertibaa177f2019-12-10 22:00:43 +00002673 CalculateReducedOutputTensoInfo(inputTensorInfo, positiveAxisSet, keepDims, outputTensorInfo);
Ferran Balaguer51dd62f2019-01-11 19:29:18 +00002674
2675 if (inputTensorInfo.GetNumDimensions() > positiveAxisSet.size())
2676 {
2677 meanDescriptor.m_Axis.assign(positiveAxisSet.begin(), positiveAxisSet.end());
2678 }
2679
2680 IConnectableLayer* layer = m_Network->AddMeanLayer(meanDescriptor, nodeDef.name().c_str());
2681 layer->GetOutputSlot(0).SetTensorInfo(outputTensorInfo);
2682 inputSlot.Connect(layer->GetInputSlot(0));
2683
2684 return std::make_unique<SingleLayerParsedTfOperation>(this, nodeDef, layer);
2685}
2686
telsoa01c577f2c2018-08-31 09:22:23 +01002687/// An ParsedTfOperation for a Mul node.
2688/// Creation of the armnn Mul layer is deferred until it is actually needed, because Mul nodes
2689/// are also used for the first part of a leaky relu activation function (Mul followed by Maximum)
2690/// and in these cases armnn doesn't need a separate layer for the Mul.
2691///
2692class ParsedMulTfOperation : public DeferredSingleLayerParsedTfOperation
2693{
2694public:
Kevin May7d96b162021-02-03 17:38:41 +00002695 ParsedMulTfOperation(ITfParser::TfParserImpl* parser, const tensorflow::NodeDef& node)
telsoa01c577f2c2018-08-31 09:22:23 +01002696 : DeferredSingleLayerParsedTfOperation(parser, node)
2697 {
2698 }
2699
2700 void CreateLayerDeferred() override
2701 {
Narumol Prangnawaratac2770a2020-04-01 16:51:23 +01002702 ARMNN_ASSERT(m_Layer == nullptr);
telsoa01c577f2c2018-08-31 09:22:23 +01002703 m_Layer = m_Parser->AddMultiplicationLayer(m_Node);
2704 }
2705};
2706
Kevin May7d96b162021-02-03 17:38:41 +00002707ParsedTfOperationPtr ITfParser::TfParserImpl::ParseMul(const tensorflow::NodeDef& nodeDef,
2708 const tensorflow::GraphDef& graphDef)
surmeh01bceff2f2018-03-29 16:29:27 +01002709{
Jan Eilers8eb25602020-03-09 12:13:48 +00002710 IgnoreUnused(graphDef);
surmeh01bceff2f2018-03-29 16:29:27 +01002711
telsoa01c577f2c2018-08-31 09:22:23 +01002712 return std::make_unique<ParsedMulTfOperation>(this, nodeDef);
surmeh01bceff2f2018-03-29 16:29:27 +01002713}
2714
Kevin May7d96b162021-02-03 17:38:41 +00002715ParsedTfOperationPtr ITfParser::TfParserImpl::ParsePlaceholder(const tensorflow::NodeDef& nodeDef,
surmeh01bceff2f2018-03-29 16:29:27 +01002716 const tensorflow::GraphDef& graphDef)
2717{
Jan Eilers8eb25602020-03-09 12:13:48 +00002718 IgnoreUnused(graphDef);
surmeh01bceff2f2018-03-29 16:29:27 +01002719
2720 std::vector<OutputOfParsedTfOperation> inputs = GetInputParsedTfOperationsChecked(nodeDef, 0);
2721
Matthew Sloyan589e3e82020-09-11 16:17:48 +01002722 const LayerBindingId layerId = armnn::numeric_cast<LayerBindingId>(m_NetworkInputsBindingInfo.size());
surmeh01bceff2f2018-03-29 16:29:27 +01002723
2724 auto it = m_InputShapes.find(nodeDef.name());
2725 if (it == m_InputShapes.end())
2726 {
telsoa01c577f2c2018-08-31 09:22:23 +01002727 throw ParseException(
James Ward58dec6b2020-09-11 17:32:44 +01002728 fmt::format("Missing input shape for Placeholder '{}' {}",
2729 nodeDef.name(),
2730 CHECK_LOCATION().AsString()));
surmeh01bceff2f2018-03-29 16:29:27 +01002731 }
2732 TensorInfo tensorInfo(it->second, DataType::Float32);
2733
2734 IConnectableLayer* const layer = m_Network->AddInputLayer(layerId, nodeDef.name().c_str());
2735
2736 layer->GetOutputSlot(0).SetTensorInfo(tensorInfo);
2737
2738 TrackInputBinding(layer, layerId, tensorInfo);
2739
2740 return std::make_unique<SingleLayerParsedTfOperation>(this, nodeDef, layer);
2741}
2742
Kevin May7d96b162021-02-03 17:38:41 +00002743ParsedTfOperationPtr ITfParser::TfParserImpl::ParseRealDiv(const tensorflow::NodeDef& nodeDef,
2744 const tensorflow::GraphDef& graphDef)
saoste01bbd40612018-08-28 15:41:51 +01002745{
Jan Eilers8eb25602020-03-09 12:13:48 +00002746 IgnoreUnused(graphDef);
saoste01bbd40612018-08-28 15:41:51 +01002747 return AddRealDivLayer(nodeDef);
2748}
2749
Kevin May7d96b162021-02-03 17:38:41 +00002750ParsedTfOperationPtr ITfParser::TfParserImpl::ParseRelu(const tensorflow::NodeDef& nodeDef,
surmeh01bceff2f2018-03-29 16:29:27 +01002751 const tensorflow::GraphDef& graphDef)
2752{
Jan Eilers8eb25602020-03-09 12:13:48 +00002753 IgnoreUnused(graphDef);
surmeh01bceff2f2018-03-29 16:29:27 +01002754
2755 ActivationDescriptor activationDesc;
2756 activationDesc.m_Function = ActivationFunction::ReLu;
2757 return AddActivationLayer(nodeDef, activationDesc);
2758}
2759
Kevin May7d96b162021-02-03 17:38:41 +00002760ParsedTfOperationPtr ITfParser::TfParserImpl::ParseRelu6(const tensorflow::NodeDef& nodeDef,
surmeh01bceff2f2018-03-29 16:29:27 +01002761 const tensorflow::GraphDef& graphDef)
2762{
Jan Eilers8eb25602020-03-09 12:13:48 +00002763 IgnoreUnused(graphDef);
surmeh01bceff2f2018-03-29 16:29:27 +01002764
2765 ActivationDescriptor activationDesc;
2766 activationDesc.m_Function = ActivationFunction::BoundedReLu;
2767 activationDesc.m_A = 6.0f;
2768 activationDesc.m_B = 0.0f;
2769
2770 return AddActivationLayer(nodeDef, activationDesc);
2771}
2772
Kevin May7d96b162021-02-03 17:38:41 +00002773ParsedTfOperationPtr ITfParser::TfParserImpl::ParseSigmoid(const tensorflow::NodeDef& nodeDef,
surmeh01bceff2f2018-03-29 16:29:27 +01002774 const tensorflow::GraphDef& graphDef)
2775{
Jan Eilers8eb25602020-03-09 12:13:48 +00002776 IgnoreUnused(graphDef);
surmeh01bceff2f2018-03-29 16:29:27 +01002777
2778 ActivationDescriptor activationDesc;
2779 activationDesc.m_Function = ActivationFunction::Sigmoid;
2780
2781 return AddActivationLayer(nodeDef, activationDesc);
2782}
2783
Kevin May7d96b162021-02-03 17:38:41 +00002784ParsedTfOperationPtr ITfParser::TfParserImpl::ParseRsqrt(const tensorflow::NodeDef &nodeDef,
Mohamed Nour Abouelseoud7a8892f2019-01-09 14:19:58 +00002785 const tensorflow::GraphDef &graphDef)
2786{
Jan Eilers8eb25602020-03-09 12:13:48 +00002787 IgnoreUnused(graphDef);
Mohamed Nour Abouelseoud7a8892f2019-01-09 14:19:58 +00002788
2789 std::vector<OutputOfParsedTfOperation> inputs = GetInputParsedTfOperationsChecked(nodeDef, 1);
2790
josh minor4a3c6102020-01-06 16:40:46 -06002791 ElementwiseUnaryDescriptor descriptor(UnaryOperation::Rsqrt);
2792 IConnectableLayer* const layer = m_Network->AddElementwiseUnaryLayer(descriptor, nodeDef.name().c_str());
Mohamed Nour Abouelseoud7a8892f2019-01-09 14:19:58 +00002793
2794 IOutputSlot& prevLayerOutputSlot = inputs[0].m_IndexedValue->ResolveArmnnOutputSlot(inputs[0].m_Index);
2795 prevLayerOutputSlot.Connect(layer->GetInputSlot(0));
2796 layer->GetOutputSlot(0).SetTensorInfo(prevLayerOutputSlot.GetTensorInfo());
2797
2798 return std::make_unique<SingleLayerParsedTfOperation>(this, nodeDef, layer);
2799}
2800
Kevin May7d96b162021-02-03 17:38:41 +00002801ParsedTfOperationPtr ITfParser::TfParserImpl::ParseSoftmax(const tensorflow::NodeDef& nodeDef,
surmeh01bceff2f2018-03-29 16:29:27 +01002802 const tensorflow::GraphDef& graphDef)
2803{
Jan Eilers8eb25602020-03-09 12:13:48 +00002804 IgnoreUnused(graphDef);
surmeh01bceff2f2018-03-29 16:29:27 +01002805
2806 std::vector<OutputOfParsedTfOperation> inputs = GetInputParsedTfOperationsChecked(nodeDef, 1);
2807
2808 SoftmaxDescriptor softmaxDescriptor;
2809 IConnectableLayer* const layer = m_Network->AddSoftmaxLayer(softmaxDescriptor, nodeDef.name().c_str());
2810
2811 IOutputSlot& prevLayerSlot = inputs[0].m_IndexedValue->ResolveArmnnOutputSlot(inputs[0].m_Index);
2812 prevLayerSlot.Connect(layer->GetInputSlot(0));
2813 layer->GetOutputSlot(0).SetTensorInfo(prevLayerSlot.GetTensorInfo());
2814
2815 return std::make_unique<SingleLayerParsedTfOperation>(this, nodeDef, layer);
2816}
2817
Kevin May7d96b162021-02-03 17:38:41 +00002818ParsedTfOperationPtr ITfParser::TfParserImpl::ParseSplit(const tensorflow::NodeDef& nodeDef,
Sadik Armagan2ad6cb42018-12-27 11:23:44 +00002819 const tensorflow::GraphDef& graphDef)
2820{
Jan Eilers8eb25602020-03-09 12:13:48 +00002821 IgnoreUnused(graphDef);
Sadik Armagan2ad6cb42018-12-27 11:23:44 +00002822
2823 std::vector<OutputOfConstNodeDef> nodes = GetTfInputNodes(nodeDef);
2824 unsigned int numInputs = static_cast<unsigned int>(nodes.size());
2825 std::vector<OutputOfParsedTfOperation> inputs = GetInputParsedTfOperationsChecked(nodeDef, numInputs);
2826
Saoirse Stewart91c0eff2019-02-27 11:07:57 +00002827 // Constant tensor index
2828 unsigned int index = GetConstInputIndex(inputs);
Sadik Armagan2ad6cb42018-12-27 11:23:44 +00002829 // Get the axis tensor data
Saoirse Stewart91c0eff2019-02-27 11:07:57 +00002830 ParsedConstTfOperation<int32_t>* shapeNode =
Jan Eilersbb446e52020-04-02 13:56:54 +01002831 PolymorphicDowncast<ParsedConstTfOperation<int32_t>*>(inputs[index].m_IndexedValue);
Saoirse Stewart91c0eff2019-02-27 11:07:57 +00002832
Sadik Armagan2ad6cb42018-12-27 11:23:44 +00002833 std::vector<int32_t> axisTensorData;
2834 shapeNode->GetConstTensor(axisTensorData);
2835
2836 // This splitDim indicates the data format: 3 is the NHWC, 1 is the NCHW.
2837 const unsigned int splitDim = static_cast<unsigned int>(axisTensorData[0]);
2838
2839 // Armnn supports split along the channel dimension for data formats NHWC and NCHW.
2840 if (splitDim == 0 || splitDim == 2)
2841 {
Saoirse Stewart91c0eff2019-02-27 11:07:57 +00002842 throw armnn::ParseException(
James Ward58dec6b2020-09-11 17:32:44 +01002843 fmt::format("Dimension {} for split is not supported by Armnn. "
2844 "Node {} {}",
2845 splitDim,
2846 nodeDef.name(),
2847 CHECK_LOCATION().AsString()));
Sadik Armagan2ad6cb42018-12-27 11:23:44 +00002848 }
2849
Saoirse Stewart315258e2019-02-28 11:32:41 +00002850 // As Armnn only supports splitter outputs of the same shape, therefore num_split will be limited to an integer.
2851 uint32_t num_split = ReadMandatoryNodeUint32Attribute(nodeDef, "num_split");
Sadik Armagan2ad6cb42018-12-27 11:23:44 +00002852
Saoirse Stewart91c0eff2019-02-27 11:07:57 +00002853 IOutputSlot& inputSlot = inputs[1 - index].m_IndexedValue->ResolveArmnnOutputSlot(inputs[1 - index].m_Index);
Sadik Armagan2ad6cb42018-12-27 11:23:44 +00002854 TensorInfo inputTensorInfo = inputSlot.GetTensorInfo();
2855
Matthew Jacksondba634f2019-08-15 15:14:18 +01002856 const unsigned int supportedNumDims = 4;
Saoirse Stewart91c0eff2019-02-27 11:07:57 +00002857 auto inputDimSize = inputTensorInfo.GetNumDimensions();
2858
Matthew Jacksondba634f2019-08-15 15:14:18 +01002859 if (inputDimSize != supportedNumDims)
Sadik Armagan2ad6cb42018-12-27 11:23:44 +00002860 {
2861 throw armnn::ParseException(
James Ward58dec6b2020-09-11 17:32:44 +01002862 fmt::format("The number of dimensions: {} for input tensors of the "
2863 "split op should be {} {}",
2864 inputTensorInfo.GetNumDimensions(),
2865 supportedNumDims,
2866 CHECK_LOCATION().AsString()));
Sadik Armagan2ad6cb42018-12-27 11:23:44 +00002867 }
Sadik Armagan2ad6cb42018-12-27 11:23:44 +00002868
2869 std::vector<unsigned int> splitterDimSizes(inputDimSize);
2870
2871 // Add current input shape to splitterDimSizes
2872 for (unsigned int i = 0; i < inputDimSize; ++i)
2873 {
2874 splitterDimSizes[i] = inputTensorInfo.GetShape()[i];
2875 }
2876
2877 if (splitterDimSizes[splitDim] % num_split != 0)
2878 {
2879 throw ParseException("Number of splits must evenly divide the dimension");
2880 }
2881 splitterDimSizes[splitDim] /= num_split;
2882
2883 SplitterDescriptor splitDesc(num_split);
2884 for (unsigned int g = 0; g < num_split; ++g)
2885 {
2886 // Set the size of the views.
2887 for (unsigned int dimIdx = 0; dimIdx < splitterDimSizes.size(); ++dimIdx)
2888 {
2889 splitDesc.SetViewSize(g, dimIdx, splitterDimSizes[dimIdx]);
2890 }
2891 splitDesc.SetViewOriginCoord(g, splitDim, splitterDimSizes[splitDim] * g);
2892 }
2893
2894 IConnectableLayer *layer = m_Network->AddSplitterLayer(splitDesc, nodeDef.name().c_str());
2895
2896 inputSlot.Connect(layer->GetInputSlot(0));
2897
2898 TensorShape outShape = TensorShape(static_cast<unsigned int>(splitterDimSizes.size()),
2899 splitterDimSizes.data());
2900
2901 for (unsigned int i = 0; i < layer->GetNumOutputSlots(); ++i)
2902 {
2903 layer->GetOutputSlot(i).SetTensorInfo(armnn::TensorInfo(outShape, inputTensorInfo.GetDataType()));
2904 }
2905
2906 return std::make_unique<SingleLayerParsedTfOperation>(this, nodeDef, layer);
2907}
2908
Kevin May7d96b162021-02-03 17:38:41 +00002909ParsedTfOperationPtr ITfParser::TfParserImpl::ParseSoftplus(const tensorflow::NodeDef& nodeDef,
surmeh01bceff2f2018-03-29 16:29:27 +01002910 const tensorflow::GraphDef& graphDef)
2911{
Jan Eilers8eb25602020-03-09 12:13:48 +00002912 IgnoreUnused(graphDef);
surmeh01bceff2f2018-03-29 16:29:27 +01002913
2914 ActivationDescriptor activationDesc;
2915 activationDesc.m_Function = ActivationFunction::SoftReLu;
2916
2917 return AddActivationLayer(nodeDef, activationDesc);
2918}
2919
Kevin May7d96b162021-02-03 17:38:41 +00002920ParsedTfOperationPtr ITfParser::TfParserImpl::ParseStridedSlice(const tensorflow::NodeDef& nodeDef,
2921 const tensorflow::GraphDef& graphDef)
Georgios Pinitas5e90aab2020-02-14 14:46:51 +00002922{
Jan Eilers8eb25602020-03-09 12:13:48 +00002923 IgnoreUnused(graphDef);
Georgios Pinitas5e90aab2020-02-14 14:46:51 +00002924
2925 std::vector<OutputOfConstNodeDef> nodes = GetTfInputNodes(nodeDef);
2926 unsigned int numInputs = static_cast<unsigned int>(nodes.size());
2927 std::vector<OutputOfParsedTfOperation> inputs = GetInputParsedTfOperationsChecked(nodeDef, numInputs);
2928
2929 ParsedConstTfOperation<int32_t>* beginNode =
Jan Eilersbb446e52020-04-02 13:56:54 +01002930 PolymorphicDowncast<ParsedConstTfOperation<int32_t> *>(inputs[1].m_IndexedValue);
Georgios Pinitas5e90aab2020-02-14 14:46:51 +00002931 std::vector<int32_t> beginTensorData;
2932 beginNode->GetConstTensor(beginTensorData);
2933
2934 ParsedConstTfOperation<int32_t>* endNode =
Jan Eilersbb446e52020-04-02 13:56:54 +01002935 PolymorphicDowncast<ParsedConstTfOperation<int32_t> *>(inputs[2].m_IndexedValue);
Georgios Pinitas5e90aab2020-02-14 14:46:51 +00002936 std::vector<int32_t> endTensorData;
2937 endNode->GetConstTensor(endTensorData);
2938
2939 ParsedConstTfOperation<int32_t>* stridesNode =
Jan Eilersbb446e52020-04-02 13:56:54 +01002940 PolymorphicDowncast<ParsedConstTfOperation<int32_t> *>(inputs[3].m_IndexedValue);
Georgios Pinitas5e90aab2020-02-14 14:46:51 +00002941 std::vector<int32_t> stridesTensorData;
2942 stridesNode->GetConstTensor(stridesTensorData);
2943
2944 StridedSliceDescriptor desc;
2945 desc.m_Begin = beginTensorData;
2946 desc.m_End = endTensorData;
2947 desc.m_Stride = stridesTensorData;
2948 desc.m_BeginMask = ReadMandatoryNodeInt32Attribute(nodeDef, "begin_mask");
2949 desc.m_EndMask = ReadMandatoryNodeInt32Attribute(nodeDef, "end_mask");
2950 desc.m_EllipsisMask = ReadMandatoryNodeInt32Attribute(nodeDef, "ellipsis_mask");
2951 desc.m_NewAxisMask = ReadMandatoryNodeInt32Attribute(nodeDef, "new_axis_mask");
2952 desc.m_ShrinkAxisMask = ReadMandatoryNodeInt32Attribute(nodeDef, "shrink_axis_mask");
2953 desc.m_DataLayout = armnn::DataLayout::NHWC;
2954 IConnectableLayer* const layer = m_Network->AddStridedSliceLayer(desc, nodeDef.name().c_str());
2955
2956 IOutputSlot& prevLayerSlot = inputs[0].m_IndexedValue->ResolveArmnnOutputSlot(inputs[0].m_Index);
2957 TensorInfo inputTensorInfo = prevLayerSlot.GetTensorInfo();
2958
2959 TensorInfo outputTensorInfo;
2960 CalculateStridedSliceOutputTensorInfo(inputTensorInfo, desc, outputTensorInfo);
2961
2962 prevLayerSlot.Connect(layer->GetInputSlot(0));
2963 layer->GetOutputSlot(0).SetTensorInfo(outputTensorInfo);
2964
2965 return std::make_unique<SingleLayerParsedTfOperation>(this, nodeDef, layer);
2966}
2967
Kevin May7d96b162021-02-03 17:38:41 +00002968ParsedTfOperationPtr ITfParser::TfParserImpl::ParseTanh(const tensorflow::NodeDef& nodeDef,
2969 const tensorflow::GraphDef& graphDef)
surmeh01bceff2f2018-03-29 16:29:27 +01002970{
Jan Eilers8eb25602020-03-09 12:13:48 +00002971 IgnoreUnused(graphDef);
surmeh01bceff2f2018-03-29 16:29:27 +01002972
2973 ActivationDescriptor activationDesc;
2974 activationDesc.m_Function = ActivationFunction::TanH;
2975 activationDesc.m_A = 1.0f;
2976 activationDesc.m_B = 1.0f;
2977
2978 return AddActivationLayer(nodeDef, activationDesc);
2979}
2980
Kevin May7d96b162021-02-03 17:38:41 +00002981ParsedTfOperationPtr ITfParser::TfParserImpl::AddActivationLayer(const tensorflow::NodeDef& nodeDef,
surmeh01bceff2f2018-03-29 16:29:27 +01002982 ActivationDescriptor& activationDesc)
2983{
2984 std::vector<OutputOfParsedTfOperation> inputs = GetInputParsedTfOperationsChecked(nodeDef, 1);
2985
2986 IConnectableLayer* const layer = m_Network->AddActivationLayer(activationDesc, nodeDef.name().c_str());
2987
2988 IOutputSlot& prevLayerOutputSlot = inputs[0].m_IndexedValue->ResolveArmnnOutputSlot(inputs[0].m_Index);
2989 prevLayerOutputSlot.Connect(layer->GetInputSlot(0));
2990 layer->GetOutputSlot(0).SetTensorInfo(prevLayerOutputSlot.GetTensorInfo());
2991 return std::make_unique<SingleLayerParsedTfOperation>(this, nodeDef, layer);
2992}
2993
Kevin May7d96b162021-02-03 17:38:41 +00002994ParsedTfOperationPtr ITfParser::TfParserImpl::ParseMaxPool(const tensorflow::NodeDef& nodeDef,
surmeh01bceff2f2018-03-29 16:29:27 +01002995 const tensorflow::GraphDef& graphDef)
2996{
2997 return ParsePooling2d(nodeDef, graphDef, PoolingAlgorithm::Max);
2998}
2999
Kevin May7d96b162021-02-03 17:38:41 +00003000ParsedTfOperationPtr ITfParser::TfParserImpl::ParseAvgPool(const tensorflow::NodeDef& nodeDef,
surmeh01bceff2f2018-03-29 16:29:27 +01003001 const tensorflow::GraphDef& graphDef)
3002{
3003 return ParsePooling2d(nodeDef, graphDef, PoolingAlgorithm::Average);
3004}
3005
Kevin May7d96b162021-02-03 17:38:41 +00003006ParsedTfOperationPtr ITfParser::TfParserImpl::ParsePooling2d(const tensorflow::NodeDef& nodeDef,
surmeh01bceff2f2018-03-29 16:29:27 +01003007 const tensorflow::GraphDef& graphDef, PoolingAlgorithm pooltype)
3008{
Jan Eilers8eb25602020-03-09 12:13:48 +00003009 IgnoreUnused(graphDef);
Derek Lambertibaa177f2019-12-10 22:00:43 +00003010
surmeh01bceff2f2018-03-29 16:29:27 +01003011 std::vector<OutputOfParsedTfOperation> inputs = GetInputParsedTfOperationsChecked(nodeDef, 1);
3012 IOutputSlot& inputSlot = inputs[0].m_IndexedValue->ResolveArmnnOutputSlot(inputs[0].m_Index);
3013 TensorInfo inputTensorInfo = inputSlot.GetTensorInfo();
3014
3015 if (inputs.size() != 1)
3016 {
telsoa01c577f2c2018-08-31 09:22:23 +01003017 throw ParseException(
James Ward58dec6b2020-09-11 17:32:44 +01003018 fmt::format("2D Pooling expects one input!. Got {} for Node {} {}",
3019 inputs.size(),
3020 nodeDef.name(),
3021 CHECK_LOCATION().AsString()));
surmeh01bceff2f2018-03-29 16:29:27 +01003022 }
3023
3024 std::string paddingString = ReadMandatoryNodeStringAttribute(nodeDef, "padding");
3025 std::string dataFormat = ReadMandatoryNodeStringAttribute(nodeDef, "data_format");
3026 std::vector<uint32_t> strides = ReadMandatoryNodeUint32ListAttribute(nodeDef, "strides");
3027 std::vector<uint32_t> ksize = ReadMandatoryNodeUint32ListAttribute(nodeDef, "ksize"); // size of pool windows
3028
3029 Pooling2dDescriptor pooling2dDescriptor;
FrancisMurtaghf005e312018-12-06 15:26:04 +00003030 pooling2dDescriptor.m_PoolType = pooltype;
3031 pooling2dDescriptor.m_PaddingMethod = PaddingMethod::Exclude;
surmeh01bceff2f2018-03-29 16:29:27 +01003032 pooling2dDescriptor.m_OutputShapeRounding = OutputShapeRounding::Floor;
3033
telsoa01c577f2c2018-08-31 09:22:23 +01003034 CHECK_DATA_FORMAT(nodeDef, dataFormat, "Pooling2D");
FrancisMurtaghf005e312018-12-06 15:26:04 +00003035 DataLayout dataLayout = dataFormat == "NHWC" ? DataLayout::NHWC : DataLayout::NCHW;
3036 pooling2dDescriptor.m_DataLayout = dataLayout;
3037 DataLayoutIndexed dataLayoutIndexed(dataLayout);
telsoa01c577f2c2018-08-31 09:22:23 +01003038
FrancisMurtaghf005e312018-12-06 15:26:04 +00003039 pooling2dDescriptor.m_StrideX = strides[dataLayoutIndexed.GetWidthIndex()];
3040 pooling2dDescriptor.m_StrideY = strides[dataLayoutIndexed.GetHeightIndex()];
3041 pooling2dDescriptor.m_PoolWidth = ksize[dataLayoutIndexed.GetWidthIndex()];
3042 pooling2dDescriptor.m_PoolHeight = ksize[dataLayoutIndexed.GetHeightIndex()];
surmeh01bceff2f2018-03-29 16:29:27 +01003043
FrancisMurtaghf005e312018-12-06 15:26:04 +00003044 uint32_t inputHeight = inputTensorInfo.GetShape()[dataLayoutIndexed.GetHeightIndex()];
3045 uint32_t inputWidth = inputTensorInfo.GetShape()[dataLayoutIndexed.GetWidthIndex()];
surmeh01bceff2f2018-03-29 16:29:27 +01003046
3047 bool padding = false;
3048 TensorInfo outputInfo;
FrancisMurtaghf005e312018-12-06 15:26:04 +00003049 unsigned int outputHeight = 0;
3050 unsigned int outputWidth = 0;
telsoa01c577f2c2018-08-31 09:22:23 +01003051
3052 CHECK_PADDING_TYPE(nodeDef, paddingString);
3053
surmeh01bceff2f2018-03-29 16:29:27 +01003054 if (paddingString == "SAME")
3055 {
3056 padding = true;
FrancisMurtaghf005e312018-12-06 15:26:04 +00003057
3058 outputHeight = static_cast<uint32_t>(ceil(static_cast<float>(inputHeight) /
3059 static_cast<float>(pooling2dDescriptor.m_StrideY)));
3060 outputWidth = static_cast<uint32_t>(ceil(static_cast<float>(inputWidth) /
3061 static_cast<float>(pooling2dDescriptor.m_StrideX)));
surmeh01bceff2f2018-03-29 16:29:27 +01003062 }
3063 else if (paddingString == "VALID")
3064 {
3065 padding = false;
FrancisMurtaghf005e312018-12-06 15:26:04 +00003066
3067 outputHeight = static_cast<uint32_t>(ceil(
3068 static_cast<float>(inputHeight - pooling2dDescriptor.m_PoolHeight + 1) /
3069 static_cast<float>(pooling2dDescriptor.m_StrideY)));
3070 outputWidth = static_cast<uint32_t>(ceil(
3071 static_cast<float>(inputWidth - pooling2dDescriptor.m_PoolWidth + 1) /
3072 static_cast<float>(pooling2dDescriptor.m_StrideX)));
3073 }
3074
3075 switch (dataLayout)
3076 {
3077 case DataLayout::NHWC:
3078 outputInfo = TensorInfo({ inputTensorInfo.GetShape()[0],
3079 outputHeight,
3080 outputWidth,
3081 inputTensorInfo.GetShape()[3] },
3082 DataType::Float32);
3083 break;
3084 case DataLayout::NCHW:
3085 outputInfo = TensorInfo({ inputTensorInfo.GetShape()[0],
3086 inputTensorInfo.GetShape()[1],
3087 outputHeight,
3088 outputWidth },
3089 DataType::Float32);
3090 break;
surmeh01bceff2f2018-03-29 16:29:27 +01003091 }
surmeh01bceff2f2018-03-29 16:29:27 +01003092
Sadik Armagan60bb9d82021-01-11 15:15:01 +00003093 CalcPadding(inputWidth, pooling2dDescriptor.m_PoolWidth, pooling2dDescriptor.m_StrideX, 1u,
FrancisMurtaghf005e312018-12-06 15:26:04 +00003094 pooling2dDescriptor.m_PadLeft, pooling2dDescriptor.m_PadRight, padding);
Sadik Armagan60bb9d82021-01-11 15:15:01 +00003095 CalcPadding(inputHeight, pooling2dDescriptor.m_PoolHeight, pooling2dDescriptor.m_StrideY, 1u,
FrancisMurtaghf005e312018-12-06 15:26:04 +00003096 pooling2dDescriptor.m_PadTop, pooling2dDescriptor.m_PadBottom, padding);
surmeh01bceff2f2018-03-29 16:29:27 +01003097
3098
3099 IConnectableLayer* layer = m_Network->AddPooling2dLayer(pooling2dDescriptor, nodeDef.name().c_str());
3100 if (layer == nullptr)
3101 {
telsoa01c577f2c2018-08-31 09:22:23 +01003102 throw ParseException(
James Ward58dec6b2020-09-11 17:32:44 +01003103 fmt::format("Failed to add pooling2d layer for {} {}",
3104 nodeDef.name(),
3105 CHECK_LOCATION().AsString()));
surmeh01bceff2f2018-03-29 16:29:27 +01003106 }
3107
3108 layer->GetOutputSlot(0).SetTensorInfo(outputInfo);
3109
FrancisMurtaghf005e312018-12-06 15:26:04 +00003110 inputSlot.Connect(layer->GetInputSlot(0));
surmeh01bceff2f2018-03-29 16:29:27 +01003111
3112 return std::make_unique<SingleLayerParsedTfOperation>(this, nodeDef, layer);
3113}
3114
Kevin May7d96b162021-02-03 17:38:41 +00003115ParsedTfOperationPtr ITfParser::TfParserImpl::AddAdditionLayer(const tensorflow::NodeDef& nodeDef, bool isBiasAdd)
surmeh01bceff2f2018-03-29 16:29:27 +01003116{
3117 std::vector<OutputOfParsedTfOperation> inputs = GetInputParsedTfOperationsChecked(nodeDef, 2);
3118
3119 IOutputSlot* input0Slot = &inputs[0].m_IndexedValue->ResolveArmnnOutputSlot(inputs[0].m_Index);
3120 IOutputSlot* input1Slot = &inputs[1].m_IndexedValue->ResolveArmnnOutputSlot(inputs[1].m_Index);
3121
3122 const TensorInfo& input0Info = input0Slot->GetTensorInfo();
3123 const TensorInfo& input1Info = input1Slot->GetTensorInfo();
3124
3125 if (isBiasAdd)
3126 {
3127 // BiasAdd takes bias as a 1D tensor. We need to add a reshape layer to create a 4D tensor
3128 // with the same data in the correct dimension for broadcast in addition.
3129 if(input1Info.GetNumDimensions() != 1)
3130 {
telsoa01c577f2c2018-08-31 09:22:23 +01003131 throw ParseException(
James Ward58dec6b2020-09-11 17:32:44 +01003132 fmt::format("Unsupported bias for BiasAdd. It should be a 1D vector. "
3133 "Got {} dimensions for input {}. Node {} {}",
3134 input1Info.GetNumDimensions(),
3135 inputs[1].m_IndexedValue->GetNode().name(),
3136 nodeDef.name(),
3137 CHECK_LOCATION().AsString()));
surmeh01bceff2f2018-03-29 16:29:27 +01003138 }
3139
3140 const std::string dataFormat = ReadMandatoryNodeStringAttribute(nodeDef, "data_format");
surmeh01bceff2f2018-03-29 16:29:27 +01003141
telsoa01c577f2c2018-08-31 09:22:23 +01003142 CHECK_DATA_FORMAT(nodeDef, dataFormat, "BiasAdd");
saoste01bbd40612018-08-28 15:41:51 +01003143 input1Slot = AddBroadcastReshapeLayer(input0Slot, input1Slot, dataFormat == "NHWC", *m_Network, nodeDef);
surmeh01bceff2f2018-03-29 16:29:27 +01003144 }
3145 else
3146 {
3147 if (input0Info.GetNumDimensions() == 1)
3148 {
3149 const bool isNHWC = true;
saoste01bbd40612018-08-28 15:41:51 +01003150 input0Slot = AddBroadcastReshapeLayer(input1Slot, input0Slot, isNHWC, *m_Network, nodeDef);
surmeh01bceff2f2018-03-29 16:29:27 +01003151 }
3152
3153 if (input1Info.GetNumDimensions() == 1)
3154 {
3155 const bool isNHWC = true;
saoste01bbd40612018-08-28 15:41:51 +01003156 input1Slot = AddBroadcastReshapeLayer(input0Slot, input1Slot, isNHWC, *m_Network, nodeDef);
surmeh01bceff2f2018-03-29 16:29:27 +01003157 }
3158 }
3159
3160 IConnectableLayer* const layer = m_Network->AddAdditionLayer(nodeDef.name().c_str());
3161
3162 input0Slot->Connect(layer->GetInputSlot(0));
3163 input1Slot->Connect(layer->GetInputSlot(1));
3164
Nattapat Chaimanowongfab64f02019-02-15 16:46:24 +00003165 if (input0Info.GetNumDimensions() == input1Info.GetNumDimensions())
3166 {
3167 const TensorShape& input0Shape = input0Info.GetShape();
3168 const TensorShape& input1Shape = input1Info.GetShape();
3169
3170 std::vector<unsigned int> outputShape;
3171 outputShape.reserve(input0Shape.GetNumDimensions());
3172 TensorInfo outputInfo(input0Info);
3173
3174 for (unsigned int i = 0; i < input0Shape.GetNumDimensions(); i++)
3175 {
3176 outputShape.push_back(std::max(input0Shape[i], input1Shape[i]));
3177 }
3178
3179 outputInfo.SetShape(TensorShape(input0Shape.GetNumDimensions(), outputShape.data()));
3180
3181 layer->GetOutputSlot(0).SetTensorInfo(outputInfo);
3182 }
3183 else if (input0Info.GetNumDimensions() == 1 && isBiasAdd == false)
surmeh01bceff2f2018-03-29 16:29:27 +01003184 {
3185 layer->GetOutputSlot(0).SetTensorInfo(input1Slot->GetTensorInfo());
3186 }
3187 else
3188 {
3189 layer->GetOutputSlot(0).SetTensorInfo(input0Slot->GetTensorInfo());
3190 }
3191
3192 return std::make_unique<SingleLayerParsedTfOperation>(this, nodeDef, layer);
3193}
3194
Kevin May7d96b162021-02-03 17:38:41 +00003195ParsedTfOperationPtr ITfParser::TfParserImpl::AddRealDivLayer(const tensorflow::NodeDef& nodeDef)
saoste01bbd40612018-08-28 15:41:51 +01003196{
3197 std::vector<OutputOfParsedTfOperation> inputs = GetInputParsedTfOperationsChecked(nodeDef, 2);
3198
3199 IConnectableLayer* const layer = m_Network->AddDivisionLayer(nodeDef.name().c_str());
3200 IOutputSlot* input0Slot = &inputs[0].m_IndexedValue->ResolveArmnnOutputSlot(inputs[0].m_Index);
3201 IOutputSlot* input1Slot = &inputs[1].m_IndexedValue->ResolveArmnnOutputSlot(inputs[1].m_Index);
3202
3203 auto const input0NumDims = input0Slot->GetTensorInfo().GetNumDimensions();
3204 auto const input1NumDims = input1Slot->GetTensorInfo().GetNumDimensions();
3205
3206
3207 if (input0NumDims < input1NumDims)
3208 {
3209 const bool isNHWC = true;
3210 input0Slot = AddBroadcastReshapeLayer(input1Slot, input0Slot, isNHWC, *m_Network, nodeDef);
3211 }
3212 if (input1NumDims < input0NumDims)
3213 {
3214 const bool isNHWC = true;
3215 input1Slot = AddBroadcastReshapeLayer(input0Slot, input1Slot, isNHWC, *m_Network, nodeDef);
3216 }
3217
3218 input0Slot->Connect(layer->GetInputSlot(0));
3219 input1Slot->Connect(layer->GetInputSlot(1));
3220
3221 if (input0NumDims < input1NumDims)
3222 {
3223 layer->GetOutputSlot(0).SetTensorInfo(input1Slot->GetTensorInfo());
3224 }
3225 else
3226 {
3227 layer->GetOutputSlot(0).SetTensorInfo(input0Slot->GetTensorInfo());
3228
3229 }
3230 return std::make_unique<SingleLayerParsedTfOperation>(this, nodeDef, layer);
3231}
3232
Kevin May7d96b162021-02-03 17:38:41 +00003233ParsedTfOperationPtr ITfParser::TfParserImpl::AddMaximumLayer(const tensorflow::NodeDef& nodeDef)
Sadik Armagan975c09a2018-12-04 10:02:08 +00003234{
3235 std::vector<OutputOfParsedTfOperation> inputs = GetInputParsedTfOperationsChecked(nodeDef, 2);
3236
3237 IOutputSlot* input0Slot = &inputs[0].m_IndexedValue->ResolveArmnnOutputSlot(inputs[0].m_Index);
3238 IOutputSlot* input1Slot = &inputs[1].m_IndexedValue->ResolveArmnnOutputSlot(inputs[1].m_Index);
3239
3240 auto const input0NumDims = input0Slot->GetTensorInfo().GetNumDimensions();
3241 auto const input1NumDims = input1Slot->GetTensorInfo().GetNumDimensions();
3242
3243 if (input0NumDims < input1NumDims)
3244 {
3245 const bool isNHWC = true;
3246 input0Slot = AddBroadcastReshapeLayer(input1Slot, input0Slot, isNHWC, *m_Network, nodeDef);
3247 }
3248 if (input1NumDims < input0NumDims)
3249 {
3250 const bool isNHWC = true;
3251 input1Slot = AddBroadcastReshapeLayer(input0Slot, input1Slot, isNHWC, *m_Network, nodeDef);
3252 }
3253
3254 IConnectableLayer* const layer = m_Network->AddMaximumLayer(nodeDef.name().c_str());
3255
3256 input0Slot->Connect(layer->GetInputSlot(0));
3257 input1Slot->Connect(layer->GetInputSlot(1));
3258
3259 TensorInfo outputInfo = input0Slot->GetTensorInfo();
3260 std::vector<unsigned int> outputShape;
3261
3262 const TensorShape& input0Shape = input0Slot->GetTensorInfo().GetShape();
3263 const TensorShape& input1Shape = input1Slot->GetTensorInfo().GetShape();
3264
3265 for (unsigned int i = 0; i < input0Shape.GetNumDimensions(); i++)
3266 {
3267 outputShape.push_back(std::max(input0Shape[i], input1Shape[i]));
3268 }
3269
3270 outputInfo.SetShape(TensorShape(input0Shape.GetNumDimensions(), outputShape.data()));
3271 layer->GetOutputSlot(0).SetTensorInfo(outputInfo);
3272
3273 return std::make_unique<SingleLayerParsedTfOperation>(this, nodeDef, layer);
3274}
3275
Kevin May7d96b162021-02-03 17:38:41 +00003276IConnectableLayer* ITfParser::TfParserImpl::AddMultiplicationLayer(const tensorflow::NodeDef& nodeDef)
telsoa01c577f2c2018-08-31 09:22:23 +01003277{
3278 std::vector<OutputOfParsedTfOperation> inputs = GetInputParsedTfOperationsChecked(nodeDef, 2);
3279
3280 IConnectableLayer* const layer = m_Network->AddMultiplicationLayer(nodeDef.name().c_str());
3281 IOutputSlot* input0Slot = &inputs[0].m_IndexedValue->ResolveArmnnOutputSlot(inputs[0].m_Index);
3282 IOutputSlot* input1Slot = &inputs[1].m_IndexedValue->ResolveArmnnOutputSlot(inputs[1].m_Index);
3283
3284 auto const input0NumDims = input0Slot->GetTensorInfo().GetNumDimensions();
3285 auto const input1NumDims = input1Slot->GetTensorInfo().GetNumDimensions();
3286
3287 if (input0NumDims < input1NumDims)
3288 {
3289 const bool isNHWC = true;
saoste01bbd40612018-08-28 15:41:51 +01003290 input0Slot = AddBroadcastReshapeLayer(input1Slot, input0Slot, isNHWC, *m_Network, nodeDef);
telsoa01c577f2c2018-08-31 09:22:23 +01003291 }
3292 if (input1NumDims < input0NumDims)
3293 {
3294 const bool isNHWC = true;
saoste01bbd40612018-08-28 15:41:51 +01003295 input1Slot = AddBroadcastReshapeLayer(input0Slot, input1Slot, isNHWC, *m_Network, nodeDef);
telsoa01c577f2c2018-08-31 09:22:23 +01003296 }
3297
3298 input0Slot->Connect(layer->GetInputSlot(0));
3299 input1Slot->Connect(layer->GetInputSlot(1));
3300
3301 if (input0NumDims < input1NumDims)
3302 {
3303 layer->GetOutputSlot(0).SetTensorInfo(input1Slot->GetTensorInfo());
3304 }
3305 else
3306 {
3307 layer->GetOutputSlot(0).SetTensorInfo(input0Slot->GetTensorInfo());
3308 }
3309 return layer;
3310}
3311
Kevin May7d96b162021-02-03 17:38:41 +00003312IConnectableLayer* ITfParser::TfParserImpl::AddFullyConnectedLayer(const tensorflow::NodeDef& matMulNodeDef,
surmeh01bceff2f2018-03-29 16:29:27 +01003313 const tensorflow::NodeDef* addNodeDef, const char* armnnLayerName)
3314{
telsoa01c577f2c2018-08-31 09:22:23 +01003315 // Finds bias const (if applicable).
surmeh01bceff2f2018-03-29 16:29:27 +01003316 ParsedConstTfOperation<float>* biasNode = nullptr;
3317 if (addNodeDef != nullptr)
3318 {
3319 std::vector<OutputOfParsedTfOperation> addInputs = GetInputParsedTfOperationsChecked(*addNodeDef, 2);
telsoa01c577f2c2018-08-31 09:22:23 +01003320 // Finds our inputs.
surmeh01bceff2f2018-03-29 16:29:27 +01003321 if (HasParsedConstTensor<float>(addInputs[0].m_IndexedValue->GetNode().name()))
3322 {
Jan Eilersbb446e52020-04-02 13:56:54 +01003323 biasNode = PolymorphicDowncast<ParsedConstTfOperation<float>*>(addInputs[0].m_IndexedValue);
surmeh01bceff2f2018-03-29 16:29:27 +01003324 }
3325 else if (HasParsedConstTensor<float>(addInputs[1].m_IndexedValue->GetNode().name()))
3326 {
Jan Eilersbb446e52020-04-02 13:56:54 +01003327 biasNode = PolymorphicDowncast<ParsedConstTfOperation<float>*>(addInputs[1].m_IndexedValue);
surmeh01bceff2f2018-03-29 16:29:27 +01003328 }
3329 else
3330 {
telsoa01c577f2c2018-08-31 09:22:23 +01003331 throw ParseException(
James Ward58dec6b2020-09-11 17:32:44 +01003332 fmt::format("ArmNN only supports fully connected layers with constant bias. "
3333 "Inputs {} and {}. AddNode {}. MatMulNode {} {}",
3334 addInputs[0].m_IndexedValue->GetNode().name(),
3335 addInputs[1].m_IndexedValue->GetNode().name(),
3336 addNodeDef->name(),
3337 matMulNodeDef.name(),
3338 CHECK_LOCATION().AsString()));
surmeh01bceff2f2018-03-29 16:29:27 +01003339 }
3340 }
3341
telsoa01c577f2c2018-08-31 09:22:23 +01003342 // Finds matmul inputs.
surmeh01bceff2f2018-03-29 16:29:27 +01003343 ParsedConstTfOperation<float>* weightNode = nullptr;
3344 ParsedTfOperation* inputNode = nullptr;
3345 unsigned int inputIdx = 0;
3346 std::vector<OutputOfParsedTfOperation> mulInputs = GetInputParsedTfOperationsChecked(matMulNodeDef, 2);
3347 if (HasParsedConstTensor<float>(mulInputs[0].m_IndexedValue->GetNode().name()))
3348 {
Jan Eilersbb446e52020-04-02 13:56:54 +01003349 weightNode = PolymorphicDowncast<ParsedConstTfOperation<float>*>(mulInputs[0].m_IndexedValue);
surmeh01bceff2f2018-03-29 16:29:27 +01003350 inputNode = mulInputs[1].m_IndexedValue;
3351 inputIdx = mulInputs[1].m_Index;
3352 }
3353 else if (HasParsedConstTensor<float>(mulInputs[1].m_IndexedValue->GetNode().name()))
3354 {
Jan Eilersbb446e52020-04-02 13:56:54 +01003355 weightNode = PolymorphicDowncast<ParsedConstTfOperation<float>*>(mulInputs[1].m_IndexedValue);
surmeh01bceff2f2018-03-29 16:29:27 +01003356 inputNode = mulInputs[0].m_IndexedValue;
3357 inputIdx = mulInputs[0].m_Index;
3358 }
3359 else
3360 {
telsoa01c577f2c2018-08-31 09:22:23 +01003361 throw ParseException(
James Ward58dec6b2020-09-11 17:32:44 +01003362 fmt::format("ArmNN only supports fully connected layers with constant weights. "
3363 "Inputs {} and {}. MatMulNode {} {}",
3364 mulInputs[0].m_IndexedValue->GetNode().name(),
3365 mulInputs[1].m_IndexedValue->GetNode().name(),
3366 matMulNodeDef.name(),
3367 CHECK_LOCATION().AsString()));
surmeh01bceff2f2018-03-29 16:29:27 +01003368 }
3369
3370 std::vector<float> weightTensorData;
telsoa01c577f2c2018-08-31 09:22:23 +01003371 // Handles weight.
Matteo Martincigh482ca852018-12-12 09:20:55 +00003372 ConstTensor weights = weightNode->GetConstTensor(weightTensorData);
surmeh01bceff2f2018-03-29 16:29:27 +01003373
3374 FullyConnectedDescriptor desc;
3375 desc.m_BiasEnabled = addNodeDef != nullptr;
3376
3377 IConnectableLayer* layer = nullptr;
Matteo Martincighfc598e12019-05-14 10:36:13 +01003378 Optional<ConstTensor> optionalBiases;
3379 std::vector<float> biasTensorData;
telsoa01c577f2c2018-08-31 09:22:23 +01003380 // Makes the layer.
surmeh01bceff2f2018-03-29 16:29:27 +01003381 if (addNodeDef != nullptr)
3382 {
Matteo Martincigh482ca852018-12-12 09:20:55 +00003383 ConstTensor biases = biasNode->GetConstTensor(biasTensorData);
surmeh01bceff2f2018-03-29 16:29:27 +01003384
3385 if (weights.GetShape()[1] != biases.GetShape()[0])
3386 {
telsoa01c577f2c2018-08-31 09:22:23 +01003387 throw ParseException(
James Ward58dec6b2020-09-11 17:32:44 +01003388 fmt::format("Shape of matmul weights and bias do not match. "
3389 "AddNode {}. MatMulNode {} {}",
3390 addNodeDef->name(),
3391 matMulNodeDef.name(),
3392 CHECK_LOCATION().AsString()));
surmeh01bceff2f2018-03-29 16:29:27 +01003393 }
3394
Matteo Martincighfc598e12019-05-14 10:36:13 +01003395 optionalBiases = Optional<ConstTensor>(biases);
surmeh01bceff2f2018-03-29 16:29:27 +01003396 }
Matteo Martincighfc598e12019-05-14 10:36:13 +01003397 layer = m_Network->AddFullyConnectedLayer(desc, weights, optionalBiases, armnnLayerName);
surmeh01bceff2f2018-03-29 16:29:27 +01003398
Narumol Prangnawaratac2770a2020-04-01 16:51:23 +01003399 ARMNN_ASSERT(layer != nullptr);
surmeh01bceff2f2018-03-29 16:29:27 +01003400
3401 inputNode->ResolveArmnnOutputSlot(inputIdx).Connect(layer->GetInputSlot(0));
3402 unsigned int batches = inputNode->ResolveArmnnOutputSlot(inputIdx).GetTensorInfo().GetShape()[0];
3403
telsoa01c577f2c2018-08-31 09:22:23 +01003404 // Handles output.
surmeh01bceff2f2018-03-29 16:29:27 +01003405 TensorInfo outputInfo({ batches, weights.GetShape()[1] }, DataType::Float32);
3406 layer->GetOutputSlot(0).SetTensorInfo(outputInfo);
3407 return layer;
3408}
3409
Kevin May7d96b162021-02-03 17:38:41 +00003410void ITfParser::TfParserImpl::LoadNodeDef(const tensorflow::NodeDef& nodeDef, const tensorflow::GraphDef& graphDef)
surmeh01bceff2f2018-03-29 16:29:27 +01003411{
telsoa01c577f2c2018-08-31 09:22:23 +01003412 // Gets the type of the node (assume float).
surmeh01bceff2f2018-03-29 16:29:27 +01003413 tensorflow::DataType type = tensorflow::DT_FLOAT;
3414 if (nodeDef.attr().count("T") != 0)
3415 {
3416 auto attr = nodeDef.attr().at("T");
3417 type = attr.type();
3418 }
3419 else if (nodeDef.attr().count("dtype") != 0)
3420 {
3421 auto attr = nodeDef.attr().at("dtype");
3422 type = attr.type();
3423 }
3424
Ferran Balaguerc602f292019-02-08 17:09:55 +00003425 if ((type != tensorflow::DT_FLOAT && type != tensorflow::DT_INT32) && nodeDef.op() != "Const")
surmeh01bceff2f2018-03-29 16:29:27 +01003426 {
telsoa01c577f2c2018-08-31 09:22:23 +01003427 throw ParseException(
James Ward58dec6b2020-09-11 17:32:44 +01003428 fmt::format("Currently only FLOAT and INT32 are supported for tensorflow nodes (apart from Const). "
3429 "Got {} for Node {} {}",
3430 tensorflow::DataType_Name(type),
3431 nodeDef.name(),
3432 CHECK_LOCATION().AsString()));
surmeh01bceff2f2018-03-29 16:29:27 +01003433 }
3434
3435 const std::string& operation = nodeDef.op();
narpra016f37f832018-12-21 18:30:00 +00003436 auto itControlInput = std::find(m_ControlInputs.begin(), m_ControlInputs.end(), operation);
3437 if (itControlInput != m_ControlInputs.end())
3438 {
3439 // We currently allow Control Input from TensorFlow graph but we ignore them from ArmNN graph.
3440 return;
3441 }
surmeh01bceff2f2018-03-29 16:29:27 +01003442 auto it = ms_OperationNameToParsingFunctions.find(operation);
3443 if (it != ms_OperationNameToParsingFunctions.end())
3444 {
3445 auto func = it->second;
3446 ParsedTfOperationPtr parsedTfOperation = (this->*func)(nodeDef, graphDef);
3447 ParsedTfOperation* parsedTfOperationRaw = parsedTfOperation.get();
3448
telsoa01c577f2c2018-08-31 09:22:23 +01003449 // Stores the parsed operation so that dependent layers can connect to it.
surmeh01bceff2f2018-03-29 16:29:27 +01003450 auto it = m_ParsedTfOperations.find(nodeDef.name());
3451 if (it != m_ParsedTfOperations.end())
3452 {
James Ward58dec6b2020-09-11 17:32:44 +01003453 throw ParseException(fmt::format("Name {} used by more than one node", nodeDef.name()));
surmeh01bceff2f2018-03-29 16:29:27 +01003454 }
3455 m_ParsedTfOperations[nodeDef.name()] = std::move(parsedTfOperation);
3456
telsoa01c577f2c2018-08-31 09:22:23 +01003457 // If this node was requested as an output from the network, then adds an ArmNN output layer.
surmeh01bceff2f2018-03-29 16:29:27 +01003458 if (std::find(m_RequestedOutputs.begin(), m_RequestedOutputs.end(), nodeDef.name()) !=
3459 m_RequestedOutputs.end())
3460 {
3461 auto outId = ParseOutputId(nodeDef.name());
Matthew Sloyan589e3e82020-09-11 16:17:48 +01003462 const LayerBindingId layerId = armnn::numeric_cast<LayerBindingId>(m_NetworkOutputsBindingInfo.size());
surmeh01bceff2f2018-03-29 16:29:27 +01003463 IOutputSlot& prevSlot = parsedTfOperationRaw->ResolveArmnnOutputSlot(outId.m_Index);
3464
3465 TensorInfo tensorInfo = prevSlot.GetTensorInfo();
3466
3467 IConnectableLayer* outputLayer = m_Network->AddOutputLayer(layerId, nodeDef.name().c_str());
3468
3469 prevSlot.Connect(outputLayer->GetInputSlot(0));
3470
3471 TrackOutputBinding(outputLayer, layerId, tensorInfo);
3472 }
3473 }
3474 else
3475 {
telsoa01c577f2c2018-08-31 09:22:23 +01003476 throw ParseException(
James Ward58dec6b2020-09-11 17:32:44 +01003477 fmt::format("Unsupported operation {} in tensorflow::GraphDef {}",
3478 operation,
3479 CHECK_LOCATION().AsString()));
surmeh01bceff2f2018-03-29 16:29:27 +01003480 }
3481}
3482
Kevin May7d96b162021-02-03 17:38:41 +00003483void ITfParser::TfParserImpl::LoadGraphDef(const tensorflow::GraphDef& graphDef)
surmeh01bceff2f2018-03-29 16:29:27 +01003484{
telsoa01c577f2c2018-08-31 09:22:23 +01003485 // Adds all nodes to our map.
surmeh01bceff2f2018-03-29 16:29:27 +01003486 m_NodesByName.clear();
3487 m_NetworkInputsBindingInfo.clear();
3488 m_NetworkOutputsBindingInfo.clear();
3489
3490 for (int i = 0; i < graphDef.node_size(); ++i)
3491 {
3492 const tensorflow::NodeDef& node = graphDef.node(i);
3493 m_NodesByName[node.name()] = &node;
3494 }
3495
Francis Murtaghbb190a62019-04-04 11:16:29 +01003496 // Checks that the input nodes the user has requested exist.
3497 for (const auto& pair : m_InputShapes)
3498 {
3499 const std::string& requestedInputName = pair.first;
3500 auto nodeIt = m_NodesByName.find(requestedInputName);
3501 if (nodeIt == m_NodesByName.end())
3502 {
3503 throw ParseException(
James Ward58dec6b2020-09-11 17:32:44 +01003504 fmt::format("Couldn't find requested input node '{}' in graph {}",
3505 requestedInputName,
3506 CHECK_LOCATION().AsString()));
Francis Murtaghbb190a62019-04-04 11:16:29 +01003507 }
3508 }
3509
telsoa01c577f2c2018-08-31 09:22:23 +01003510 // Finds the output nodes the user requested.
surmeh01bceff2f2018-03-29 16:29:27 +01003511 std::vector<const tensorflow::NodeDef*> targetNodes;
3512 for (const std::string& requestedOutputName : m_RequestedOutputs)
3513 {
3514 auto nodeIt = m_NodesByName.find(requestedOutputName);
3515 if (nodeIt == m_NodesByName.end())
3516 {
telsoa01c577f2c2018-08-31 09:22:23 +01003517 throw ParseException(
James Ward58dec6b2020-09-11 17:32:44 +01003518 fmt::format("Couldn't find requested output node '{}' in graph {}",
3519 requestedOutputName,
3520 CHECK_LOCATION().AsString()));
surmeh01bceff2f2018-03-29 16:29:27 +01003521 }
3522 targetNodes.push_back(nodeIt->second);
3523 }
3524
telsoa01c577f2c2018-08-31 09:22:23 +01003525 // Sorts them into a linear ordering such that all inputs of a node are before the node itself.
surmeh01bceff2f2018-03-29 16:29:27 +01003526 std::vector<const tensorflow::NodeDef*> sortedNodes;
3527 if (!armnnUtils::GraphTopologicalSort<const tensorflow::NodeDef*>(
3528 targetNodes,
3529 [this](const tensorflow::NodeDef* node)
3530 {
3531 auto outputs = GetTfInputNodes(*node);
3532 std::vector<const tensorflow::NodeDef*> nodesOnly;
3533 for (const auto & o : outputs) {
3534 nodesOnly.push_back(o.m_IndexedValue);
3535 }
3536 return nodesOnly;
3537 },
3538 sortedNodes))
3539 {
telsoa01c577f2c2018-08-31 09:22:23 +01003540 throw ParseException(
James Ward58dec6b2020-09-11 17:32:44 +01003541 fmt::format("Cycle detected in graph {}",
3542 CHECK_LOCATION().AsString()));
surmeh01bceff2f2018-03-29 16:29:27 +01003543 }
3544
telsoa01c577f2c2018-08-31 09:22:23 +01003545 // Parses each node in order, knowing that all inputs of a node will be processed before the node itself.
surmeh01bceff2f2018-03-29 16:29:27 +01003546 for (const auto& it : sortedNodes)
3547 {
3548 const tensorflow::NodeDef& currentNode = *it;
3549 LoadNodeDef(currentNode, graphDef);
3550 }
3551}
3552
Kevin May7d96b162021-02-03 17:38:41 +00003553INetworkPtr ITfParser::TfParserImpl::CreateNetworkFromTextFile(const char* graphFile,
surmeh01bceff2f2018-03-29 16:29:27 +01003554 const std::map<std::string, TensorShape>& inputShapes,
3555 const std::vector<std::string>& requestedOutputs)
3556{
3557 FILE* fd = fopen(graphFile, "r");
3558
3559 if (fd == nullptr)
3560 {
telsoa01c577f2c2018-08-31 09:22:23 +01003561 throw FileNotFoundException(
James Ward58dec6b2020-09-11 17:32:44 +01003562 fmt::format("Graph file {} failed to open {}",
3563 graphFile,
3564 CHECK_LOCATION().AsString()));
surmeh01bceff2f2018-03-29 16:29:27 +01003565 }
3566
telsoa01c577f2c2018-08-31 09:22:23 +01003567 // Parses the file into a message.
surmeh01bceff2f2018-03-29 16:29:27 +01003568 tensorflow::GraphDef graphDef;
3569 auto input = new google::protobuf::io::FileInputStream(fileno(fd));
3570 bool success = google::protobuf::TextFormat::Parse(input, &graphDef);
3571 delete input;
3572 fclose(fd);
3573
3574 if (!success)
3575 {
telsoa01c577f2c2018-08-31 09:22:23 +01003576 throw ParseException(
James Ward58dec6b2020-09-11 17:32:44 +01003577 fmt::format("Failed to parse graph file {}",
3578 CHECK_LOCATION().AsString()));
surmeh01bceff2f2018-03-29 16:29:27 +01003579 }
3580
3581 return CreateNetworkFromGraphDef(graphDef, inputShapes, requestedOutputs);
3582}
3583
Kevin May7d96b162021-02-03 17:38:41 +00003584INetworkPtr ITfParser::TfParserImpl::CreateNetworkFromString(const char* protoText,
surmeh01bceff2f2018-03-29 16:29:27 +01003585 const std::map<std::string, TensorShape>& inputShapes,
3586 const std::vector<std::string>& requestedOutputs)
3587{
telsoa01c577f2c2018-08-31 09:22:23 +01003588 // Parses the string into a message.
surmeh01bceff2f2018-03-29 16:29:27 +01003589 tensorflow::GraphDef graphDef;
3590 bool success = google::protobuf::TextFormat::ParseFromString(protoText, &graphDef);
3591
3592 if (!success)
3593 {
telsoa01c577f2c2018-08-31 09:22:23 +01003594 throw ParseException(
James Ward58dec6b2020-09-11 17:32:44 +01003595 fmt::format("Failed to parse graph file {}",
3596 CHECK_LOCATION().AsString()));
surmeh01bceff2f2018-03-29 16:29:27 +01003597 }
3598
3599 return CreateNetworkFromGraphDef(graphDef, inputShapes, requestedOutputs);
3600}
3601
Kevin May7d96b162021-02-03 17:38:41 +00003602INetworkPtr ITfParser::TfParserImpl::CreateNetworkFromBinaryFile(const char* graphFile,
surmeh01bceff2f2018-03-29 16:29:27 +01003603 const std::map<std::string, TensorShape>& inputShapes,
3604 const std::vector<std::string>& requestedOutputs)
3605{
3606 FILE* fd = fopen(graphFile, "rb");
3607
3608 if (fd == nullptr)
3609 {
telsoa01c577f2c2018-08-31 09:22:23 +01003610 throw FileNotFoundException(
James Ward58dec6b2020-09-11 17:32:44 +01003611 fmt::format("Graph file {} failed to open {}",
3612 graphFile,
3613 CHECK_LOCATION().AsString()));
surmeh01bceff2f2018-03-29 16:29:27 +01003614 }
3615
telsoa01c577f2c2018-08-31 09:22:23 +01003616 // Parses the file into a message.
surmeh01bceff2f2018-03-29 16:29:27 +01003617 tensorflow::GraphDef graphDef;
3618
3619 google::protobuf::io::FileInputStream inStream(fileno(fd));
3620 google::protobuf::io::CodedInputStream codedStream(&inStream);
Nikhil Raje5181532020-10-09 14:52:25 +01003621 codedStream.SetTotalBytesLimit(INT_MAX);
surmeh01bceff2f2018-03-29 16:29:27 +01003622 bool success = graphDef.ParseFromCodedStream(&codedStream);
3623 fclose(fd);
3624
3625 if (!success)
3626 {
telsoa01c577f2c2018-08-31 09:22:23 +01003627 throw ParseException(
James Ward58dec6b2020-09-11 17:32:44 +01003628 fmt::format("Failed to parse protobuf file {} {}",
3629 graphFile,
3630 CHECK_LOCATION().AsString()));
surmeh01bceff2f2018-03-29 16:29:27 +01003631 }
3632
3633 return CreateNetworkFromGraphDef(graphDef, inputShapes, requestedOutputs);
3634}
3635
Kevin May7d96b162021-02-03 17:38:41 +00003636INetworkPtr ITfParser::TfParserImpl::CreateNetworkFromGraphDef(const tensorflow::GraphDef& graphDef,
surmeh01bceff2f2018-03-29 16:29:27 +01003637 const std::map<std::string, TensorShape>& inputShapes,
3638 const std::vector<std::string>& requestedOutputs)
3639{
3640 m_Network = INetwork::Create();
3641
3642 m_InputShapes = inputShapes;
3643 if (requestedOutputs.size() == 0)
3644 {
telsoa01c577f2c2018-08-31 09:22:23 +01003645 throw ParseException(
James Ward58dec6b2020-09-11 17:32:44 +01003646 fmt::format("requestedOutputs must have at least one entry {}",
3647 CHECK_LOCATION().AsString()));
surmeh01bceff2f2018-03-29 16:29:27 +01003648 }
3649 m_RequestedOutputs = requestedOutputs;
3650
3651 try
3652 {
3653 LoadGraphDef(graphDef);
3654 }
3655 catch (const ParseException& e)
3656 {
3657 Cleanup();
3658 throw e;
3659 }
3660
3661 Cleanup();
3662
3663 return std::move(m_Network);
3664}
3665
Kevin May7d96b162021-02-03 17:38:41 +00003666void ITfParser::TfParserImpl::Cleanup()
surmeh01bceff2f2018-03-29 16:29:27 +01003667{
telsoa01c577f2c2018-08-31 09:22:23 +01003668 // Cleanup, in case we reuse this parser.
surmeh01bceff2f2018-03-29 16:29:27 +01003669 m_InputShapes.clear();
3670 m_RequestedOutputs.clear();
3671 m_NodesByName.clear();
3672 m_ParsedTfOperations.clear();
3673}
3674
Kevin May7d96b162021-02-03 17:38:41 +00003675BindingPointInfo ITfParser::TfParserImpl::GetNetworkInputBindingInfo(const std::string& name) const
surmeh01bceff2f2018-03-29 16:29:27 +01003676{
3677 return GetBindingInfo(name, "input", m_NetworkInputsBindingInfo);
3678}
3679
Kevin May7d96b162021-02-03 17:38:41 +00003680BindingPointInfo ITfParser::TfParserImpl::GetNetworkOutputBindingInfo(const std::string& name) const
surmeh01bceff2f2018-03-29 16:29:27 +01003681{
3682 return GetBindingInfo(name, "output", m_NetworkOutputsBindingInfo);
3683}
3684
Kevin May7d96b162021-02-03 17:38:41 +00003685std::pair<LayerBindingId, TensorInfo> ITfParser::TfParserImpl::GetBindingInfo(const std::string& layerName,
surmeh01bceff2f2018-03-29 16:29:27 +01003686 const char* bindingPointDesc,
3687 const std::unordered_map<std::string, BindingPointInfo>& nameToBindingInfo)
3688{
3689 auto it = nameToBindingInfo.find(layerName);
3690 if (it == nameToBindingInfo.end())
3691 {
telsoa01c577f2c2018-08-31 09:22:23 +01003692 throw InvalidArgumentException(
James Ward58dec6b2020-09-11 17:32:44 +01003693 fmt::format("Unknown {} '{}' {}",
3694 bindingPointDesc,
3695 layerName,
3696 CHECK_LOCATION().AsString()));
surmeh01bceff2f2018-03-29 16:29:27 +01003697 }
3698 return it->second;
3699}
3700
Kevin May7d96b162021-02-03 17:38:41 +00003701void ITfParser::TfParserImpl::TrackInputBinding(IConnectableLayer* layer,
3702 LayerBindingId id,
3703 const TensorInfo& tensorInfo)
surmeh01bceff2f2018-03-29 16:29:27 +01003704{
3705 return TrackBindingPoint(layer, id, tensorInfo, "input", m_NetworkInputsBindingInfo);
3706}
3707
Kevin May7d96b162021-02-03 17:38:41 +00003708void ITfParser::TfParserImpl::TrackOutputBinding(IConnectableLayer* layer,
3709 LayerBindingId id,
3710 const TensorInfo& tensorInfo)
surmeh01bceff2f2018-03-29 16:29:27 +01003711{
3712 return TrackBindingPoint(layer, id, tensorInfo, "output", m_NetworkOutputsBindingInfo);
3713}
3714
Kevin May7d96b162021-02-03 17:38:41 +00003715void ITfParser::TfParserImpl::TrackBindingPoint(IConnectableLayer* layer,
surmeh01bceff2f2018-03-29 16:29:27 +01003716 LayerBindingId id,
3717 const TensorInfo& tensorInfo,
3718 const char* bindingPointDesc,
3719 std::unordered_map<std::string, BindingPointInfo>& nameToBindingInfo)
3720{
3721 const std::string layerName = layer->GetName();
3722 auto it = nameToBindingInfo.find(layerName);
3723 if (it == nameToBindingInfo.end())
3724 {
3725 nameToBindingInfo[layerName] = std::make_pair(id, tensorInfo);
3726 }
3727 else
3728 {
telsoa01c577f2c2018-08-31 09:22:23 +01003729 throw ParseException(
James Ward58dec6b2020-09-11 17:32:44 +01003730 fmt::format("Id {} used by more than one {} layer {}",
3731 id,
3732 bindingPointDesc,
3733 CHECK_LOCATION().AsString()));
surmeh01bceff2f2018-03-29 16:29:27 +01003734 }
3735}
3736
3737} // namespace armnnTfParser