From 9d74ba6e85a043e9603445e062315f5c4965fbd6 Mon Sep 17 00:00:00 2001 From: Francis Murtagh Date: Wed, 19 Jan 2022 16:31:58 +0000 Subject: IVGCVSW-6634 SubgraphView: Add method of returning a GetSubgraphWorkingCopy * Add pointer to SubgraphView allowing it to store a working copy implementation of its own representation of graph. * Make SubgraphView a friend of Graph to allow access to layers. * Add constructor to SubgraphView taking SubgraphViewWorkingCopyPtr * Rewrite Graph::SubstituteSubgraph for use on SubgraphView * Add GetWorkingCopy() method * Add tests for replacement of multiplication with DepthwiseConv2d * Check GetBackendHint() has value before passing to PrecompiledLayer * Add GetOwningIConnectableLayer to IInputSlot to allow traversing from IConnectableLayer->IOutputSlot->IInputSlot->IConnectableLayer Signed-off-by: Francis Murtagh Change-Id: Iaaef14448d8b73867eaee9d69f4f98d5d1bf171c --- src/armnn/SubgraphView.cpp | 233 ++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 230 insertions(+), 3 deletions(-) (limited to 'src/armnn/SubgraphView.cpp') diff --git a/src/armnn/SubgraphView.cpp b/src/armnn/SubgraphView.cpp index aac2a354d1..5f972a9767 100644 --- a/src/armnn/SubgraphView.cpp +++ b/src/armnn/SubgraphView.cpp @@ -65,9 +65,9 @@ SubgraphView::SubgraphView(InputSlots&& inputs, OutputSlots&& outputs, Layers&& } /// IConnectable Duplication to maintain backwards compatibility -SubgraphView::SubgraphView(SubgraphView::IConnectableLayers &&layers, - SubgraphView::IInputSlots &&inputs, - SubgraphView::IOutputSlots &&outputs) +SubgraphView::SubgraphView(SubgraphView::IConnectableLayers&& layers, + SubgraphView::IInputSlots&& inputs, + SubgraphView::IOutputSlots&& outputs) : m_IInputSlots{inputs} , m_IOutputSlots{outputs} , m_IConnectableLayers(IConnectableLayers{layers.begin(), layers.end()}) @@ -79,6 +79,42 @@ SubgraphView::SubgraphView(SubgraphView::IConnectableLayers &&layers, }; std::transform(layers.begin(), layers.end(), std::back_inserter(m_Layers), f); + m_InputSlots.resize(inputs.size()); + m_IInputSlots.resize(inputs.size()); + for (unsigned int i = 0; i < inputs.size(); i++) + { + m_InputSlots.at(i) = PolymorphicDowncast(inputs[i]); + m_IInputSlots.at(i) = inputs[i]; + } + + m_OutputSlots.resize(outputs.size()); + m_IOutputSlots.resize(outputs.size()); + for (unsigned int i = 0; i < outputs.size(); i++) + { + m_OutputSlots.at(i) = PolymorphicDowncast(outputs[i]); + m_IOutputSlots.at(i) = outputs[i]; + } + + ArrangeBySortOrder(); + CheckSubgraph(); +} + +/// IConnectable Duplication to maintain backwards compatibility +SubgraphView::SubgraphView(SubgraphView::IConnectableLayers&& layers, + SubgraphView::IInputSlots&& inputs, + SubgraphView::IOutputSlots&& outputs, + std::shared_ptr ptr) + : m_IInputSlots{inputs} + , m_IOutputSlots{outputs} + , m_IConnectableLayers(IConnectableLayers{layers.begin(), layers.end()}) + , p_WorkingCopyImpl(std::move(ptr)) +{ + // Cast from IConnectableLayer to Layer for backward compatibility + auto f = [](IConnectableLayer* value) + { + return PolymorphicDowncast(value); + }; + std::transform(layers.begin(), layers.end(), std::back_inserter(m_Layers), f); m_InputSlots.resize(inputs.size()); m_IInputSlots.resize(inputs.size()); @@ -367,4 +403,195 @@ void SubgraphView::ArrangeBySortOrder() m_IConnectableLayers.sort(compareIConnectableLayerPriority); } +struct SubgraphView::SubgraphViewWorkingCopy +{ +public: + + SubgraphViewWorkingCopy() = default; + SubgraphViewWorkingCopy(Graph graph) + : m_Graph(graph) + {}; + + Graph m_Graph; + +}; + +SubgraphView SubgraphView::GetWorkingCopy() +{ + if (p_WorkingCopyImpl) + { + throw Exception("The SubgraphView calling GetWorkingCopy() is already a working copy. This function " + "should be called on original SubgraphView obtained from OptimizeSubgraphView()"); + } + + // Create a cut down SubgraphView with underlying graph containing only the relevant layers. + // It needs its own underlying layers so that they can be replaced safely. + Graph newGraph = Graph(); + std::unordered_map originalToClonedLayerMap; + std::list originalSubgraphLayers = GetIConnectableLayers(); + + auto ptr = std::make_shared(std::move(newGraph)); + SubgraphView::IInputSlots workingCopyInputs; + + for (auto&& originalLayer : originalSubgraphLayers) + { + Layer* const layer = PolymorphicDowncast(originalLayer)->Clone(ptr->m_Graph); + originalToClonedLayerMap.emplace(originalLayer, layer); + } + + // Add IInputSlots to workingCopy + std::vector processed; + for (auto originalSubgraphInputSlot : GetIInputSlots()) + { + const IConnectableLayer& originalSubgraphLayer = + PolymorphicDowncast(originalSubgraphInputSlot)->GetOwningLayer(); + + // Only need process Slots of layer once + if (std::find(processed.begin(), processed.end(), &originalSubgraphLayer) == processed.end()) + { + IConnectableLayer* clonedLayer = originalToClonedLayerMap[&originalSubgraphLayer]; + + // Add the InputSlot to WorkingCopy InputSlots + for (unsigned int i = 0; i < clonedLayer->GetNumInputSlots(); i++) + { + workingCopyInputs.push_back(&clonedLayer->GetInputSlot(i)); + } + processed.push_back(&originalSubgraphLayer); + } + } + // Empty processed + processed.clear(); + + for (auto originalSubgraphLayer : originalSubgraphLayers) + { + IConnectableLayer* const clonedLayer = originalToClonedLayerMap[originalSubgraphLayer]; + + // connect all cloned layers as per original subgraph + for (unsigned int i = 0; i < clonedLayer->GetNumOutputSlots(); i++) + { + // OutputLayers have no OutputSlots to be connected + if (clonedLayer->GetType() != LayerType::Output) + { + auto& outputSlot = clonedLayer->GetOutputSlot(i); + for (unsigned int k = 0; k < originalSubgraphLayer->GetNumOutputSlots(); k++) + { + auto& originalOutputSlot = originalSubgraphLayer->GetOutputSlot(k); + for (unsigned int j = 0; j < originalOutputSlot.GetNumConnections(); j++) + { + // nextLayer is the layer with IInputSlot connected to IOutputSlot we are working on + const IConnectableLayer& nextLayer = + originalOutputSlot.GetConnection(j)->GetOwningIConnectableLayer(); + + // Check the layer is in our map and so has a clonedLayer + if (originalToClonedLayerMap.find(&nextLayer) != originalToClonedLayerMap.end()) + { + IConnectableLayer* newGraphTargetLayer = originalToClonedLayerMap[&nextLayer]; + + IInputSlot& inputSlot = + newGraphTargetLayer->GetInputSlot( + PolymorphicDowncast( + &originalOutputSlot)->GetConnection(j)->GetSlotIndex()); + + // Then make the connection + outputSlot.Connect(inputSlot); + } + } + // Copy the tensorInfo to the clonedOutputSlot + outputSlot.SetTensorInfo(originalOutputSlot.GetTensorInfo()); + } + } + } + } + + SubgraphView::IOutputSlots workingCopyOutputs; + + // Add IOutputSlots to workingCopy + for (auto outputSlot : GetIOutputSlots()) + { + + const IConnectableLayer& originalSubgraphLayer = outputSlot->GetOwningIConnectableLayer(); + + // OutputLayers have no OutputSlots to be connected + // Only need process Slots of layer once + if (originalSubgraphLayer.GetType() != LayerType::Output && + std::find(processed.begin(), processed.end(), &originalSubgraphLayer) == processed.end()) + { + IConnectableLayer* clonedLayer = originalToClonedLayerMap[&originalSubgraphLayer]; + + // Add the OutputSlot to WorkingCopy InputSlots + for (unsigned int i = 0; i < clonedLayer->GetNumOutputSlots(); i++) + { + workingCopyOutputs.push_back(&clonedLayer->GetOutputSlot(i)); + } + processed.push_back(&originalSubgraphLayer); + } + } + processed.clear(); + + SubgraphView::IConnectableLayers workingCopyLayers; + for (auto& pair : originalToClonedLayerMap) + { + workingCopyLayers.push_back(pair.second); + } + + return {std::move(workingCopyLayers), + std::move(workingCopyInputs), + std::move(workingCopyOutputs), + ptr}; +} + +void SubgraphView::SubstituteSubgraph(SubgraphView& subgraph, IConnectableLayer* substituteLayer) +{ + ARMNN_ASSERT(substituteLayer != nullptr); + SubgraphView substituteSubgraph(substituteLayer); + + SubstituteSubgraph(subgraph, substituteSubgraph); +} + +void SubgraphView::SubstituteSubgraph(SubgraphView& patternSubgraph, const SubgraphView& substituteSubgraph) +{ + if (!p_WorkingCopyImpl) + { + throw NullPointerException("The SubgraphView calling SubstituteSubgraphView is not a working copy. " + "Call this function on SubgraphView returned from SubgraphView::GetWorkingCopy()"); + } + + // Add substitute layer to the Main graph i.e. graph in p_WorkingCopyImpl + auto workingCopyGraph = &p_WorkingCopyImpl->m_Graph; + substituteSubgraph.ForEachIConnectableLayer([workingCopyGraph](IConnectableLayer* iConnectableLayer) + { + // Search WorkingCopy Graph for substituteLayer and add if missing + if (std::find(std::begin(workingCopyGraph->m_Layers), + std::end(workingCopyGraph->m_Layers), + iConnectableLayer) == + std::end(workingCopyGraph->m_Layers)) + { + auto layer = PolymorphicDowncast(iConnectableLayer); + + layer->Reparent(*workingCopyGraph, + (workingCopyGraph->m_Layers).end()); + + workingCopyGraph->m_LayersInOrder = false; + } + }); + + // Replace the old connections with connections to new layer + workingCopyGraph->ReplaceSubgraphConnections(patternSubgraph, substituteSubgraph); + + // Update input/outputSlot pointers + m_IInputSlots = std::move(substituteSubgraph.m_IInputSlots); + m_IOutputSlots = std::move(substituteSubgraph.m_IOutputSlots); + + // Delete the old layers. + workingCopyGraph->EraseSubgraphLayers(patternSubgraph); + + // Sort + workingCopyGraph->TopologicalSort(); + + // Update SubgraphView layer pointers to match those of the internal WorkingCopy layer pointers + m_IConnectableLayers = IConnectableLayers{ workingCopyGraph->m_Layers.begin(), + workingCopyGraph->m_Layers.end() }; +} + + } // namespace armnn -- cgit v1.2.1