18 #include <fmt/format.h>
20 #include <unordered_map>
28 : m_LayersInOrder(other.m_LayersInOrder)
29 , m_AllowExpandedDims(other.m_AllowExpandedDims)
30 , m_ShapeInferenceMethod(other.m_ShapeInferenceMethod)
31 , m_Profiler(other.m_Profiler)
33 std::unordered_map<const Layer*, Layer*> otherToClonedMap;
35 for (
auto&& otherLayer : other.m_Layers)
38 otherToClonedMap.emplace(otherLayer, layer);
42 for (
auto&& otherLayer : other.m_Layers)
44 Layer*
const thisLayer = otherToClonedMap[otherLayer];
47 for (
auto&& otherOutputSlot : otherLayer->GetOutputSlots())
49 for (
auto&& otherInputSlot : otherOutputSlot.GetConnections())
51 const Layer& otherTgtLayer = otherInputSlot->GetOwningLayer();
52 Layer*
const thisTgtLayer = otherToClonedMap[&otherTgtLayer];
56 if (otherInputSlot->IsTensorInfoOverridden())
60 outputSlot->Connect(inputSlot);
62 outputSlot->SetTensorInfo(otherOutputSlot.GetTensorInfo());
80 auto numInputSlots = it->GetNumInputSlots();
81 auto numOutputSlots = it->GetNumOutputSlots();
87 guid += std::to_string(it->GetGuid());
90 <<
":" << it->GetBackendId().Get()
92 <<
" has " << numInputSlots <<
" input slots"
93 <<
" and " << numOutputSlots <<
" output slots.";
95 for (
auto i : it->GetInputSlots())
97 std::ostringstream message;
98 auto inputTensorShape = i.GetConnectedOutputSlot()->GetTensorInfo().GetShape();
99 unsigned int numDims = inputTensorShape.GetNumDimensions();
101 message <<
"The input slot has shape [ ";
102 for (
unsigned int dim=0; dim < numDims; dim++)
104 message << inputTensorShape[dim] <<
",";
109 message <<
" Scale: " << i.GetConnectedOutputSlot()->GetTensorInfo().GetQuantizationScale();
110 message <<
" Offset: " << i.GetConnectedOutputSlot()->GetTensorInfo().GetQuantizationOffset();
111 message <<
" The input slot is connected to: ";
112 message << i.GetConnectedOutputSlot()->GetOwningIConnectableLayer().GetGuid();
117 for (
unsigned int i = 0; i < it->GetNumOutputSlots(); i++)
120 std::ostringstream message;
121 auto outputTensorShape = layer->
GetOutputSlots()[i].GetTensorInfo().GetShape();
122 unsigned int numDims = outputTensorShape.GetNumDimensions();
124 message <<
"The output slot has shape [ ";
125 for (
unsigned int dim=0; dim < numDims; dim++)
127 message << outputTensorShape[dim] <<
",";
132 message <<
" Scale: " << layer->
GetOutputSlots()[i].GetTensorInfo().GetQuantizationScale();
133 message <<
" Offset: " << layer->
GetOutputSlots()[i].GetTensorInfo().GetQuantizationOffset();
134 message <<
" The output slot is connected to: ";
135 message << layer->
GetOutputSlots()[i].GetConnection(0)->GetOwningIConnectableLayer().GetGuid();
149 DotGraph graph(stream,
"Optimized");
168 for (
auto&& layer : m_Layers)
175 layer->SerializeLayerParameters(extractParams);
179 for (
auto&& layer : m_Layers)
183 for (
unsigned int i=0;i<layer->GetNumInputSlots(); i++)
187 DotEdge edge(stream, fromId, toId);
192 std::stringstream ss;
214 std::unordered_set<const ITensorHandle*> preallocatedTensors;
215 std::unordered_map<const ITensorHandle*, unsigned int> handleReferenceCounts;
219 auto TraceSubTensorHandleAncestry = [](
ITensorHandle*
const subTensorHandle)
222 while (ancestor && ancestor->
GetParent())
232 return tensorHandle && preallocatedTensors.find(tensorHandle) != preallocatedTensors.end();
237 for (
auto&& layer : m_Layers)
241 for (
auto&& slot = layer->BeginOutputSlots(); slot != layer->EndOutputSlots(); ++slot)
243 ITensorHandle *tensorHandle = TraceSubTensorHandleAncestry(slot->GetOutputHandler().GetData());
245 if (tensorHandle && !IsPreallocated(tensorHandle))
248 preallocatedTensors.insert(tensorHandle);
255 for (
auto&& layer : m_Layers)
259 for (
auto&& slot = layer->BeginOutputSlots(); slot != layer->EndOutputSlots(); ++slot)
261 ITensorHandle *tensorHandle = TraceSubTensorHandleAncestry(slot->GetOutputHandler().GetData());
263 if (tensorHandle && !IsPreallocated(tensorHandle))
265 unsigned int numConnections = slot->GetNumConnections();
266 if (handleReferenceCounts.find(tensorHandle) == handleReferenceCounts.end())
268 handleReferenceCounts[tensorHandle] = numConnections;
270 if (handleReferenceCounts[tensorHandle] == 0u)
278 handleReferenceCounts[tensorHandle] += numConnections;
285 for (
auto&& slot = layer->BeginInputSlots(); slot != layer->EndInputSlots(); ++slot)
288 slot->GetConnectedOutputSlot()->GetOutputHandler().GetData());
290 if (tensorHandle && !IsPreallocated(tensorHandle))
292 --handleReferenceCounts[tensorHandle];
294 if (handleReferenceCounts[tensorHandle] == 0u)
298 handleReferenceCounts.erase(tensorHandle);
309 if (!m_LayersInOrder)
312 for (
auto&& it : m_Layers)
317 auto compareLayerPriority = [](
const LayerList::value_type& layerA,
const LayerList::value_type& layerB)
319 return layerA->GetPriority() < layerB->GetPriority();
322 m_Layers.sort(compareLayerPriority);
324 m_LayersInOrder =
true;
335 auto MayNeedCompatibilityLayer = [](
const Layer& layer)
340 throw armnn::Exception(
"AddCompatibilityLayers: All layers must be assigned to a backend at this point.");
347 auto IsCompatibilityStrategy = [](
EdgeStrategy strategy)
353 ForEachLayer([
this, &backends, ®istry, MayNeedCompatibilityLayer, IsCompatibilityStrategy](
Layer* srcLayer)
357 if (!MayNeedCompatibilityLayer(*srcLayer))
363 const std::vector<OutputSlot>& srcOutputSlots = srcLayer->
GetOutputSlots();
364 for (
unsigned int srcOutputIndex = 0; srcOutputIndex < srcOutputSlots.size(); srcOutputIndex++)
367 const std::vector<InputSlot*> srcConnections = srcOutputSlot.
GetConnections();
368 const std::vector<EdgeStrategy> srcEdgeStrategies = srcOutputSlot.
GetEdgeStrategies();
369 for (
unsigned int srcConnectionIndex = 0; srcConnectionIndex < srcConnections.size(); srcConnectionIndex++)
371 InputSlot* dstInputSlot = srcConnections[srcConnectionIndex];
377 EdgeStrategy strategy = srcEdgeStrategies[srcConnectionIndex];
381 "while adding copy layers for compatibility");
385 if (MayNeedCompatibilityLayer(dstLayer) &&
386 IsCompatibilityStrategy(strategy))
391 const std::string compLayerName = fmt::format(
"[ {} ({}) -> {} ({}) ]",
396 Layer* compLayer =
nullptr;
399 compLayer = InsertNewLayer<MemCopyLayer>(*dstInputSlot, compLayerName.c_str());
408 compLayer = InsertNewLayer<MemImportLayer>(*dstInputSlot, compLayerName.c_str());
414 auto backendIt = backends.find(dstLayer.
GetBackendId());
415 if (backendIt != backends.end() &&
417 backendIt->second->SupportsTensorAllocatorAPI())
419 auto backend = backendIt->second.get();
420 auto tensorHandleFactoryIds = backend->GetHandleFactoryPreferences();
423 for (
auto preference : tensorHandleFactoryIds)
425 auto factory = registry.
GetFactory(preference);
429 auto srcFactory = registry.
GetFactory(srcPref);
433 bool canExportImport =
434 (factory->GetImportFlags() & srcFactory->GetExportFlags()) != 0;
436 if (factory->SupportsMapUnmap() || canExportImport)
460 const std::vector<InputSlot*>& newSourceConnections = srcOutputSlot.
GetConnections();
461 auto newSrcConnectionIndex = std::distance(newSourceConnections.begin(),
462 std::find(newSourceConnections.begin(),
463 newSourceConnections.end(),
467 srcOutputSlot.
SetEdgeStrategy(armnn::numeric_cast<unsigned int>(newSrcConnectionIndex),
491 if (std::find(std::begin(m_Layers),
493 iConnectableLayer) == std::end(m_Layers))
495 auto layer = PolymorphicDowncast<Layer*>(iConnectableLayer);
496 layer->Reparent(*
this, m_Layers.end());
497 m_LayersInOrder =
false;
501 ReplaceSubgraphConnections(subgraph, substituteSubgraph);
502 EraseSubgraphLayers(subgraph);
510 throw armnn::Exception(
"New sub-graph used for substitution must not be empty");
514 std::for_each(substituteSubgraphLayers.begin(), substituteSubgraphLayers.end(), [&](IConnectableLayer* layer)
516 layer = PolymorphicDowncast<Layer*>(layer);
517 if (std::find(m_Layers.begin(), m_Layers.end(), layer) == m_Layers.end())
519 throw armnn::Exception(
"Substitute layer is not a member of graph");
526 unsigned int subgraphNumInputSlots = armnn::numeric_cast<unsigned int>(subgraphInputSlots.size());
527 unsigned int subgraphNumOutputSlots = armnn::numeric_cast<unsigned int>(subgraphOutputSlots.size());
532 if (subgraphNumInputSlots != substituteSubgraphInputSlots.size())
534 throw armnn::Exception(
"subgraph and substitute subgraph input slot sizes must be the same.");
537 if (subgraphNumOutputSlots != substituteSubgraphOutputSlots.size())
539 throw armnn::Exception(
"subgraph and substitute subgraph output slot sizes must be the same.");
545 for (
unsigned int inputSlotIdx = 0; inputSlotIdx < subgraphNumInputSlots; ++inputSlotIdx)
547 IInputSlot* subgraphInputSlot = subgraphInputSlots.at(inputSlotIdx);
548 if (!subgraphInputSlot)
556 if (subgraphInputSlot->GetConnection())
558 IOutputSlot* connectedOutputSlot = subgraphInputSlot->GetConnection();
559 InputSlot* inputSlot = PolymorphicDowncast<InputSlot*>(subgraphInputSlot);
560 bool isOverridden = inputSlot->IsTensorInfoOverridden();
562 if (!connectedOutputSlot)
567 connectedOutputSlot->Disconnect(*subgraphInputSlot);
569 IInputSlot* substituteInputSlot = substituteSubgraphInputSlots.at(inputSlotIdx);
570 if (!substituteInputSlot)
575 connectedOutputSlot->Connect(*substituteInputSlot);
579 TensorInfo overridden = inputSlot->GetTensorInfo();
580 InputSlot* newInputSlot = PolymorphicDowncast<InputSlot*>(substituteInputSlot);
581 newInputSlot->SetTensorInfo(overridden);
587 for(
unsigned int outputSlotIdx = 0; outputSlotIdx < subgraphNumOutputSlots; ++outputSlotIdx)
589 auto subgraphOutputSlot =
590 PolymorphicDowncast<OutputSlot*>(subgraphOutputSlots.at(outputSlotIdx));
591 if (!subgraphOutputSlot)
596 auto substituteOutputSlot =
597 PolymorphicDowncast<OutputSlot*>(substituteSubgraphOutputSlots.at(outputSlotIdx));
598 if (!substituteOutputSlot)
603 subgraphOutputSlot->MoveAllConnections(*substituteOutputSlot);
607 void Graph::EraseSubgraphLayers(SubgraphView &subgraph)
610 for (
auto iConnectableLayer : subgraph.GetIConnectableLayers())
612 auto layer = PolymorphicDowncast<Layer*>(iConnectableLayer);
622 void Graph::VerifyConstantLayerSetTensorInfo()
const
624 for (
auto&& layer : TopologicalSort())
628 for (
auto&& output: layer->GetOutputSlots())
630 if (!output.IsTensorInfoSet())
632 std::ostringstream message;
633 message <<
"Output slot TensorInfo not set on "
645 void Graph::InferTensorInfos()
647 for (
auto&& layer : TopologicalSort())
649 for (
auto&& input : layer->GetInputSlots())
651 const IOutputSlot* source = input.GetConnectedOutputSlot();
656 ConstructErrorMessageForUnconnectedInputs(layer, input.GetSlotIndex());
661 std::ostringstream message;
662 message <<
"Output slot TensorInfo not set on "
665 << std::quoted(layer->GetName());
670 if (layer->m_ShapeInferenceMethod == ShapeInferenceMethod::ValidateOnly)
672 layer->ValidateTensorShapesFromInputs();
684 void Graph::ConstructErrorMessageForUnconnectedInputs(
Layer*
const layer,
685 unsigned int slotIndex)
687 std::ostringstream message;
688 bool noWeightsAndBias =
false;
695 message << std::endl;
701 if (biasSource == NULL)
703 message <<
"Weights and bias layers not set." << std::endl;
704 noWeightsAndBias =
true;
709 if (!noWeightsAndBias)
713 message <<
"Weights layer not set." << std::endl;
717 message <<
"Bias layer not set." << std::endl;
722 std::string slotString = noWeightsAndBias ?
"1 & 2" : std::to_string(slotIndex);
723 message <<
"Input slot(s) "
727 <<
" not connected to an output slot. " << std::endl
729 << std::quoted(layer->
GetName());
730 throw LayerValidationException(message.str());
733 const std::shared_ptr<IProfiler>& Graph::GetProfiler()
const
738 void Graph::SetLayersOutOfOrder()
740 m_LayersInOrder =
false;