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;
#define ARMNN_THROW_INVALIDARG_MSG_IF_FALSE(_cond, _str)
#define ARMNN_LOG(severity)
#define ARMNN_SCOPED_PROFILING_EVENT(backendId, name)
arm::pipe::ProfilingGuid LayerGuid
Define LayerGuid type.
DotAttributeSet & AddAttribute(const std::string &name, const std::stringstream &value)
DotAttributeSet & GetAttributeSet()
DotAttributeSet & GetAttributeSet()
NodeContent & GetContents()
Base class for all ArmNN exceptions so that users can filter to just those.
Status SerializeToDot(std::ostream &stream)
Status AllocateDynamicBuffers()
Allocates memory for all tensors under output tensor handers of each layer.
Status Print(bool extended=false) const
Graph & TopologicalSort()
Sorts layers in topological order and return this.
Graph(bool shapeInferenceMethod=false, bool allowExpandedDims=false)
void SubstituteSubgraph(SubgraphView &subgraph, IConnectableLayer *substituteLayer)
Substitutes the given sub-graph with either a new layer or a new sub-graph.
void AddCompatibilityLayers(std::map< BackendId, std::unique_ptr< class IBackendInternal >> &backends, TensorHandleFactoryRegistry ®istry)
Modifies the graph in-place, removing edges connecting layers using different compute devices,...
void ForEachLayer(Func func) const
Interface for a layer that is connectable to other layers via InputSlots and OutputSlots.
An output connection slot for a layer.
virtual bool IsTensorInfoSet() const =0
static const FactoryId LegacyFactoryId
virtual void Manage()=0
Indicate to the memory manager that this resource is active.
virtual ITensorHandle * GetParent() const =0
Get the parent tensor if this is a subtensor.
virtual void Allocate()=0
Indicate to the memory manager that this resource is no longer active.
const OutputSlot & GetOutputSlot(unsigned int index=0) const override
Get the const output slot handle by slot index.
void SetBackendId(const BackendId &id) override
Set the backend of the IConnectableLayer.
const char * GetName() const override
Returns the name of the layer.
std::vector< OutputSlot >::iterator BeginOutputSlots()
LayerGuid GetGuid() const final
Returns the unique id of the layer.
const std::vector< OutputSlot > & GetOutputSlots() const
unsigned int GetNumInputSlots() const override
Returns the number of connectable input slots.
const InputSlot & GetInputSlot(unsigned int index) const override
Get a const input slot handle by slot index.
LayerType GetType() const override
Returns the armnn::LayerType of this layer.
virtual Layer * Clone(Graph &graph) const =0
Creates a dynamically-allocated copy of this layer.
const BackendId & GetBackendId() const
NodeContent & AddContent(const std::string &content)
const std::vector< EdgeStrategy > & GetEdgeStrategies() const
void SetEdgeStrategy(unsigned int connectionIndex, EdgeStrategy strategy)
const std::vector< InputSlot * > & GetConnections() const
Layer & GetOwningLayer() const
const TensorInfo & GetTensorInfo() const override
void SetTensorHandleFactory(const ITensorHandleFactory::FactoryId &id)
ITensorHandleFactory::FactoryId GetTensorHandleFactoryId() const
The SubgraphView class represents a subgraph of a Graph.
std::vector< IOutputSlot * > IOutputSlots
std::vector< IInputSlot * > IInputSlots
const IConnectableLayers & GetIConnectableLayers() const
void ForEachIConnectableLayer(Func func) const
std::list< IConnectableLayer * > IConnectableLayers
const IInputSlots & GetIInputSlots() const
const IOutputSlots & GetIOutputSlots() const
ITensorHandleFactory * GetFactory(ITensorHandleFactory::FactoryId id) const
Find a TensorHandleFactory by Id Returns nullptr if not found.
const TensorShape & GetShape() const
Copyright (c) 2021 ARM Limited and Contributors.
std::function< void(const std::string &name, const std::string &value)> ParameterStringifyFunction
const char * GetLayerTypeAsCString(LayerType type)
@ ExportToTarget
Destination backend can work directly with tensors on source backend.
@ DirectCompatibility
No strategy has been defined. Used internally to verify integrity of optimizations.
@ CopyToTarget
Source backends tensor data can be exported to destination backend tensor without copy.