21 #include <google/protobuf/io/zero_copy_stream_impl.h> 22 #include <google/protobuf/text_format.h> 24 #include <tensorflow/core/framework/graph.pb.h> 27 #include <fmt/format.h> 31 using namespace armnn;
42 template <
typename Callable>
43 void ReadMandatoryNodeAttributeImpl(
const tensorflow::NodeDef& nodeDef,
44 const std::string& attribName,
45 tensorflow::AttrValue::ValueCase expectedValueCase,
48 auto iter = nodeDef.attr().find(attribName);
49 if (iter != nodeDef.attr().end())
51 const auto& attrValue = iter->second;
52 if (attrValue.value_case() == expectedValueCase)
59 fmt::format(
"Attribute {} of node {} expected to have {} as tensorflow::AttrValue::ValueCase, " 60 "but found {} instead {}",
63 static_cast<int>(expectedValueCase),
64 static_cast<int>(attrValue.value_case()),
71 fmt::format(
"Could not find required attribute {} in node {} {}",
78 template <
typename Callable>
79 void ReadOptionalNodeAttributeImpl(
const tensorflow::NodeDef& nodeDef,
80 const std::string& attribName,
81 tensorflow::AttrValue::ValueCase expectedValueCase,
84 auto iter = nodeDef.attr().find(attribName);
85 if (iter != nodeDef.attr().end())
87 const auto& attrValue = iter->second;
88 if (attrValue.value_case() == expectedValueCase)
95 fmt::format(
"Attribute {} of node {} expected to have {} as tensorflow::AttrValue::ValueCase, " 96 "but found {} instead {}",
99 static_cast<int>(expectedValueCase),
100 static_cast<int>(attrValue.value_case()),
106 float ReadMandatoryNodeFloatAttribute(
const tensorflow::NodeDef& nodeDef,
const std::string& name)
108 float attribValue = 0.0f;
109 ReadMandatoryNodeAttributeImpl(nodeDef, name, tensorflow::AttrValue::kF,
110 [&attribValue](
const tensorflow::AttrValue& attrValue)
112 attribValue = attrValue.f();
117 int32_t ReadMandatoryNodeInt32Attribute(
const tensorflow::NodeDef& nodeDef,
const std::string& name)
119 int32_t attribValue = 0u;
120 ReadMandatoryNodeAttributeImpl(nodeDef, name, tensorflow::AttrValue::kI,
121 [&attribValue](
const tensorflow::AttrValue& attrValue)
123 attribValue =
static_cast<int32_t
>(attrValue.i());
128 bool ReadMandatoryNodeBoolAttribute(
const tensorflow::NodeDef& nodeDef,
const std::string& name)
130 bool attribValue =
false;
131 ReadMandatoryNodeAttributeImpl(nodeDef, name, tensorflow::AttrValue::kB,
132 [&attribValue](
const tensorflow::AttrValue& attrValue)
134 attribValue =
static_cast<bool>(attrValue.b());
139 uint32_t ReadMandatoryNodeUint32Attribute(
const tensorflow::NodeDef& nodeDef,
const std::string& name)
141 uint32_t attribValue = 0u;
142 ReadMandatoryNodeAttributeImpl(nodeDef, name, tensorflow::AttrValue::kI,
143 [&attribValue](
const tensorflow::AttrValue& attrValue)
145 attribValue =
static_cast<uint32_t
>(attrValue.i());
150 std::string ReadMandatoryNodeStringAttribute(
const tensorflow::NodeDef& nodeDef,
const std::string& name)
152 std::string attribValue =
"";
153 ReadMandatoryNodeAttributeImpl(nodeDef, name, tensorflow::AttrValue::kS,
154 [&attribValue](
const tensorflow::AttrValue& attrValue)
156 attribValue = attrValue.s();
161 std::vector<uint32_t> ReadMandatoryNodeUint32ListAttribute(
const tensorflow::NodeDef& nodeDef,
162 const std::string& name)
164 std::vector<uint32_t> attriList;
165 ReadMandatoryNodeAttributeImpl(nodeDef, name, tensorflow::AttrValue::kList,
166 [&attriList](
const tensorflow::AttrValue& attrValue)
168 for (
int attriNum = 0; attriNum < attrValue.list().i_size(); ++attriNum)
170 attriList.push_back(static_cast<uint32_t>(attrValue.list().i(attriNum)));
177 std::vector<uint32_t> ReadOptionalNodeUint32ListAttribute(
const tensorflow::NodeDef& nodeDef,
178 const std::string& name)
180 std::vector<uint32_t> attriList;
181 ReadOptionalNodeAttributeImpl(nodeDef, name, tensorflow::AttrValue::kList,
182 [&attriList](
const tensorflow::AttrValue& attrValue)
184 for (
int attriNum = 0; attriNum < attrValue.list().i_size(); ++attriNum)
186 attriList.push_back(static_cast<uint32_t>(attrValue.list().i(attriNum)));
193 std::string ReadOptionalNodeStringAttribute(
const tensorflow::NodeDef& nodeDef,
194 const std::string& name,
195 const std::string& defaultValue =
"")
197 std::string attribValue = defaultValue;
198 ReadOptionalNodeAttributeImpl(nodeDef, name, tensorflow::AttrValue::kS,
199 [&attribValue](
const tensorflow::AttrValue& attrValue)
201 attribValue = attrValue.s();
206 bool ReadOptionalNodeBoolAttribute(
const tensorflow::NodeDef& nodeDef,
207 const std::string& name,
208 bool defaultValue =
false)
210 bool attribValue = defaultValue;
211 ReadOptionalNodeAttributeImpl(nodeDef, name, tensorflow::AttrValue::kB,
212 [&attribValue](
const tensorflow::AttrValue& attrValue)
214 attribValue = attrValue.b();
219 tensorflow::DataType ReadMandatoryNodeTypeAttribute(
const tensorflow::NodeDef& nodeDef,
const std::string& name)
222 ReadMandatoryNodeAttributeImpl(nodeDef, name, tensorflow::AttrValue::kType,
223 [&attribValue](
const tensorflow::AttrValue& attrValue)
225 attribValue = attrValue.type();
232 std::vector<unsigned int> outDims(targetDims.begin(), targetDims.end());
233 const auto stretchDim = std::find(targetDims.begin(), targetDims.end(), -1);
235 if (stretchDim != targetDims.end())
237 if (std::find(std::next(stretchDim), targetDims.end(), -1) != targetDims.end())
240 fmt::format(
"At most one component of shape can be -1 {}",
244 auto targetNumElements =
246 std::accumulate(targetDims.begin(), targetDims.end(), -1, std::multiplies<int32_t>()));
247 auto stretchIndex =
static_cast<size_t>(std::distance(targetDims.begin(), stretchDim));
248 outDims[stretchIndex] = input.
GetNumElements() / targetNumElements;
252 reshapeInfo.
SetShape(
TensorShape{
static_cast<unsigned int>(outDims.size()), outDims.data() });
259 INetwork& m_Network,
const tensorflow::NodeDef& nodeDef)
263 const unsigned int matchDim = inputTensorInfo.
GetNumDimensions() - (isNHWC ? 1 : 3);
264 std::array<unsigned int, MaxNumOfTensorDimensions> reshapedDimensions;
265 std::fill_n(reshapedDimensions.begin(), inputTensorInfo.
GetNumDimensions(), 1);
266 reshapedDimensions[matchDim] = input1Info.
GetShape()[0];
271 const std::string reshapeLayerName =
"reshape_for-" + nodeDef.name();
284 OutputId ParseOutputId(
const std::string & name)
286 unsigned int outputNum = 0;
287 size_t colonPos = name.find_last_of(
":");
288 if (colonPos != std::string::npos)
290 int n = std::stoi(name.substr(colonPos+1));
294 fmt::format(
"Output tensor id is out of range for {} {}",
298 outputNum =
static_cast<unsigned int>(n);
300 return OutputId(name.substr(0,colonPos),outputNum);
303 #define CHECK_DATA_FORMAT(NODE_DEF, FORMAT, NODE_TYPE) \ 304 if( FORMAT != "NHWC" && FORMAT != "NCHW" ) \ 306 throw ParseException( \ 307 fmt::format("Unsupported data format {} passed for {} node {}. " \ 308 "Only NHWC and NCHW supported {}", \ 312 CHECK_LOCATION().AsString())); \ 315 #define CHECK_PADDING_TYPE(NODE_DEF, PADDING) \ 316 if(PADDING != "SAME" && PADDING != "VALID" ) \ 318 throw ParseException( \ 319 fmt::format("Only 'SAME' and 'VALID' padding supported. Got {} for {} {}", \ 322 CHECK_LOCATION().AsString())); \ 327 const std::map<std::string, TfParser::OperationParsingFunction> TfParser::ms_OperationNameToParsingFunctions = {
328 {
"Const", &TfParser::ParseConst },
329 {
"Add", &TfParser::ParseAdd },
330 {
"AddN", &TfParser::ParseAddN },
331 {
"BiasAdd", &TfParser::ParseBiasAdd },
332 {
"Identity", &TfParser::ParseIdentity },
333 {
"Conv2D", &TfParser::ParseConv2D },
334 {
"DepthwiseConv2dNative", &TfParser::ParseDepthwiseConv2D },
335 {
"ExpandDims", &TfParser::ParseExpandDims },
336 {
"FusedBatchNorm", &TfParser::ParseFusedBatchNorm },
337 {
"Gather", &TfParser::ParseGather},
338 {
"Greater", &TfParser::ParseGreater},
339 {
"ConcatV2", &TfParser::ParseConcat },
340 {
"LRN", &TfParser::ParseLrn },
341 {
"MatMul", &TfParser::ParseMatMul },
342 {
"Mean", &TfParser::ParseMean },
343 {
"Mul", &TfParser::ParseMul },
344 {
"Placeholder", &TfParser::ParsePlaceholder },
345 {
"RealDiv", &TfParser::ParseRealDiv },
346 {
"Relu", &TfParser::ParseRelu },
347 {
"Relu6", &TfParser::ParseRelu6 },
348 {
"Reshape", &TfParser::ParseReshape },
349 {
"ResizeBilinear", &TfParser::ParseResizeBilinear },
350 {
"Rsqrt", &TfParser::ParseRsqrt },
351 {
"Shape", &TfParser::ParseShape },
352 {
"Squeeze", &TfParser::ParseSqueeze },
353 {
"Sigmoid", &TfParser::ParseSigmoid },
354 {
"Softmax", &TfParser::ParseSoftmax },
355 {
"Softplus", &TfParser::ParseSoftplus },
356 {
"Split", &TfParser::ParseSplit },
357 {
"StridedSlice", &TfParser::ParseStridedSlice },
358 {
"Tanh", &TfParser::ParseTanh },
359 {
"MaxPool", &TfParser::ParseMaxPool },
360 {
"AvgPool", &TfParser::ParseAvgPool },
361 {
"Maximum", &TfParser::ParseMaximum },
362 {
"Minimum", &TfParser::ParseMinimum },
363 {
"Equal", &TfParser::ParseEqual },
364 {
"Pad", &TfParser::ParsePad },
365 {
"Sub", &TfParser::ParseSub },
366 {
"Pack" , &TfParser::ParseStack },
367 {
"Stack", &TfParser::ParseStack },
368 {
"Transpose", &TfParser::ParseTranspose },
371 const std::list<std::string> TfParser::m_ControlInputs = {
391 uint32_t filterSize,
bool samePadding,
392 uint32_t* paddingFront, uint32_t* paddingBack) {
397 uint32_t outputSize = (inputSize + stride - 1) / stride;
398 uint32_t temp = (outputSize - 1) * stride + filterSize;
399 if (temp > inputSize) {
400 *paddingFront = (temp - inputSize) / 2;
401 *paddingBack = (temp - inputSize) - *paddingFront;
406 void CalcPadding(uint32_t input, uint32_t kernel, uint32_t stride, uint32_t& outPadHead, uint32_t& outPadTail,
415 class ParsedTfOperation
418 ParsedTfOperation(
TfParser* parser,
const tensorflow::NodeDef& node)
424 virtual ~ParsedTfOperation() {};
426 const tensorflow::NodeDef& GetNode()
const {
return m_Node; }
430 virtual IOutputSlot& ResolveArmnnOutputSlot(
unsigned int tfOutputIndex) = 0;
433 virtual ParsedTfOperation* ResolveIdentityOperations()
440 const tensorflow::NodeDef& m_Node;
445 class SingleLayerParsedTfOperation :
public ParsedTfOperation
449 : ParsedTfOperation(parser, node)
454 IOutputSlot& ResolveArmnnOutputSlot(
unsigned int tfOutputIndex)
override 458 unsigned int armnnOutputSlotIdx = tfOutputIndex;
462 fmt::format(
"The requested output slot #{} " 463 "for {} does not exist {}",
476 class DeferredSingleLayerParsedTfOperation :
public SingleLayerParsedTfOperation
479 DeferredSingleLayerParsedTfOperation(
TfParser* parser,
const tensorflow::NodeDef& node)
480 : SingleLayerParsedTfOperation(parser, node,
nullptr)
484 IOutputSlot& ResolveArmnnOutputSlot(
unsigned int tfOutputIndex)
override 488 CreateLayerDeferred();
490 return SingleLayerParsedTfOperation::ResolveArmnnOutputSlot(tfOutputIndex);
494 virtual void CreateLayerDeferred() = 0;
499 : m_Network(nullptr, nullptr)
504 const tensorflow::NodeDef* TfParser::ResolveIdentityNode(
const tensorflow::NodeDef* nodeDef)
506 if (nodeDef->op() !=
"Identity")
511 if (nodeDef->input_size() != 1)
514 fmt::format(
"Identity node should have a single input! {} has {} inputs {}",
516 nodeDef->input_size(),
520 auto it = m_NodesByName.find(nodeDef->input(0));
521 if (it != m_NodesByName.end())
523 const tensorflow::NodeDef* inputNode = it->second;
524 return ResolveIdentityNode(inputNode);
529 fmt::format(
"Cannot find what the Identity node {} is linked to! {}",
535 std::vector<OutputOfConstNodeDef>
536 TfParser::GetTfInputNodes(
const tensorflow::NodeDef& nodeDef)
const 538 std::vector<OutputOfConstNodeDef> ret;
540 if (nodeDef.op() ==
"Const")
546 ret.reserve(armnn::numeric_cast<size_t>(nodeDef.input_size()));
547 for (
int j = 0; j < nodeDef.input_size(); ++j)
549 OutputId outputId = ParseOutputId(nodeDef.input(j));
551 if (nodeDef.input(j)[0] ==
'^')
558 if (inputIt == m_NodesByName.end())
561 fmt::format(
"Can't find node '{}', which is listed as an input of '{}' {}",
572 std::vector<OutputOfParsedTfOperation>
573 TfParser::GetInputParsedTfOperationsChecked(
const tensorflow::NodeDef& nodeDef,
574 std::size_t expectedNumInputs)
577 std::vector<OutputOfConstNodeDef> nodes = GetTfInputNodes(nodeDef);
578 const std::size_t numInputs = nodes.size();
579 if (numInputs != expectedNumInputs)
582 fmt::format(
"Unexpected number of inputs for node {}. Expected {}, found {} {}",
589 std::vector<OutputOfParsedTfOperation> result;
590 for (
auto&& node : nodes)
592 auto it = m_ParsedTfOperations.find(node.m_IndexedValue->name());
593 if (it == m_ParsedTfOperations.end())
596 fmt::format(
"Node with name '{}' has not been parsed {}",
597 node.m_IndexedValue->name(),
600 ParsedTfOperation* parsedOp = it->second.get();
602 parsedOp = parsedOp->ResolveIdentityOperations();
609 const tensorflow::NodeDef& nodeDef,
612 const std::string& layerName)
619 if (input0Dim != input1Dim)
623 if (input0Dim == 1 && input1Dim == 4)
627 else if (input0Dim == 4 && input1Dim == 1)
634 fmt::format(
"Unsupported broadcast configuration for {} operation {} {}",
647 std::vector<unsigned int> outputShape;
654 outputShape.push_back(std::max(input0Shape[i], input1Shape[i]));
664 const tensorflow::NodeDef& nodeDef,
667 unsigned int numberOfAddition,
668 unsigned long numberOfLayersToConnect,
673 std::string layerName(nodeDef.name());
674 if (isOdd || numberOfLayersToConnect != 2)
677 layerName.append(
"_addN_").append(std::to_string(numberOfAddition));
679 return CreateAdditionLayer(nodeDef, input0Slot, input1Slot, layerName);
683 const tensorflow::NodeDef& nodeDef,
686 unsigned int numberOfAddition)
690 std::string layerName(nodeDef.name());
691 layerName.append(
"_addN_").append(std::to_string(numberOfAddition));
692 return CreateAdditionLayer(nodeDef, input0Slot, input1Slot, layerName);
696 const tensorflow::NodeDef& nodeDef,
702 return CreateAdditionLayer(nodeDef, input0Slot, input1Slot, nodeDef.name());
705 ParsedTfOperationPtr TfParser::ParseAddN(
const tensorflow::NodeDef& nodeDef,
const tensorflow::GraphDef& graphDef)
708 uint32_t numberOfInputs = ReadMandatoryNodeUint32Attribute(nodeDef,
"N");
709 if (numberOfInputs < 2)
713 fmt::format(
"AddN Node with name '{}' has less than two ({}) inputs {}",
715 std::to_string(numberOfInputs),
718 else if (numberOfInputs == 2)
721 return AddAdditionLayer(nodeDef,
false);
728 std::vector<OutputOfParsedTfOperation> inputs = GetInputParsedTfOperationsChecked(nodeDef, numberOfInputs);
729 unsigned int numberOfAdditions = 0;
730 std::vector<IConnectableLayer*> layers;
732 for (
unsigned int i = 0; i < numberOfInputs; ++i)
735 bool onSecondItem = i % 2;
740 nodeDef, inputs[ i - 1], inputs[i], numberOfAdditions);
741 layers.push_back(newLayer);
745 std::vector<IConnectableLayer*> layersToConnect(layers);
746 unsigned long numberOfLayersToConnect = layersToConnect.size();
747 bool isOdd = numberOfInputs % 2;
749 while (numberOfLayersToConnect > 1)
752 for (
unsigned long i = 0; i < numberOfLayersToConnect; ++i) {
753 bool onSecondItem = i % 2;
758 layersToConnect[i - 1],
761 numberOfLayersToConnect,
763 layers.push_back(newLayer);
767 layersToConnect = layers;
768 numberOfLayersToConnect = layersToConnect.size();
777 finalLayer = CreateAdditionLayer(nodeDef, inputs[numberOfInputs - 1], finalLayer);
779 return std::make_unique<SingleLayerParsedTfOperation>(
this, nodeDef, finalLayer);
783 ParsedTfOperationPtr TfParser::ParseAdd(
const tensorflow::NodeDef& nodeDef,
const tensorflow::GraphDef& graphDef)
786 std::vector<OutputOfParsedTfOperation> inputs = GetInputParsedTfOperationsChecked(nodeDef, 2);
790 if (inputs[0].m_IndexedValue->GetNode().op() ==
"MatMul" &&
791 HasParsedConstTensor<float>(inputs[1].m_IndexedValue->GetNode().name()))
794 AddFullyConnectedLayer(inputs[0].m_IndexedValue->GetNode(),
795 &nodeDef,nodeDef.name().c_str());
796 return std::make_unique<SingleLayerParsedTfOperation>(
this, nodeDef, layer);
798 else if (HasParsedConstTensor<float>(inputs[0].m_IndexedValue->GetNode().name()) &&
799 inputs[1].m_IndexedValue->GetNode().op() ==
"MatMul")
802 AddFullyConnectedLayer(inputs[1].m_IndexedValue->GetNode(),
803 &nodeDef,nodeDef.name().c_str());
804 return std::make_unique<SingleLayerParsedTfOperation>(
this, nodeDef, layer);
809 return AddAdditionLayer(nodeDef);
813 ParsedTfOperationPtr TfParser::ParseBiasAdd(
const tensorflow::NodeDef& nodeDef,
const tensorflow::GraphDef& graphDef)
816 return AddAdditionLayer(nodeDef,
true);
820 class ParsedIdentityTfOperation :
public ParsedTfOperation
823 ParsedIdentityTfOperation(
TfParser* parser,
const tensorflow::NodeDef& node, ParsedTfOperation* representative)
824 : ParsedTfOperation(parser, node)
825 , m_Representative(representative)
829 virtual IOutputSlot& ResolveArmnnOutputSlot(
unsigned int tfOutputIndex)
override 832 return m_Representative->ResolveArmnnOutputSlot(tfOutputIndex);
835 virtual ParsedTfOperation* ResolveIdentityOperations()
override 837 return m_Representative->ResolveIdentityOperations();
841 ParsedTfOperation* m_Representative;
844 ParsedTfOperationPtr TfParser::ParseIdentity(
const tensorflow::NodeDef& nodeDef,
const tensorflow::GraphDef& graphDef)
847 std::vector<OutputOfParsedTfOperation> inputs = GetInputParsedTfOperationsChecked(nodeDef, 1);
849 return std::make_unique<ParsedIdentityTfOperation>(
this, nodeDef, inputs[0].m_IndexedValue);
855 template <
typename T>
860 const T* tensorData,
const TensorInfo& tensorInfo)
861 : DeferredSingleLayerParsedTfOperation(parser, node),
863 m_TensorInfo(tensorInfo)
868 void CreateLayerDeferred()
override 871 m_Layer = m_Parser->m_Network->AddConstantLayer(
ConstTensor(m_TensorInfo, m_Storage), m_Node.name().c_str());
875 ConstTensor GetConstTensor(std::vector<T>& outputTensorData)
const 879 memcpy(outputTensorData.data(), m_Storage.data(), m_TensorInfo.
GetNumBytes());
882 ConstTensor constTensor(m_TensorInfo, outputTensorData);
886 const T* GetStorage()
const 888 return m_Storage.data();
898 std::vector<T> m_Storage;
904 const tensorflow::NodeDef& nodeDef)
908 case tensorflow::DT_FLOAT:
909 return DataType::Float32;
911 case tensorflow::DT_INT32:
912 return DataType::Signed32;
916 fmt::format(
"Unknown DataType {} for node {} {}",
917 tensorflow::DataType_Name(tfDataType),
923 struct ParseTfTensorValueList
925 template<
typename DataType>
927 const tensorflow::TensorProto& tfTensor,
928 unsigned int dstElements,
929 std::vector<int8_t>& outputData);
931 template <
typename DataType>
932 static void ReadData(
const void* srcData,
unsigned int numSrcElements,
933 std::vector<int8_t>& dstData,
unsigned int numDstElements)
936 if (numSrcElements == 0)
942 if (numDstElements == 0)
944 numDstElements = numSrcElements;
948 dstData.resize(std::max(numSrcElements, numDstElements) *
sizeof(
DataType));
954 std::copy(srcTensor, srcTensor + numSrcElements, dstTensor);
956 if (numDstElements > numSrcElements)
959 std::fill(dstTensor + numSrcElements, dstTensor + numDstElements, srcTensor[numSrcElements - 1]);
966 void ParseTfTensorValueList::Parse<float>(
const tensorflow::TensorProto& tfTensor,
967 unsigned int dstElements, std::vector<int8_t>& outputData)
969 ReadData<float>(tfTensor.float_val().data(),
static_cast<unsigned int>(tfTensor.float_val_size()),
970 outputData, dstElements);
974 void ParseTfTensorValueList::Parse<int32_t>(
const tensorflow::TensorProto& tfTensor,
975 unsigned int dstElements, std::vector<int8_t>& outputData)
977 ReadData<int32_t>(tfTensor.int_val().data(),
static_cast<unsigned int>(tfTensor.int_val_size()),
978 outputData, dstElements);
981 template <
template<
typename>
class OperatorType,
typename T = int8_t>
982 struct MakeTfOperation
984 template<
typename DataType,
class... Args>
985 inline static std::unique_ptr<OperatorType<DataType>> Parse(
TfParser* parser,
const tensorflow::NodeDef& node,
988 return std::make_unique<OperatorType<DataType>>(parser, node, std::forward<Args>(args)...);
993 struct MakeTfOperation<ParsedConstTfOperation>
995 template<
typename DataType,
class... Args>
996 inline static std::unique_ptr<ParsedConstTfOperation<DataType>> Parse(
TfParser* parser,
997 const tensorflow::NodeDef& node,
const std::vector<int8_t>& tensorData,
const TensorInfo& tensorInfo)
999 return std::make_unique<ParsedConstTfOperation<DataType>>(parser, node,
1000 reinterpret_cast<const DataType*
>(tensorData.data()), tensorInfo);
1004 template <
class FuncType>
1005 struct InvokeParseFunction
1007 template<
class ResType,
class... Args>
1008 inline static ResType Result(
DataType dataType, Args&&... args)
1010 if (dataType == DataType::Float32)
1012 return FuncType::template Parse<float>(std::forward<Args>(args)...);
1014 else if (dataType == DataType::Signed32)
1016 return FuncType::template Parse<int32_t>(std::forward<Args>(args)...);
1022 template<
class... Args>
1023 inline static void Result(
DataType dataType, Args&&... args)
1025 if (dataType == DataType::Float32)
1027 FuncType::template Parse<float>(std::forward<Args>(args)...);
1029 else if (dataType == DataType::Signed32)
1031 FuncType::template Parse<int32_t>(std::forward<Args>(args)...);
1036 ParsedTfOperationPtr TfParser::ParseConst(
const tensorflow::NodeDef& nodeDef,
const tensorflow::GraphDef& graphDef)
1041 if (nodeDef.attr().count(
"value") == 0)
1044 fmt::format(
"Value not found for Const node - {} {}",
1049 const tensorflow::TensorProto& tfTensor = nodeDef.attr().at(
"value").tensor();
1050 const tensorflow::TensorShapeProto& tfTensorShape = tfTensor.tensor_shape();
1053 const auto GetDimensionSize = [](
auto& d) {
return d.size(); };
1055 std::vector<unsigned int> dimensionSizes;
1056 std::transform(tfTensorShape.dim().begin(), tfTensorShape.dim().end(),
1057 std::back_inserter(dimensionSizes), GetDimensionSize);
1061 unsigned int numElements = 0U;
1063 if (!dimensionSizes.empty())
1065 numElements = std::accumulate(dimensionSizes.begin(), dimensionSizes.end(),
1066 1U, std::multiplies<unsigned int>());
1069 std::vector<int8_t> tensorData;
1072 if (tfTensor.tensor_content().empty())
1074 InvokeParseFunction<ParseTfTensorValueList>::Result<
void>(dataType, tfTensor, numElements, tensorData);
1078 if (numElements == 0)
1080 const unsigned int tfNumElements =
1081 static_cast<unsigned int>(tensorData.size()) /
GetDataTypeSize(dataType);
1082 dimensionSizes.push_back(tfNumElements);
1088 tensorData.assign(tfTensor.tensor_content().begin(), tfTensor.tensor_content().end());
1091 if (numElements == 0)
1094 fmt::format(
"No tensor shape found for Const node - {} {}",
1101 if (tensorData.empty())
1104 fmt::format(
"No tensor data found for Const node - {} {}",
1109 const TensorInfo tensorInfo(static_cast<unsigned int>(dimensionSizes.size()),
1110 dimensionSizes.data(),
1115 if (tensorData.size() > tensorInfo.GetNumBytes())
1118 fmt::format(
"Number of elements ({}) should be less than or equal " 1119 "to the number of elements implied by the shape argument ({}) for Const node - {} {}",
1121 tensorInfo.GetNumElements(),
1126 return InvokeParseFunction<MakeTfOperation<ParsedConstTfOperation>>::Result<ParsedTfOperationPtr>(
1127 dataType,
this, nodeDef, tensorData, tensorInfo);
1130 template<
typename Type>
1131 bool TfParser::HasParsedConstTensor(
const std::string & nodeName)
const 1133 auto it = m_ParsedTfOperations.find(nodeName);
1134 if (it == m_ParsedTfOperations.end())
1138 return dynamic_cast<ParsedConstTfOperation<Type>*
>(it->second.get()) !=
nullptr;
1141 template<
typename Type>
1142 bool TfParser::HasParsedConstTensor(ParsedTfOperation* parsedTfOpPtr)
const 1144 return dynamic_cast<ParsedConstTfOperation<Type>*
>(parsedTfOpPtr) !=
nullptr;
1147 unsigned int TfParser::GetConstInputIndex(
const std::vector<OutputOfParsedTfOperation>& inputs)
1149 for (
unsigned int i = 0; i < inputs.size(); i++)
1151 if (HasParsedConstTensor<int32_t>(inputs[i].m_IndexedValue->GetNode().name()))
1157 fmt::format(
"ArmNN only supports operators with constant axis. {}",
1163 const tensorflow::GraphDef& graphDef)
1166 std::vector<OutputOfParsedTfOperation> inputs = GetInputParsedTfOperationsChecked(nodeDef, 2);
1167 IOutputSlot& inputSlot = inputs[0].m_IndexedValue->ResolveArmnnOutputSlot(inputs[0].m_Index);
1170 if (!HasParsedConstTensor<float>(inputs[1].m_IndexedValue->GetNode().name()))
1173 fmt::format(
"ArmNN only supports Convolution layers with constant weights for {}, input {} {}",
1175 inputs[1].m_IndexedValue->GetNode().name(),
1178 ParsedConstTfOperation<float>* weightNode =
1179 PolymorphicDowncast<ParsedConstTfOperation<float> *>(inputs[1].m_IndexedValue);
1181 std::string paddingString = ReadMandatoryNodeStringAttribute(nodeDef,
"padding");
1182 std::string dataFormat = ReadMandatoryNodeStringAttribute(nodeDef,
"data_format");
1183 std::vector<uint32_t> strides = ReadMandatoryNodeUint32ListAttribute(nodeDef,
"strides");
1186 std::vector<uint32_t> dilations = ReadOptionalNodeUint32ListAttribute(nodeDef,
"dilations");
1187 if (!dilations.empty())
1189 for (
auto dilation : dilations)
1194 fmt::format(
"ArmNN only supports Convolution layers with dilations [1,1,1,1] for {} {}",
1206 DataLayout dataLayout = dataFormat ==
"NHWC" ? DataLayout::NHWC : DataLayout::NCHW;
1223 dataLayout == DataLayout::NHWC ?
1224 std::initializer_list<unsigned int>{ 1, 2, 3, 0 } :
1225 std::initializer_list<unsigned int>{ 2, 3, 1, 0 };
1228 const TensorInfo& weightTensorInfo = weightNode->GetTensorInfo();
1232 std::vector<float> weightTensorSwizzledData(weightTensorInfo.
GetNumElements());
1234 weightNode->GetStorage(), weightTensorSwizzledData.data(),
sizeof(float));
1237 ConstTensor weightTensor(weightTensorSwizzledInfo, weightTensorSwizzledData);
1242 bool padding =
false;
1244 unsigned int outputHeight = 0;
1245 unsigned int outputWidth = 0;
1249 if (paddingString ==
"SAME")
1253 outputHeight =
static_cast<uint32_t
>(ceil(static_cast<float>(inputHeight) /
1255 outputWidth =
static_cast<uint32_t
>(ceil(static_cast<float>(inputWidth) /
1258 else if (paddingString ==
"VALID")
1262 outputHeight =
static_cast<uint32_t
>(ceil(static_cast<float>(inputHeight - weightHeight + 1) /
1264 outputWidth =
static_cast<uint32_t
>(ceil(static_cast<float>(inputWidth - weightWidth + 1) /
1270 case DataLayout::NHWC:
1277 case DataLayout::NCHW:
1293 nodeDef.name().c_str());
1297 return std::make_unique<SingleLayerParsedTfOperation>(
this, nodeDef, layer);
1301 const tensorflow::GraphDef& graphDef)
1304 std::vector<OutputOfParsedTfOperation> inputs = GetInputParsedTfOperationsChecked(nodeDef, 2);
1305 IOutputSlot& inputSlot = inputs[0].m_IndexedValue->ResolveArmnnOutputSlot(inputs[0].m_Index);
1308 if (!HasParsedConstTensor<float>(inputs[1].m_IndexedValue->GetNode().name()))
1311 fmt::format(
"ArmNN only supports Depthwise Convolution layer with constant weights. " 1312 "Non const input found {} for node {} {}",
1313 inputs[1].m_IndexedValue->GetNode().name(),
1318 ParsedConstTfOperation<float>* weightNode =
1319 PolymorphicDowncast<ParsedConstTfOperation<float> *>(inputs[1].m_IndexedValue);
1321 std::string paddingString = ReadMandatoryNodeStringAttribute(nodeDef,
"padding");
1322 std::string dataFormat = ReadMandatoryNodeStringAttribute(nodeDef,
"data_format");
1323 std::vector<uint32_t> strides = ReadMandatoryNodeUint32ListAttribute(nodeDef,
"strides");
1330 DataLayout dataLayout = dataFormat ==
"NHWC" ? DataLayout::NHWC : DataLayout::NCHW;
1348 const TensorInfo& weightTensorInfo = weightNode->GetTensorInfo();
1352 std::vector<float> weightTensorSwizzledData(weightTensorInfo.
GetNumElements());
1354 weightNode->GetStorage(), weightTensorSwizzledData.data(),
sizeof(float));
1357 ConstTensor weightTensor(weightTensorSwizzledInfo, weightTensorSwizzledData);
1359 uint32_t weightHeight = weightTensor.
GetShape()[2];
1360 uint32_t weightWidth = weightTensor.
GetShape()[3];
1362 bool padding =
false;
1364 unsigned int outputHeight = 0;
1365 unsigned int outputWidth = 0;
1369 if (paddingString ==
"SAME")
1373 outputHeight =
static_cast<uint32_t
>(ceil(static_cast<float>(inputHeight) /
1375 outputWidth =
static_cast<uint32_t
>(ceil(static_cast<float>(inputWidth) /
1378 else if (paddingString ==
"VALID")
1382 outputHeight =
static_cast<uint32_t
>(ceil(static_cast<float>(inputHeight - weightHeight + 1) /
1384 outputWidth =
static_cast<uint32_t
>(ceil(static_cast<float>(inputWidth - weightWidth + 1) /
1390 case DataLayout::NHWC:
1397 case DataLayout::NCHW:
1413 nodeDef.name().c_str());
1417 return std::make_unique<SingleLayerParsedTfOperation>(
this, nodeDef, layer);
1422 std::int32_t expandDim)
1428 fmt::format(
"Unsupported number of dimensions: {} for input shape for ExpandDims {} {}",
1435 std::vector<uint32_t> outputDims;
1438 if (expandDim >= -1 - inputDimSize && expandDim <= inputDimSize)
1442 auto currentDimension = inputTensorInfo.
GetShape()[i];
1443 outputDims.push_back(currentDimension);
1449 auto getPosition = std::next(outputDims.begin() + 0, expandDim);
1450 outputDims.insert(getPosition, 1);
1458 auto getPosition = std::next(outputDims.begin() + outputDimSize, expandDim);
1459 outputDims.insert(getPosition, 1);
1465 fmt::format(
"Cannot expand dimension {} in input tensor with {} dimension {}",
1471 if (outputDims.size() > 4)
1474 fmt::format(
"Unsupported number of dimensions: {} for output shape for ExpandDims {} {}",
1486 return outTensorInfo;
1489 ParsedTfOperationPtr TfParser::ParseExpandDims(
const tensorflow::NodeDef& nodeDef,
const tensorflow::GraphDef& graphDef)
1496 std::vector<OutputOfConstNodeDef> nodes = GetTfInputNodes(nodeDef);
1497 const std::size_t numInputs = nodes.size();
1498 std::vector<OutputOfParsedTfOperation> inputs;
1499 std::int32_t expandDim;
1502 inputs = GetInputParsedTfOperationsChecked(nodeDef, 1);
1503 expandDim = ReadMandatoryNodeInt32Attribute(nodeDef,
"Tdim");
1507 inputs = GetInputParsedTfOperationsChecked(nodeDef, 2);
1510 IOutputSlot& prevLayerOutputSlot = inputs[1].m_IndexedValue->ResolveArmnnOutputSlot(inputs[1].m_Index);
1516 fmt::format(
"The axis parameter of ExpandDims operation given as second input is not of type int32." 1517 " Input {0} Node {1} {2}",
1518 inputs[1].m_IndexedValue->GetNode().name(),
1524 if (!HasParsedConstTensor<int32_t>(inputs[1].m_IndexedValue->GetNode().name()))
1527 fmt::format(
"ArmNN only supports ExpandDims layers with constant axis/dim parameter. " 1528 "Input {0} Node {1} {2}",
1529 inputs[1].m_IndexedValue->GetNode().name(),
1541 fmt::format(
"The axis parameter of ExpandDims operation given as second input is not " 1542 "allowed to hold more than one value. " 1543 "Input {0} Node {1} {2}",
1544 inputs[1].m_IndexedValue->GetNode().name(),
1549 ParsedConstTfOperation<int32_t>* expandDimsNode =
1550 PolymorphicDowncast<ParsedConstTfOperation<int32_t>*>(inputs[1].m_IndexedValue);
1552 memcpy(&expandDim, expandDimsNode->GetStorage(),
sizeof(expandDim));
1556 IOutputSlot& prevLayerOutputSlot = inputs[0].m_IndexedValue->ResolveArmnnOutputSlot(inputs[0].m_Index);
1568 return std::make_unique<SingleLayerParsedTfOperation>(
this, nodeDef, layer);
1572 const tensorflow::GraphDef& graphDef)
1575 std::vector<OutputOfParsedTfOperation> inputs = GetInputParsedTfOperationsChecked(nodeDef, 5);
1577 if (!HasParsedConstTensor<float>(inputs[1].m_IndexedValue->GetNode().name()))
1580 fmt::format(
"ArmNN only supports FusedBatchNormalization layers with constant scale. " 1581 "Input {}. Node {} {}",
1582 inputs[1].m_IndexedValue->GetNode().name(),
1586 ParsedConstTfOperation<float>* scaleNode =
1587 PolymorphicDowncast<ParsedConstTfOperation<float> *>(inputs[1].m_IndexedValue);
1589 if (!HasParsedConstTensor<float>(inputs[2].m_IndexedValue->GetNode().name()))
1592 fmt::format(
"ArmNN only supports FusedBatchNormalization layers with constant offset. " 1593 "Input {}. Node {} {}",
1594 inputs[2].m_IndexedValue->GetNode().name(),
1598 ParsedConstTfOperation<float>* offsetNode =
1599 PolymorphicDowncast<ParsedConstTfOperation<float> *>(inputs[2].m_IndexedValue);
1601 if (!HasParsedConstTensor<float>(inputs[3].m_IndexedValue->GetNode().name()))
1604 fmt::format(
"ArmNN only supports FusedBatchNormalization layers with constant mean. " 1605 "Input {}. Node {} {}",
1606 inputs[3].m_IndexedValue->GetNode().name(),
1610 ParsedConstTfOperation<float>* meanNode =
1611 PolymorphicDowncast<ParsedConstTfOperation<float> *>(inputs[3].m_IndexedValue);
1613 if (!HasParsedConstTensor<float>(inputs[4].m_IndexedValue->GetNode().name()))
1616 fmt::format(
"ArmNN only supports FusedBatchNormalization layers with constant variance. " 1617 "Input {}. Node {} {}",
1618 inputs[4].m_IndexedValue->GetNode().name(),
1622 ParsedConstTfOperation<float>* varianceNode =
1623 PolymorphicDowncast<ParsedConstTfOperation<float> *>(inputs[4].m_IndexedValue);
1625 const std::string dataFormat = ReadOptionalNodeStringAttribute(nodeDef,
"data_format",
"NHWC");
1630 desc.
m_Eps = ReadMandatoryNodeFloatAttribute(nodeDef,
"epsilon");
1631 desc.
m_DataLayout = dataFormat ==
"NHWC" ? DataLayout::NHWC : DataLayout::NCHW;
1635 std::vector<float> scaleTensorData;
1636 ConstTensor scaleTensor = scaleNode->GetConstTensor(scaleTensorData);
1638 std::vector<float> offsetTensorData;
1639 ConstTensor offsetTensor = offsetNode->GetConstTensor(offsetTensorData);
1641 std::vector<float> meanTensorData;
1642 ConstTensor meanTensor = meanNode->GetConstTensor(meanTensorData);
1644 std::vector<float> varianceTensorData;
1645 ConstTensor varianceTensor = varianceNode->GetConstTensor(varianceTensorData);
1652 nodeDef.name().c_str());
1654 IOutputSlot& inputSlot = inputs[0].m_IndexedValue->ResolveArmnnOutputSlot(inputs[0].m_Index);
1659 return std::make_unique<SingleLayerParsedTfOperation>(
this, nodeDef, layer);
1662 bool TfParser::IsSupportedLeakyReluPattern(
const tensorflow::NodeDef& mulNodeDef,
1663 size_t alphaLayerIndex,
1668 const tensorflow::NodeDef& otherNodeDef = otherOp.
m_IndexedValue->GetNode();
1677 if (mulNodeDef.op() ==
"Mul")
1679 size_t otherLayerIndex = (alphaLayerIndex == 0 ? 1 : 0);
1680 std::vector<OutputOfParsedTfOperation> inputs = GetInputParsedTfOperationsChecked(mulNodeDef, 2);
1683 ARMNN_ASSERT((otherLayerIndex == 0 || alphaLayerIndex == 0));
1684 ARMNN_ASSERT((otherLayerIndex == 1 || alphaLayerIndex == 1));
1685 ARMNN_ASSERT(((otherLayerIndex + alphaLayerIndex) == 1));
1687 if (inputs[otherLayerIndex].m_IndexedValue->GetNode().name() == otherNodeDef.name())
1689 if (HasParsedConstTensor<float>(inputs[alphaLayerIndex].m_IndexedValue->GetNode().name()))
1691 ParsedConstTfOperation<float>* alpha =
1692 PolymorphicDowncast<ParsedConstTfOperation<float> *>(
1693 inputs[alphaLayerIndex].m_IndexedValue);
1695 std::vector<float> const_data;
1696 ConstTensor const_tensor = alpha->GetConstTensor(const_data);
1698 if (const_data.size() == 1)
1700 desc.
m_Function = ActivationFunction::LeakyReLu;
1701 desc.
m_A = const_data[0];
1713 const tensorflow::GraphDef& graphDef)
1716 std::vector<OutputOfParsedTfOperation> inputs = GetInputParsedTfOperationsChecked(nodeDef, 2);
1717 if (inputs.size() != 2)
1720 fmt::format(
"Maximum expects two inputs!. Got {} for Node {} {}",
1726 auto inputNode0 = inputs[0].m_IndexedValue->GetNode();
1727 auto inputNode1 = inputs[1].m_IndexedValue->GetNode();
1740 if (IsSupportedLeakyReluPattern(inputNode0, 0, inputs[1], &outputOfLeakyRelu, desc) ||
1741 IsSupportedLeakyReluPattern(inputNode0, 1, inputs[1], &outputOfLeakyRelu, desc) ||
1742 IsSupportedLeakyReluPattern(inputNode1, 0, inputs[0], &outputOfLeakyRelu, desc) ||
1743 IsSupportedLeakyReluPattern(inputNode1, 1, inputs[0], &outputOfLeakyRelu, desc))
1750 return std::make_unique<SingleLayerParsedTfOperation>(
this, nodeDef, layer);
1756 return AddMaximumLayer(nodeDef);
1760 std::pair<armnn::IOutputSlot*, armnn::IOutputSlot*> TfParser::ProcessElementwiseInputSlots(
1761 const tensorflow::NodeDef& nodeDef,
const std::string& layerName)
1763 std::vector<OutputOfParsedTfOperation> inputs = GetInputParsedTfOperationsChecked(nodeDef, 2);
1765 IOutputSlot* input0Slot = &inputs[0].m_IndexedValue->ResolveArmnnOutputSlot(inputs[0].m_Index);
1766 IOutputSlot* input1Slot = &inputs[1].m_IndexedValue->ResolveArmnnOutputSlot(inputs[1].m_Index);
1770 if (input0Dim != input1Dim)
1774 if (input0Dim == 1 && input1Dim == 4)
1778 else if (input0Dim == 4 && input1Dim == 1)
1785 fmt::format(
"Unsupported broadcast configuration for {} operation {} {}",
1791 return {input0Slot, input1Slot};
1798 const tensorflow::NodeDef& nodeDef)
1805 std::vector<unsigned int> outputShape;
1812 outputShape.push_back(std::max(input0Shape[i], input1Shape[i]));
1818 return std::make_unique<SingleLayerParsedTfOperation>(
this, nodeDef, layer);
1825 const tensorflow::NodeDef& nodeDef)
1831 std::vector<unsigned int> outputShape;
1838 outputShape.push_back(std::max(input0Shape[i], input1Shape[i]));
1844 return std::make_unique<SingleLayerParsedTfOperation>(
this, nodeDef, layer);
1848 const tensorflow::GraphDef& graphDef)
1851 std::vector<OutputOfParsedTfOperation> inputs = GetInputParsedTfOperationsChecked(nodeDef, 2);
1852 IOutputSlot& params = inputs[0].m_IndexedValue->ResolveArmnnOutputSlot(inputs[0].m_Index);
1853 IOutputSlot& indices = inputs[1].m_IndexedValue->ResolveArmnnOutputSlot(inputs[1].m_Index);
1855 descriptor.
m_Axis = ReadMandatoryNodeInt32Attribute(nodeDef,
"axis");
1860 unsigned int outputDim = paramsDim - 1 + indicesDim;
1862 std::vector<unsigned int> dimSizes;
1864 for (
unsigned int i = 0; i < indicesDim; ++i)
1868 for (
unsigned int i = 1; i < paramsDim; ++i)
1880 params.
Connect(layer->GetInputSlot(0));
1881 indices.
Connect(layer->GetInputSlot(1));
1883 return std::make_unique<SingleLayerParsedTfOperation>(
this, nodeDef, layer);
1887 const tensorflow::GraphDef& graphDef)
1890 std::pair<armnn::IOutputSlot*, armnn::IOutputSlot*> inputLayers = ProcessElementwiseInputSlots(nodeDef,
"Greater");
1897 return ProcessComparisonLayer(input0Slot, input1Slot, layer, nodeDef);
1901 const tensorflow::GraphDef& graphDef)
1904 std::pair<armnn::IOutputSlot*, armnn::IOutputSlot*> inputLayers = ProcessElementwiseInputSlots(nodeDef,
"Equal");
1911 return ProcessComparisonLayer(input0Slot, input1Slot, layer, nodeDef);
1915 const tensorflow::GraphDef& graphDef)
1918 std::pair<armnn::IOutputSlot*, armnn::IOutputSlot*> inputLayers = ProcessElementwiseInputSlots(nodeDef,
"Minimum");
1924 return ProcessElementwiseLayer(input0Slot, input1Slot, layer, nodeDef);
1927 ParsedTfOperationPtr TfParser::ParseSub(
const tensorflow::NodeDef& nodeDef,
const tensorflow::GraphDef& graphDef)
1930 std::vector<OutputOfParsedTfOperation> inputs = GetInputParsedTfOperationsChecked(nodeDef, 2);
1932 IOutputSlot* input0Slot = &inputs[0].m_IndexedValue->ResolveArmnnOutputSlot(inputs[0].m_Index);
1933 IOutputSlot* input1Slot = &inputs[1].m_IndexedValue->ResolveArmnnOutputSlot(inputs[1].m_Index);
1940 const bool isNHWC =
true;
1946 const bool isNHWC =
true;
1964 return std::make_unique<SingleLayerParsedTfOperation>(
this, nodeDef, layer);
1967 ParsedTfOperationPtr TfParser::ParseStack(
const tensorflow::NodeDef& nodeDef,
const tensorflow::GraphDef& graphDef)
1970 std::vector<OutputOfConstNodeDef> nodes = GetTfInputNodes(nodeDef);
1972 unsigned int numInputs =
static_cast<unsigned int>(nodes.size());
1976 fmt::format(
"Pack/Stack expects at least one input. Got {} for Node {} {}",
1982 std::vector<OutputOfParsedTfOperation> inputs = GetInputParsedTfOperationsChecked(nodeDef, numInputs);
1984 IOutputSlot* input0Slot = &inputs[0].m_IndexedValue->ResolveArmnnOutputSlot(inputs[0].m_Index);
1989 int32_t axis = ReadMandatoryNodeInt32Attribute(nodeDef,
"axis");
1990 const int sNumDimensions = (
static_cast<int>(numDimensions) + 1);
1991 if (!(axis < sNumDimensions && axis >= -sNumDimensions))
1994 fmt::format(
"Axis index is not in range. Got {} for Node {} {}",
2002 axis =
static_cast<int32_t
>(numDimensions) + axis + 1;
2006 stackDescriptor.
m_Axis =
static_cast<uint32_t
>(axis);
2007 stackDescriptor.
m_NumInputs =
static_cast<uint32_t
>(numInputs);
2010 const unsigned int supportedNumDims = 4;
2011 for (
unsigned int viewIndex = 0; viewIndex < numInputs; ++viewIndex)
2013 IOutputSlot& inputSlot = inputs[viewIndex].m_IndexedValue->ResolveArmnnOutputSlot(inputs[viewIndex].m_Index);
2020 fmt::format(
"The number of dimensions: {} for input tensors of the " 2021 "Pack/Stack op. Number of dimensions should be less than {} {}",
2028 std::vector<unsigned int> outputDimensions;
2031 outputDimensions.push_back(stackDescriptor.
m_InputShape[i]);
2033 outputDimensions.insert(outputDimensions.begin() + axis, numInputs);
2038 for (
unsigned int viewIndex = 0; viewIndex < numInputs; ++viewIndex)
2040 IOutputSlot& inputSlot = inputs[viewIndex].m_IndexedValue->ResolveArmnnOutputSlot(inputs[viewIndex].m_Index);
2046 outputDimensions.data(),
2049 return std::make_unique<SingleLayerParsedTfOperation>(
this, nodeDef, layer);
2052 ParsedTfOperationPtr TfParser::ParseTranspose(
const tensorflow::NodeDef& nodeDef,
const tensorflow::GraphDef& graphDef)
2056 auto inputs = GetInputParsedTfOperationsChecked(nodeDef, 2);
2057 const auto inputCount = inputs.size();
2059 if (inputCount != 2)
2062 fmt::format(
"The number of given input is {}. It should be two for Transpose op." 2069 auto* input0Slot = &inputs[0].m_IndexedValue->ResolveArmnnOutputSlot(inputs[0].m_Index);
2071 const auto constInput = inputs[GetConstInputIndex(inputs)];
2072 auto* permuteVectorInput =
2073 PolymorphicDowncast<ParsedConstTfOperation<int32_t>*>(constInput.m_IndexedValue);
2074 const auto& permuteVectorInfo = permuteVectorInput->
GetTensorInfo();
2076 std::vector<int32_t> permuteVectorData;
2077 permuteVectorInput->GetConstTensor(permuteVectorData);
2079 std::vector<unsigned int> armnnPermuteVectorData(permuteVectorData.begin(), permuteVectorData.end());
2081 const auto permutationVector =
PermutationVector(armnnPermuteVectorData.data(), permuteVectorInfo.GetNumElements());
2094 return std::make_unique<SingleLayerParsedTfOperation>(
this, nodeDef, layer);
2099 const std::string& nodeName)
2101 unsigned int rank = paddingTensor.
GetShape()[0];
2103 if (rank != expectedRank)
2106 fmt::format(
"Expected the padding tensor to be of rank {} not {} on Node {} {}.",
2112 unsigned int second = paddingTensor.
GetShape()[1];
2116 fmt::format(
"Expected the padding tensor to be of dimensions " 2117 "[{1}, 2] not [{1}, {2}] on Node {3} {4}.",
2127 const std::vector<std::pair<unsigned int, unsigned int>>& padList)
2130 std::vector<unsigned int> outDims;
2131 for (
unsigned int i = 0; i < numDims; ++i)
2133 unsigned int dimSize = inputTensorInfo.
GetShape()[i];
2134 const std::pair<unsigned int, unsigned int>& dimPadding = padList[i];
2135 dimSize += dimPadding.first;
2136 dimSize += dimPadding.second;
2137 outDims.push_back(dimSize);
2139 TensorInfo paddedTensorInfo = inputTensorInfo;
2140 unsigned int outDimsSize =
static_cast<unsigned int>(outDims.size());
2142 return paddedTensorInfo;
2146 const tensorflow::GraphDef& graphDef)
2152 std::vector<OutputOfParsedTfOperation> inputs = GetInputParsedTfOperationsChecked(nodeDef, 2);
2153 IOutputSlot& previousLayerOutputSlot = inputs[0].m_IndexedValue->ResolveArmnnOutputSlot(inputs[0].m_Index);
2155 if (!HasParsedConstTensor<int32_t>(inputs[1].m_IndexedValue))
2158 fmt::format(
"ArmNN only supports Pad with constant padding. " 2159 "Input {}. Node {} {}",
2160 inputs[1].m_IndexedValue->GetNode().name(),
2165 ParsedConstTfOperation<int32_t>* paddingTensorOp =
2166 PolymorphicDowncast<ParsedConstTfOperation<int32_t>*>(inputs[1].m_IndexedValue);
2168 std::vector<int32_t> paddingTensorData;
2169 ConstTensor paddingTensor = paddingTensorOp->GetConstTensor(paddingTensorData);
2176 std::vector<std::pair<unsigned int, unsigned int>> padList;
2177 unsigned int rank =
CheckPaddingTensor(paddingTensor, inputTensorInfo, nodeDef.name());
2178 for (
unsigned int i = 0; i < rank; ++i)
2180 std::pair<unsigned int, unsigned int> paddingForDim;
2181 for (
unsigned int j = 0; j < 2; j++)
2183 unsigned int index = (i * 2) + j;
2184 int paddingAmount = paddingTensorData[index];
2186 if (paddingAmount < 0)
2189 fmt::format(
"Negative amount {} specified at [{}, {}] of padding tensor on Node {} {}.",
2198 paddingForDim.first =
static_cast<unsigned int>(paddingAmount);
2202 paddingForDim.second =
static_cast<unsigned int>(paddingAmount);
2205 padList.push_back(paddingForDim);
2213 return std::make_unique<SingleLayerParsedTfOperation>(
this, nodeDef, layer);
2217 const tensorflow::GraphDef& graphDef)
2220 std::vector<OutputOfConstNodeDef> nodes = GetTfInputNodes(nodeDef);
2223 unsigned int numInputs =
static_cast<unsigned int>(nodes.size());
2225 std::vector<OutputOfParsedTfOperation> inputs = GetInputParsedTfOperationsChecked(nodeDef, numInputs);
2228 unsigned int index = GetConstInputIndex(inputs);
2230 ParsedConstTfOperation<int32_t>* shapeNode =
2231 PolymorphicDowncast<ParsedConstTfOperation<int32_t>*>(inputs[index].m_IndexedValue);
2233 std::vector<int32_t> axisTensorData;
2234 shapeNode->GetConstTensor(axisTensorData);
2237 const unsigned int concatDim =
static_cast<unsigned int>(axisTensorData[0]);
2240 if (concatDim == 0 || concatDim == 2)
2243 fmt::format(
"Dimension {} for concatenation is not supported by Armnn. " 2250 const unsigned int supportedNumDims = 4;
2251 unsigned int numConcatViews = numInputs - 1;
2252 OriginsDescriptor concatDescriptor(static_cast<uint32_t>(numConcatViews), supportedNumDims);
2255 unsigned int mergeDim = 0;
2256 for (
unsigned int viewIndex = 0; viewIndex < numConcatViews; ++viewIndex)
2259 IOutputSlot& inputSlot = inputs[viewIndex].m_IndexedValue->ResolveArmnnOutputSlot(inputs[viewIndex].m_Index);
2266 fmt::format(
"The number of dimensions: {} for input tensors of the " 2267 "concatenation op should be {} {}",
2274 mergeDims = inputTensorInfo.
GetShape();
2275 unsigned int* viewOrigin =
const_cast<unsigned int*
>(concatDescriptor.
GetViewOrigin(viewIndex));
2276 std::fill(viewOrigin, viewOrigin + supportedNumDims, 0);
2280 mergeDim += mergeDims[concatDim];
2284 mergeDims[concatDim] = mergeDim;
2289 for (
unsigned int viewIndex = 0; viewIndex < numConcatViews; ++viewIndex)
2291 IOutputSlot& inputSlot = inputs[viewIndex].m_IndexedValue->ResolveArmnnOutputSlot(inputs[viewIndex].m_Index);
2295 return std::make_unique<SingleLayerParsedTfOperation>(
this, nodeDef, layer);
2299 const tensorflow::GraphDef& graphDef)
2309 if (tfDataType != tensorflow::DT_INT32)
2312 fmt::format(
"Armnn only supports DT_INT32 as out_type. Got {} for Node {} {}",
2313 tensorflow::DataType_Name(tfDataType),
2318 const std::vector<OutputOfParsedTfOperation> inputs = GetInputParsedTfOperationsChecked(nodeDef, 1);
2319 IOutputSlot& prevLayerOutputSlot = inputs[0].m_IndexedValue->ResolveArmnnOutputSlot(inputs[0].m_Index);
2323 std::vector<int32_t> shapeTensorData;
2324 shapeTensorData.reserve(prevLayerDimensions);
2326 for (
unsigned int i=0; i<prevLayerDimensions; ++i)
2328 shapeTensorData.push_back(static_cast<int32_t>(prevLayerTensorInfo.
GetShape()[i]));
2331 TensorInfo shapeTensorInfo(1, &prevLayerDimensions, DataType::Signed32);
2333 return std::make_unique<ParsedConstTfOperation<int32_t>>(
this,
2335 &shapeTensorData[0],
2340 const tensorflow::GraphDef& graphDef)
2343 std::vector<OutputOfParsedTfOperation> inputs = GetInputParsedTfOperationsChecked(nodeDef, 2);
2344 ParsedTfOperation* inputNode = inputs[0].m_IndexedValue;
2346 if (!HasParsedConstTensor<int32_t>(inputs[1].m_IndexedValue->GetNode().name()))
2349 fmt::format(
"ArmNN only supports Reshape layers with constant shapes. " 2350 "Input {} Node {} {}",
2351 inputs[1].m_IndexedValue->GetNode().name(),
2355 ParsedConstTfOperation<int32_t>* shapeNode =
2356 PolymorphicDowncast<ParsedConstTfOperation<int32_t>*>(inputs[1].m_IndexedValue);
2358 armnn::IOutputSlot& prevLayerOutputSlot = inputNode->ResolveArmnnOutputSlot(inputs[0].m_Index);
2359 TensorInfo inputTensorInfo = prevLayerOutputSlot.GetTensorInfo();
2361 std::vector<int32_t> shapeTensorData;
2362 ConstTensor shapeTensor = shapeNode->GetConstTensor(shapeTensorData);
2363 const TensorInfo outputTensorInfo = PrepareReshape(inputTensorInfo, shapeTensorData);
2373 return std::make_unique<SingleLayerParsedTfOperation>(
this, nodeDef, layer);
2377 const tensorflow::GraphDef& graphDef)
2380 std::vector<OutputOfParsedTfOperation> inputs = GetInputParsedTfOperationsChecked(nodeDef, 2);
2382 if (!HasParsedConstTensor<int32_t>(inputs[1].m_IndexedValue->GetNode().name()))
2385 fmt::format(
"ArmNN only supports ResizeBilinear layers with constant sizes. " 2386 "Input {}. Node {} {}",
2387 inputs[1].m_IndexedValue->GetNode().name(),
2391 ParsedConstTfOperation<int32_t>* sizeNode =
2392 PolymorphicDowncast<ParsedConstTfOperation<int32_t>*>(inputs[1].m_IndexedValue);
2395 if (ReadOptionalNodeBoolAttribute(nodeDef,
"align_corners",
false))
2398 fmt::format(
"ArmNN only supports ResizeBilinear layers with align_corners set to false. " 2405 std::vector<int32_t> sizeTensorData;
2406 ConstTensor sizeTensor = sizeNode->GetConstTensor(sizeTensorData);
2412 desc.
m_TargetWidth =
static_cast<uint32_t
> (sizeTensorData[1]);
2417 IOutputSlot& inputSlot = inputs[0].m_IndexedValue->ResolveArmnnOutputSlot(inputs[0].m_Index);
2421 unsigned int outBatch = inputTensorInfo.
GetShape()[0];
2422 unsigned int outChannels = inputTensorInfo.
GetShape()[3];
2425 TensorShape outShape({outBatch, outHeight, outWidth, outChannels });
2432 return std::make_unique<SingleLayerParsedTfOperation>(
this, nodeDef, layer);
2441 if (tfDataType == tensorflow::DT_FLOAT)
2443 type = DataType::Float32;
2445 else if (tfDataType == tensorflow::DT_INT32)
2447 type = DataType::Signed32;
2452 fmt::format(
"Unsupported DataType {} for Squeeze operation {} {}",
2453 tensorflow::DataType_Name(tfDataType),
2462 fmt::format(
"Unsupported number of dimensions: {} for input shape for Squeeze {} {}",
2468 std::vector<uint32_t> squeezeDims = ReadOptionalNodeUint32ListAttribute(nodeDef,
"squeeze_dims");
2469 static const uint32_t dimensionSequence[] = { 0, 1, 2, 3 };
2471 if (squeezeDims.empty())
2473 squeezeDims.assign(dimensionSequence,
2477 std::vector<uint32_t> outputDims;
2480 bool skipSqueeze = (std::find(squeezeDims.begin(), squeezeDims.end(), i) == squeezeDims.end());
2481 auto currentDimension = inputTensorInfo.
GetShape()[i];
2482 if (skipSqueeze || currentDimension != 1)
2484 outputDims.push_back(currentDimension);
2488 if (outputDims.size() > 4)
2491 fmt::format(
"Unsupported number of dimensions: {} for output shape for Squeeze {} {}",
2502 outTensorInfo.SetDataType(type);
2504 return outTensorInfo;
2507 ParsedTfOperationPtr TfParser::ParseSqueeze(
const tensorflow::NodeDef& nodeDef,
const tensorflow::GraphDef& graphDef)
2510 std::vector<OutputOfParsedTfOperation> inputs = GetInputParsedTfOperationsChecked(nodeDef, 1);
2512 IOutputSlot& prevLayerOutputSlot = inputs[0].m_IndexedValue->ResolveArmnnOutputSlot(inputs[0].m_Index);
2524 return std::make_unique<SingleLayerParsedTfOperation>(
this, nodeDef, layer);
2527 ParsedTfOperationPtr TfParser::ParseLrn(
const tensorflow::NodeDef& nodeDef,
const tensorflow::GraphDef& graphDef)
2530 std::vector<OutputOfParsedTfOperation> inputs = GetInputParsedTfOperationsChecked(nodeDef, 1);
2533 normalizationDescriptor.
m_NormMethodType = NormalizationAlgorithmMethod::LocalBrightness;
2534 normalizationDescriptor.
m_NormChannelType = NormalizationAlgorithmChannel::Across;
2535 normalizationDescriptor.
m_Alpha = ReadMandatoryNodeFloatAttribute(nodeDef,
"alpha");
2536 normalizationDescriptor.
m_Beta = ReadMandatoryNodeFloatAttribute(nodeDef,
"beta");
2537 normalizationDescriptor.
m_K = ReadMandatoryNodeFloatAttribute(nodeDef,
"bias");
2538 normalizationDescriptor.
m_NormSize = ReadMandatoryNodeUint32Attribute(nodeDef,
"depth_radius");
2544 IOutputSlot& prevLayerOutputSlot = inputs[0].m_IndexedValue->ResolveArmnnOutputSlot(inputs[0].m_Index);
2546 nodeDef.name().c_str());
2550 return std::make_unique<SingleLayerParsedTfOperation>(
this, nodeDef, layer);
2562 : DeferredSingleLayerParsedTfOperation(parser, node)
2566 void CreateLayerDeferred()
override 2569 m_Layer = m_Parser->AddFullyConnectedLayer(m_Node,
nullptr, m_Node.name().c_str());
2573 ParsedTfOperationPtr TfParser::ParseMatMul(
const tensorflow::NodeDef& nodeDef,
const tensorflow::GraphDef& graphDef)
2578 return std::make_unique<ParsedMatMulTfOperation>(
this, nodeDef);
2581 ParsedTfOperationPtr TfParser::ParseMean(
const tensorflow::NodeDef& nodeDef,
const tensorflow::GraphDef& graphDef)
2584 std::vector<OutputOfParsedTfOperation> inputs = GetInputParsedTfOperationsChecked(nodeDef, 2);
2585 IOutputSlot& inputSlot = inputs[0].m_IndexedValue->ResolveArmnnOutputSlot(inputs[0].m_Index);
2588 if (inputs.size() != 2)
2591 fmt::format(
"Mean expects two inputs!. Got {} for Node {} {}",
2597 bool keepDims = ReadMandatoryNodeBoolAttribute(nodeDef,
"keep_dims");
2599 ParsedConstTfOperation<int32_t>* axisNode =
2600 PolymorphicDowncast<ParsedConstTfOperation<int32_t>*>(inputs[1].m_IndexedValue);
2602 const TensorInfo& axisTensorInfo = axisNode->GetTensorInfo();
2604 ConstTensor axisTensor(axisTensorInfo, axisNode->GetStorage());
2605 const int* axisData =
static_cast<const int*
>(axisTensor.GetMemoryArea());
2614 std::vector<int> rawAxisVector(axisData, axisData + axisTensorInfo.GetNumElements());
2615 std::set<unsigned int> positiveAxisSet;
2618 std::transform(rawAxisVector.begin(), rawAxisVector.end(),
2619 std::inserter(positiveAxisSet, positiveAxisSet.begin()),
2620 [rank](
int i) ->
unsigned int {
return static_cast<unsigned int>((i + rank) % rank); });
2626 meanDescriptor.
m_Axis.assign(positiveAxisSet.begin(), positiveAxisSet.end());
2633 return std::make_unique<SingleLayerParsedTfOperation>(
this, nodeDef, layer);
2645 : DeferredSingleLayerParsedTfOperation(parser, node)
2649 void CreateLayerDeferred()
override 2652 m_Layer = m_Parser->AddMultiplicationLayer(m_Node);
2656 ParsedTfOperationPtr TfParser::ParseMul(
const tensorflow::NodeDef& nodeDef,
const tensorflow::GraphDef& graphDef)
2660 return std::make_unique<ParsedMulTfOperation>(
this, nodeDef);
2664 const tensorflow::GraphDef& graphDef)
2668 std::vector<OutputOfParsedTfOperation> inputs = GetInputParsedTfOperationsChecked(nodeDef, 0);
2672 auto it = m_InputShapes.find(nodeDef.name());
2673 if (it == m_InputShapes.end())
2676 fmt::format(
"Missing input shape for Placeholder '{}' {}",
2680 TensorInfo tensorInfo(it->second, DataType::Float32);
2686 TrackInputBinding(layer, layerId, tensorInfo);
2688 return std::make_unique<SingleLayerParsedTfOperation>(
this, nodeDef, layer);
2691 ParsedTfOperationPtr TfParser::ParseRealDiv(
const tensorflow::NodeDef& nodeDef,
const tensorflow::GraphDef& graphDef)
2694 return AddRealDivLayer(nodeDef);
2698 const tensorflow::GraphDef& graphDef)
2703 activationDesc.
m_Function = ActivationFunction::ReLu;
2704 return AddActivationLayer(nodeDef, activationDesc);
2708 const tensorflow::GraphDef& graphDef)
2713 activationDesc.
m_Function = ActivationFunction::BoundedReLu;
2714 activationDesc.
m_A = 6.0f;
2715 activationDesc.
m_B = 0.0f;
2717 return AddActivationLayer(nodeDef, activationDesc);
2721 const tensorflow::GraphDef& graphDef)
2726 activationDesc.
m_Function = ActivationFunction::Sigmoid;
2728 return AddActivationLayer(nodeDef, activationDesc);
2732 const tensorflow::GraphDef &graphDef)
2736 std::vector<OutputOfParsedTfOperation> inputs = GetInputParsedTfOperationsChecked(nodeDef, 1);
2741 IOutputSlot& prevLayerOutputSlot = inputs[0].m_IndexedValue->ResolveArmnnOutputSlot(inputs[0].m_Index);
2745 return std::make_unique<SingleLayerParsedTfOperation>(
this, nodeDef, layer);
2749 const tensorflow::GraphDef& graphDef)
2753 std::vector<OutputOfParsedTfOperation> inputs = GetInputParsedTfOperationsChecked(nodeDef, 1);
2758 IOutputSlot& prevLayerSlot = inputs[0].m_IndexedValue->ResolveArmnnOutputSlot(inputs[0].m_Index);
2762 return std::make_unique<SingleLayerParsedTfOperation>(
this, nodeDef, layer);
2766 const tensorflow::GraphDef& graphDef)
2770 std::vector<OutputOfConstNodeDef> nodes = GetTfInputNodes(nodeDef);
2771 unsigned int numInputs =
static_cast<unsigned int>(nodes.size());
2772 std::vector<OutputOfParsedTfOperation> inputs = GetInputParsedTfOperationsChecked(nodeDef, numInputs);
2775 unsigned int index = GetConstInputIndex(inputs);
2777 ParsedConstTfOperation<int32_t>* shapeNode =
2778 PolymorphicDowncast<ParsedConstTfOperation<int32_t>*>(inputs[index].m_IndexedValue);
2780 std::vector<int32_t> axisTensorData;
2781 shapeNode->GetConstTensor(axisTensorData);
2784 const unsigned int splitDim =
static_cast<unsigned int>(axisTensorData[0]);
2787 if (splitDim == 0 || splitDim == 2)
2790 fmt::format(
"Dimension {} for split is not supported by Armnn. " 2798 uint32_t num_split = ReadMandatoryNodeUint32Attribute(nodeDef,
"num_split");
2800 IOutputSlot& inputSlot = inputs[1 - index].m_IndexedValue->ResolveArmnnOutputSlot(inputs[1 - index].m_Index);
2803 const unsigned int supportedNumDims = 4;
2806 if (inputDimSize != supportedNumDims)
2809 fmt::format(
"The number of dimensions: {} for input tensors of the " 2810 "split op should be {} {}",
2816 std::vector<unsigned int> splitterDimSizes(inputDimSize);
2819 for (
unsigned int i = 0; i < inputDimSize; ++i)
2821 splitterDimSizes[i] = inputTensorInfo.
GetShape()[i];
2824 if (splitterDimSizes[splitDim] % num_split != 0)
2826 throw ParseException(
"Number of splits must evenly divide the dimension");
2828 splitterDimSizes[splitDim] /= num_split;
2831 for (
unsigned int g = 0; g < num_split; ++g)
2834 for (
unsigned int dimIdx = 0; dimIdx < splitterDimSizes.size(); ++dimIdx)
2836 splitDesc.
SetViewSize(g, dimIdx, splitterDimSizes[dimIdx]);
2846 splitterDimSizes.data());
2853 return std::make_unique<SingleLayerParsedTfOperation>(
this, nodeDef, layer);
2857 const tensorflow::GraphDef& graphDef)
2862 activationDesc.
m_Function = ActivationFunction::SoftReLu;
2864 return AddActivationLayer(nodeDef, activationDesc);
2868 const tensorflow::GraphDef& graphDef)
2872 std::vector<OutputOfConstNodeDef> nodes = GetTfInputNodes(nodeDef);
2873 unsigned int numInputs =
static_cast<unsigned int>(nodes.size());
2874 std::vector<OutputOfParsedTfOperation> inputs = GetInputParsedTfOperationsChecked(nodeDef, numInputs);
2876 ParsedConstTfOperation<int32_t>* beginNode =
2877 PolymorphicDowncast<ParsedConstTfOperation<int32_t> *>(inputs[1].m_IndexedValue);
2878 std::vector<int32_t> beginTensorData;
2879 beginNode->GetConstTensor(beginTensorData);
2881 ParsedConstTfOperation<int32_t>* endNode =
2882 PolymorphicDowncast<ParsedConstTfOperation<int32_t> *>(inputs[2].m_IndexedValue);
2883 std::vector<int32_t> endTensorData;
2884 endNode->GetConstTensor(endTensorData);
2886 ParsedConstTfOperation<int32_t>* stridesNode =
2887 PolymorphicDowncast<ParsedConstTfOperation<int32_t> *>(inputs[3].m_IndexedValue);
2888 std::vector<int32_t> stridesTensorData;
2889 stridesNode->GetConstTensor(stridesTensorData);
2892 desc.
m_Begin = beginTensorData;
2893 desc.
m_End = endTensorData;
2895 desc.
m_BeginMask = ReadMandatoryNodeInt32Attribute(nodeDef,
"begin_mask");
2896 desc.
m_EndMask = ReadMandatoryNodeInt32Attribute(nodeDef,
"end_mask");
2897 desc.
m_EllipsisMask = ReadMandatoryNodeInt32Attribute(nodeDef,
"ellipsis_mask");
2898 desc.
m_NewAxisMask = ReadMandatoryNodeInt32Attribute(nodeDef,
"new_axis_mask");
2899 desc.
m_ShrinkAxisMask = ReadMandatoryNodeInt32Attribute(nodeDef,
"shrink_axis_mask");
2903 IOutputSlot& prevLayerSlot = inputs[0].m_IndexedValue->ResolveArmnnOutputSlot(inputs[0].m_Index);
2912 return std::make_unique<SingleLayerParsedTfOperation>(
this, nodeDef, layer);
2915 ParsedTfOperationPtr TfParser::ParseTanh(
const tensorflow::NodeDef& nodeDef,
const tensorflow::GraphDef& graphDef)
2920 activationDesc.
m_Function = ActivationFunction::TanH;
2921 activationDesc.
m_A = 1.0f;
2922 activationDesc.
m_B = 1.0f;
2924 return AddActivationLayer(nodeDef, activationDesc);
2930 std::vector<OutputOfParsedTfOperation> inputs = GetInputParsedTfOperationsChecked(nodeDef, 1);
2934 IOutputSlot& prevLayerOutputSlot = inputs[0].m_IndexedValue->ResolveArmnnOutputSlot(inputs[0].m_Index);
2937 return std::make_unique<SingleLayerParsedTfOperation>(
this, nodeDef, layer);
2941 const tensorflow::GraphDef& graphDef)
2943 return ParsePooling2d(nodeDef, graphDef, PoolingAlgorithm::Max);
2947 const tensorflow::GraphDef& graphDef)
2949 return ParsePooling2d(nodeDef, graphDef, PoolingAlgorithm::Average);
2957 std::vector<OutputOfParsedTfOperation> inputs = GetInputParsedTfOperationsChecked(nodeDef, 1);
2958 IOutputSlot& inputSlot = inputs[0].m_IndexedValue->ResolveArmnnOutputSlot(inputs[0].m_Index);
2961 if (inputs.size() != 1)
2964 fmt::format(
"2D Pooling expects one input!. Got {} for Node {} {}",
2970 std::string paddingString = ReadMandatoryNodeStringAttribute(nodeDef,
"padding");
2971 std::string dataFormat = ReadMandatoryNodeStringAttribute(nodeDef,
"data_format");
2972 std::vector<uint32_t> strides = ReadMandatoryNodeUint32ListAttribute(nodeDef,
"strides");
2973 std::vector<uint32_t> ksize = ReadMandatoryNodeUint32ListAttribute(nodeDef,
"ksize");
2981 DataLayout dataLayout = dataFormat ==
"NHWC" ? DataLayout::NHWC : DataLayout::NCHW;
2993 bool padding =
false;
2995 unsigned int outputHeight = 0;
2996 unsigned int outputWidth = 0;
3000 if (paddingString ==
"SAME")
3004 outputHeight =
static_cast<uint32_t
>(ceil(static_cast<float>(inputHeight) /
3005 static_cast<float>(pooling2dDescriptor.
m_StrideY)));
3006 outputWidth =
static_cast<uint32_t
>(ceil(static_cast<float>(inputWidth) /
3007 static_cast<float>(pooling2dDescriptor.
m_StrideX)));
3009 else if (paddingString ==
"VALID")
3013 outputHeight =
static_cast<uint32_t
>(ceil(
3014 static_cast<float>(inputHeight - pooling2dDescriptor.
m_PoolHeight + 1) /
3015 static_cast<float>(pooling2dDescriptor.
m_StrideY)));
3016 outputWidth =
static_cast<uint32_t
>(ceil(
3017 static_cast<float>(inputWidth - pooling2dDescriptor.
m_PoolWidth + 1) /
3018 static_cast<float>(pooling2dDescriptor.
m_StrideX)));
3023 case DataLayout::NHWC:
3030 case DataLayout::NCHW:
3046 if (layer ==
nullptr)
3049 fmt::format(
"Failed to add pooling2d layer for {} {}",
3058 return std::make_unique<SingleLayerParsedTfOperation>(
this, nodeDef, layer);
3061 ParsedTfOperationPtr TfParser::AddAdditionLayer(
const tensorflow::NodeDef& nodeDef,
bool isBiasAdd)
3063 std::vector<OutputOfParsedTfOperation> inputs = GetInputParsedTfOperationsChecked(nodeDef, 2);
3065 IOutputSlot* input0Slot = &inputs[0].m_IndexedValue->ResolveArmnnOutputSlot(inputs[0].m_Index);
3066 IOutputSlot* input1Slot = &inputs[1].m_IndexedValue->ResolveArmnnOutputSlot(inputs[1].m_Index);
3078 fmt::format(
"Unsupported bias for BiasAdd. It should be a 1D vector. " 3079 "Got {} dimensions for input {}. Node {} {}",
3081 inputs[1].m_IndexedValue->GetNode().name(),
3086 const std::string dataFormat = ReadMandatoryNodeStringAttribute(nodeDef,
"data_format");
3095 const bool isNHWC =
true;
3101 const bool isNHWC =
true;
3116 std::vector<unsigned int> outputShape;
3122 outputShape.push_back(std::max(input0Shape[i], input1Shape[i]));
3138 return std::make_unique<SingleLayerParsedTfOperation>(
this, nodeDef, layer);
3143 std::vector<OutputOfParsedTfOperation> inputs = GetInputParsedTfOperationsChecked(nodeDef, 2);
3146 IOutputSlot* input0Slot = &inputs[0].m_IndexedValue->ResolveArmnnOutputSlot(inputs[0].m_Index);
3147 IOutputSlot* input1Slot = &inputs[1].m_IndexedValue->ResolveArmnnOutputSlot(inputs[1].m_Index);
3153 if (input0NumDims < input1NumDims)
3155 const bool isNHWC =
true;
3158 if (input1NumDims < input0NumDims)
3160 const bool isNHWC =
true;
3167 if (input0NumDims < input1NumDims)
3176 return std::make_unique<SingleLayerParsedTfOperation>(
this, nodeDef, layer);
3181 std::vector<OutputOfParsedTfOperation> inputs = GetInputParsedTfOperationsChecked(nodeDef, 2);
3183 IOutputSlot* input0Slot = &inputs[0].m_IndexedValue->ResolveArmnnOutputSlot(inputs[0].m_Index);
3184 IOutputSlot* input1Slot = &inputs[1].m_IndexedValue->ResolveArmnnOutputSlot(inputs[1].m_Index);
3189 if (input0NumDims < input1NumDims)
3191 const bool isNHWC =
true;
3194 if (input1NumDims < input0NumDims)
3196 const bool isNHWC =
true;
3206 std::vector<unsigned int> outputShape;
3213 outputShape.push_back(std::max(input0Shape[i], input1Shape[i]));
3219 return std::make_unique<SingleLayerParsedTfOperation>(
this, nodeDef, layer);
3222 IConnectableLayer* TfParser::AddMultiplicationLayer(
const tensorflow::NodeDef& nodeDef)
3224 std::vector<OutputOfParsedTfOperation> inputs = GetInputParsedTfOperationsChecked(nodeDef, 2);
3227 IOutputSlot* input0Slot = &inputs[0].m_IndexedValue->ResolveArmnnOutputSlot(inputs[0].m_Index);
3228 IOutputSlot* input1Slot = &inputs[1].m_IndexedValue->ResolveArmnnOutputSlot(inputs[1].m_Index);
3233 if (input0NumDims < input1NumDims)
3235 const bool isNHWC =
true;
3238 if (input1NumDims < input0NumDims)
3240 const bool isNHWC =
true;
3247 if (input0NumDims < input1NumDims)
3258 IConnectableLayer* TfParser::AddFullyConnectedLayer(
const tensorflow::NodeDef& matMulNodeDef,
3259 const tensorflow::NodeDef* addNodeDef,
const char* armnnLayerName)
3262 ParsedConstTfOperation<float>* biasNode =
nullptr;
3263 if (addNodeDef !=
nullptr)
3265 std::vector<OutputOfParsedTfOperation> addInputs = GetInputParsedTfOperationsChecked(*addNodeDef, 2);
3267 if (HasParsedConstTensor<float>(addInputs[0].m_IndexedValue->GetNode().name()))
3269 biasNode = PolymorphicDowncast<ParsedConstTfOperation<float>*>(addInputs[0].m_IndexedValue);
3271 else if (HasParsedConstTensor<float>(addInputs[1].m_IndexedValue->GetNode().name()))
3273 biasNode = PolymorphicDowncast<ParsedConstTfOperation<float>*>(addInputs[1].m_IndexedValue);
3278 fmt::format(
"ArmNN only supports fully connected layers with constant bias. " 3279 "Inputs {} and {}. AddNode {}. MatMulNode {} {}",
3280 addInputs[0].m_IndexedValue->GetNode().name(),
3281 addInputs[1].m_IndexedValue->GetNode().name(),
3283 matMulNodeDef.name(),
3289 ParsedConstTfOperation<float>* weightNode =
nullptr;
3290 ParsedTfOperation* inputNode =
nullptr;
3291 unsigned int inputIdx = 0;
3292 std::vector<OutputOfParsedTfOperation> mulInputs = GetInputParsedTfOperationsChecked(matMulNodeDef, 2);
3293 if (HasParsedConstTensor<float>(mulInputs[0].m_IndexedValue->GetNode().name()))
3295 weightNode = PolymorphicDowncast<ParsedConstTfOperation<float>*>(mulInputs[0].m_IndexedValue);
3296 inputNode = mulInputs[1].m_IndexedValue;
3297 inputIdx = mulInputs[1].m_Index;
3299 else if (HasParsedConstTensor<float>(mulInputs[1].m_IndexedValue->GetNode().name()))
3301 weightNode = PolymorphicDowncast<ParsedConstTfOperation<float>*>(mulInputs[1].m_IndexedValue);
3302 inputNode = mulInputs[0].m_IndexedValue;
3303 inputIdx = mulInputs[0].m_Index;
3308 fmt::format(
"ArmNN only supports fully connected layers with constant weights. " 3309 "Inputs {} and {}. MatMulNode {} {}",
3310 mulInputs[0].m_IndexedValue->GetNode().name(),
3311 mulInputs[1].m_IndexedValue->GetNode().name(),
3312 matMulNodeDef.name(),
3316 std::vector<float> weightTensorData;
3318 ConstTensor weights = weightNode->GetConstTensor(weightTensorData);
3325 std::vector<float> biasTensorData;
3327 if (addNodeDef !=
nullptr)
3329 ConstTensor biases = biasNode->GetConstTensor(biasTensorData);
3334 fmt::format(
"Shape of matmul weights and bias do not match. " 3335 "AddNode {}. MatMulNode {} {}",
3337 matMulNodeDef.name(),
3347 inputNode->ResolveArmnnOutputSlot(inputIdx).Connect(layer->GetInputSlot(0));
3348 unsigned int batches = inputNode->ResolveArmnnOutputSlot(inputIdx).GetTensorInfo().GetShape()[0];
3352 layer->GetOutputSlot(0).SetTensorInfo(outputInfo);
3356 void TfParser::LoadNodeDef(
const tensorflow::NodeDef& nodeDef,
const tensorflow::GraphDef& graphDef)
3360 if (nodeDef.attr().count(
"T") != 0)
3362 auto attr = nodeDef.attr().at(
"T");
3365 else if (nodeDef.attr().count(
"dtype") != 0)
3367 auto attr = nodeDef.attr().at(
"dtype");
3371 if ((type != tensorflow::DT_FLOAT && type != tensorflow::DT_INT32) && nodeDef.op() !=
"Const")
3374 fmt::format(
"Currently only FLOAT and INT32 are supported for tensorflow nodes (apart from Const). " 3375 "Got {} for Node {} {}",
3376 tensorflow::DataType_Name(type),
3381 const std::string& operation = nodeDef.op();
3382 auto itControlInput = std::find(m_ControlInputs.begin(), m_ControlInputs.end(), operation);
3383 if (itControlInput != m_ControlInputs.end())
3388 auto it = ms_OperationNameToParsingFunctions.find(operation);
3389 if (it != ms_OperationNameToParsingFunctions.end())
3391 auto func = it->second;
3393 ParsedTfOperation* parsedTfOperationRaw = parsedTfOperation.get();
3396 auto it = m_ParsedTfOperations.find(nodeDef.name());
3397 if (it != m_ParsedTfOperations.end())
3399 throw ParseException(fmt::format(
"Name {} used by more than one node", nodeDef.name()));
3401 m_ParsedTfOperations[nodeDef.name()] = std::move(parsedTfOperation);
3404 if (std::find(m_RequestedOutputs.begin(), m_RequestedOutputs.end(), nodeDef.name()) !=
3405 m_RequestedOutputs.end())
3407 auto outId = ParseOutputId(nodeDef.name());
3409 IOutputSlot& prevSlot = parsedTfOperationRaw->ResolveArmnnOutputSlot(outId.m_Index);
3411 TensorInfo tensorInfo = prevSlot.GetTensorInfo();
3417 TrackOutputBinding(outputLayer, layerId, tensorInfo);
3423 fmt::format(
"Unsupported operation {} in tensorflow::GraphDef {}",
3429 void TfParser::LoadGraphDef(
const tensorflow::GraphDef& graphDef)
3432 m_NodesByName.clear();
3433 m_NetworkInputsBindingInfo.clear();
3434 m_NetworkOutputsBindingInfo.clear();
3436 for (
int i = 0; i < graphDef.node_size(); ++i)
3438 const tensorflow::NodeDef& node = graphDef.node(i);
3439 m_NodesByName[node.name()] = &node;
3443 for (
const auto& pair : m_InputShapes)
3445 const std::string& requestedInputName = pair.first;
3446 auto nodeIt = m_NodesByName.find(requestedInputName);
3447 if (nodeIt == m_NodesByName.end())
3450 fmt::format(
"Couldn't find requested input node '{}' in graph {}",
3457 std::vector<const tensorflow::NodeDef*> targetNodes;
3458 for (
const std::string& requestedOutputName : m_RequestedOutputs)
3460 auto nodeIt = m_NodesByName.find(requestedOutputName);
3461 if (nodeIt == m_NodesByName.end())
3464 fmt::format(
"Couldn't find requested output node '{}' in graph {}",
3465 requestedOutputName,
3468 targetNodes.push_back(nodeIt->second);
3472 std::vector<const tensorflow::NodeDef*> sortedNodes;
3473 if (!armnnUtils::GraphTopologicalSort<const tensorflow::NodeDef*>(
3475 [
this](
const tensorflow::NodeDef* node)
3477 auto outputs = GetTfInputNodes(*node);
3478 std::vector<const tensorflow::NodeDef*> nodesOnly;
3479 for (
const auto & o : outputs) {
3480 nodesOnly.push_back(o.m_IndexedValue);
3487 fmt::format(
"Cycle detected in graph {}",
3492 for (
const auto& it : sortedNodes)
3494 const tensorflow::NodeDef& currentNode = *it;
3495 LoadNodeDef(currentNode, graphDef);
3500 const std::map<std::string, TensorShape>& inputShapes,
3501 const std::vector<std::string>& requestedOutputs)
3503 FILE* fd = fopen(graphFile,
"r");
3508 fmt::format(
"Graph file {} failed to open {}",
3514 tensorflow::GraphDef graphDef;
3515 auto input =
new google::protobuf::io::FileInputStream(fileno(fd));
3516 bool success = google::protobuf::TextFormat::Parse(input, &graphDef);
3523 fmt::format(
"Failed to parse graph file {}",
3527 return CreateNetworkFromGraphDef(graphDef, inputShapes, requestedOutputs);
3531 const std::map<std::string, TensorShape>& inputShapes,
3532 const std::vector<std::string>& requestedOutputs)
3535 tensorflow::GraphDef graphDef;
3536 bool success = google::protobuf::TextFormat::ParseFromString(protoText, &graphDef);
3541 fmt::format(
"Failed to parse graph file {}",
3545 return CreateNetworkFromGraphDef(graphDef, inputShapes, requestedOutputs);
3549 const std::map<std::string, TensorShape>& inputShapes,
3550 const std::vector<std::string>& requestedOutputs)
3552 FILE* fd = fopen(graphFile,
"rb");
3557 fmt::format(
"Graph file {} failed to open {}",
3563 tensorflow::GraphDef graphDef;
3565 google::protobuf::io::FileInputStream inStream(fileno(fd));
3566 google::protobuf::io::CodedInputStream codedStream(&inStream);
3567 codedStream.SetTotalBytesLimit(INT_MAX);
3568 bool success = graphDef.ParseFromCodedStream(&codedStream);
3574 fmt::format(
"Failed to parse protobuf file {} {}",
3579 return CreateNetworkFromGraphDef(graphDef, inputShapes, requestedOutputs);
3582 INetworkPtr TfParser::CreateNetworkFromGraphDef(
const tensorflow::GraphDef& graphDef,
3583 const std::map<std::string, TensorShape>& inputShapes,
3584 const std::vector<std::string>& requestedOutputs)
3586 m_Network = INetwork::Create();
3588 m_InputShapes = inputShapes;
3589 if (requestedOutputs.size() == 0)
3592 fmt::format(
"requestedOutputs must have at least one entry {}",
3595 m_RequestedOutputs = requestedOutputs;
3599 LoadGraphDef(graphDef);
3609 return std::move(m_Network);
3612 void TfParser::Cleanup()
3615 m_InputShapes.clear();
3616 m_RequestedOutputs.clear();
3617 m_NodesByName.clear();
3618 m_ParsedTfOperations.clear();
3623 return GetBindingInfo(name,
"input", m_NetworkInputsBindingInfo);
3628 return GetBindingInfo(name,
"output", m_NetworkOutputsBindingInfo);
3631 std::pair<LayerBindingId, TensorInfo> TfParser::GetBindingInfo(
const std::string& layerName,
3632 const char* bindingPointDesc,
3633 const std::unordered_map<std::string, BindingPointInfo>& nameToBindingInfo)
3635 auto it = nameToBindingInfo.find(layerName);
3636 if (it == nameToBindingInfo.end())
3639 fmt::format(
"Unknown {} '{}' {}",
3649 return TrackBindingPoint(layer,
id, tensorInfo,
"input", m_NetworkInputsBindingInfo);
3654 return TrackBindingPoint(layer,
id, tensorInfo,
"output", m_NetworkOutputsBindingInfo);
3660 const char* bindingPointDesc,
3661 std::unordered_map<std::string, BindingPointInfo>& nameToBindingInfo)
3663 const std::string layerName = layer->
GetName();
3664 auto it = nameToBindingInfo.find(layerName);
3665 if (it == nameToBindingInfo.end())
3667 nameToBindingInfo[layerName] = std::make_pair(
id, tensorInfo);
3672 fmt::format(
"Id {} used by more than one {} layer {}",
uint32_t m_PadBottom
Padding bottom value in the height dimension.
bool m_BiasEnabled
Enable/disable bias.
std::unique_ptr< ITfParser, void(*)(ITfParser *parser)> ITfParserPtr
virtual IConnectableLayer * AddGatherLayer(const char *name=nullptr)=0
Add Gather layer to the network.
virtual IConnectableLayer * AddComparisonLayer(const ComparisonDescriptor &comparisonDescriptor, const char *name=nullptr)=0
Add a Comparison layer to the network.
virtual unsigned int GetNumOutputSlots() const =0
Returns the number of connectable output slots.
DataLayout m_DataLayout
The data layout to be used (NCHW, NHWC).
uint32_t m_Axis
0-based axis along which to stack the input tensors.
A ViewsDescriptor for the SplitterLayer.
Interface for a layer that is connectable to other layers via InputSlots and OutputSlots.
virtual IConnectableLayer * AddMeanLayer(const MeanDescriptor &meanDescriptor, const char *name=nullptr)=0
Add a Mean layer to the network.
uint32_t m_PadBottom
Padding bottom value in the height dimension.
bool m_BiasEnabled
Enable/disable bias.
unsigned int GetWidthIndex() const
float m_K
Kappa value used for the across channel normalization equation.
friend class ParsedMulTfOperation
WithOutputTensorIndex< ParsedTfOperation * > OutputOfParsedTfOperation
const TensorShape & GetShape() const
uint32_t m_PadBottom
Padding bottom value in the height dimension.
uint32_t m_PadLeft
Padding left value in the width dimension.
armnn::BindingPointInfo BindingPointInfo
int32_t m_ShrinkAxisMask
Shrink axis mask value. If set, the nth specification shrinks the dimensionality by 1...
A ReshapeDescriptor for the ReshapeLayer.
WithOutputTensorIndex< std::string > OutputId
std::vector< int > m_Begin
Begin values for the input that will be sliced.
DataLayout m_DataLayout
The data layout to be used (NCHW, NHWC).
virtual IConnectableLayer * AddSoftmaxLayer(const SoftmaxDescriptor &softmaxDescriptor, const char *name=nullptr)=0
Adds a softmax layer to the network.
DataLayout m_DataLayout
The data layout to be used (NCHW, NHWC).
A ComparisonDescriptor for the ComparisonLayer.
TensorShape m_InputShape
Required shape of all input tensors.
virtual IConnectableLayer * AddMinimumLayer(const char *name=nullptr)=0
Add a Minimum layer to the network.
uint32_t m_PoolWidth
Pooling width value.
A Convolution2dDescriptor for the Convolution2dLayer.
float m_Alpha
Alpha value for the normalization equation.
uint32_t m_PadLeft
Padding left value in the width dimension.
virtual IConnectableLayer * AddPadLayer(const PadDescriptor &padDescriptor, const char *name=nullptr)=0
Adds a fully pad layer to the network.
void CalculateReducedOutputTensoInfo(const armnn::TensorInfo &inputTensorInfo, const std::set< unsigned int > &axisSet, bool keepDims, armnn::TensorInfo &outputTensorInfo)
Creates a tensor info after reducing the dimensions mentioned in axisData.
const TensorShape & GetShape() const
unsigned int GetNumBytes() const
ResizeMethod m_Method
The Interpolation method to use (Bilinear, NearestNeighbor).
virtual IConnectableLayer * AddActivationLayer(const ActivationDescriptor &activationDescriptor, const char *name=nullptr)=0
Adds an activation layer to the network.
float m_Eps
Value to add to the variance. Used to avoid dividing by zero.
PaddingMethod m_PaddingMethod
The padding method to be used. (Exclude, IgnoreValue).
DataLayout m_DataLayout
The data layout to be used (NCHW, NHWC).
virtual IConnectableLayer * AddElementwiseUnaryLayer(const ElementwiseUnaryDescriptor &elementwiseUnaryDescriptor, const char *name=nullptr)=0
Add an ElementwiseUnary layer to the network.
unsigned int CheckPaddingTensor(const ConstTensor &paddingTensor, const TensorInfo &inputTensorInfo, const std::string &nodeName)
virtual BindingPointInfo GetNetworkOutputBindingInfo(const std::string &name) const override
Retrieves binding info (layer id and tensor info) for the network output identified by the given laye...
Main network class which provides the interface for building up a neural network. ...
virtual IConnectableLayer * AddBatchNormalizationLayer(const BatchNormalizationDescriptor &desc, const ConstTensor &mean, const ConstTensor &variance, const ConstTensor &beta, const ConstTensor &gamma, const char *name=nullptr)=0
Adds a batch normalization layer to the network.
uint32_t m_PadTop
Padding top value in the height dimension.
void CalcPadding(uint32_t input, uint32_t kernel, uint32_t stride, uint32_t &outPadHead, uint32_t &outPadTail, bool samePadding)
uint32_t m_PadRight
Padding right value in the width dimension.
DataLayout m_DataLayout
The data layout to be used (NCHW, NHWC).
Copyright (c) 2020 ARM Limited.
void IgnoreUnused(Ts &&...)
int32_t m_BeginMask
Begin mask value.
int32_t m_EndMask
End mask value.
friend class ParsedConstTfOperation
const armnn::PermutationVector NHWCToArmNN
unsigned int GetNumOutputSlots() const override
Returns the number of connectable output slots.
uint32_t m_StrideX
Stride value when proceeding through input for the width dimension.
int LayerBindingId
Type of identifiers for bindable layers (inputs, outputs).
virtual IConnectableLayer * AddNormalizationLayer(const NormalizationDescriptor &normalizationDescriptor, const char *name=nullptr)=0
Adds a normalization layer to the network.
virtual IConnectableLayer * AddFullyConnectedLayer(const FullyConnectedDescriptor &fullyConnectedDescriptor, const ConstTensor &weights, const Optional< ConstTensor > &biases, const char *name=nullptr)=0
Adds a fully connected layer to the network.
unsigned int GetHeightIndex() const
virtual void SetTensorInfo(const TensorInfo &tensorInfo)=0
NormalizationAlgorithmMethod m_NormMethodType
Normalization method algorithm to use (LocalBrightness, LocalContrast).
void SetShape(const TensorShape &newShape)
A ResizeDescriptor for the ResizeLayer.
std::vector< unsigned int > m_Axis
Values for the dimensions to reduce.
A StackDescriptor for the StackLayer.
virtual IConnectableLayer * AddConvolution2dLayer(const Convolution2dDescriptor &convolution2dDescriptor, const ConstTensor &weights, const Optional< ConstTensor > &biases, const char *name=nullptr)=0
Adds a 2D convolution layer to the network.
TensorShape m_TargetShape
Target shape value.
virtual IConnectableLayer * AddOutputLayer(LayerBindingId id, const char *name=nullptr)=0
Adds an output layer to the network.
std::unique_ptr< ParsedTfOperation > ParsedTfOperationPtr
TensorInfo OutputShapeOfExpandDims(const tensorflow::NodeDef &nodeDef, TensorInfo inputTensorInfo, std::int32_t expandDim)
virtual IConnectableLayer * AddAdditionLayer(const char *name=nullptr)=0
Adds an addition layer to the network.
uint32_t m_PoolHeight
Pooling height value.
uint32_t m_PadTop
Padding top value in the height dimension.
A PadDescriptor for the PadLayer.
void Permute(const armnn::TensorShape &dstShape, const armnn::PermutationVector &mappings, const void *src, void *dst, size_t dataTypeSize)
virtual IConnectableLayer * AddConcatLayer(const ConcatDescriptor &concatDescriptor, const char *name=nullptr)=0
Adds a concatenation layer to the network.
const uint32_t * GetViewOrigin(uint32_t idx) const
Return the view origin at the int value idx.
uint32_t m_StrideX
Stride value when proceeding through input for the width dimension.
virtual IConnectableLayer * AddStackLayer(const StackDescriptor &descriptor, const char *name=nullptr)=0
Adds a stack layer to the network.
WithOutputTensorIndex< const tensorflow::NodeDef * > OutputOfConstNodeDef
uint32_t m_StrideX
Stride value when proceeding through input for the width dimension.
uint32_t m_PadRight
Padding right value in the width dimension.
uint32_t m_PadTop
Padding top value in the height dimension.
Status SetViewSize(uint32_t view, uint32_t coord, uint32_t value)
Set the size of the views.
virtual IConnectableLayer * AddResizeLayer(const ResizeDescriptor &resizeDescriptor, const char *name=nullptr)=0
Adds a resize layer to the network.
int32_t m_NewAxisMask
New axis mask value.
virtual IConnectableLayer * AddPooling2dLayer(const Pooling2dDescriptor &pooling2dDescriptor, const char *name=nullptr)=0
Adds a pooling layer to the network.
bool m_KeepDims
Enable/disable keep dimensions. If true, then the reduced dimensions that are of length 1 are kept...
An output connection slot for a layer.
Provides access to the appropriate indexes for Channels, Height and Width based on DataLayout...
DataType GetDataType() const
An OriginsDescriptor for the ConcatLayer.
A FullyConnectedDescriptor for the FullyConnectedLayer.
int32_t m_EllipsisMask
Ellipsis mask value.
virtual IConnectableLayer * AddSplitterLayer(const ViewsDescriptor &splitterDescriptor, const char *name=nullptr)=0
Adds a splitter layer to the network.
bool m_BiasEnabled
Enable/disable bias.
WithOutputTensorIndex wraps a value and an index.
A tensor defined by a TensorInfo (shape and data type) and an immutable backing store.
uint32_t m_TargetWidth
Target width value.
A GatherDescriptor for the GatherLayer.
virtual armnn::INetworkPtr CreateNetworkFromString(const char *protoText, const std::map< std::string, armnn::TensorShape > &inputShapes, const std::vector< std::string > &requestedOutputs) override
Creates the network directly from protobuf text in a string. Useful for debugging/testing.
#define ARMNN_ASSERT(COND)
std::vector< int > m_Stride
Stride values for the input that will be sliced.
An ActivationDescriptor for the ActivationLayer.
friend class ParsedMatMulTfOperation
void CalculateSamePadding(uint32_t inputSize, uint32_t stride, uint32_t filterSize, bool samePadding, uint32_t *paddingFront, uint32_t *paddingBack)
void SetDataType(DataType type)
uint32_t m_NumInputs
Number of input tensors.
uint32_t m_TargetHeight
Target height value.
virtual IConnectableLayer * AddDepthwiseConvolution2dLayer(const DepthwiseConvolution2dDescriptor &convolution2dDescriptor, const ConstTensor &weights, const Optional< ConstTensor > &biases, const char *name=nullptr)=0
Adds a 2D depthwise convolution layer to the network.
uint32_t m_StrideY
Stride value when proceeding through input for the height dimension.
DataLayout m_DataLayout
The data layout to be used (NCHW, NHWC).
void CalculateStridedSliceOutputTensorInfo(const armnn::TensorInfo &inputTensorInfo, const armnn::StridedSliceDescriptor &desc, armnn::TensorInfo &outputTensorInfo)
Create output tensor info for a StridedSlice operator.
std::vector< int > m_End
End values for the input that will be sliced.
NormalizationAlgorithmChannel m_NormChannelType
Normalization channel algorithm to use (Across, Within).
DataType ConvertTfTensorDataType(const tensorflow::DataType tfDataType, const tensorflow::NodeDef &nodeDef)
float m_A
Alpha upper bound value used by the activation functions. (BoundedReLu, Linear, TanH, Elu).
#define CHECK_DATA_FORMAT(NODE_DEF, FORMAT, NODE_TYPE)
virtual IConnectableLayer * AddStridedSliceLayer(const StridedSliceDescriptor &stridedSliceDescriptor, const char *name=nullptr)=0
Adds a strided slice layer to the network.
EmptyOptional is used to initialize the Optional class in case we want to have default value for an O...
virtual IConnectableLayer * AddReshapeLayer(const ReshapeDescriptor &reshapeDescriptor, const char *name=nullptr)=0
Adds a reshape layer to the network.
int32_t m_Axis
The axis in params to gather indices from.
A ElementwiseUnaryDescriptor for the ElementwiseUnaryLayer.
PoolingAlgorithm m_PoolType
The pooling algorithm to use (Max. Average, L2).
uint32_t m_StrideY
Stride value when proceeding through input for the height dimension.
Parses a directed acyclic graph from a tensorflow protobuf file.
virtual IConnectableLayer * AddMaximumLayer(const char *name=nullptr)=0
Add a Maximum layer to the network.
unsigned int GetNumDimensions() const
Function that returns the tensor rank.
virtual armnn::INetworkPtr CreateNetworkFromTextFile(const char *graphFile, const std::map< std::string, armnn::TensorShape > &inputShapes, const std::vector< std::string > &requestedOutputs) override
Creates the network from a protobuf text file on the disk.
OutputShapeRounding m_OutputShapeRounding
The rounding method for the output shape. (Floor, Ceiling).
void SetConcatAxis(unsigned int concatAxis)
Set the concatenation axis value.
void SetTensorInfo(const TensorInfo &tensorInfo) override
virtual const IInputSlot & GetInputSlot(unsigned int index) const =0
Get a const input slot handle by slot index.
A MeanDescriptor for the MeanLayer.
std::enable_if_t< std::is_unsigned< Source >::value &&std::is_unsigned< Dest >::value, Dest > numeric_cast(Source source)
armnn::TensorInfo GetTensorInfo(unsigned int numberOfBatches, unsigned int numberOfChannels, unsigned int height, unsigned int width, const armnn::DataLayout dataLayout, const armnn::DataType dataType)
const OutputSlot & GetOutputSlot(unsigned int index=0) const override
Get the const output slot handle by slot index.
A TransposeDescriptor for the TransposeLayer.
A StridedSliceDescriptor for the StridedSliceLayer.
virtual const TensorInfo & GetTensorInfo() const =0
virtual const IOutputSlot & GetOutputSlot(unsigned int index) const =0
Get the const output slot handle by slot index.
const char * GetName() const override
Returns the name of the layer.
virtual const char * GetName() const =0
Returns the name of the layer.
std::unique_ptr< INetwork, void(*)(INetwork *network)> INetworkPtr
armnn::TensorShape TransposeTensorShape(const armnn::TensorShape &srcShape, const armnn::PermutationVector &mappings)
#define CHECK_PADDING_TYPE(NODE_DEF, PADDING)
virtual int Connect(IInputSlot &destination)=0
OptimizeForType< Layer, AddBroadcastReshapeLayerImpl > AddBroadcastReshapeLayer
A Pooling2dDescriptor for the Pooling2dLayer.
A NormalizationDescriptor for the NormalizationLayer.
virtual BindingPointInfo GetNetworkInputBindingInfo(const std::string &name) const override
Retrieves binding info (layer id and tensor info) for the network input identified by the given layer...
DataLayout m_DataLayout
The data layout to be used (NCHW, NHWC).
const armnn::PermutationVector ArmNNToNHWC
unsigned int GetNumDimensions() const
virtual IConnectableLayer * AddDivisionLayer(const char *name=nullptr)=0
Adds a division layer to the network.
virtual IConnectableLayer * AddInputLayer(LayerBindingId id, const char *name=nullptr)=0
Adds an input layer to the network.
float m_B
Beta lower bound value used by the activation functions. (BoundedReLu, Linear, TanH).
armnn::TensorShape Permuted(const armnn::TensorShape &srcShape, const armnn::PermutationVector &mappings)
A SoftmaxDescriptor for the SoftmaxLayer.
float m_Beta
Beta value for the normalization equation.
uint32_t m_NormSize
Depth radius value.
Status SetViewOriginCoord(uint32_t view, uint32_t coord, uint32_t value)
Set the view origin coordinates.
ActivationFunction m_Function
The activation function to use (Sigmoid, TanH, Linear, ReLu, BoundedReLu, SoftReLu, LeakyReLu, Abs, Sqrt, Square, Elu).
virtual armnn::INetworkPtr CreateNetworkFromBinaryFile(const char *graphFile, const std::map< std::string, armnn::TensorShape > &inputShapes, const std::vector< std::string > &requestedOutputs) override
Creates the network from a protobuf binary file on the disk.
uint32_t m_StrideY
Stride value when proceeding through input for the height dimension.
A DepthwiseConvolution2dDescriptor for the DepthwiseConvolution2dLayer.
A BatchNormalizationDescriptor for the BatchNormalizationLayer.
uint32_t m_PadLeft
Padding left value in the width dimension.
unsigned int GetNumElements() const
Status SetViewOriginCoord(uint32_t view, uint32_t coord, uint32_t value)
Set the view origin coordinates.
constexpr unsigned int GetDataTypeSize(DataType dataType)
virtual IConnectableLayer * AddTransposeLayer(const TransposeDescriptor &transposeDescriptor, const char *name=nullptr)=0
Adds a transpose layer to the network.
TensorInfo CalculatePaddedOutputTensorInfo(const TensorInfo &inputTensorInfo, const std::vector< std::pair< unsigned int, unsigned int >> &padList)
uint32_t m_PadRight
Padding right value in the width dimension.
virtual IConnectableLayer * AddMultiplicationLayer(const char *name=nullptr)=0
Adds a multiplication layer to the network.
virtual IConnectableLayer * AddSubtractionLayer(const char *name=nullptr)=0
Adds a subtraction layer to the network.
TensorInfo OutputShapeOfSqueeze(const tensorflow::NodeDef &nodeDef, TensorInfo inputTensorInfo)