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)
37 Layer* const layer = otherLayer->Clone(*this);
38 otherToClonedMap.emplace(otherLayer, layer);
42 for (
auto&& otherLayer : other.m_Layers)
44 Layer* const thisLayer = otherToClonedMap[otherLayer];
46 auto outputSlot = thisLayer->BeginOutputSlots();
47 for (auto&& otherOutputSlot : otherLayer->GetOutputSlots())
49 for (auto&& otherInputSlot : otherOutputSlot.GetConnections())
51 const Layer& otherTgtLayer = otherInputSlot->GetOwningLayer();
52 Layer* const thisTgtLayer = otherToClonedMap[&otherTgtLayer];
54 InputSlot& inputSlot = thisTgtLayer->GetInputSlot(otherInputSlot->GetSlotIndex());
56 if (otherInputSlot->IsTensorInfoOverridden())
58 inputSlot.SetTensorInfo(otherInputSlot->GetTensorInfo());
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;
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(),
491 if (std::find(std::begin(m_Layers),
493 iConnectableLayer) == std::end(m_Layers))
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");
513 const SubgraphView::IConnectableLayers& substituteSubgraphLayers = substituteSubgraph.
GetIConnectableLayers();
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");
523 const SubgraphView::IInputSlots& subgraphInputSlots = subgraph.
GetIInputSlots();
524 const SubgraphView::IOutputSlots& subgraphOutputSlots = subgraph.
GetIOutputSlots();
529 const SubgraphView::IInputSlots& substituteSubgraphInputSlots = substituteSubgraph.
GetIInputSlots();
530 const SubgraphView::IOutputSlots& substituteSubgraphOutputSlots = substituteSubgraph.
GetIOutputSlots();
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);
607void Graph::EraseSubgraphLayers(SubgraphView &subgraph)
610 for (
auto iConnectableLayer : subgraph.GetIConnectableLayers())
612 auto layer = PolymorphicDowncast<Layer*>(iConnectableLayer);
628 for (
auto&& output: layer->GetOutputSlots())
630 if (!output.IsTensorInfoSet())
632 std::ostringstream message;
633 message <<
"Output slot TensorInfo not set on "
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());
672 layer->ValidateTensorShapesFromInputs();
684void 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());
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.
friend class SubgraphView
Status SerializeToDot(std::ostream &stream)
LayerT * InsertNewLayer(InputSlot &insertBefore, Args &&... args)
Inserts a new layer between the output slot currently connected to insertBefore and insertBefore itse...
Status AllocateDynamicBuffers()
Allocates memory for all tensors under output tensor handers of each layer.
Status Print(bool extended=false) const
void VerifyConstantLayerSetTensorInfo() const
For each ConstantLayer in Graph, ensures TensorInfo is set on all output slots.
const std::shared_ptr< IProfiler > & GetProfiler() const
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 SetLayersOutOfOrder()
Graph & TopologicalSort()
Sorts layers in topological order and return this.
void ForEachLayer(Func func) const
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,...
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 std::vector< OutputSlot > & GetOutputSlots() const
const InputSlot & GetInputSlot(unsigned int index) const override
Get a const input slot handle by slot index.
void SetBackendId(const BackendId &id) override
Set the backend of the IConnectableLayer.
const OutputSlot & GetOutputSlot(unsigned int index=0) const override
Get the const output slot handle by slot index.
LayerGuid GetGuid() const final
Returns the unique id of the layer.
unsigned int GetNumInputSlots() const override
Returns the number of connectable input slots.
const char * GetName() const override
Returns the name of the layer.
LayerType GetType() const override
Returns the armnn::LayerType of this layer.
const BackendId & GetBackendId() const
NodeContent & AddContent(const std::string &content)
void SetEdgeStrategy(unsigned int connectionIndex, EdgeStrategy strategy)
Layer & GetOwningLayer() const
const std::vector< EdgeStrategy > & GetEdgeStrategies() const
const std::vector< InputSlot * > & GetConnections() 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.
const IConnectableLayers & GetIConnectableLayers() const
void ForEachIConnectableLayer(Func func) const
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::enable_if_t< std::is_unsigned< Source >::value &&std::is_unsigned< Dest >::value, Dest > numeric_cast(Source source)
std::function< void(const std::string &name, const std::string &value)> ParameterStringifyFunction
DestType PolymorphicDowncast(SourceType *value)
Polymorphic downcast for build in pointers only.
const char * GetLayerTypeAsCString(LayerType type)
@ ValidateOnly
Validate all output shapes.
@ 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.