From ff05cc50880032614675e9422ba829785f61ba14 Mon Sep 17 00:00:00 2001 From: Derek Lamberti Date: Fri, 26 Apr 2019 13:05:17 +0100 Subject: IVGCVSW-2405 Rename SubGraph to SubgraphView Change-Id: Ie50aeccf053c20c3a01a75042bbc3acd824375af Signed-off-by: Derek Lamberti Signed-off-by: Matteo Martincigh --- src/armnn/Graph.cpp | 74 +- src/armnn/Graph.hpp | 12 +- src/armnn/ISubGraphConverter.hpp | 26 - src/armnn/ISubgraphViewConverter.hpp | 26 + src/armnn/Network.cpp | 33 +- src/armnn/NetworkUtils.cpp | 2 +- src/armnn/SubGraph.cpp | 216 ----- src/armnn/SubGraph.hpp | 109 --- src/armnn/SubGraphSelector.cpp | 197 ---- src/armnn/SubGraphSelector.hpp | 38 - src/armnn/SubgraphView.cpp | 219 +++++ src/armnn/SubgraphView.hpp | 109 +++ src/armnn/SubgraphViewSelector.cpp | 197 ++++ src/armnn/SubgraphViewSelector.hpp | 38 + src/armnn/layers/PreCompiledLayer.cpp | 2 +- src/armnn/test/SubGraphTests.cpp | 1044 --------------------- src/armnn/test/SubgraphViewTests.cpp | 1047 ++++++++++++++++++++++ src/armnnTfLiteParser/TfLiteParser.cpp | 50 +- src/armnnTfLiteParser/TfLiteParser.hpp | 2 +- src/armnnTfLiteParser/test/GetTensorIds.cpp | 4 +- src/armnnTfLiteParser/test/LoadModel.cpp | 4 +- src/backends/README.md | 6 +- src/backends/backendsCommon/IBackendInternal.hpp | 14 +- src/backends/cl/ClBackend.cpp | 12 +- src/backends/cl/ClBackend.hpp | 8 +- src/backends/neon/NeonBackend.cpp | 12 +- src/backends/neon/NeonBackend.hpp | 8 +- src/backends/reference/RefBackend.cpp | 12 +- src/backends/reference/RefBackend.hpp | 8 +- 29 files changed, 1769 insertions(+), 1760 deletions(-) delete mode 100644 src/armnn/ISubGraphConverter.hpp create mode 100644 src/armnn/ISubgraphViewConverter.hpp delete mode 100644 src/armnn/SubGraph.cpp delete mode 100644 src/armnn/SubGraph.hpp delete mode 100644 src/armnn/SubGraphSelector.cpp delete mode 100644 src/armnn/SubGraphSelector.hpp create mode 100644 src/armnn/SubgraphView.cpp create mode 100644 src/armnn/SubgraphView.hpp create mode 100644 src/armnn/SubgraphViewSelector.cpp create mode 100644 src/armnn/SubgraphViewSelector.hpp delete mode 100644 src/armnn/test/SubGraphTests.cpp create mode 100644 src/armnn/test/SubgraphViewTests.cpp (limited to 'src') diff --git a/src/armnn/Graph.cpp b/src/armnn/Graph.cpp index be0dd5f2ce..335f1951bb 100644 --- a/src/armnn/Graph.cpp +++ b/src/armnn/Graph.cpp @@ -4,7 +4,7 @@ // #include "Graph.hpp" -#include "SubGraph.hpp" +#include "SubgraphView.hpp" #include "LayersFwd.hpp" #include @@ -298,88 +298,88 @@ void Graph::AddCopyLayers() } } -void Graph::SubstituteSubGraph(std::unique_ptr subGraph, IConnectableLayer* substituteLayer) +void Graph::SubstituteSubgraph(std::unique_ptr subgraph, IConnectableLayer* substituteLayer) { - BOOST_ASSERT(subGraph != nullptr); + BOOST_ASSERT(subgraph != nullptr); BOOST_ASSERT(substituteLayer != nullptr); - ReplaceSubGraphConnections(*subGraph, substituteLayer); - EraseSubGraphLayers(*subGraph); + ReplaceSubgraphConnections(*subgraph, substituteLayer); + EraseSubgraphLayers(*subgraph); } -void Graph::SubstituteSubGraph(std::unique_ptr subGraph, const SubGraph& substituteSubGraph) +void Graph::SubstituteSubgraph(std::unique_ptr subgraph, const SubgraphView& substituteSubgraph) { - BOOST_ASSERT(subGraph); + BOOST_ASSERT(subgraph); - ReplaceSubGraphConnections(*subGraph, substituteSubGraph); - EraseSubGraphLayers(*subGraph); + ReplaceSubgraphConnections(*subgraph, substituteSubgraph); + EraseSubgraphLayers(*subgraph); } -void Graph::ReplaceSubGraphConnections(const SubGraph& subGraph, IConnectableLayer* substituteLayer) +void Graph::ReplaceSubgraphConnections(const SubgraphView& subgraph, IConnectableLayer* substituteLayer) { BOOST_ASSERT(substituteLayer != nullptr); // Create a new sub-graph with only the given layer, using // the given sub-graph as a reference of which parent graph to use - SubGraph substituteSubGraph(subGraph, substituteLayer); - ReplaceSubGraphConnections(subGraph, substituteSubGraph); + SubgraphView substituteSubgraph(subgraph, substituteLayer); + ReplaceSubgraphConnections(subgraph, substituteSubgraph); } -void Graph::ReplaceSubGraphConnections(const SubGraph& subGraph, const SubGraph& substituteSubGraph) +void Graph::ReplaceSubgraphConnections(const SubgraphView& subgraph, const SubgraphView& substituteSubgraph) { - BOOST_ASSERT_MSG(!substituteSubGraph.GetLayers().empty(), "New sub-graph used for substitution must not be empty"); + BOOST_ASSERT_MSG(!substituteSubgraph.GetLayers().empty(), "New sub-graph used for substitution must not be empty"); - const SubGraph::Layers& substituteSubGraphLayers = substituteSubGraph.GetLayers(); - std::for_each(substituteSubGraphLayers.begin(), substituteSubGraphLayers.end(), [&](Layer* layer) + const SubgraphView::Layers& substituteSubgraphLayers = substituteSubgraph.GetLayers(); + std::for_each(substituteSubgraphLayers.begin(), substituteSubgraphLayers.end(), [&](Layer* layer) { BOOST_ASSERT_MSG(std::find(m_Layers.begin(), m_Layers.end(), layer) != m_Layers.end(), "Substitute layer is not a member of graph"); }); - const SubGraph::InputSlots& subGraphInputSlots = subGraph.GetInputSlots(); - const SubGraph::OutputSlots& subGraphOutputSlots = subGraph.GetOutputSlots(); + const SubgraphView::InputSlots& subgraphInputSlots = subgraph.GetInputSlots(); + const SubgraphView::OutputSlots& subgraphOutputSlots = subgraph.GetOutputSlots(); - unsigned int subGraphNumInputSlots = boost::numeric_cast(subGraphInputSlots.size()); - unsigned int subGraphNumOutputSlots = boost::numeric_cast(subGraphOutputSlots.size()); + unsigned int subgraphNumInputSlots = boost::numeric_cast(subgraphInputSlots.size()); + unsigned int subgraphNumOutputSlots = boost::numeric_cast(subgraphOutputSlots.size()); - const SubGraph::InputSlots& substituteSubGraphInputSlots = substituteSubGraph.GetInputSlots(); - const SubGraph::OutputSlots& substituteSubGraphOutputSlots = substituteSubGraph.GetOutputSlots(); + const SubgraphView::InputSlots& substituteSubgraphInputSlots = substituteSubgraph.GetInputSlots(); + const SubgraphView::OutputSlots& substituteSubgraphOutputSlots = substituteSubgraph.GetOutputSlots(); - BOOST_ASSERT(subGraphNumInputSlots == substituteSubGraphInputSlots.size()); - BOOST_ASSERT(subGraphNumOutputSlots == substituteSubGraphOutputSlots.size()); + BOOST_ASSERT(subgraphNumInputSlots == substituteSubgraphInputSlots.size()); + BOOST_ASSERT(subgraphNumOutputSlots == substituteSubgraphOutputSlots.size()); // Disconnect the sub-graph and replace it with the substitute sub-graph // Step 1: process input slots - for (unsigned int inputSlotIdx = 0; inputSlotIdx < subGraphNumInputSlots; ++inputSlotIdx) + for (unsigned int inputSlotIdx = 0; inputSlotIdx < subgraphNumInputSlots; ++inputSlotIdx) { - InputSlot* subGraphInputSlot = subGraphInputSlots.at(inputSlotIdx); - BOOST_ASSERT(subGraphInputSlot); + InputSlot* subgraphInputSlot = subgraphInputSlots.at(inputSlotIdx); + BOOST_ASSERT(subgraphInputSlot); - IOutputSlot* connectedOutputSlot = subGraphInputSlot->GetConnection(); + IOutputSlot* connectedOutputSlot = subgraphInputSlot->GetConnection(); BOOST_ASSERT(connectedOutputSlot); - connectedOutputSlot->Disconnect(*subGraphInputSlot); + connectedOutputSlot->Disconnect(*subgraphInputSlot); - IInputSlot* substituteInputSlot = substituteSubGraphInputSlots.at(inputSlotIdx); + IInputSlot* substituteInputSlot = substituteSubgraphInputSlots.at(inputSlotIdx); BOOST_ASSERT(substituteInputSlot); connectedOutputSlot->Connect(*substituteInputSlot); } // Step 2: process output slots - for(unsigned int outputSlotIdx = 0; outputSlotIdx < subGraphNumOutputSlots; ++outputSlotIdx) + for(unsigned int outputSlotIdx = 0; outputSlotIdx < subgraphNumOutputSlots; ++outputSlotIdx) { - OutputSlot* subGraphOutputSlot = subGraphOutputSlots.at(outputSlotIdx); - BOOST_ASSERT(subGraphOutputSlot); + OutputSlot* subgraphOutputSlot = subgraphOutputSlots.at(outputSlotIdx); + BOOST_ASSERT(subgraphOutputSlot); - OutputSlot* substituteOutputSlot = substituteSubGraphOutputSlots.at(outputSlotIdx); + OutputSlot* substituteOutputSlot = substituteSubgraphOutputSlots.at(outputSlotIdx); BOOST_ASSERT(substituteOutputSlot); - subGraphOutputSlot->MoveAllConnections(*substituteOutputSlot); + subgraphOutputSlot->MoveAllConnections(*substituteOutputSlot); } } -void Graph::EraseSubGraphLayers(const SubGraph &subGraph) +void Graph::EraseSubgraphLayers(const SubgraphView &subgraph) { - for (auto layer : subGraph.GetLayers()) + for (auto layer : subgraph.GetLayers()) { EraseLayer(layer); } diff --git a/src/armnn/Graph.hpp b/src/armnn/Graph.hpp index dd6a825f57..cc0ccaea77 100644 --- a/src/armnn/Graph.hpp +++ b/src/armnn/Graph.hpp @@ -24,7 +24,7 @@ namespace armnn { -class SubGraph; +class SubgraphView; class Graph { @@ -163,8 +163,8 @@ public: /// Substitutes the given sub-graph with either a new layer or a new sub-graph. /// In either case, the given layer or all the layers in the given sub-graph must belong to this graph. - void SubstituteSubGraph(std::unique_ptr subGraph, IConnectableLayer* substituteLayer); - void SubstituteSubGraph(std::unique_ptr subGraph, const SubGraph& substituteSubGraph); + void SubstituteSubgraph(std::unique_ptr subgraph, IConnectableLayer* substituteLayer); + void SubstituteSubgraph(std::unique_ptr subgraph, const SubgraphView& substituteSubgraph); void InferTensorInfos(); @@ -217,9 +217,9 @@ private: std::unordered_set m_OutputIds; std::unordered_map m_PosInGraphMap; - void ReplaceSubGraphConnections(const SubGraph& subGraph, IConnectableLayer* substituteLayer); - void ReplaceSubGraphConnections(const SubGraph& subGraph, const SubGraph& substituteSubGraph); - void EraseSubGraphLayers(const SubGraph &subGraph); + void ReplaceSubgraphConnections(const SubgraphView& subgraph, IConnectableLayer* substituteLayer); + void ReplaceSubgraphConnections(const SubgraphView& subgraph, const SubgraphView& substituteSubgraph); + void EraseSubgraphLayers(const SubgraphView &subgraph); /// Mutable to allow sorting on const object. mutable LayerList m_Layers; diff --git a/src/armnn/ISubGraphConverter.hpp b/src/armnn/ISubGraphConverter.hpp deleted file mode 100644 index 3a6866e102..0000000000 --- a/src/armnn/ISubGraphConverter.hpp +++ /dev/null @@ -1,26 +0,0 @@ -// -// Copyright © 2017 Arm Ltd. All rights reserved. -// SPDX-License-Identifier: MIT -// - -#pragma once - -#include -#include -#include - -namespace armnn -{ - -using CompiledBlobDeleter = std::function; -using CompiledBlobPtr = std::unique_ptr; - -class ISubGraphConverter -{ -public: - virtual ~ISubGraphConverter() {} - - virtual std::vector GetOutput() = 0; -}; - -} // namespace armnn diff --git a/src/armnn/ISubgraphViewConverter.hpp b/src/armnn/ISubgraphViewConverter.hpp new file mode 100644 index 0000000000..1f4b4e5de1 --- /dev/null +++ b/src/armnn/ISubgraphViewConverter.hpp @@ -0,0 +1,26 @@ +// +// Copyright © 2017 Arm Ltd. All rights reserved. +// SPDX-License-Identifier: MIT +// + +#pragma once + +#include +#include +#include + +namespace armnn +{ + +using CompiledBlobDeleter = std::function; +using CompiledBlobPtr = std::unique_ptr; + +class ISubgraphViewConverter +{ +public: + virtual ~ISubgraphViewConverter() {} + + virtual std::vector GetOutput() = 0; +}; + +} // namespace armnn diff --git a/src/armnn/Network.cpp b/src/armnn/Network.cpp index a38bcf1910..9ef0c568a3 100644 --- a/src/armnn/Network.cpp +++ b/src/armnn/Network.cpp @@ -8,7 +8,7 @@ #include "Layer.hpp" #include "DeviceSpec.hpp" #include "Optimizer.hpp" -#include "SubGraphSelector.hpp" +#include "SubgraphViewSelector.hpp" #include "BackendSettings.hpp" #include "optimizations/All.hpp" @@ -311,11 +311,11 @@ OptimizationResult AssignBackends(OptimizedNetwork* optNetObjPtr, OptimizationResult AssignBackends(OptimizedNetwork* optNetObjPtr, BackendSettings& backendSettings, - SubGraph& subGraph, + SubgraphView& subgraph, Optional&> errMessages) { - Graph::Iterator firstLayer = subGraph.begin(); - Graph::Iterator lastLayer = subGraph.end(); + Graph::Iterator firstLayer = subgraph.begin(); + Graph::Iterator lastLayer = subgraph.end(); return AssignBackends(optNetObjPtr, backendSettings, firstLayer, @@ -335,7 +335,7 @@ OptimizationResult ApplyBackendOptimizations(OptimizedNetwork* optNetObjPtr, Graph& optGraph = optNetObjPtr->GetGraph(); // Get the entire graph as a sub-graph - SubGraph mainSubGraph(optGraph); + SubgraphView mainSubgraph(optGraph); // Run backend specific optimizations auto const& backendRegistry = BackendRegistryInstance(); @@ -346,8 +346,8 @@ OptimizationResult ApplyBackendOptimizations(OptimizedNetwork* optNetObjPtr, BOOST_ASSERT(backendObjPtr); // Select sub-graphs based on backend - SubGraphSelector::SubGraphs subGraphs = - SubGraphSelector::SelectSubGraphs(mainSubGraph, + SubgraphViewSelector::Subgraphs subgraphs = + SubgraphViewSelector::SelectSubgraphs(mainSubgraph, // Select layers assigned to the requested backend [&backendObjPtr](const Layer& layer) { @@ -355,18 +355,19 @@ OptimizationResult ApplyBackendOptimizations(OptimizedNetwork* optNetObjPtr, layer.GetType() != LayerType::Output && layer.GetBackendId() == backendObjPtr->GetId(); }); - if (subGraphs.empty()) + if (subgraphs.empty()) { // No sub-graphs found, try with next selected backend continue; } // Try to optimize each sub-graph - for (auto& subGraph : subGraphs) + for (auto& subgraph : subgraphs) { // Try to optimize the current sub-graph bool optimizationAttempted = false; - SubGraph::SubGraphPtr optSubGraph = backendObjPtr->OptimizeSubGraph(*subGraph, optimizationAttempted); + SubgraphView::SubgraphViewPtr optSubgraph = backendObjPtr->OptimizeSubgraphView(*subgraph, + optimizationAttempted); // Check if the optimization has been attempted if (!optimizationAttempted) @@ -376,27 +377,27 @@ OptimizationResult ApplyBackendOptimizations(OptimizedNetwork* optNetObjPtr, } // Optimization attempted, check the resulting optimized sub-graph - if (optSubGraph) + if (optSubgraph) { // Sub-graph optimized, substitute the sub-graph with the new optimized one in the main optimized graph - optGraph.SubstituteSubGraph(std::move(subGraph), *optSubGraph); + optGraph.SubstituteSubgraph(std::move(subgraph), *optSubgraph); // Assign the current backend to the optimized sub-graph - std::for_each(optSubGraph->begin(), optSubGraph->end(), [&selectedBackend](Layer* l) + std::for_each(optSubgraph->begin(), optSubgraph->end(), [&selectedBackend](Layer* l) { BOOST_ASSERT(l); l->SetBackendId(selectedBackend); }); // Recreate the sub-graph representing the entire graph - mainSubGraph.Update(optGraph); + mainSubgraph.Update(optGraph); } else { // An error occurred: the optimization was attempted but not performed, try different backends std::stringstream warningMsg; warningMsg << "Sub-graph failed to get optimized on " << backendObjPtr->GetId() << ". " - << "Re-assigning backends to " << subGraph->GetLayers().size() << " layers inside sub-graph"; + << "Re-assigning backends to " << subgraph->GetLayers().size() << " layers inside sub-graph"; ReportWarning(warningMsg.str(), errMessages); // Failed to optimize the given sub-graph, re-assign the sub-graph layers to other available backends @@ -407,7 +408,7 @@ OptimizationResult ApplyBackendOptimizations(OptimizedNetwork* optNetObjPtr, } OptimizationResult reassignmentResult = AssignBackends(optNetObjPtr, backendSettings, - *subGraph, + *subgraph, errMessages); if (reassignmentResult.m_Error) { diff --git a/src/armnn/NetworkUtils.cpp b/src/armnn/NetworkUtils.cpp index 126b56b084..66940e4eb5 100644 --- a/src/armnn/NetworkUtils.cpp +++ b/src/armnn/NetworkUtils.cpp @@ -5,7 +5,7 @@ #include "NetworkUtils.hpp" -#include "SubGraphSelector.hpp" +#include "SubgraphViewSelector.hpp" #include diff --git a/src/armnn/SubGraph.cpp b/src/armnn/SubGraph.cpp deleted file mode 100644 index d0fc760c15..0000000000 --- a/src/armnn/SubGraph.cpp +++ /dev/null @@ -1,216 +0,0 @@ -// -// Copyright © 2017 Arm Ltd. All rights reserved. -// SPDX-License-Identifier: MIT -// - -#include "SubGraph.hpp" -#include "Graph.hpp" - -#include - -#include - -namespace armnn -{ - -namespace -{ - -template -void AssertIfNullsOrDuplicates(const C& container, const std::string& errorMessage) -{ - using T = typename C::value_type; - std::unordered_set duplicateSet; - std::for_each(container.begin(), container.end(), [&duplicateSet, &errorMessage](const T& i) - { - // Ignore unused for release builds - boost::ignore_unused(errorMessage); - - // Check if the item is valid - BOOST_ASSERT_MSG(i, errorMessage.c_str()); - - // Check if a duplicate has been found - BOOST_ASSERT_MSG(duplicateSet.find(i) == duplicateSet.end(), errorMessage.c_str()); - - duplicateSet.insert(i); - }); -} - -} // anonymous namespace - -SubGraph::SubGraph(Graph& graph) - : m_InputSlots{} - , m_OutputSlots{} - , m_Layers(graph.begin(), graph.end()) - , m_ParentGraph(&graph) -{ - CheckSubGraph(); -} - -SubGraph::SubGraph(Graph* parentGraph, InputSlots&& inputs, OutputSlots&& outputs, Layers&& layers) - : m_InputSlots{inputs} - , m_OutputSlots{outputs} - , m_Layers{layers} - , m_ParentGraph(parentGraph) -{ - CheckSubGraph(); -} - -SubGraph::SubGraph(const SubGraph& referenceSubGraph, InputSlots&& inputs, OutputSlots&& outputs, Layers&& layers) - : m_InputSlots{inputs} - , m_OutputSlots{outputs} - , m_Layers{layers} - , m_ParentGraph(referenceSubGraph.m_ParentGraph) -{ - CheckSubGraph(); -} - -SubGraph::SubGraph(const SubGraph& subGraph) - : m_InputSlots(subGraph.m_InputSlots.begin(), subGraph.m_InputSlots.end()) - , m_OutputSlots(subGraph.m_OutputSlots.begin(), subGraph.m_OutputSlots.end()) - , m_Layers(subGraph.m_Layers.begin(), subGraph.m_Layers.end()) - , m_ParentGraph(subGraph.m_ParentGraph) -{ - CheckSubGraph(); -} - -SubGraph::SubGraph(SubGraph&& subGraph) - : m_InputSlots(std::move(subGraph.m_InputSlots)) - , m_OutputSlots(std::move(subGraph.m_OutputSlots)) - , m_Layers(std::move(subGraph.m_Layers)) - , m_ParentGraph(std::exchange(subGraph.m_ParentGraph, nullptr)) -{ - CheckSubGraph(); -} - -SubGraph::SubGraph(const SubGraph& referenceSubGraph, IConnectableLayer* layer) - : m_InputSlots{} - , m_OutputSlots{} - , m_Layers{boost::polymorphic_downcast(layer)} - , m_ParentGraph(referenceSubGraph.m_ParentGraph) -{ - unsigned int numInputSlots = layer->GetNumInputSlots(); - m_InputSlots.resize(numInputSlots); - for (unsigned int i = 0; i < numInputSlots; i++) - { - m_InputSlots.at(i) = boost::polymorphic_downcast(&(layer->GetInputSlot(i))); - } - - unsigned int numOutputSlots = layer->GetNumOutputSlots(); - m_OutputSlots.resize(numOutputSlots); - for (unsigned int i = 0; i < numOutputSlots; i++) - { - m_OutputSlots.at(i) = boost::polymorphic_downcast(&(layer->GetOutputSlot(i))); - } - - CheckSubGraph(); -} - -void SubGraph::CheckSubGraph() -{ - // Check that the sub-graph has a valid parent graph - BOOST_ASSERT_MSG(m_ParentGraph, "Sub-graphs must have a parent graph"); - - // Check for invalid or duplicate input slots - AssertIfNullsOrDuplicates(m_InputSlots, "Sub-graphs cannot contain null or duplicate input slots"); - - // Check for invalid or duplicate output slots - AssertIfNullsOrDuplicates(m_OutputSlots, "Sub-graphs cannot contain null or duplicate output slots"); - - // Check for invalid or duplicate layers - AssertIfNullsOrDuplicates(m_Layers, "Sub-graphs cannot contain null or duplicate layers"); - - // Check that all the layers of the sub-graph belong to the parent graph - std::for_each(m_Layers.begin(), m_Layers.end(), [&](const Layer* l) - { - BOOST_ASSERT_MSG(std::find(m_ParentGraph->begin(), m_ParentGraph->end(), l) != m_ParentGraph->end(), - "Sub-graph layer is not a member of the parent graph"); - }); -} - -void SubGraph::Update(Graph &graph) -{ - m_InputSlots.clear(); - m_OutputSlots.clear(); - m_Layers.assign(graph.begin(), graph.end()); - m_ParentGraph = &graph; - - CheckSubGraph(); -} - -const SubGraph::InputSlots& SubGraph::GetInputSlots() const -{ - return m_InputSlots; -} - -const SubGraph::OutputSlots& SubGraph::GetOutputSlots() const -{ - return m_OutputSlots; -} - -const InputSlot* SubGraph::GetInputSlot(unsigned int index) const -{ - return m_InputSlots.at(index); -} - -InputSlot* SubGraph::GetInputSlot(unsigned int index) -{ - return m_InputSlots.at(index); -} - -const OutputSlot* SubGraph::GetOutputSlot(unsigned int index) const -{ - return m_OutputSlots.at(index); -} - -OutputSlot* SubGraph::GetOutputSlot(unsigned int index) -{ - return m_OutputSlots.at(index); -} - -unsigned int SubGraph::GetNumInputSlots() const -{ - return boost::numeric_cast(m_InputSlots.size()); -} - -unsigned int SubGraph::GetNumOutputSlots() const -{ - return boost::numeric_cast(m_OutputSlots.size()); -} - -const SubGraph::Layers & SubGraph::GetLayers() const -{ - return m_Layers; -} - -SubGraph::Layers::iterator SubGraph::begin() -{ - return m_Layers.begin(); -} - -SubGraph::Iterator SubGraph::end() -{ - return m_Layers.end(); -} - -SubGraph::ConstIterator SubGraph::begin() const -{ - return m_Layers.begin(); -} - -SubGraph::ConstIterator SubGraph::end() const -{ - return m_Layers.end(); -} - -SubGraph::ConstIterator SubGraph::cbegin() const -{ - return begin(); -} - -SubGraph::ConstIterator SubGraph::cend() const -{ - return end(); -} - -} // namespace armnn diff --git a/src/armnn/SubGraph.hpp b/src/armnn/SubGraph.hpp deleted file mode 100644 index cb44f8ce3d..0000000000 --- a/src/armnn/SubGraph.hpp +++ /dev/null @@ -1,109 +0,0 @@ -// -// Copyright © 2017 Arm Ltd. All rights reserved. -// SPDX-License-Identifier: MIT -// - -#pragma once - -#include "Layer.hpp" -#include "Graph.hpp" - -#include -#include - -namespace armnn -{ - -/// -/// The SubGraph class represents a subgraph of a Graph. -/// The data it holds, points to data held by layers of the Graph, so the -/// the contents of the SubGraph becomes invalid when the Layers are destroyed -/// or changed. -/// -class SubGraph final -{ -public: - using SubGraphPtr = std::unique_ptr; - using InputSlots = std::vector; - using OutputSlots = std::vector; - using Layers = std::list; - using Iterator = Layers::iterator; - using ConstIterator = Layers::const_iterator; - - /// Empty subgraphs are not allowed, they must at least have a parent graph. - SubGraph() = delete; - - /// Constructs a sub-graph from the entire given graph. - SubGraph(Graph& graph); - - /// Constructs a sub-graph with the given arguments and binds it to the specified parent graph. - SubGraph(Graph* parentGraph, InputSlots&& inputs, OutputSlots&& outputs, Layers&& layers); - - /// Constructs a sub-graph with the given arguments and uses the specified sub-graph to get a reference - /// to the parent graph. - SubGraph(const SubGraph& referenceSubGraph, InputSlots&& inputs, OutputSlots&& outputs, Layers&& layers); - - /// Copy-constructor. - SubGraph(const SubGraph& subGraph); - - /// Move-constructor. - SubGraph(SubGraph&& subGraph); - - /// Constructs a sub-graph with only the given layer and uses the specified sub-graph to get a reference - /// to the parent graph. - SubGraph(const SubGraph& referenceSubGraph, IConnectableLayer* layer); - - /// Updates this sub-graph with the contents of the whole given graph. - void Update(Graph& graph); - - /// Adds a new layer, of type LayerType, to the parent graph of this sub-graph. - template - LayerT* AddLayer(Args&&... args) const; - - const InputSlots& GetInputSlots() const; - const OutputSlots& GetOutputSlots() const; - const Layers& GetLayers() const; - - const InputSlot* GetInputSlot(unsigned int index) const; - InputSlot* GetInputSlot(unsigned int index); - - const OutputSlot* GetOutputSlot(unsigned int index) const; - OutputSlot* GetOutputSlot(unsigned int index); - - unsigned int GetNumInputSlots() const; - unsigned int GetNumOutputSlots() const; - - Iterator begin(); - Iterator end(); - - ConstIterator begin() const; - ConstIterator end() const; - - ConstIterator cbegin() const; - ConstIterator cend() const; - -private: - void CheckSubGraph(); - - /// The list of pointers to the input slots of the parent graph. - InputSlots m_InputSlots; - - /// The list of pointers to the output slots of the parent graph. - OutputSlots m_OutputSlots; - - /// The list of pointers to the layers of the parent graph. - Layers m_Layers; - - /// Pointer to the graph this sub-graph is a view of. - Graph* m_ParentGraph; -}; - -template -LayerT* SubGraph::AddLayer(Args&&... args) const -{ - BOOST_ASSERT(m_ParentGraph); - - return m_ParentGraph->AddLayer(args...); -} - -} // namespace armnn diff --git a/src/armnn/SubGraphSelector.cpp b/src/armnn/SubGraphSelector.cpp deleted file mode 100644 index be51a7d4e3..0000000000 --- a/src/armnn/SubGraphSelector.cpp +++ /dev/null @@ -1,197 +0,0 @@ -// -// Copyright © 2017 Arm Ltd. All rights reserved. -// SPDX-License-Identifier: MIT -// - -#include "SubGraphSelector.hpp" -#include "Graph.hpp" -#include -#include -#include - -namespace armnn -{ - -namespace -{ - -struct LayerSelectionInfo -{ - using LayerInfoContainer = std::unordered_map; - static constexpr uint32_t InitialSplitId() { return 1; } - - LayerSelectionInfo(Layer* layer, const SubGraphSelector::LayerSelectorFunction& selector) - : m_Layer{layer} - , m_SplitId{0} - , m_IsSelected{selector(*layer)} - , m_IsVisited(false) - { - // fill topology information by storing direct children - for (auto&& slot = m_Layer->BeginOutputSlots(); slot != m_Layer->EndOutputSlots(); ++slot) - { - for (InputSlot* childLayerInputSlot : slot->GetConnections()) - { - Layer& childLayer = childLayerInputSlot->GetOwningLayer(); - m_DirectChildren.push_back(&childLayer); - } - } - } - - void MarkChildrenSplits(LayerInfoContainer& network, - uint32_t splitId, - bool prevSelected) - { - if (m_IsVisited) - { - return; - } - m_IsVisited = true; - - if (m_SplitId < splitId) - { - m_SplitId = splitId; - } - - // introduce a new split point at all non-selected points, but only if the - // previous point was selected. this prevents creating a new subgraph at - // every non-selected layer - if (!m_IsSelected && prevSelected) - { - ++m_SplitId; - } - - for (auto& layer : m_DirectChildren) - { - auto it = network.find(layer); - BOOST_ASSERT_MSG(it != network.end(), "All layers must be part of the topology."); - if (it != network.end()) - { - it->second.MarkChildrenSplits(network, m_SplitId, m_IsSelected); - } - } - } - - bool IsInputLayer() const - { - return m_Layer->GetType() == armnn::LayerType::Input; - } - - void CollectNonSelectedInputs(SubGraph::InputSlots& inputSlots, - const SubGraphSelector::LayerSelectorFunction& selector) - { - for (auto&& slot = m_Layer->BeginInputSlots(); slot != m_Layer->EndInputSlots(); ++slot) - { - OutputSlot* parentLayerOutputSlot = slot->GetConnectedOutputSlot(); - BOOST_ASSERT_MSG(parentLayerOutputSlot != nullptr, "The input slots must be connected here."); - if (parentLayerOutputSlot) - { - Layer& parentLayer = parentLayerOutputSlot->GetOwningLayer(); - if (selector(parentLayer) == false) - { - inputSlots.push_back(&(*slot)); - } - } - } - } - - void CollectNonSelectedOutputSlots(SubGraph::OutputSlots& outputSlots, - const SubGraphSelector::LayerSelectorFunction& selector) - { - for (auto&& slot = m_Layer->BeginOutputSlots(); slot != m_Layer->EndOutputSlots(); ++slot) - { - for (InputSlot* childLayerInputSlot : slot->GetConnections()) - { - Layer& childLayer = childLayerInputSlot->GetOwningLayer(); - if (selector(childLayer) == false) - { - outputSlots.push_back(&(*slot)); - } - } - } - } - - std::vector m_DirectChildren; - Layer* m_Layer; - uint32_t m_SplitId; - bool m_IsSelected; - bool m_IsVisited; -}; - -} // namespace - -SubGraphSelector::SubGraphs -SubGraphSelector::SelectSubGraphs(Graph& graph, const LayerSelectorFunction& selector) -{ - SubGraph subGraph(graph); - return SubGraphSelector::SelectSubGraphs(subGraph, selector); -} - -SubGraphSelector::SubGraphs -SubGraphSelector::SelectSubGraphs(SubGraph& subGraph, const LayerSelectorFunction& selector) -{ - LayerSelectionInfo::LayerInfoContainer layerInfo; - - for (auto& layer : subGraph) - { - layerInfo.emplace(layer, LayerSelectionInfo{layer, selector}); - } - - uint32_t splitNo = LayerSelectionInfo::InitialSplitId(); - for (auto& info : layerInfo) - { - if (info.second.IsInputLayer()) - { - // For each input layer we mark the graph where subgraph - // splits need to happen because of the dependency between - // the selected and non-selected nodes - info.second.MarkChildrenSplits(layerInfo, splitNo, false); - } - } - - // Collect all selected layers keyed by split id into a map - using SelectionInfoPtrs = std::vector; - std::unordered_map splitMap; - for (auto& info : layerInfo) - { - if (info.second.m_IsSelected) - { - auto it = splitMap.find(info.second.m_SplitId); - if (it == splitMap.end()) - { - splitMap.insert(std::make_pair(info.second.m_SplitId, SelectionInfoPtrs{&info.second})); - } - else - { - it->second.push_back(&info.second); - } - } - } - - // Now each non-empty split id represents a subgraph - SubGraphs result; - for (auto& splitGraph : splitMap) - { - if (splitGraph.second.empty() == false) - { - SubGraph::InputSlots inputs; - SubGraph::OutputSlots outputs; - SubGraph::Layers layers; - for (auto&& infoPtr : splitGraph.second) - { - infoPtr->CollectNonSelectedInputs(inputs, selector); - infoPtr->CollectNonSelectedOutputSlots(outputs, selector); - layers.push_back(infoPtr->m_Layer); - } - // Create a new sub-graph with the new lists of input/output slots and layer, using - // the given sub-graph as a reference of which parent graph to use - result.emplace_back(std::make_unique(subGraph, - std::move(inputs), - std::move(outputs), - std::move(layers))); - } - } - - return result; -} - -} // namespace armnn diff --git a/src/armnn/SubGraphSelector.hpp b/src/armnn/SubGraphSelector.hpp deleted file mode 100644 index 371ba78f18..0000000000 --- a/src/armnn/SubGraphSelector.hpp +++ /dev/null @@ -1,38 +0,0 @@ -// -// Copyright © 2017 Arm Ltd. All rights reserved. -// SPDX-License-Identifier: MIT -// -#pragma once - -#include "SubGraph.hpp" -#include -#include - -namespace armnn -{ - -class Layer; -class Graph; - -class SubGraphSelector final -{ -public: - using SubGraphPtr = std::unique_ptr; - using SubGraphs = std::vector; - using LayerSelectorFunction = std::function; - - /// Selects subgraphs from a graph based on the selector function and the algorithm. - /// Since the SubGraphs object returns modifiable pointers to the input and output slots of the graph: - /// 1) the graph/sub-graph cannot be const - /// 2) the caller needs to make sure that the SubGraphs lifetime is shorter than the parent graph's - static SubGraphs SelectSubGraphs(Graph& graph, const LayerSelectorFunction& selector); - static SubGraphs SelectSubGraphs(SubGraph& subGraph, const LayerSelectorFunction& selector); - -private: - // this is a utility class, don't construct or copy - SubGraphSelector() = delete; - SubGraphSelector(const SubGraphSelector&) = delete; - SubGraphSelector & operator=(const SubGraphSelector&) = delete; -}; - -} // namespace armnn diff --git a/src/armnn/SubgraphView.cpp b/src/armnn/SubgraphView.cpp new file mode 100644 index 0000000000..23f969d8ee --- /dev/null +++ b/src/armnn/SubgraphView.cpp @@ -0,0 +1,219 @@ +// +// Copyright © 2017 Arm Ltd. All rights reserved. +// SPDX-License-Identifier: MIT +// + +#include "SubgraphView.hpp" +#include "Graph.hpp" + +#include + +#include + +namespace armnn +{ + +namespace +{ + +template +void AssertIfNullsOrDuplicates(const C& container, const std::string& errorMessage) +{ + using T = typename C::value_type; + std::unordered_set duplicateSet; + std::for_each(container.begin(), container.end(), [&duplicateSet, &errorMessage](const T& i) + { + // Ignore unused for release builds + boost::ignore_unused(errorMessage); + + // Check if the item is valid + BOOST_ASSERT_MSG(i, errorMessage.c_str()); + + // Check if a duplicate has been found + BOOST_ASSERT_MSG(duplicateSet.find(i) == duplicateSet.end(), errorMessage.c_str()); + + duplicateSet.insert(i); + }); +} + +} // anonymous namespace + +SubgraphView::SubgraphView(Graph& graph) + : m_InputSlots{} + , m_OutputSlots{} + , m_Layers(graph.begin(), graph.end()) + , m_ParentGraph(&graph) +{ + CheckSubgraph(); +} + +SubgraphView::SubgraphView(Graph* parentGraph, InputSlots&& inputs, OutputSlots&& outputs, Layers&& layers) + : m_InputSlots{inputs} + , m_OutputSlots{outputs} + , m_Layers{layers} + , m_ParentGraph(parentGraph) +{ + CheckSubgraph(); +} + +SubgraphView::SubgraphView(const SubgraphView& referenceSubgraph, + InputSlots&& inputs, + OutputSlots&& outputs, + Layers&& layers) + : m_InputSlots{inputs} + , m_OutputSlots{outputs} + , m_Layers{layers} + , m_ParentGraph(referenceSubgraph.m_ParentGraph) +{ + CheckSubgraph(); +} + +SubgraphView::SubgraphView(const SubgraphView& subgraph) + : m_InputSlots(subgraph.m_InputSlots.begin(), subgraph.m_InputSlots.end()) + , m_OutputSlots(subgraph.m_OutputSlots.begin(), subgraph.m_OutputSlots.end()) + , m_Layers(subgraph.m_Layers.begin(), subgraph.m_Layers.end()) + , m_ParentGraph(subgraph.m_ParentGraph) +{ + CheckSubgraph(); +} + +SubgraphView::SubgraphView(SubgraphView&& subgraph) + : m_InputSlots(std::move(subgraph.m_InputSlots)) + , m_OutputSlots(std::move(subgraph.m_OutputSlots)) + , m_Layers(std::move(subgraph.m_Layers)) + , m_ParentGraph(std::exchange(subgraph.m_ParentGraph, nullptr)) +{ + CheckSubgraph(); +} + +SubgraphView::SubgraphView(const SubgraphView& referenceSubgraph, IConnectableLayer* layer) + : m_InputSlots{} + , m_OutputSlots{} + , m_Layers{boost::polymorphic_downcast(layer)} + , m_ParentGraph(referenceSubgraph.m_ParentGraph) +{ + unsigned int numInputSlots = layer->GetNumInputSlots(); + m_InputSlots.resize(numInputSlots); + for (unsigned int i = 0; i < numInputSlots; i++) + { + m_InputSlots.at(i) = boost::polymorphic_downcast(&(layer->GetInputSlot(i))); + } + + unsigned int numOutputSlots = layer->GetNumOutputSlots(); + m_OutputSlots.resize(numOutputSlots); + for (unsigned int i = 0; i < numOutputSlots; i++) + { + m_OutputSlots.at(i) = boost::polymorphic_downcast(&(layer->GetOutputSlot(i))); + } + + CheckSubgraph(); +} + +void SubgraphView::CheckSubgraph() +{ + // Check that the sub-graph has a valid parent graph + BOOST_ASSERT_MSG(m_ParentGraph, "Sub-graphs must have a parent graph"); + + // Check for invalid or duplicate input slots + AssertIfNullsOrDuplicates(m_InputSlots, "Sub-graphs cannot contain null or duplicate input slots"); + + // Check for invalid or duplicate output slots + AssertIfNullsOrDuplicates(m_OutputSlots, "Sub-graphs cannot contain null or duplicate output slots"); + + // Check for invalid or duplicate layers + AssertIfNullsOrDuplicates(m_Layers, "Sub-graphs cannot contain null or duplicate layers"); + + // Check that all the layers of the sub-graph belong to the parent graph + std::for_each(m_Layers.begin(), m_Layers.end(), [&](const Layer* l) + { + BOOST_ASSERT_MSG(std::find(m_ParentGraph->begin(), m_ParentGraph->end(), l) != m_ParentGraph->end(), + "Sub-graph layer is not a member of the parent graph"); + }); +} + +void SubgraphView::Update(Graph &graph) +{ + m_InputSlots.clear(); + m_OutputSlots.clear(); + m_Layers.assign(graph.begin(), graph.end()); + m_ParentGraph = &graph; + + CheckSubgraph(); +} + +const SubgraphView::InputSlots& SubgraphView::GetInputSlots() const +{ + return m_InputSlots; +} + +const SubgraphView::OutputSlots& SubgraphView::GetOutputSlots() const +{ + return m_OutputSlots; +} + +const InputSlot* SubgraphView::GetInputSlot(unsigned int index) const +{ + return m_InputSlots.at(index); +} + +InputSlot* SubgraphView::GetInputSlot(unsigned int index) +{ + return m_InputSlots.at(index); +} + +const OutputSlot* SubgraphView::GetOutputSlot(unsigned int index) const +{ + return m_OutputSlots.at(index); +} + +OutputSlot* SubgraphView::GetOutputSlot(unsigned int index) +{ + return m_OutputSlots.at(index); +} + +unsigned int SubgraphView::GetNumInputSlots() const +{ + return boost::numeric_cast(m_InputSlots.size()); +} + +unsigned int SubgraphView::GetNumOutputSlots() const +{ + return boost::numeric_cast(m_OutputSlots.size()); +} + +const SubgraphView::Layers & SubgraphView::GetLayers() const +{ + return m_Layers; +} + +SubgraphView::Layers::iterator SubgraphView::begin() +{ + return m_Layers.begin(); +} + +SubgraphView::Iterator SubgraphView::end() +{ + return m_Layers.end(); +} + +SubgraphView::ConstIterator SubgraphView::begin() const +{ + return m_Layers.begin(); +} + +SubgraphView::ConstIterator SubgraphView::end() const +{ + return m_Layers.end(); +} + +SubgraphView::ConstIterator SubgraphView::cbegin() const +{ + return begin(); +} + +SubgraphView::ConstIterator SubgraphView::cend() const +{ + return end(); +} + +} // namespace armnn diff --git a/src/armnn/SubgraphView.hpp b/src/armnn/SubgraphView.hpp new file mode 100644 index 0000000000..b211ad4046 --- /dev/null +++ b/src/armnn/SubgraphView.hpp @@ -0,0 +1,109 @@ +// +// Copyright © 2017 Arm Ltd. All rights reserved. +// SPDX-License-Identifier: MIT +// + +#pragma once + +#include "Layer.hpp" +#include "Graph.hpp" + +#include +#include + +namespace armnn +{ + +/// +/// The SubgraphView class represents a subgraph of a Graph. +/// The data it holds, points to data held by layers of the Graph, so the +/// the contents of the SubgraphView becomes invalid when the Layers are destroyed +/// or changed. +/// +class SubgraphView final +{ +public: + using SubgraphViewPtr = std::unique_ptr; + using InputSlots = std::vector; + using OutputSlots = std::vector; + using Layers = std::list; + using Iterator = Layers::iterator; + using ConstIterator = Layers::const_iterator; + + /// Empty subgraphs are not allowed, they must at least have a parent graph. + SubgraphView() = delete; + + /// Constructs a sub-graph from the entire given graph. + SubgraphView(Graph& graph); + + /// Constructs a sub-graph with the given arguments and binds it to the specified parent graph. + SubgraphView(Graph* parentGraph, InputSlots&& inputs, OutputSlots&& outputs, Layers&& layers); + + /// Constructs a sub-graph with the given arguments and uses the specified sub-graph to get a reference + /// to the parent graph. + SubgraphView(const SubgraphView& referenceSubgraph, InputSlots&& inputs, OutputSlots&& outputs, Layers&& layers); + + /// Copy-constructor. + SubgraphView(const SubgraphView& subgraph); + + /// Move-constructor. + SubgraphView(SubgraphView&& subgraph); + + /// Constructs a sub-graph with only the given layer and uses the specified sub-graph to get a reference + /// to the parent graph. + SubgraphView(const SubgraphView& referenceSubgraph, IConnectableLayer* layer); + + /// Updates this sub-graph with the contents of the whole given graph. + void Update(Graph& graph); + + /// Adds a new layer, of type LayerType, to the parent graph of this sub-graph. + template + LayerT* AddLayer(Args&&... args) const; + + const InputSlots& GetInputSlots() const; + const OutputSlots& GetOutputSlots() const; + const Layers& GetLayers() const; + + const InputSlot* GetInputSlot(unsigned int index) const; + InputSlot* GetInputSlot(unsigned int index); + + const OutputSlot* GetOutputSlot(unsigned int index) const; + OutputSlot* GetOutputSlot(unsigned int index); + + unsigned int GetNumInputSlots() const; + unsigned int GetNumOutputSlots() const; + + Iterator begin(); + Iterator end(); + + ConstIterator begin() const; + ConstIterator end() const; + + ConstIterator cbegin() const; + ConstIterator cend() const; + +private: + void CheckSubgraph(); + + /// The list of pointers to the input slots of the parent graph. + InputSlots m_InputSlots; + + /// The list of pointers to the output slots of the parent graph. + OutputSlots m_OutputSlots; + + /// The list of pointers to the layers of the parent graph. + Layers m_Layers; + + /// Pointer to the graph this sub-graph is a view of. + Graph* m_ParentGraph; +}; + +template +LayerT* SubgraphView::AddLayer(Args&&... args) const +{ + BOOST_ASSERT(m_ParentGraph); + + return m_ParentGraph->AddLayer(args...); +} + +} // namespace armnn diff --git a/src/armnn/SubgraphViewSelector.cpp b/src/armnn/SubgraphViewSelector.cpp new file mode 100644 index 0000000000..7663c31173 --- /dev/null +++ b/src/armnn/SubgraphViewSelector.cpp @@ -0,0 +1,197 @@ +// +// Copyright © 2017 Arm Ltd. All rights reserved. +// SPDX-License-Identifier: MIT +// + +#include "SubgraphViewSelector.hpp" +#include "Graph.hpp" +#include +#include +#include + +namespace armnn +{ + +namespace +{ + +struct LayerSelectionInfo +{ + using LayerInfoContainer = std::unordered_map; + static constexpr uint32_t InitialSplitId() { return 1; } + + LayerSelectionInfo(Layer* layer, const SubgraphViewSelector::LayerSelectorFunction& selector) + : m_Layer{layer} + , m_SplitId{0} + , m_IsSelected{selector(*layer)} + , m_IsVisited(false) + { + // fill topology information by storing direct children + for (auto&& slot = m_Layer->BeginOutputSlots(); slot != m_Layer->EndOutputSlots(); ++slot) + { + for (InputSlot* childLayerInputSlot : slot->GetConnections()) + { + Layer& childLayer = childLayerInputSlot->GetOwningLayer(); + m_DirectChildren.push_back(&childLayer); + } + } + } + + void MarkChildrenSplits(LayerInfoContainer& network, + uint32_t splitId, + bool prevSelected) + { + if (m_IsVisited) + { + return; + } + m_IsVisited = true; + + if (m_SplitId < splitId) + { + m_SplitId = splitId; + } + + // introduce a new split point at all non-selected points, but only if the + // previous point was selected. this prevents creating a new subgraph at + // every non-selected layer + if (!m_IsSelected && prevSelected) + { + ++m_SplitId; + } + + for (auto& layer : m_DirectChildren) + { + auto it = network.find(layer); + BOOST_ASSERT_MSG(it != network.end(), "All layers must be part of the topology."); + if (it != network.end()) + { + it->second.MarkChildrenSplits(network, m_SplitId, m_IsSelected); + } + } + } + + bool IsInputLayer() const + { + return m_Layer->GetType() == armnn::LayerType::Input; + } + + void CollectNonSelectedInputs(SubgraphView::InputSlots& inputSlots, + const SubgraphViewSelector::LayerSelectorFunction& selector) + { + for (auto&& slot = m_Layer->BeginInputSlots(); slot != m_Layer->EndInputSlots(); ++slot) + { + OutputSlot* parentLayerOutputSlot = slot->GetConnectedOutputSlot(); + BOOST_ASSERT_MSG(parentLayerOutputSlot != nullptr, "The input slots must be connected here."); + if (parentLayerOutputSlot) + { + Layer& parentLayer = parentLayerOutputSlot->GetOwningLayer(); + if (selector(parentLayer) == false) + { + inputSlots.push_back(&(*slot)); + } + } + } + } + + void CollectNonSelectedOutputSlots(SubgraphView::OutputSlots& outputSlots, + const SubgraphViewSelector::LayerSelectorFunction& selector) + { + for (auto&& slot = m_Layer->BeginOutputSlots(); slot != m_Layer->EndOutputSlots(); ++slot) + { + for (InputSlot* childLayerInputSlot : slot->GetConnections()) + { + Layer& childLayer = childLayerInputSlot->GetOwningLayer(); + if (selector(childLayer) == false) + { + outputSlots.push_back(&(*slot)); + } + } + } + } + + std::vector m_DirectChildren; + Layer* m_Layer; + uint32_t m_SplitId; + bool m_IsSelected; + bool m_IsVisited; +}; + +} // namespace + +SubgraphViewSelector::Subgraphs +SubgraphViewSelector::SelectSubgraphs(Graph& graph, const LayerSelectorFunction& selector) +{ + SubgraphView subgraph(graph); + return SubgraphViewSelector::SelectSubgraphs(subgraph, selector); +} + +SubgraphViewSelector::Subgraphs +SubgraphViewSelector::SelectSubgraphs(SubgraphView& subgraph, const LayerSelectorFunction& selector) +{ + LayerSelectionInfo::LayerInfoContainer layerInfo; + + for (auto& layer : subgraph) + { + layerInfo.emplace(layer, LayerSelectionInfo{layer, selector}); + } + + uint32_t splitNo = LayerSelectionInfo::InitialSplitId(); + for (auto& info : layerInfo) + { + if (info.second.IsInputLayer()) + { + // For each input layer we mark the graph where subgraph + // splits need to happen because of the dependency between + // the selected and non-selected nodes + info.second.MarkChildrenSplits(layerInfo, splitNo, false); + } + } + + // Collect all selected layers keyed by split id into a map + using SelectionInfoPtrs = std::vector; + std::unordered_map splitMap; + for (auto& info : layerInfo) + { + if (info.second.m_IsSelected) + { + auto it = splitMap.find(info.second.m_SplitId); + if (it == splitMap.end()) + { + splitMap.insert(std::make_pair(info.second.m_SplitId, SelectionInfoPtrs{&info.second})); + } + else + { + it->second.push_back(&info.second); + } + } + } + + // Now each non-empty split id represents a subgraph + Subgraphs result; + for (auto& splitGraph : splitMap) + { + if (splitGraph.second.empty() == false) + { + SubgraphView::InputSlots inputs; + SubgraphView::OutputSlots outputs; + SubgraphView::Layers layers; + for (auto&& infoPtr : splitGraph.second) + { + infoPtr->CollectNonSelectedInputs(inputs, selector); + infoPtr->CollectNonSelectedOutputSlots(outputs, selector); + layers.push_back(infoPtr->m_Layer); + } + // Create a new sub-graph with the new lists of input/output slots and layer, using + // the given sub-graph as a reference of which parent graph to use + result.emplace_back(std::make_unique(subgraph, + std::move(inputs), + std::move(outputs), + std::move(layers))); + } + } + + return result; +} + +} // namespace armnn diff --git a/src/armnn/SubgraphViewSelector.hpp b/src/armnn/SubgraphViewSelector.hpp new file mode 100644 index 0000000000..9d881faa7c --- /dev/null +++ b/src/armnn/SubgraphViewSelector.hpp @@ -0,0 +1,38 @@ +// +// Copyright © 2017 Arm Ltd. All rights reserved. +// SPDX-License-Identifier: MIT +// +#pragma once + +#include "SubgraphView.hpp" +#include +#include + +namespace armnn +{ + +class Layer; +class Graph; + +class SubgraphViewSelector final +{ +public: + using SubgraphViewPtr = std::unique_ptr; + using Subgraphs = std::vector; + using LayerSelectorFunction = std::function; + + /// Selects subgraphs from a graph based on the selector function and the algorithm. + /// Since the Subgraphs object returns modifiable pointers to the input and output slots of the graph: + /// 1) the graph/sub-graph cannot be const + /// 2) the caller needs to make sure that the Subgraphs lifetime is shorter than the parent graph's + static Subgraphs SelectSubgraphs(Graph& graph, const LayerSelectorFunction& selector); + static Subgraphs SelectSubgraphs(SubgraphView& subgraph, const LayerSelectorFunction& selector); + +private: + // this is a utility class, don't construct or copy + SubgraphViewSelector() = delete; + SubgraphViewSelector(const SubgraphViewSelector&) = delete; + SubgraphViewSelector & operator=(const SubgraphViewSelector&) = delete; +}; + +} // namespace armnn diff --git a/src/armnn/layers/PreCompiledLayer.cpp b/src/armnn/layers/PreCompiledLayer.cpp index 29b35147f6..963924ded5 100644 --- a/src/armnn/layers/PreCompiledLayer.cpp +++ b/src/armnn/layers/PreCompiledLayer.cpp @@ -38,7 +38,7 @@ std::unique_ptr PreCompiledLayer::CreateWorkload(const armnn::Graph& void PreCompiledLayer::ValidateTensorShapesFromInputs() { - // NOTE: since the PreCompiledLayer is an internal layer created from a valid SubGraph, + // NOTE: since the PreCompiledLayer is an internal layer created from a valid SubgraphView, // we do not need to validate its input shapes } diff --git a/src/armnn/test/SubGraphTests.cpp b/src/armnn/test/SubGraphTests.cpp deleted file mode 100644 index e5b444a076..0000000000 --- a/src/armnn/test/SubGraphTests.cpp +++ /dev/null @@ -1,1044 +0,0 @@ -// -// Copyright © 2017 Arm Ltd. All rights reserved. -// SPDX-License-Identifier: MIT -// -#include - -#include - -#include -#include -#include - -#include - -using namespace armnn; - -namespace -{ - -bool AreAnySubGraphLayersPresentInGraph(const SubGraph::Layers &subGraphLayers, const Graph &graph) -{ - for(auto&& layer : subGraphLayers) - { - auto posInGraph = std::find(graph.begin(), graph.end(), layer); - if(posInGraph != graph.end()) - { - return true; - } - } - - return false; -} - -// -// this helper only works if all layers where the inputs connect to are not selected -// -SubGraph::InputSlots CreateInputsFrom(const std::vector& layers) -{ - SubGraph::InputSlots result; - for (auto&& layer : layers) - { - for (auto&& it = layer->BeginInputSlots(); it != layer->EndInputSlots(); ++it) - { - result.push_back(&(*it)); - } - } - return result; -} - -// -// this helper only works if all layers where the outputs connect to are not selected -// -SubGraph::OutputSlots CreateOutputsFrom(const std::vector& layers) -{ - SubGraph::OutputSlots result; - for (auto && layer : layers) - { - for (auto&& it = layer->BeginOutputSlots(); it != layer->EndOutputSlots(); ++it) - { - result.push_back(&(*it)); - } - } - return result; -} - -// -// this takes the inputs, outputs and layers as a copy and the move these copies into the -// resulting subgraph, so the pass bay value is intentional -// -SubGraphSelector::SubGraphPtr CreateSubGraphFrom(Graph& graph, - SubGraph::InputSlots&& inputs, - SubGraph::OutputSlots&& outputs, - SubGraph::Layers&& layers) -{ - return std::make_unique(&graph, std::move(inputs), std::move(outputs), std::move(layers)); -} - -template -std::vector ToSortedArray(Iterator begin, Iterator end) -{ - std::vector result(begin, end); - std::sort(result.begin(), result.end()); - return result; -} - -template -void CompareVectors(const std::vector & result, const std::vector & expected) -{ - BOOST_CHECK_EQUAL_COLLECTIONS(result.begin(), result.end(), expected.begin(), expected.end()); -} - -void CompareSubGraphs(SubGraphSelector::SubGraphPtr & result, - SubGraphSelector::SubGraphPtr & expected) -{ - // expect both to be valid subgraphs - BOOST_TEST((result.get() != nullptr)); - BOOST_TEST((expected.get() != nullptr)); - - if (result.get() != nullptr && expected.get() != nullptr) - { - // try to detect all other obvious errors too, mainly because here - // we can get a nicer error message from boost, the collection test - // also report error for these - BOOST_TEST(result->GetInputSlots().size() == expected->GetInputSlots().size()); - BOOST_TEST(result->GetOutputSlots().size() == expected->GetOutputSlots().size()); - BOOST_TEST(result->GetLayers().size() == expected->GetLayers().size()); - - auto resultLayers = ToSortedArray(result->GetLayers().begin(), - result->GetLayers().end()); - auto expectedLayers = ToSortedArray(expected->GetLayers().begin(), - expected->GetLayers().end()); - CompareVectors(resultLayers, expectedLayers); - - auto resultInputs = ToSortedArray(result->GetInputSlots().begin(), - result->GetInputSlots().end()); - auto expectedInputs = ToSortedArray(expected->GetInputSlots().begin(), - expected->GetInputSlots().end()); - CompareVectors(resultInputs, expectedInputs); - - auto resultOutputs = ToSortedArray(result->GetOutputSlots().begin(), - result->GetOutputSlots().end()); - auto expectedOutputs = ToSortedArray(expected->GetOutputSlots().begin(), - expected->GetOutputSlots().end()); - CompareVectors(resultOutputs, expectedOutputs); - } -} - -} // namespace - -BOOST_AUTO_TEST_SUITE(SubGraphSubstitution) - -BOOST_AUTO_TEST_CASE(SingleInputSingleOutput) -{ - // Construct graph - Graph graph; - - Layer* const inputLayer = graph.AddLayer(0, "input"); - - Convolution2dDescriptor convDescriptor; - Layer* const convLayer1 = graph.AddLayer(convDescriptor, "conv1"); - Layer* const convLayer2 = graph.AddLayer(convDescriptor, "conv2"); - - Layer* const outputLayer = graph.AddLayer(0, "output"); - - inputLayer->GetOutputSlot(0).Connect(convLayer1->GetInputSlot(0)); - convLayer1->GetOutputSlot(0).Connect(convLayer2->GetInputSlot(0)); - convLayer2->GetOutputSlot(0).Connect(outputLayer->GetInputSlot(0)); - - // Construct sub-graph - SubGraphSelector::SubGraphPtr subGraph = CreateSubGraphFrom(graph, - CreateInputsFrom({convLayer1}), - CreateOutputsFrom({convLayer2}), - {}); - - // Save sub-graph connections for comparison after substitution - IOutputSlot* subGraphInputConn = subGraph->GetInputSlot(0)->GetConnection(); - IInputSlot* subGraphOutputConn = subGraph->GetOutputSlot(0)->GetConnection(0); - - // Construct dummy pre-compiled layer - PreCompiledDescriptor preCompiledDescriptor(1, 1); - Layer* const preCompiledLayer = graph.AddLayer(preCompiledDescriptor, "pre-compiled"); - - // Substitute sub-graph with pre-compiled layer - graph.SubstituteSubGraph(std::move(subGraph), preCompiledLayer); - - // Check that connections are correct after substitution - BOOST_CHECK_EQUAL(preCompiledLayer->GetInputSlot(0).GetConnection(), subGraphInputConn); - BOOST_CHECK_EQUAL(preCompiledLayer->GetOutputSlot(0).GetConnection(0), subGraphOutputConn); -} - -BOOST_AUTO_TEST_CASE(MultiInputSingleOutput) -{ - // Construct graph - Graph graph; - - Layer* const inputLayer = graph.AddLayer(0, "input"); - - ViewsDescriptor splitterDescriptor(2); - Layer* const splitterLayer = graph.AddLayer(splitterDescriptor, "splitter"); - - Convolution2dDescriptor convDescriptor; - Layer* const convLayer1 = graph.AddLayer(convDescriptor, "conv1"); - Layer* const convLayer2 = graph.AddLayer(convDescriptor, "conv2"); - - OriginsDescriptor mergerDescriptor(2); - Layer* const mergerLayer = graph.AddLayer(mergerDescriptor, "merger"); - - Layer* const outputLayer = graph.AddLayer(0, "output"); - - inputLayer->GetOutputSlot(0).Connect(splitterLayer->GetInputSlot(0)); - splitterLayer->GetOutputSlot(0).Connect(convLayer1->GetInputSlot(0)); - splitterLayer->GetOutputSlot(1).Connect(convLayer2->GetInputSlot(0)); - convLayer1->GetOutputSlot(0).Connect(mergerLayer->GetInputSlot(0)); - convLayer2->GetOutputSlot(0).Connect(mergerLayer->GetInputSlot(1)); - mergerLayer->GetOutputSlot(0).Connect(outputLayer->GetInputSlot(0)); - - // Construct sub-graph - SubGraphSelector::SubGraphPtr subGraph = CreateSubGraphFrom(graph, - CreateInputsFrom({convLayer1, convLayer2}), - CreateOutputsFrom({mergerLayer}), - {}); - - // Save sub-graph connections for comparison after substitution - IOutputSlot* subGraphInputConn1 = subGraph->GetInputSlot(0)->GetConnection(); - IOutputSlot* subGraphInputConn2 = subGraph->GetInputSlot(1)->GetConnection(); - - IInputSlot* subGraphOutputConn = subGraph->GetOutputSlot(0)->GetConnection(0); - - // Construct dummy pre-compiled layer - PreCompiledDescriptor preCompiledDescriptor(2, 1); - Layer* const preCompiledLayer = graph.AddLayer(preCompiledDescriptor, "pre-compiled"); - - // Substitute sub-graph with pre-compiled layer - graph.SubstituteSubGraph(std::move(subGraph), preCompiledLayer); - - // Check that connections are correct after substitution - BOOST_CHECK_EQUAL(preCompiledLayer->GetInputSlot(0).GetConnection(), subGraphInputConn1); - BOOST_CHECK_EQUAL(preCompiledLayer->GetInputSlot(1).GetConnection(), subGraphInputConn2); - - BOOST_CHECK_EQUAL(preCompiledLayer->GetOutputSlot(0).GetConnection(0), subGraphOutputConn); -} - -BOOST_AUTO_TEST_CASE(SingleInputMultiOutput) -{ - // Construct graph - Graph graph; - - Layer* const inputLayer = graph.AddLayer(0, "input"); - - Convolution2dDescriptor convDescriptor; - Layer* const convLayer1 = graph.AddLayer(convDescriptor, "conv1"); - Layer* const convLayer2 = graph.AddLayer(convDescriptor, "conv2"); - OriginsDescriptor mergerDescriptor(2); - Layer* const mergerLayer = graph.AddLayer(mergerDescriptor, "merger"); - Layer* const outputLayer = graph.AddLayer(0, "output"); - - ViewsDescriptor splitterDescriptor(2); - Layer* const splitterLayer = graph.AddLayer(splitterDescriptor, "splitter"); - - inputLayer->GetOutputSlot(0).Connect(splitterLayer->GetInputSlot(0)); - splitterLayer->GetOutputSlot(0).Connect(convLayer1->GetInputSlot(0)); - splitterLayer->GetOutputSlot(1).Connect(convLayer2->GetInputSlot(0)); - convLayer1->GetOutputSlot(0).Connect(mergerLayer->GetInputSlot(0)); - convLayer2->GetOutputSlot(0).Connect(mergerLayer->GetInputSlot(1)); - mergerLayer->GetOutputSlot(0).Connect(outputLayer->GetInputSlot(0)); - - // Construct sub-graph - SubGraphSelector::SubGraphPtr subGraph = CreateSubGraphFrom(graph, - CreateInputsFrom({splitterLayer}), - CreateOutputsFrom({convLayer1, convLayer2}), - {}); - - // Save sub-graph connections for comparison after substitution - IOutputSlot* subGraphInputConn1 = subGraph->GetInputSlot(0)->GetConnection(); - - IInputSlot* subGraphOutputConn1 = subGraph->GetOutputSlot(0)->GetConnection(0); - IInputSlot* subGraphOutputConn2 = subGraph->GetOutputSlot(1)->GetConnection(0); - - // Construct dummy pre-compiled layer - PreCompiledDescriptor preCompiledDescriptor(1, 2); - Layer* const preCompiledLayer = graph.AddLayer(preCompiledDescriptor, "pre-compiled"); - - // Substitute sub-graph with pre-compiled layer - graph.SubstituteSubGraph(std::move(subGraph), preCompiledLayer); - - // Check that connections are correct after substitution - BOOST_CHECK_EQUAL(preCompiledLayer->GetInputSlot(0).GetConnection(), subGraphInputConn1); - - BOOST_CHECK_EQUAL(preCompiledLayer->GetOutputSlot(0).GetConnection(0), subGraphOutputConn1); - BOOST_CHECK_EQUAL(preCompiledLayer->GetOutputSlot(1).GetConnection(0), subGraphOutputConn2); -} - -BOOST_AUTO_TEST_CASE(MultiInputMultiOutput) -{ - // Construct graph - Graph graph; - - Layer* const inputLayer = graph.AddLayer(0, "input"); - - ViewsDescriptor splitterDescriptor(2); - Layer* const splitterLayer = graph.AddLayer(splitterDescriptor, "splitter"); - - Convolution2dDescriptor convDescriptor; - Layer* const convLayer1 = graph.AddLayer(convDescriptor, "conv1"); - Layer* const convLayer2 = graph.AddLayer(convDescriptor, "conv2"); - - OriginsDescriptor mergerDescriptor(2); - Layer* const mergerLayer = graph.AddLayer(mergerDescriptor, "merger"); - - Layer* const outputLayer = graph.AddLayer(0, "output"); - - inputLayer->GetOutputSlot(0).Connect(splitterLayer->GetInputSlot(0)); - splitterLayer->GetOutputSlot(0).Connect(convLayer1->GetInputSlot(0)); - splitterLayer->GetOutputSlot(1).Connect(convLayer2->GetInputSlot(0)); - convLayer1->GetOutputSlot(0).Connect(mergerLayer->GetInputSlot(0)); - convLayer2->GetOutputSlot(0).Connect(mergerLayer->GetInputSlot(1)); - mergerLayer->GetOutputSlot(0).Connect(outputLayer->GetInputSlot(0)); - - // Construct sub-graph - SubGraphSelector::SubGraphPtr subGraph = CreateSubGraphFrom(graph, - CreateInputsFrom({convLayer1, convLayer2}), - CreateOutputsFrom({convLayer1, convLayer2}), - {}); - - // Save sub-graph connections for comparison after substitution - IOutputSlot* subGraphInputConn1 = subGraph->GetInputSlot(0)->GetConnection(); - IOutputSlot* subGraphInputConn2 = subGraph->GetInputSlot(1)->GetConnection(); - - IInputSlot* subGraphOutputConn1 = subGraph->GetOutputSlot(0)->GetConnection(0); - IInputSlot* subGraphOutputConn2 = subGraph->GetOutputSlot(1)->GetConnection(0); - - // Construct dummy pre-compiled layer - PreCompiledDescriptor preCompiledDescriptor(2, 2); - Layer* const preCompiledLayer = graph.AddLayer(preCompiledDescriptor, "pre-compiled"); - - // Substitute sub-graph with pre-compiled layer - graph.SubstituteSubGraph(std::move(subGraph), preCompiledLayer); - - // Check that connections are correct after substitution - BOOST_CHECK_EQUAL(preCompiledLayer->GetInputSlot(0).GetConnection(), subGraphInputConn1); - BOOST_CHECK_EQUAL(preCompiledLayer->GetInputSlot(1).GetConnection(), subGraphInputConn2); - - BOOST_CHECK_EQUAL(preCompiledLayer->GetOutputSlot(0).GetConnection(0), subGraphOutputConn1); - BOOST_CHECK_EQUAL(preCompiledLayer->GetOutputSlot(1).GetConnection(0), subGraphOutputConn2); -} - -BOOST_AUTO_TEST_CASE(EraseReplacedLayers) -{ - // Construct graph - Graph graph; - - graph.AddLayer(0, "input"); - - ViewsDescriptor splitterDescriptor(2); - Layer* const splitterLayer = graph.AddLayer(splitterDescriptor, "splitter"); - - Convolution2dDescriptor convDescriptor; - Layer* const convLayer1 = graph.AddLayer(convDescriptor, "conv1"); - Layer* const convLayer2 = graph.AddLayer(convDescriptor, "conv2"); - - OriginsDescriptor mergerDescriptor(2); - Layer* const mergerLayer = graph.AddLayer(mergerDescriptor, "merger"); - - graph.AddLayer(0, "output"); - - // Construct sub-graph - SubGraphSelector::SubGraphPtr subGraph = CreateSubGraphFrom(graph, - {}, - {}, - {splitterLayer, convLayer1, convLayer2, mergerLayer}); - - // Construct dummy pre-compiled layer - PreCompiledDescriptor preCompiledDescriptor(0, 0); - Layer* const preCompiledLayer = graph.AddLayer(preCompiledDescriptor, "pre-compiled"); - - // Save sub-graph layers for later verification - const SubGraph::Layers subGraphLayers = subGraph->GetLayers(); - - // Substitute sub-graph with pre-compiled layer - graph.SubstituteSubGraph(std::move(subGraph), preCompiledLayer); - - // Check that the layers belonging to the sub-graph have been erased from the graph after substitution - BOOST_CHECK(!AreAnySubGraphLayersPresentInGraph(subGraphLayers, graph)); -} - -BOOST_AUTO_TEST_SUITE_END() - -BOOST_AUTO_TEST_SUITE(SubGraphSelection) - -BOOST_AUTO_TEST_CASE(SubGraphForEmptyGraph) -{ - Graph graph; - SubGraph subGraph(graph); - - BOOST_TEST(subGraph.GetInputSlots().empty()); - BOOST_TEST(subGraph.GetOutputSlots().empty()); - BOOST_TEST(subGraph.GetLayers().empty()); -} - -BOOST_AUTO_TEST_CASE(SubGraphForEntireGraph) -{ - Graph graph; - - auto output = graph.AddLayer(0, "output"); - auto mid0 = graph.InsertNewLayer(output->GetInputSlot(0), - ActivationDescriptor{}, - "mid0"); - auto mid1 = graph.InsertNewLayer(mid0->GetInputSlot(0), - ActivationDescriptor{}, - "mid1"); - graph.InsertNewLayer(mid1->GetInputSlot(0), 0, "input"); - - SubGraph subGraph(graph); - - BOOST_TEST(subGraph.GetInputSlots().empty()); - BOOST_TEST(subGraph.GetOutputSlots().empty()); - BOOST_TEST(subGraph.GetLayers().size() == graph.GetNumLayers()); -} - -BOOST_AUTO_TEST_CASE(NoSubGraphsForNoMatch) -{ - Graph graph; - - auto output = graph.AddLayer(0, "output"); - graph.InsertNewLayer(output->GetInputSlot(0), 0, "input"); - - SubGraphSelector::SubGraphs subGraphs = - SubGraphSelector::SelectSubGraphs(graph, [](const Layer &) { return false; }); - - BOOST_TEST(subGraphs.empty()); -} - -BOOST_AUTO_TEST_CASE(OneSubGraphsSelectedASingleMatch) -{ - Graph graph; - - auto output = graph.AddLayer(0, "output"); - graph.InsertNewLayer(output->GetInputSlot(0), 0, "input"); - - SubGraphSelector::SubGraphs subGraphs = - SubGraphSelector::SelectSubGraphs( - graph, - // select the output layer only - [](const Layer & l) - { - bool isOutput = l.GetNameStr().compare("output") == 0; - return isOutput; - }); - - BOOST_TEST(subGraphs.size() == 1); - if (subGraphs.size() == 1) - { - auto expected = CreateSubGraphFrom(graph, - CreateInputsFrom({output}), - // outputs of 'output' will be empty - CreateOutputsFrom({output}), - {output}); - - CompareSubGraphs(subGraphs[0], expected); - } -} - -BOOST_AUTO_TEST_CASE(MultipleLayersSelectedInTheMiddle) -{ - Graph graph; - - auto output = graph.AddLayer(0, "output"); - auto mid0 = graph.InsertNewLayer(output->GetInputSlot(0), - ActivationDescriptor{}, - "mid0"); - auto mid1 = graph.InsertNewLayer(mid0->GetInputSlot(0), - ActivationDescriptor{}, - "mid1"); - graph.InsertNewLayer(mid1->GetInputSlot(0), 0, "input"); - - SubGraphSelector::SubGraphs subGraphs = - SubGraphSelector::SelectSubGraphs( - graph, - // select the middle layers only - [](const Layer & l) - { - bool toSelect = (l.GetType() == LayerType::Activation); - return toSelect; - }); - - BOOST_TEST(subGraphs.size() == 1); - if (subGraphs.size() == 1) - { - auto expected = CreateSubGraphFrom(graph, - CreateInputsFrom({mid1}), - CreateOutputsFrom({mid0}), - {mid1, mid0}); - - CompareSubGraphs(subGraphs[0], expected); - } -} - -BOOST_AUTO_TEST_CASE(IslandInTheMiddle) -{ - // This case represent the scenario when a non-selected X1 node placed in the middle - // of the selected M* nodes: - // - // X0 -> M1 -> M2 -> M3 -> X2 - // X0 -> M4 -> X1 -> M5 -> X2 - // - /* - X0 - / \ - M1 M4 - | | - M2 X1 < the island in the middle ! - | | - M3 M5 - \ / - X2 - */ - // The expected result for this is that M1,M2,M3,M4 will be part of one subgraph and - // M5 will be part of another subgraph and the input and output slots in the subgraphs - // will be set accordingly. - // - Graph graph; - - OriginsDescriptor mergerDescriptor(2); - auto x2 = graph.AddLayer(mergerDescriptor, "x2"); - auto m3 = graph.InsertNewLayer(x2->GetInputSlot(0), - ActivationDescriptor{}, - "m3"); - auto m2 = graph.InsertNewLayer(m3->GetInputSlot(0), - ActivationDescriptor{}, - "m2"); - auto m1 = graph.InsertNewLayer(m2->GetInputSlot(0), - ActivationDescriptor{}, - "m1"); - auto x0 = graph.InsertNewLayer(m1->GetInputSlot(0), 0, "x0"); - - auto m5 = graph.InsertNewLayer(x2->GetInputSlot(1), - ActivationDescriptor{}, - "m5"); - auto x1 = graph.InsertNewLayer(m5->GetInputSlot(0), - Convolution2dDescriptor{}, - "x1"); - auto m4 = graph.InsertNewLayer(x1->GetInputSlot(0), - ActivationDescriptor{}, - "m4"); - - // Connect the other branch to the input layer - x0->GetOutputSlot(0).Connect(m4->GetInputSlot(0)); - - // All selected 'M*' layers will be of Activation type - SubGraphSelector::SubGraphs subGraphs = - SubGraphSelector::SelectSubGraphs( - graph, - // select the middle layers only - [](const Layer & l) - { - bool toSelect = (l.GetType() == LayerType::Activation); - return toSelect; - }); - - // expected results to test against - auto largerSubGraph = CreateSubGraphFrom(graph, - CreateInputsFrom({m1, m4}), - CreateOutputsFrom({m3, m4}), - {m1, m4, m2, m3}); - - auto smallerSubGraph = CreateSubGraphFrom(graph, - CreateInputsFrom({m5}), - CreateOutputsFrom({m5}), - {m5}); - - BOOST_TEST(subGraphs.size() == 2); - if (subGraphs.size() == 2) - { - // we need to have valid subgraph pointers here - BOOST_TEST((subGraphs[0] != nullptr)); - BOOST_TEST((subGraphs[1] != nullptr)); - - if (subGraphs[0].get() != nullptr && subGraphs[1].get() != nullptr) - { - // sort the subgraphs by layer size, so it is simpler to test - std::sort(subGraphs.begin(), subGraphs.end(), - [](SubGraphSelector::SubGraphPtr & lhs, SubGraphSelector::SubGraphPtr & rhs) - { - return (lhs->GetLayers().size() < rhs->GetLayers().size()); - } - ); - - // one subgraph needs to be size=1 and the other one is 4 - BOOST_TEST(subGraphs[0]->GetLayers().size() == 1); - BOOST_TEST(subGraphs[1]->GetLayers().size() == 4); - - CompareSubGraphs(subGraphs[0], smallerSubGraph); - CompareSubGraphs(subGraphs[1], largerSubGraph); - } - } -} - -BOOST_AUTO_TEST_CASE(MultipleSimpleSubGraphs) -{ - // This test case represents the scenario when we have two distinct subgraphs - // in a simple linear network. The selected nodes are the M* and the - // non-selected ones are the X* - // - // X1 -> M1 -> M2 -> X2 -> M3 -> X3 - // - // The expected results is two subgraphs, one with {M1, M2} and another one - // with {M3} - // - Graph graph; - - // the graph is constructed in reverse order - auto x3 = graph.AddLayer(0, "output"); - auto m3 = graph.InsertNewLayer(x3->GetInputSlot(0), - ActivationDescriptor{}, - "m3"); - auto x2 = graph.InsertNewLayer(m3->GetInputSlot(0), - Convolution2dDescriptor{}, - "x2"); - auto m2 = graph.InsertNewLayer(x2->GetInputSlot(0), - ActivationDescriptor{}, - "m2"); - auto m1 = graph.InsertNewLayer(m2->GetInputSlot(0), - ActivationDescriptor{}, - "m1"); - graph.InsertNewLayer(m1->GetInputSlot(0), 0, "x1"); - - // All selected 'M*' layers will be of Activation type - SubGraphSelector::SubGraphs subGraphs = - SubGraphSelector::SelectSubGraphs( - graph, - // select the middle layers only - [](const Layer & l) - { - bool toSelect = (l.GetType() == LayerType::Activation); - return toSelect; - }); - - // expected results to test against - auto largerSubGraph = CreateSubGraphFrom(graph, - CreateInputsFrom({m1}), - CreateOutputsFrom({m2}), - {m1, m2}); - - auto smallerSubGraph = CreateSubGraphFrom(graph, - CreateInputsFrom({m3}), - CreateOutputsFrom({m3}), - {m3}); - - BOOST_TEST(subGraphs.size() == 2); - if (subGraphs.size() == 2) - { - // we need to have valid subgraph pointers here - BOOST_TEST((subGraphs[0] != nullptr)); - BOOST_TEST((subGraphs[1] != nullptr)); - - if (subGraphs[0].get() != nullptr && subGraphs[1].get() != nullptr) - { - // sort the subgraphs by layer size, so it is simpler to test - std::sort(subGraphs.begin(), subGraphs.end(), - [](SubGraphSelector::SubGraphPtr & lhs, SubGraphSelector::SubGraphPtr & rhs) - { - return (lhs->GetLayers().size() < rhs->GetLayers().size()); - } - ); - - BOOST_TEST(subGraphs[0]->GetLayers().size() == 1); - BOOST_TEST(subGraphs[1]->GetLayers().size() == 2); - - CompareSubGraphs(subGraphs[0], smallerSubGraph); - CompareSubGraphs(subGraphs[1], largerSubGraph); - } - } -} - -BOOST_AUTO_TEST_CASE(SimpleLinearTest) -{ - //X1 -> M1 -> M2 -> X2 - //Where the input slots of M1 and the output slots of M2 are to be the sub graph boundaries. - Graph graph; - - ActivationDescriptor activationDefaults; - - auto layerX1 = graph.AddLayer(0, "layerX1"); - auto layerX2 = graph.AddLayer(0, "layerX2"); - auto layerM1 = graph.AddLayer(activationDefaults, "layerM1"); - auto layerM2 = graph.AddLayer(activationDefaults, "layerM2"); - - // X1 - // | - // M1 - // | - // M2 - // | - // X2 - - layerX1->GetOutputSlot(0).Connect(layerM1->GetInputSlot(0)); - layerM1->GetOutputSlot(0).Connect(layerM2->GetInputSlot(0)); - layerM2->GetOutputSlot(0).Connect(layerX2->GetInputSlot(0)); - - SubGraphSelector::SubGraphs subGraphs = - SubGraphSelector::SelectSubGraphs( - graph, - // select the activation layers M1 and M2 - [](const Layer & l) - { - bool toSelect = (l.GetType() == LayerType::Activation); - return toSelect; - }); - - BOOST_CHECK(subGraphs.size() == 1); - if(subGraphs.size() == 1) - { - auto expected = CreateSubGraphFrom(graph, - CreateInputsFrom({layerM1}), - CreateOutputsFrom({layerM2}), - {layerM1, layerM2}); - - CompareSubGraphs(subGraphs[0], expected); - } -} - -BOOST_AUTO_TEST_CASE(MultiInputSingleOutput) -{ - //X1 -> M1 -> M3 -> X3 - //X2 -> M2 -> M3 -> X3 - //Where the input slots of {M1, M2} and the output slots of M3 are to be the subgraph boundaries. - Graph graph; - - ActivationDescriptor activationDefaults; - - auto layerX1 = graph.AddLayer(0, "layerX1"); - auto layerX2 = graph.AddLayer(1, "layerX2"); - auto layerM1 = graph.AddLayer(activationDefaults, "layerM1"); - auto layerM2 = graph.AddLayer(activationDefaults, "layerM2"); - auto layerM3 = graph.AddLayer("layerM3"); - auto layerX3 = graph.AddLayer(0, "layerX3"); - - // X1 X2 - // | | - // M1 M2 - // \ | - // \ | - // \| - // M3 - // | - // | - // X3 - - layerX1->GetOutputSlot(0).Connect(layerM1->GetInputSlot(0)); - layerX2->GetOutputSlot(0).Connect(layerM2->GetInputSlot(0)); - layerM1->GetOutputSlot(0).Connect(layerM3->GetInputSlot(0)); - layerM2->GetOutputSlot(0).Connect(layerM3->GetInputSlot(1)); - layerM3->GetOutputSlot(0).Connect(layerX3->GetInputSlot(0)); - - SubGraphSelector::SubGraphs subGraphs = - SubGraphSelector::SelectSubGraphs( - graph, - // select Activation and Addition Layers M1, M2 and M3 - [](const Layer & l) - { - bool toSelect = (l.GetType() == LayerType::Activation - || l.GetType() == LayerType::Addition); - return toSelect; - }); - - BOOST_CHECK(subGraphs.size() == 1); - if (subGraphs.size() == 1) - { - auto expected = CreateSubGraphFrom(graph, - CreateInputsFrom({layerM1, layerM2}), - CreateOutputsFrom({layerM3}), - {layerM1, layerM2, layerM3}); - - CompareSubGraphs(subGraphs[0], expected); - } -} - -BOOST_AUTO_TEST_CASE(SingleInputMultiOutput) -{ - //X1 -> M1 -> M2 -> X2 - //X1 -> M1 -> M3 -> X3 - //Where the input slots of M1 and the output slots of {M2, M3} are to be the subgraph boundaries. - Graph graph; - - ActivationDescriptor activationDefaults; - ViewsDescriptor viewDefaults(2,4); - - Layer* layerX1 = graph.AddLayer(0, "layerX1"); - Layer* layerM1 = graph.AddLayer(viewDefaults, "layerM1"); - Layer* layerM2 = graph.AddLayer(activationDefaults, "layerM2"); - Layer* layerM3 = graph.AddLayer(activationDefaults, "layerM3"); - Layer* layerX2 = graph.AddLayer(0, "layerX2"); - Layer* layerX3 = graph.AddLayer(1, "layerX3"); - - // X2 - // | - // M1 - // /| - // / | - // / | - // M2 M3 - // | | - // | | - // X2 X3 - - layerX1->GetOutputSlot(0).Connect(layerM1->GetInputSlot(0)); - layerM1->GetOutputSlot(0).Connect(layerM2->GetInputSlot(0)); - layerM1->GetOutputSlot(1).Connect(layerM3->GetInputSlot(0)); - layerM2->GetOutputSlot(0).Connect(layerX2->GetInputSlot(0)); - layerM3->GetOutputSlot(0).Connect(layerX3->GetInputSlot(0)); - - SubGraphSelector::SubGraphs subGraphs = - SubGraphSelector::SelectSubGraphs( - graph, - // select Activation and Splitter Layers M1, M2 and M3 - [](const Layer & l) - { - bool toSelect = (l.GetType() == LayerType::Activation - || l.GetType() == LayerType::Splitter); - return toSelect; - }); - - BOOST_CHECK(subGraphs.size() == 1); - if(subGraphs.size() == 1) - { - auto expected = CreateSubGraphFrom(graph, - CreateInputsFrom({layerM1}), - CreateOutputsFrom({layerM2, layerM3}), - {layerM1, layerM2, layerM3}); - - CompareSubGraphs(subGraphs[0], expected); - } -} - -BOOST_AUTO_TEST_CASE(MultiInputMultiOutput) -{ - // This case represents the scenario with multiple inputs and multiple outputs - // - // X1 -> M1 -> M3 -> M4 -> X3 - // X2 -> M2 -> M3 -> M5 -> X4 - // - // Where the input slots of {M1, M2} and the output slots of {M4, M5} are to be the subgraph - // boundaries. - - Graph graph; - - ActivationDescriptor activationDefaults; - OriginsDescriptor mergerDescriptor(2); - - auto x1 = graph.AddLayer(0, "x1"); - auto x2 = graph.AddLayer(1, "x2"); - - auto m1 = graph.AddLayer(activationDefaults, "m1"); - auto m2 = graph.AddLayer(activationDefaults, "m2"); - auto m3 = graph.AddLayer(mergerDescriptor, "m3"); - - auto m4 = graph.AddLayer(activationDefaults, "m4"); - auto m5 = graph.AddLayer(activationDefaults, "m5"); - - auto x3 = graph.AddLayer(0, "x3"); - auto x4 = graph.AddLayer(1, "x4"); - - x1->GetOutputSlot(0).Connect(m1->GetInputSlot(0)); - x2->GetOutputSlot(0).Connect(m2->GetInputSlot(0)); - - m1->GetOutputSlot(0).Connect(m3->GetInputSlot(0)); - m2->GetOutputSlot(0).Connect(m3->GetInputSlot(1)); - - m3->GetOutputSlot(0).Connect(m4->GetInputSlot(0)); - m3->GetOutputSlot(0).Connect(m5->GetInputSlot(0)); - - m4->GetOutputSlot(0).Connect(x3->GetInputSlot(0)); - m5->GetOutputSlot(0).Connect(x4->GetInputSlot(0)); - - - SubGraphSelector::SubGraphs subGraphs = - SubGraphSelector::SelectSubGraphs( - graph, - // select Activation and Merger Layers M1, M2, M3, M4, M5 - [](const Layer & l) - { - bool toSelect = (l.GetType() == LayerType::Activation - || l.GetType() == LayerType::Merger); - return toSelect; - }); - - - BOOST_CHECK(subGraphs.size() == 1); - if (subGraphs.size() == 1) - { - auto expected = CreateSubGraphFrom(graph, - CreateInputsFrom({m1, m2}), - CreateOutputsFrom({m4, m5}), - {m1, m2, m3, m4, m5}); - - CompareSubGraphs(subGraphs[0], expected); - } -} - -BOOST_AUTO_TEST_SUITE_END() - -BOOST_AUTO_TEST_SUITE(IntegrationTests) - -BOOST_AUTO_TEST_CASE(SingleSubGraph) -{ - // This test case represents the scenario when we have one subgraph - // in which two layers have GpuAcc backend assigned - - //Construct graph - Graph graph; - - Layer* const inputLayer = graph.AddLayer(0, "input"); - - Convolution2dDescriptor convDescriptor; - Layer* const convLayer1 = graph.AddLayer(convDescriptor, "conv1"); - convLayer1->SetBackendId(Compute::GpuAcc); - - Layer* const convLayer2 = graph.AddLayer(convDescriptor, "conv2"); - convLayer2->SetBackendId(Compute::GpuAcc); - - Layer* const outputLayer = graph.AddLayer(0, "output"); - - inputLayer->GetOutputSlot(0).Connect(convLayer1->GetInputSlot(0)); - convLayer1->GetOutputSlot(0).Connect(convLayer2->GetInputSlot(0)); - convLayer2->GetOutputSlot(0).Connect(outputLayer->GetInputSlot(0)); - - // GpuAcc sub graph selector - SubGraphSelector::SubGraphs subGraphs = - SubGraphSelector::SelectSubGraphs( - graph, - // select the GpuAcc layers only - [](const Layer & l){ - bool toSelect = (l.GetBackendId() == Compute::GpuAcc); - return toSelect; - }); - - BOOST_TEST(subGraphs.size() == 1); - if(subGraphs.size() == 1) - { - BOOST_TEST((subGraphs[0] != nullptr)); - - if (subGraphs[0].get() != nullptr) - { - unsigned int numInputSlots = boost::numeric_cast(subGraphs[0]->GetInputSlots().size()); - unsigned int numOutputSlots = boost::numeric_cast(subGraphs[0]->GetOutputSlots().size()); - - BOOST_TEST((numInputSlots == 1)); - BOOST_TEST((numOutputSlots == 1)); - - // Save sub-graph connections for comparison after substitution - IOutputSlot* subGraphInputConn1 = subGraphs[0]->GetInputSlot(0)->GetConnection(); - IInputSlot* subGraphOutputConn1 = subGraphs[0]->GetOutputSlot(0)->GetConnection(0); - - // Construct dummy pre-compiled layer - PreCompiledDescriptor preCompiledDescriptor(numInputSlots, numOutputSlots); - Layer* const preCompiledLayer = graph.AddLayer(preCompiledDescriptor, "pre-compiled"); - - // Substitute sub-graph with pre-compiled layer - graph.SubstituteSubGraph((std::move(subGraphs[0])), preCompiledLayer); - - // Check that connections are correct after substitution - BOOST_CHECK_EQUAL(preCompiledLayer->GetInputSlot(0).GetConnection(), subGraphInputConn1); - - BOOST_CHECK_EQUAL(preCompiledLayer->GetOutputSlot(0).GetConnection(0), subGraphOutputConn1); - } - } -} - -BOOST_AUTO_TEST_CASE(MultipleSubGraphs) -{ - // This test case represents the scenario when we have two subgraphs - // in which two layers have CpuAcc backend assigned - - //Construct graph - Graph graph; - - Layer* const inputLayer = graph.AddLayer(0, "input"); - - ViewsDescriptor splitterDescriptor(2); - Layer* const splitterLayer = graph.AddLayer(splitterDescriptor, "splitter"); - splitterLayer->SetBackendId(Compute::CpuAcc); - - Convolution2dDescriptor convDescriptor; - Layer* const convLayer1 = graph.AddLayer(convDescriptor, "conv1"); - Layer* const convLayer2 = graph.AddLayer(convDescriptor, "conv2"); - - OriginsDescriptor mergerDescriptor(2); - Layer* const mergerLayer = graph.AddLayer(mergerDescriptor, "merger"); - mergerLayer->SetBackendId(Compute::CpuAcc); - - Layer* const outputLayer = graph.AddLayer(0, "output"); - - inputLayer->GetOutputSlot(0).Connect(splitterLayer->GetInputSlot(0)); - splitterLayer->GetOutputSlot(0).Connect(convLayer1->GetInputSlot(0)); - splitterLayer->GetOutputSlot(1).Connect(convLayer2->GetInputSlot(0)); - convLayer1->GetOutputSlot(0).Connect(mergerLayer->GetInputSlot(0)); - convLayer2->GetOutputSlot(0).Connect(mergerLayer->GetInputSlot(1)); - mergerLayer->GetOutputSlot(0).Connect(outputLayer->GetInputSlot(0)); - - // CpuAcc sub graph selector - SubGraphSelector::SubGraphs subGraphs = - SubGraphSelector::SelectSubGraphs( - graph, - // select the CpuAcc layers only - [](const Layer & l){ - bool toSelect = (l.GetBackendId() == Compute::CpuAcc); - return toSelect; - }); - - BOOST_TEST(subGraphs.size() == 2); - if(subGraphs.size() == 2) - { - BOOST_TEST((subGraphs[0] != nullptr)); - BOOST_TEST((subGraphs[1] != nullptr)); - - if (subGraphs[0].get() != nullptr && subGraphs[1].get() != nullptr) - { - //Sort subGraphs by their inputSlot size. - std::sort(subGraphs.begin(), subGraphs.end(), - [](SubGraphSelector::SubGraphPtr & lhs, SubGraphSelector::SubGraphPtr & rhs) - { - return (lhs->GetInputSlots().size() < rhs->GetInputSlots().size()); - } - ); - - unsigned int numInputSlots1 = boost::numeric_cast(subGraphs[0]->GetInputSlots().size()); - unsigned int numOutputSlots1 = boost::numeric_cast(subGraphs[0]->GetOutputSlots().size()); - - unsigned int numInputSlots2 = boost::numeric_cast(subGraphs[1]->GetInputSlots().size()); - unsigned int numOutputSlots2 = boost::numeric_cast(subGraphs[1]->GetOutputSlots().size()); - - // Save sub-graph connections for comparison after substitution - IOutputSlot* subGraph1InputConn = subGraphs[0]->GetInputSlot(0)->GetConnection(); - IInputSlot* subGraph1OutputConn1 = subGraphs[0]->GetOutputSlot(0)->GetConnection(0); - IInputSlot* subGraph1OutputConn2 = subGraphs[0]->GetOutputSlot(1)->GetConnection(0); - - // Save sub-graph connections for comparison after substitution - IOutputSlot* subGraph2InputConn1 = subGraphs[1]->GetInputSlot(0)->GetConnection(); - IOutputSlot* subGraph2InputConn2 = subGraphs[1]->GetInputSlot(1)->GetConnection(); - IInputSlot* subGraph2OutputConn = subGraphs[1]->GetOutputSlot(0)->GetConnection(0); - - PreCompiledDescriptor preCompiledDescriptor1(numInputSlots1, numOutputSlots1); - Layer* const preCompiledLayer1 = graph.AddLayer(preCompiledDescriptor1, "pre-compiled1"); - - PreCompiledDescriptor preCompiledDescriptor2(numInputSlots2, numOutputSlots2); - Layer* const preCompiledLayer2 = graph.AddLayer(preCompiledDescriptor2, "pre-compiled2"); - - // Substitute sub-graph with pre-compiled layer - graph.SubstituteSubGraph((std::move(subGraphs[0])), preCompiledLayer1); - graph.SubstituteSubGraph((std::move(subGraphs[1])), preCompiledLayer2); - - // Check that connections are correct after substitution - BOOST_CHECK_EQUAL(preCompiledLayer1->GetInputSlot(0).GetConnection(), subGraph1InputConn); - BOOST_CHECK_EQUAL(preCompiledLayer1->GetOutputSlot(0).GetConnection(0), subGraph1OutputConn1); - BOOST_CHECK_EQUAL(preCompiledLayer1->GetOutputSlot(1).GetConnection(0), subGraph1OutputConn2); - - BOOST_CHECK_EQUAL(preCompiledLayer2->GetInputSlot(0).GetConnection(), subGraph2InputConn1); - BOOST_CHECK_EQUAL(preCompiledLayer2->GetInputSlot(1).GetConnection(), subGraph2InputConn2); - BOOST_CHECK_EQUAL(preCompiledLayer2->GetOutputSlot(0).GetConnection(0), subGraph2OutputConn); - } - } -} - -BOOST_AUTO_TEST_SUITE_END() diff --git a/src/armnn/test/SubgraphViewTests.cpp b/src/armnn/test/SubgraphViewTests.cpp new file mode 100644 index 0000000000..603bb159d0 --- /dev/null +++ b/src/armnn/test/SubgraphViewTests.cpp @@ -0,0 +1,1047 @@ +// +// Copyright © 2017 Arm Ltd. All rights reserved. +// SPDX-License-Identifier: MIT +// +#include + +#include + +#include +#include +#include + +#include + +using namespace armnn; + +namespace +{ + +bool AreAnySubgraphLayersPresentInGraph(const SubgraphView::Layers &subgraphLayers, const Graph &graph) +{ + for(auto&& layer : subgraphLayers) + { + auto posInGraph = std::find(graph.begin(), graph.end(), layer); + if(posInGraph != graph.end()) + { + return true; + } + } + + return false; +} + +// +// this helper only works if all layers where the inputs connect to are not selected +// +SubgraphView::InputSlots CreateInputsFrom(const std::vector& layers) +{ + SubgraphView::InputSlots result; + for (auto&& layer : layers) + { + for (auto&& it = layer->BeginInputSlots(); it != layer->EndInputSlots(); ++it) + { + result.push_back(&(*it)); + } + } + return result; +} + +// +// this helper only works if all layers where the outputs connect to are not selected +// +SubgraphView::OutputSlots CreateOutputsFrom(const std::vector& layers) +{ + SubgraphView::OutputSlots result; + for (auto && layer : layers) + { + for (auto&& it = layer->BeginOutputSlots(); it != layer->EndOutputSlots(); ++it) + { + result.push_back(&(*it)); + } + } + return result; +} + +// +// this takes the inputs, outputs and layers as a copy and the move these copies into the +// resulting subgraph, so the pass bay value is intentional +// +SubgraphViewSelector::SubgraphViewPtr CreateSubgraphViewFrom(Graph& graph, + SubgraphView::InputSlots&& inputs, + SubgraphView::OutputSlots&& outputs, + SubgraphView::Layers&& layers) +{ + return std::make_unique(&graph, std::move(inputs), std::move(outputs), std::move(layers)); +} + +template +std::vector ToSortedArray(Iterator begin, Iterator end) +{ + std::vector result(begin, end); + std::sort(result.begin(), result.end()); + return result; +} + +template +void CompareVectors(const std::vector& result, const std::vector& expected) +{ + BOOST_CHECK_EQUAL_COLLECTIONS(result.begin(), result.end(), expected.begin(), expected.end()); +} + +void CompareSubgraphViews(SubgraphViewSelector::SubgraphViewPtr& result, + SubgraphViewSelector::SubgraphViewPtr& expected) +{ + // expect both to be valid subgraphs + BOOST_TEST((result.get() != nullptr)); + BOOST_TEST((expected.get() != nullptr)); + + if (result.get() != nullptr && expected.get() != nullptr) + { + // try to detect all other obvious errors too, mainly because here + // we can get a nicer error message from boost, the collection test + // also report error for these + BOOST_TEST(result->GetInputSlots().size() == expected->GetInputSlots().size()); + BOOST_TEST(result->GetOutputSlots().size() == expected->GetOutputSlots().size()); + BOOST_TEST(result->GetLayers().size() == expected->GetLayers().size()); + + auto resultLayers = ToSortedArray(result->GetLayers().begin(), + result->GetLayers().end()); + auto expectedLayers = ToSortedArray(expected->GetLayers().begin(), + expected->GetLayers().end()); + CompareVectors(resultLayers, expectedLayers); + + auto resultInputs = ToSortedArray(result->GetInputSlots().begin(), + result->GetInputSlots().end()); + auto expectedInputs = ToSortedArray(expected->GetInputSlots().begin(), + expected->GetInputSlots().end()); + CompareVectors(resultInputs, expectedInputs); + + auto resultOutputs = ToSortedArray(result->GetOutputSlots().begin(), + result->GetOutputSlots().end()); + auto expectedOutputs = ToSortedArray(expected->GetOutputSlots().begin(), + expected->GetOutputSlots().end()); + CompareVectors(resultOutputs, expectedOutputs); + } +} + +} // namespace + +BOOST_AUTO_TEST_SUITE(SubgraphSubstitution) + +BOOST_AUTO_TEST_CASE(SingleInputSingleOutput) +{ + // Construct graph + Graph graph; + + Layer* const inputLayer = graph.AddLayer(0, "input"); + + Convolution2dDescriptor convDescriptor; + Layer* const convLayer1 = graph.AddLayer(convDescriptor, "conv1"); + Layer* const convLayer2 = graph.AddLayer(convDescriptor, "conv2"); + + Layer* const outputLayer = graph.AddLayer(0, "output"); + + inputLayer->GetOutputSlot(0).Connect(convLayer1->GetInputSlot(0)); + convLayer1->GetOutputSlot(0).Connect(convLayer2->GetInputSlot(0)); + convLayer2->GetOutputSlot(0).Connect(outputLayer->GetInputSlot(0)); + + // Construct sub-graph + SubgraphViewSelector::SubgraphViewPtr subgraph = CreateSubgraphViewFrom(graph, + CreateInputsFrom({convLayer1}), + CreateOutputsFrom({convLayer2}), + {}); + + // Save sub-graph connections for comparison after substitution + IOutputSlot* subgraphInputConn = subgraph->GetInputSlot(0)->GetConnection(); + IInputSlot* subgraphOutputConn = subgraph->GetOutputSlot(0)->GetConnection(0); + + // Construct dummy pre-compiled layer + PreCompiledDescriptor preCompiledDescriptor(1, 1); + Layer* const preCompiledLayer = graph.AddLayer(preCompiledDescriptor, "pre-compiled"); + + // Substitute sub-graph with pre-compiled layer + graph.SubstituteSubgraph(std::move(subgraph), preCompiledLayer); + + // Check that connections are correct after substitution + BOOST_CHECK_EQUAL(preCompiledLayer->GetInputSlot(0).GetConnection(), subgraphInputConn); + BOOST_CHECK_EQUAL(preCompiledLayer->GetOutputSlot(0).GetConnection(0), subgraphOutputConn); +} + +BOOST_AUTO_TEST_CASE(MultiInputSingleOutput) +{ + // Construct graph + Graph graph; + + Layer* const inputLayer = graph.AddLayer(0, "input"); + + ViewsDescriptor splitterDescriptor(2); + Layer* const splitterLayer = graph.AddLayer(splitterDescriptor, "splitter"); + + Convolution2dDescriptor convDescriptor; + Layer* const convLayer1 = graph.AddLayer(convDescriptor, "conv1"); + Layer* const convLayer2 = graph.AddLayer(convDescriptor, "conv2"); + + OriginsDescriptor mergerDescriptor(2); + Layer* const mergerLayer = graph.AddLayer(mergerDescriptor, "merger"); + + Layer* const outputLayer = graph.AddLayer(0, "output"); + + inputLayer->GetOutputSlot(0).Connect(splitterLayer->GetInputSlot(0)); + splitterLayer->GetOutputSlot(0).Connect(convLayer1->GetInputSlot(0)); + splitterLayer->GetOutputSlot(1).Connect(convLayer2->GetInputSlot(0)); + convLayer1->GetOutputSlot(0).Connect(mergerLayer->GetInputSlot(0)); + convLayer2->GetOutputSlot(0).Connect(mergerLayer->GetInputSlot(1)); + mergerLayer->GetOutputSlot(0).Connect(outputLayer->GetInputSlot(0)); + + // Construct sub-graph + SubgraphViewSelector::SubgraphViewPtr subgraph = CreateSubgraphViewFrom(graph, + CreateInputsFrom({convLayer1, convLayer2}), + CreateOutputsFrom({mergerLayer}), + {}); + + // Save sub-graph connections for comparison after substitution + IOutputSlot* subgraphInputConn1 = subgraph->GetInputSlot(0)->GetConnection(); + IOutputSlot* subgraphInputConn2 = subgraph->GetInputSlot(1)->GetConnection(); + + IInputSlot* subgraphOutputConn = subgraph->GetOutputSlot(0)->GetConnection(0); + + // Construct dummy pre-compiled layer + PreCompiledDescriptor preCompiledDescriptor(2, 1); + Layer* const preCompiledLayer = graph.AddLayer(preCompiledDescriptor, "pre-compiled"); + + // Substitute sub-graph with pre-compiled layer + graph.SubstituteSubgraph(std::move(subgraph), preCompiledLayer); + + // Check that connections are correct after substitution + BOOST_CHECK_EQUAL(preCompiledLayer->GetInputSlot(0).GetConnection(), subgraphInputConn1); + BOOST_CHECK_EQUAL(preCompiledLayer->GetInputSlot(1).GetConnection(), subgraphInputConn2); + + BOOST_CHECK_EQUAL(preCompiledLayer->GetOutputSlot(0).GetConnection(0), subgraphOutputConn); +} + +BOOST_AUTO_TEST_CASE(SingleInputMultiOutput) +{ + // Construct graph + Graph graph; + + Layer* const inputLayer = graph.AddLayer(0, "input"); + + Convolution2dDescriptor convDescriptor; + Layer* const convLayer1 = graph.AddLayer(convDescriptor, "conv1"); + Layer* const convLayer2 = graph.AddLayer(convDescriptor, "conv2"); + OriginsDescriptor mergerDescriptor(2); + Layer* const mergerLayer = graph.AddLayer(mergerDescriptor, "merger"); + Layer* const outputLayer = graph.AddLayer(0, "output"); + + ViewsDescriptor splitterDescriptor(2); + Layer* const splitterLayer = graph.AddLayer(splitterDescriptor, "splitter"); + + inputLayer->GetOutputSlot(0).Connect(splitterLayer->GetInputSlot(0)); + splitterLayer->GetOutputSlot(0).Connect(convLayer1->GetInputSlot(0)); + splitterLayer->GetOutputSlot(1).Connect(convLayer2->GetInputSlot(0)); + convLayer1->GetOutputSlot(0).Connect(mergerLayer->GetInputSlot(0)); + convLayer2->GetOutputSlot(0).Connect(mergerLayer->GetInputSlot(1)); + mergerLayer->GetOutputSlot(0).Connect(outputLayer->GetInputSlot(0)); + + // Construct sub-graph + SubgraphViewSelector::SubgraphViewPtr subgraph = CreateSubgraphViewFrom(graph, + CreateInputsFrom({splitterLayer}), + CreateOutputsFrom({convLayer1, convLayer2}), + {}); + + // Save sub-graph connections for comparison after substitution + IOutputSlot* subgraphInputConn1 = subgraph->GetInputSlot(0)->GetConnection(); + + IInputSlot* subgraphOutputConn1 = subgraph->GetOutputSlot(0)->GetConnection(0); + IInputSlot* subgraphOutputConn2 = subgraph->GetOutputSlot(1)->GetConnection(0); + + // Construct dummy pre-compiled layer + PreCompiledDescriptor preCompiledDescriptor(1, 2); + Layer* const preCompiledLayer = graph.AddLayer(preCompiledDescriptor, "pre-compiled"); + + // Substitute sub-graph with pre-compiled layer + graph.SubstituteSubgraph(std::move(subgraph), preCompiledLayer); + + // Check that connections are correct after substitution + BOOST_CHECK_EQUAL(preCompiledLayer->GetInputSlot(0).GetConnection(), subgraphInputConn1); + + BOOST_CHECK_EQUAL(preCompiledLayer->GetOutputSlot(0).GetConnection(0), subgraphOutputConn1); + BOOST_CHECK_EQUAL(preCompiledLayer->GetOutputSlot(1).GetConnection(0), subgraphOutputConn2); +} + +BOOST_AUTO_TEST_CASE(MultiInputMultiOutput) +{ + // Construct graph + Graph graph; + + Layer* const inputLayer = graph.AddLayer(0, "input"); + + ViewsDescriptor splitterDescriptor(2); + Layer* const splitterLayer = graph.AddLayer(splitterDescriptor, "splitter"); + + Convolution2dDescriptor convDescriptor; + Layer* const convLayer1 = graph.AddLayer(convDescriptor, "conv1"); + Layer* const convLayer2 = graph.AddLayer(convDescriptor, "conv2"); + + OriginsDescriptor mergerDescriptor(2); + Layer* const mergerLayer = graph.AddLayer(mergerDescriptor, "merger"); + + Layer* const outputLayer = graph.AddLayer(0, "output"); + + inputLayer->GetOutputSlot(0).Connect(splitterLayer->GetInputSlot(0)); + splitterLayer->GetOutputSlot(0).Connect(convLayer1->GetInputSlot(0)); + splitterLayer->GetOutputSlot(1).Connect(convLayer2->GetInputSlot(0)); + convLayer1->GetOutputSlot(0).Connect(mergerLayer->GetInputSlot(0)); + convLayer2->GetOutputSlot(0).Connect(mergerLayer->GetInputSlot(1)); + mergerLayer->GetOutputSlot(0).Connect(outputLayer->GetInputSlot(0)); + + // Construct sub-graph + SubgraphViewSelector::SubgraphViewPtr subgraph = CreateSubgraphViewFrom(graph, + CreateInputsFrom({convLayer1, convLayer2}), + CreateOutputsFrom({convLayer1, convLayer2}), + {}); + + // Save sub-graph connections for comparison after substitution + IOutputSlot* subgraphInputConn1 = subgraph->GetInputSlot(0)->GetConnection(); + IOutputSlot* subgraphInputConn2 = subgraph->GetInputSlot(1)->GetConnection(); + + IInputSlot* subgraphOutputConn1 = subgraph->GetOutputSlot(0)->GetConnection(0); + IInputSlot* subgraphOutputConn2 = subgraph->GetOutputSlot(1)->GetConnection(0); + + // Construct dummy pre-compiled layer + PreCompiledDescriptor preCompiledDescriptor(2, 2); + Layer* const preCompiledLayer = graph.AddLayer(preCompiledDescriptor, "pre-compiled"); + + // Substitute sub-graph with pre-compiled layer + graph.SubstituteSubgraph(std::move(subgraph), preCompiledLayer); + + // Check that connections are correct after substitution + BOOST_CHECK_EQUAL(preCompiledLayer->GetInputSlot(0).GetConnection(), subgraphInputConn1); + BOOST_CHECK_EQUAL(preCompiledLayer->GetInputSlot(1).GetConnection(), subgraphInputConn2); + + BOOST_CHECK_EQUAL(preCompiledLayer->GetOutputSlot(0).GetConnection(0), subgraphOutputConn1); + BOOST_CHECK_EQUAL(preCompiledLayer->GetOutputSlot(1).GetConnection(0), subgraphOutputConn2); +} + +BOOST_AUTO_TEST_CASE(EraseReplacedLayers) +{ + // Construct graph + Graph graph; + + graph.AddLayer(0, "input"); + + ViewsDescriptor splitterDescriptor(2); + Layer* const splitterLayer = graph.AddLayer(splitterDescriptor, "splitter"); + + Convolution2dDescriptor convDescriptor; + Layer* const convLayer1 = graph.AddLayer(convDescriptor, "conv1"); + Layer* const convLayer2 = graph.AddLayer(convDescriptor, "conv2"); + + OriginsDescriptor mergerDescriptor(2); + Layer* const mergerLayer = graph.AddLayer(mergerDescriptor, "merger"); + + graph.AddLayer(0, "output"); + + // Construct sub-graph + SubgraphViewSelector::SubgraphViewPtr subgraph = CreateSubgraphViewFrom(graph, + {}, + {}, + {splitterLayer, + convLayer1, + convLayer2, + mergerLayer}); + + // Construct dummy pre-compiled layer + PreCompiledDescriptor preCompiledDescriptor(0, 0); + Layer* const preCompiledLayer = graph.AddLayer(preCompiledDescriptor, "pre-compiled"); + + // Save sub-graph layers for later verification + const SubgraphView::Layers subgraphLayers = subgraph->GetLayers(); + + // Substitute sub-graph with pre-compiled layer + graph.SubstituteSubgraph(std::move(subgraph), preCompiledLayer); + + // Check that the layers belonging to the sub-graph have been erased from the graph after substitution + BOOST_CHECK(!AreAnySubgraphLayersPresentInGraph(subgraphLayers, graph)); +} + +BOOST_AUTO_TEST_SUITE_END() + +BOOST_AUTO_TEST_SUITE(SubgraphSelection) + +BOOST_AUTO_TEST_CASE(SubgraphForEmptyGraph) +{ + Graph graph; + SubgraphView subgraph(graph); + + BOOST_TEST(subgraph.GetInputSlots().empty()); + BOOST_TEST(subgraph.GetOutputSlots().empty()); + BOOST_TEST(subgraph.GetLayers().empty()); +} + +BOOST_AUTO_TEST_CASE(SubgraphForEntireGraph) +{ + Graph graph; + + auto output = graph.AddLayer(0, "output"); + auto mid0 = graph.InsertNewLayer(output->GetInputSlot(0), + ActivationDescriptor{}, + "mid0"); + auto mid1 = graph.InsertNewLayer(mid0->GetInputSlot(0), + ActivationDescriptor{}, + "mid1"); + graph.InsertNewLayer(mid1->GetInputSlot(0), 0, "input"); + + SubgraphView subgraph(graph); + + BOOST_TEST(subgraph.GetInputSlots().empty()); + BOOST_TEST(subgraph.GetOutputSlots().empty()); + BOOST_TEST(subgraph.GetLayers().size() == graph.GetNumLayers()); +} + +BOOST_AUTO_TEST_CASE(NoSubgraphsForNoMatch) +{ + Graph graph; + + auto output = graph.AddLayer(0, "output"); + graph.InsertNewLayer(output->GetInputSlot(0), 0, "input"); + + SubgraphViewSelector::Subgraphs subgraphs = + SubgraphViewSelector::SelectSubgraphs(graph, [](const Layer &) { return false; }); + + BOOST_TEST(subgraphs.empty()); +} + +BOOST_AUTO_TEST_CASE(OneSubgraphsSelectedASingleMatch) +{ + Graph graph; + + auto output = graph.AddLayer(0, "output"); + graph.InsertNewLayer(output->GetInputSlot(0), 0, "input"); + + SubgraphViewSelector::Subgraphs subgraphs = + SubgraphViewSelector::SelectSubgraphs( + graph, + // select the output layer only + [](const Layer & l) + { + bool isOutput = l.GetNameStr().compare("output") == 0; + return isOutput; + }); + + BOOST_TEST(subgraphs.size() == 1); + if (subgraphs.size() == 1) + { + auto expected = CreateSubgraphViewFrom(graph, + CreateInputsFrom({output}), + // outputs of 'output' will be empty + CreateOutputsFrom({output}), + {output}); + + CompareSubgraphViews(subgraphs[0], expected); + } +} + +BOOST_AUTO_TEST_CASE(MultipleLayersSelectedInTheMiddle) +{ + Graph graph; + + auto output = graph.AddLayer(0, "output"); + auto mid0 = graph.InsertNewLayer(output->GetInputSlot(0), + ActivationDescriptor{}, + "mid0"); + auto mid1 = graph.InsertNewLayer(mid0->GetInputSlot(0), + ActivationDescriptor{}, + "mid1"); + graph.InsertNewLayer(mid1->GetInputSlot(0), 0, "input"); + + SubgraphViewSelector::Subgraphs subgraphs = + SubgraphViewSelector::SelectSubgraphs( + graph, + // select the middle layers only + [](const Layer & l) + { + bool toSelect = (l.GetType() == LayerType::Activation); + return toSelect; + }); + + BOOST_TEST(subgraphs.size() == 1); + if (subgraphs.size() == 1) + { + auto expected = CreateSubgraphViewFrom(graph, + CreateInputsFrom({mid1}), + CreateOutputsFrom({mid0}), + {mid1, mid0}); + + CompareSubgraphViews(subgraphs[0], expected); + } +} + +BOOST_AUTO_TEST_CASE(IslandInTheMiddle) +{ + // This case represent the scenario when a non-selected X1 node placed in the middle + // of the selected M* nodes: + // + // X0 -> M1 -> M2 -> M3 -> X2 + // X0 -> M4 -> X1 -> M5 -> X2 + // + /* + X0 + / \ + M1 M4 + | | + M2 X1 < the island in the middle ! + | | + M3 M5 + \ / + X2 + */ + // The expected result for this is that M1,M2,M3,M4 will be part of one subgraph and + // M5 will be part of another subgraph and the input and output slots in the subgraphs + // will be set accordingly. + // + Graph graph; + + OriginsDescriptor mergerDescriptor(2); + auto x2 = graph.AddLayer(mergerDescriptor, "x2"); + auto m3 = graph.InsertNewLayer(x2->GetInputSlot(0), + ActivationDescriptor{}, + "m3"); + auto m2 = graph.InsertNewLayer(m3->GetInputSlot(0), + ActivationDescriptor{}, + "m2"); + auto m1 = graph.InsertNewLayer(m2->GetInputSlot(0), + ActivationDescriptor{}, + "m1"); + auto x0 = graph.InsertNewLayer(m1->GetInputSlot(0), 0, "x0"); + + auto m5 = graph.InsertNewLayer(x2->GetInputSlot(1), + ActivationDescriptor{}, + "m5"); + auto x1 = graph.InsertNewLayer(m5->GetInputSlot(0), + Convolution2dDescriptor{}, + "x1"); + auto m4 = graph.InsertNewLayer(x1->GetInputSlot(0), + ActivationDescriptor{}, + "m4"); + + // Connect the other branch to the input layer + x0->GetOutputSlot(0).Connect(m4->GetInputSlot(0)); + + // All selected 'M*' layers will be of Activation type + SubgraphViewSelector::Subgraphs subgraphs = + SubgraphViewSelector::SelectSubgraphs( + graph, + // select the middle layers only + [](const Layer & l) + { + bool toSelect = (l.GetType() == LayerType::Activation); + return toSelect; + }); + + // expected results to test against + auto largerSubgraph = CreateSubgraphViewFrom(graph, + CreateInputsFrom({m1, m4}), + CreateOutputsFrom({m3, m4}), + {m1, m4, m2, m3}); + + auto smallerSubgraph = CreateSubgraphViewFrom(graph, + CreateInputsFrom({m5}), + CreateOutputsFrom({m5}), + {m5}); + + BOOST_TEST(subgraphs.size() == 2); + if (subgraphs.size() == 2) + { + // we need to have valid subgraph pointers here + BOOST_TEST((subgraphs[0] != nullptr)); + BOOST_TEST((subgraphs[1] != nullptr)); + + if (subgraphs[0].get() != nullptr && subgraphs[1].get() != nullptr) + { + // sort the subgraphs by layer size, so it is simpler to test + std::sort(subgraphs.begin(), subgraphs.end(), + [](SubgraphViewSelector::SubgraphViewPtr & lhs, SubgraphViewSelector::SubgraphViewPtr & rhs) + { + return (lhs->GetLayers().size() < rhs->GetLayers().size()); + } + ); + + // one subgraph needs to be size=1 and the other one is 4 + BOOST_TEST(subgraphs[0]->GetLayers().size() == 1); + BOOST_TEST(subgraphs[1]->GetLayers().size() == 4); + + CompareSubgraphViews(subgraphs[0], smallerSubgraph); + CompareSubgraphViews(subgraphs[1], largerSubgraph); + } + } +} + +BOOST_AUTO_TEST_CASE(MultipleSimpleSubgraphs) +{ + // This test case represents the scenario when we have two distinct subgraphs + // in a simple linear network. The selected nodes are the M* and the + // non-selected ones are the X* + // + // X1 -> M1 -> M2 -> X2 -> M3 -> X3 + // + // The expected results is two subgraphs, one with {M1, M2} and another one + // with {M3} + // + Graph graph; + + // the graph is constructed in reverse order + auto x3 = graph.AddLayer(0, "output"); + auto m3 = graph.InsertNewLayer(x3->GetInputSlot(0), + ActivationDescriptor{}, + "m3"); + auto x2 = graph.InsertNewLayer(m3->GetInputSlot(0), + Convolution2dDescriptor{}, + "x2"); + auto m2 = graph.InsertNewLayer(x2->GetInputSlot(0), + ActivationDescriptor{}, + "m2"); + auto m1 = graph.InsertNewLayer(m2->GetInputSlot(0), + ActivationDescriptor{}, + "m1"); + graph.InsertNewLayer(m1->GetInputSlot(0), 0, "x1"); + + // All selected 'M*' layers will be of Activation type + SubgraphViewSelector::Subgraphs subgraphs = + SubgraphViewSelector::SelectSubgraphs( + graph, + // select the middle layers only + [](const Layer & l) + { + bool toSelect = (l.GetType() == LayerType::Activation); + return toSelect; + }); + + // expected results to test against + auto largerSubgraph = CreateSubgraphViewFrom(graph, + CreateInputsFrom({m1}), + CreateOutputsFrom({m2}), + {m1, m2}); + + auto smallerSubgraph = CreateSubgraphViewFrom(graph, + CreateInputsFrom({m3}), + CreateOutputsFrom({m3}), + {m3}); + + BOOST_TEST(subgraphs.size() == 2); + if (subgraphs.size() == 2) + { + // we need to have valid subgraph pointers here + BOOST_TEST((subgraphs[0] != nullptr)); + BOOST_TEST((subgraphs[1] != nullptr)); + + if (subgraphs[0].get() != nullptr && subgraphs[1].get() != nullptr) + { + // sort the subgraphs by layer size, so it is simpler to test + std::sort(subgraphs.begin(), subgraphs.end(), + [](SubgraphViewSelector::SubgraphViewPtr & lhs, SubgraphViewSelector::SubgraphViewPtr & rhs) + { + return (lhs->GetLayers().size() < rhs->GetLayers().size()); + } + ); + + BOOST_TEST(subgraphs[0]->GetLayers().size() == 1); + BOOST_TEST(subgraphs[1]->GetLayers().size() == 2); + + CompareSubgraphViews(subgraphs[0], smallerSubgraph); + CompareSubgraphViews(subgraphs[1], largerSubgraph); + } + } +} + +BOOST_AUTO_TEST_CASE(SimpleLinearTest) +{ + //X1 -> M1 -> M2 -> X2 + //Where the input slots of M1 and the output slots of M2 are to be the sub graph boundaries. + Graph graph; + + ActivationDescriptor activationDefaults; + + auto layerX1 = graph.AddLayer(0, "layerX1"); + auto layerX2 = graph.AddLayer(0, "layerX2"); + auto layerM1 = graph.AddLayer(activationDefaults, "layerM1"); + auto layerM2 = graph.AddLayer(activationDefaults, "layerM2"); + + // X1 + // | + // M1 + // | + // M2 + // | + // X2 + + layerX1->GetOutputSlot(0).Connect(layerM1->GetInputSlot(0)); + layerM1->GetOutputSlot(0).Connect(layerM2->GetInputSlot(0)); + layerM2->GetOutputSlot(0).Connect(layerX2->GetInputSlot(0)); + + SubgraphViewSelector::Subgraphs subgraphs = + SubgraphViewSelector::SelectSubgraphs( + graph, + // select the activation layers M1 and M2 + [](const Layer & l) + { + bool toSelect = (l.GetType() == LayerType::Activation); + return toSelect; + }); + + BOOST_CHECK(subgraphs.size() == 1); + if(subgraphs.size() == 1) + { + auto expected = CreateSubgraphViewFrom(graph, + CreateInputsFrom({layerM1}), + CreateOutputsFrom({layerM2}), + {layerM1, layerM2}); + + CompareSubgraphViews(subgraphs[0], expected); + } +} + +BOOST_AUTO_TEST_CASE(MultiInputSingleOutput) +{ + //X1 -> M1 -> M3 -> X3 + //X2 -> M2 -> M3 -> X3 + //Where the input slots of {M1, M2} and the output slots of M3 are to be the subgraph boundaries. + Graph graph; + + ActivationDescriptor activationDefaults; + + auto layerX1 = graph.AddLayer(0, "layerX1"); + auto layerX2 = graph.AddLayer(1, "layerX2"); + auto layerM1 = graph.AddLayer(activationDefaults, "layerM1"); + auto layerM2 = graph.AddLayer(activationDefaults, "layerM2"); + auto layerM3 = graph.AddLayer("layerM3"); + auto layerX3 = graph.AddLayer(0, "layerX3"); + + // X1 X2 + // | | + // M1 M2 + // \ | + // \ | + // \| + // M3 + // | + // | + // X3 + + layerX1->GetOutputSlot(0).Connect(layerM1->GetInputSlot(0)); + layerX2->GetOutputSlot(0).Connect(layerM2->GetInputSlot(0)); + layerM1->GetOutputSlot(0).Connect(layerM3->GetInputSlot(0)); + layerM2->GetOutputSlot(0).Connect(layerM3->GetInputSlot(1)); + layerM3->GetOutputSlot(0).Connect(layerX3->GetInputSlot(0)); + + SubgraphViewSelector::Subgraphs subgraphs = + SubgraphViewSelector::SelectSubgraphs( + graph, + // select Activation and Addition Layers M1, M2 and M3 + [](const Layer & l) + { + bool toSelect = (l.GetType() == LayerType::Activation + || l.GetType() == LayerType::Addition); + return toSelect; + }); + + BOOST_CHECK(subgraphs.size() == 1); + if (subgraphs.size() == 1) + { + auto expected = CreateSubgraphViewFrom(graph, + CreateInputsFrom({layerM1, layerM2}), + CreateOutputsFrom({layerM3}), + {layerM1, layerM2, layerM3}); + + CompareSubgraphViews(subgraphs[0], expected); + } +} + +BOOST_AUTO_TEST_CASE(SingleInputMultiOutput) +{ + //X1 -> M1 -> M2 -> X2 + //X1 -> M1 -> M3 -> X3 + //Where the input slots of M1 and the output slots of {M2, M3} are to be the subgraph boundaries. + Graph graph; + + ActivationDescriptor activationDefaults; + ViewsDescriptor viewDefaults(2,4); + + Layer* layerX1 = graph.AddLayer(0, "layerX1"); + Layer* layerM1 = graph.AddLayer(viewDefaults, "layerM1"); + Layer* layerM2 = graph.AddLayer(activationDefaults, "layerM2"); + Layer* layerM3 = graph.AddLayer(activationDefaults, "layerM3"); + Layer* layerX2 = graph.AddLayer(0, "layerX2"); + Layer* layerX3 = graph.AddLayer(1, "layerX3"); + + // X2 + // | + // M1 + // /| + // / | + // / | + // M2 M3 + // | | + // | | + // X2 X3 + + layerX1->GetOutputSlot(0).Connect(layerM1->GetInputSlot(0)); + layerM1->GetOutputSlot(0).Connect(layerM2->GetInputSlot(0)); + layerM1->GetOutputSlot(1).Connect(layerM3->GetInputSlot(0)); + layerM2->GetOutputSlot(0).Connect(layerX2->GetInputSlot(0)); + layerM3->GetOutputSlot(0).Connect(layerX3->GetInputSlot(0)); + + SubgraphViewSelector::Subgraphs subgraphs = + SubgraphViewSelector::SelectSubgraphs( + graph, + // select Activation and Splitter Layers M1, M2 and M3 + [](const Layer & l) + { + bool toSelect = (l.GetType() == LayerType::Activation + || l.GetType() == LayerType::Splitter); + return toSelect; + }); + + BOOST_CHECK(subgraphs.size() == 1); + if(subgraphs.size() == 1) + { + auto expected = CreateSubgraphViewFrom(graph, + CreateInputsFrom({layerM1}), + CreateOutputsFrom({layerM2, layerM3}), + {layerM1, layerM2, layerM3}); + + CompareSubgraphViews(subgraphs[0], expected); + } +} + +BOOST_AUTO_TEST_CASE(MultiInputMultiOutput) +{ + // This case represents the scenario with multiple inputs and multiple outputs + // + // X1 -> M1 -> M3 -> M4 -> X3 + // X2 -> M2 -> M3 -> M5 -> X4 + // + // Where the input slots of {M1, M2} and the output slots of {M4, M5} are to be the subgraph + // boundaries. + + Graph graph; + + ActivationDescriptor activationDefaults; + OriginsDescriptor mergerDescriptor(2); + + auto x1 = graph.AddLayer(0, "x1"); + auto x2 = graph.AddLayer(1, "x2"); + + auto m1 = graph.AddLayer(activationDefaults, "m1"); + auto m2 = graph.AddLayer(activationDefaults, "m2"); + auto m3 = graph.AddLayer(mergerDescriptor, "m3"); + + auto m4 = graph.AddLayer(activationDefaults, "m4"); + auto m5 = graph.AddLayer(activationDefaults, "m5"); + + auto x3 = graph.AddLayer(0, "x3"); + auto x4 = graph.AddLayer(1, "x4"); + + x1->GetOutputSlot(0).Connect(m1->GetInputSlot(0)); + x2->GetOutputSlot(0).Connect(m2->GetInputSlot(0)); + + m1->GetOutputSlot(0).Connect(m3->GetInputSlot(0)); + m2->GetOutputSlot(0).Connect(m3->GetInputSlot(1)); + + m3->GetOutputSlot(0).Connect(m4->GetInputSlot(0)); + m3->GetOutputSlot(0).Connect(m5->GetInputSlot(0)); + + m4->GetOutputSlot(0).Connect(x3->GetInputSlot(0)); + m5->GetOutputSlot(0).Connect(x4->GetInputSlot(0)); + + + SubgraphViewSelector::Subgraphs subgraphs = + SubgraphViewSelector::SelectSubgraphs( + graph, + // select Activation and Merger Layers M1, M2, M3, M4, M5 + [](const Layer & l) + { + bool toSelect = (l.GetType() == LayerType::Activation + || l.GetType() == LayerType::Merger); + return toSelect; + }); + + + BOOST_CHECK(subgraphs.size() == 1); + if (subgraphs.size() == 1) + { + auto expected = CreateSubgraphViewFrom(graph, + CreateInputsFrom({m1, m2}), + CreateOutputsFrom({m4, m5}), + {m1, m2, m3, m4, m5}); + + CompareSubgraphViews(subgraphs[0], expected); + } +} + +BOOST_AUTO_TEST_SUITE_END() + +BOOST_AUTO_TEST_SUITE(IntegrationTests) + +BOOST_AUTO_TEST_CASE(SingleSubgraph) +{ + // This test case represents the scenario when we have one subgraph + // in which two layers have GpuAcc backend assigned + + //Construct graph + Graph graph; + + Layer* const inputLayer = graph.AddLayer(0, "input"); + + Convolution2dDescriptor convDescriptor; + Layer* const convLayer1 = graph.AddLayer(convDescriptor, "conv1"); + convLayer1->SetBackendId(Compute::GpuAcc); + + Layer* const convLayer2 = graph.AddLayer(convDescriptor, "conv2"); + convLayer2->SetBackendId(Compute::GpuAcc); + + Layer* const outputLayer = graph.AddLayer(0, "output"); + + inputLayer->GetOutputSlot(0).Connect(convLayer1->GetInputSlot(0)); + convLayer1->GetOutputSlot(0).Connect(convLayer2->GetInputSlot(0)); + convLayer2->GetOutputSlot(0).Connect(outputLayer->GetInputSlot(0)); + + // GpuAcc sub graph selector + SubgraphViewSelector::Subgraphs subgraphs = + SubgraphViewSelector::SelectSubgraphs( + graph, + // select the GpuAcc layers only + [](const Layer & l){ + bool toSelect = (l.GetBackendId() == Compute::GpuAcc); + return toSelect; + }); + + BOOST_TEST(subgraphs.size() == 1); + if(subgraphs.size() == 1) + { + BOOST_TEST((subgraphs[0] != nullptr)); + + if (subgraphs[0].get() != nullptr) + { + unsigned int numInputSlots = boost::numeric_cast(subgraphs[0]->GetInputSlots().size()); + unsigned int numOutputSlots = boost::numeric_cast(subgraphs[0]->GetOutputSlots().size()); + + BOOST_TEST((numInputSlots == 1)); + BOOST_TEST((numOutputSlots == 1)); + + // Save sub-graph connections for comparison after substitution + IOutputSlot* subgraphInputConn1 = subgraphs[0]->GetInputSlot(0)->GetConnection(); + IInputSlot* subgraphOutputConn1 = subgraphs[0]->GetOutputSlot(0)->GetConnection(0); + + // Construct dummy pre-compiled layer + PreCompiledDescriptor preCompiledDescriptor(numInputSlots, numOutputSlots); + Layer* const preCompiledLayer = graph.AddLayer(preCompiledDescriptor, "pre-compiled"); + + // Substitute sub-graph with pre-compiled layer + graph.SubstituteSubgraph((std::move(subgraphs[0])), preCompiledLayer); + + // Check that connections are correct after substitution + BOOST_CHECK_EQUAL(preCompiledLayer->GetInputSlot(0).GetConnection(), subgraphInputConn1); + + BOOST_CHECK_EQUAL(preCompiledLayer->GetOutputSlot(0).GetConnection(0), subgraphOutputConn1); + } + } +} + +BOOST_AUTO_TEST_CASE(MultipleSubgraphs) +{ + // This test case represents the scenario when we have two subgraphs + // in which two layers have CpuAcc backend assigned + + //Construct graph + Graph graph; + + Layer* const inputLayer = graph.AddLayer(0, "input"); + + ViewsDescriptor splitterDescriptor(2); + Layer* const splitterLayer = graph.AddLayer(splitterDescriptor, "splitter"); + splitterLayer->SetBackendId(Compute::CpuAcc); + + Convolution2dDescriptor convDescriptor; + Layer* const convLayer1 = graph.AddLayer(convDescriptor, "conv1"); + Layer* const convLayer2 = graph.AddLayer(convDescriptor, "conv2"); + + OriginsDescriptor mergerDescriptor(2); + Layer* const mergerLayer = graph.AddLayer(mergerDescriptor, "merger"); + mergerLayer->SetBackendId(Compute::CpuAcc); + + Layer* const outputLayer = graph.AddLayer(0, "output"); + + inputLayer->GetOutputSlot(0).Connect(splitterLayer->GetInputSlot(0)); + splitterLayer->GetOutputSlot(0).Connect(convLayer1->GetInputSlot(0)); + splitterLayer->GetOutputSlot(1).Connect(convLayer2->GetInputSlot(0)); + convLayer1->GetOutputSlot(0).Connect(mergerLayer->GetInputSlot(0)); + convLayer2->GetOutputSlot(0).Connect(mergerLayer->GetInputSlot(1)); + mergerLayer->GetOutputSlot(0).Connect(outputLayer->GetInputSlot(0)); + + // CpuAcc sub graph selector + SubgraphViewSelector::Subgraphs subgraphs = + SubgraphViewSelector::SelectSubgraphs( + graph, + // select the CpuAcc layers only + [](const Layer & l){ + bool toSelect = (l.GetBackendId() == Compute::CpuAcc); + return toSelect; + }); + + BOOST_TEST(subgraphs.size() == 2); + if(subgraphs.size() == 2) + { + BOOST_TEST((subgraphs[0] != nullptr)); + BOOST_TEST((subgraphs[1] != nullptr)); + + if (subgraphs[0].get() != nullptr && subgraphs[1].get() != nullptr) + { + //Sort subgraphs by their inputSlot size. + std::sort(subgraphs.begin(), subgraphs.end(), + [](SubgraphViewSelector::SubgraphViewPtr & lhs, SubgraphViewSelector::SubgraphViewPtr & rhs) + { + return (lhs->GetInputSlots().size() < rhs->GetInputSlots().size()); + } + ); + + unsigned int numInputSlots1 = boost::numeric_cast(subgraphs[0]->GetInputSlots().size()); + unsigned int numOutputSlots1 = boost::numeric_cast(subgraphs[0]->GetOutputSlots().size()); + + unsigned int numInputSlots2 = boost::numeric_cast(subgraphs[1]->GetInputSlots().size()); + unsigned int numOutputSlots2 = boost::numeric_cast(subgraphs[1]->GetOutputSlots().size()); + + // Save sub-graph connections for comparison after substitution + IOutputSlot* subgraph1InputConn = subgraphs[0]->GetInputSlot(0)->GetConnection(); + IInputSlot* subgraph1OutputConn1 = subgraphs[0]->GetOutputSlot(0)->GetConnection(0); + IInputSlot* subgraph1OutputConn2 = subgraphs[0]->GetOutputSlot(1)->GetConnection(0); + + // Save sub-graph connections for comparison after substitution + IOutputSlot* subgraph2InputConn1 = subgraphs[1]->GetInputSlot(0)->GetConnection(); + IOutputSlot* subgraph2InputConn2 = subgraphs[1]->GetInputSlot(1)->GetConnection(); + IInputSlot* subgraph2OutputConn = subgraphs[1]->GetOutputSlot(0)->GetConnection(0); + + PreCompiledDescriptor preCompiledDescriptor1(numInputSlots1, numOutputSlots1); + Layer* const preCompiledLayer1 = graph.AddLayer(preCompiledDescriptor1, "pre-compiled1"); + + PreCompiledDescriptor preCompiledDescriptor2(numInputSlots2, numOutputSlots2); + Layer* const preCompiledLayer2 = graph.AddLayer(preCompiledDescriptor2, "pre-compiled2"); + + // Substitute sub-graph with pre-compiled layer + graph.SubstituteSubgraph((std::move(subgraphs[0])), preCompiledLayer1); + graph.SubstituteSubgraph((std::move(subgraphs[1])), preCompiledLayer2); + + // Check that connections are correct after substitution + BOOST_CHECK_EQUAL(preCompiledLayer1->GetInputSlot(0).GetConnection(), subgraph1InputConn); + BOOST_CHECK_EQUAL(preCompiledLayer1->GetOutputSlot(0).GetConnection(0), subgraph1OutputConn1); + BOOST_CHECK_EQUAL(preCompiledLayer1->GetOutputSlot(1).GetConnection(0), subgraph1OutputConn2); + + BOOST_CHECK_EQUAL(preCompiledLayer2->GetInputSlot(0).GetConnection(), subgraph2InputConn1); + BOOST_CHECK_EQUAL(preCompiledLayer2->GetInputSlot(1).GetConnection(), subgraph2InputConn2); + BOOST_CHECK_EQUAL(preCompiledLayer2->GetOutputSlot(0).GetConnection(0), subgraph2OutputConn); + } + } +} + +BOOST_AUTO_TEST_SUITE_END() diff --git a/src/armnnTfLiteParser/TfLiteParser.cpp b/src/armnnTfLiteParser/TfLiteParser.cpp index 57333439aa..0cc6f93f3a 100644 --- a/src/armnnTfLiteParser/TfLiteParser.cpp +++ b/src/armnnTfLiteParser/TfLiteParser.cpp @@ -491,15 +491,15 @@ void TfLiteParser::AddBroadcastReshapeLayer(size_t subgraphIndex, CHECK_MODEL(m_Model, subgraphIndex, operatorIndex); BOOST_ASSERT(layer != nullptr); - const auto & subGraphPtr = m_Model->subgraphs[subgraphIndex]; - const auto & operatorPtr = subGraphPtr->operators[operatorIndex]; + const auto & subgraphPtr = m_Model->subgraphs[subgraphIndex]; + const auto & operatorPtr = subgraphPtr->operators[operatorIndex]; BOOST_ASSERT(operatorPtr->inputs.size() > 1); uint32_t reshapedInputId = CHECKED_NON_NEGATIVE(operatorPtr->inputs[0]); - TensorRawPtr tensorPtr = subGraphPtr->tensors[reshapedInputId].get(); + TensorRawPtr tensorPtr = subgraphPtr->tensors[reshapedInputId].get(); uint32_t inputId = CHECKED_NON_NEGATIVE(operatorPtr->inputs[1]); - TensorRawPtr tensorPtr1 = subGraphPtr->tensors[inputId].get(); + TensorRawPtr tensorPtr1 = subgraphPtr->tensors[inputId].get(); armnn::TensorInfo reshapedTensorInfo = ToTensorInfo(tensorPtr); armnn::TensorInfo inputTensorInfo = ToTensorInfo(tensorPtr1); @@ -573,7 +573,7 @@ INetworkPtr TfLiteParser::CreateNetworkFromModel() } size_t subgraphIndex = 0; - for (SubGraphPtr const & subgraph : m_Model->subgraphs) + for (SubgraphPtr const & subgraph : m_Model->subgraphs) { m_SubgraphConnections.emplace_back(subgraph->tensors.size()); @@ -2237,15 +2237,15 @@ TfLiteParser::TensorRawPtrVector TfLiteParser::GetInputs(const ModelPtr & model, { CHECK_MODEL(model, subgraphIndex, operatorIndex); - const auto & subGraphPtr = model->subgraphs[subgraphIndex]; - const auto & operatorPtr = subGraphPtr->operators[operatorIndex]; + const auto & subgraphPtr = model->subgraphs[subgraphIndex]; + const auto & operatorPtr = subgraphPtr->operators[operatorIndex]; size_t inputCount = operatorPtr->inputs.size(); TensorRawPtrVector result(inputCount); for (size_t i=0; iinputs[i]); - result[i] = subGraphPtr->tensors[inputId].get(); + result[i] = subgraphPtr->tensors[inputId].get(); } return result; } @@ -2256,8 +2256,8 @@ TfLiteParser::TensorRawPtrVector TfLiteParser::GetOutputs(const ModelPtr & model { CHECK_MODEL(model, subgraphIndex, operatorIndex); - const auto & subGraphPtr = model->subgraphs[subgraphIndex]; - const auto & operatorPtr = subGraphPtr->operators[operatorIndex]; + const auto & subgraphPtr = model->subgraphs[subgraphIndex]; + const auto & operatorPtr = subgraphPtr->operators[operatorIndex]; size_t outputCount = operatorPtr->outputs.size(); TensorRawPtrVector result(outputCount); @@ -2265,7 +2265,7 @@ TfLiteParser::TensorRawPtrVector TfLiteParser::GetOutputs(const ModelPtr & model { uint32_t outputId = CHECKED_NON_NEGATIVE(operatorPtr->outputs[i]); CHECK_TENSOR(model, subgraphIndex, outputId); - result[i] = subGraphPtr->tensors[outputId].get(); + result[i] = subgraphPtr->tensors[outputId].get(); } return result; } @@ -2274,15 +2274,15 @@ TfLiteParser::TensorIdRawPtrVector TfLiteParser::GetSubgraphInputs(const ModelPt size_t subgraphIndex) { CHECK_SUBGRAPH(model, subgraphIndex); - const auto & subGraphPtr = model->subgraphs[subgraphIndex]; + const auto & subgraphPtr = model->subgraphs[subgraphIndex]; - size_t inputCount = subGraphPtr->inputs.size(); + size_t inputCount = subgraphPtr->inputs.size(); TensorIdRawPtrVector result(inputCount); for (size_t i=0; iinputs[i]); + uint32_t inputId = CHECKED_NON_NEGATIVE(subgraphPtr->inputs[i]); CHECK_TENSOR(model, subgraphIndex, inputId); - result[i] = std::make_pair(inputId, subGraphPtr->tensors[inputId].get()); + result[i] = std::make_pair(inputId, subgraphPtr->tensors[inputId].get()); } return result; } @@ -2291,14 +2291,14 @@ TfLiteParser::TensorIdRawPtrVector TfLiteParser::GetSubgraphOutputs(const ModelP size_t subgraphIndex) { CHECK_SUBGRAPH(model, subgraphIndex); - const auto & subGraphPtr = model->subgraphs[subgraphIndex]; + const auto & subgraphPtr = model->subgraphs[subgraphIndex]; - size_t outputCount = subGraphPtr->outputs.size(); + size_t outputCount = subgraphPtr->outputs.size(); TensorIdRawPtrVector result(outputCount); for (size_t i=0; ioutputs[i]); - result[i] = std::make_pair(outputId, subGraphPtr->tensors[outputId].get()); + uint32_t outputId = CHECKED_NON_NEGATIVE(subgraphPtr->outputs[i]); + result[i] = std::make_pair(outputId, subgraphPtr->tensors[outputId].get()); } return result; } @@ -2308,8 +2308,8 @@ std::vector& TfLiteParser::GetInputTensorIds(const ModelPtr& model, size_t operatorIndex) { CHECK_MODEL(model, subgraphIndex, operatorIndex); - const auto & subGraphPtr = model->subgraphs[subgraphIndex]; - const auto & operatorPtr = subGraphPtr->operators[operatorIndex]; + const auto & subgraphPtr = model->subgraphs[subgraphIndex]; + const auto & operatorPtr = subgraphPtr->operators[operatorIndex]; return operatorPtr->inputs; } @@ -2318,8 +2318,8 @@ std::vector& TfLiteParser::GetOutputTensorIds(const ModelPtr& model, size_t operatorIndex) { CHECK_MODEL(model, subgraphIndex, operatorIndex); - const auto & subGraphPtr = model->subgraphs[subgraphIndex]; - const auto & operatorPtr = subGraphPtr->operators[operatorIndex]; + const auto & subgraphPtr = model->subgraphs[subgraphIndex]; + const auto & operatorPtr = subgraphPtr->operators[operatorIndex]; return operatorPtr->outputs; } @@ -2420,7 +2420,7 @@ void TfLiteParser::SetupConstantLayers(size_t subgraphIndex) { CHECK_SUBGRAPH(m_Model, subgraphIndex); - const auto & subGraphPtr = m_Model->subgraphs[subgraphIndex]; + const auto & subgraphPtr = m_Model->subgraphs[subgraphIndex]; for (unsigned int subgraphIndex = 0; subgraphIndex < m_SubgraphConnections.size(); ++subgraphIndex) { for (unsigned int tensorIndex = 0; tensorIndex < m_SubgraphConnections[subgraphIndex].size(); ++tensorIndex) @@ -2428,7 +2428,7 @@ void TfLiteParser::SetupConstantLayers(size_t subgraphIndex) if (m_SubgraphConnections[subgraphIndex][tensorIndex].outputSlot == nullptr && m_SubgraphConnections[subgraphIndex][tensorIndex].inputSlots.size() > 0) { - TensorRawPtr tensorPtr = subGraphPtr->tensors[tensorIndex].get(); + TensorRawPtr tensorPtr = subgraphPtr->tensors[tensorIndex].get(); armnn::TensorInfo tensorInfo = ToTensorInfo(tensorPtr); auto tensorAndData = CreateConstTensor(tensorPtr, tensorInfo, diff --git a/src/armnnTfLiteParser/TfLiteParser.hpp b/src/armnnTfLiteParser/TfLiteParser.hpp index 929af1f0db..07a0f26590 100644 --- a/src/armnnTfLiteParser/TfLiteParser.hpp +++ b/src/armnnTfLiteParser/TfLiteParser.hpp @@ -20,7 +20,7 @@ class TfLiteParser : public ITfLiteParser public: // Shorthands for TfLite types using ModelPtr = std::unique_ptr; - using SubGraphPtr = std::unique_ptr; + using SubgraphPtr = std::unique_ptr; using OperatorPtr = std::unique_ptr; using OperatorCodePtr = std::unique_ptr; using TensorPtr = std::unique_ptr; diff --git a/src/armnnTfLiteParser/test/GetTensorIds.cpp b/src/armnnTfLiteParser/test/GetTensorIds.cpp index c55b6d3fe9..6b82bb1f97 100644 --- a/src/armnnTfLiteParser/test/GetTensorIds.cpp +++ b/src/armnnTfLiteParser/test/GetTensorIds.cpp @@ -135,13 +135,13 @@ BOOST_FIXTURE_TEST_CASE(GetOutputTensorIdsNullModel, GetInputOutputTensorIdsFixt BOOST_CHECK_THROW(TfLiteParser::GetOutputTensorIds(nullptr, 0, 0), armnn::ParseException); } -BOOST_FIXTURE_TEST_CASE(GetInputTensorIdsInvalidSubGraph, GetInputOutputTensorIdsFixture) +BOOST_FIXTURE_TEST_CASE(GetInputTensorIdsInvalidSubgraph, GetInputOutputTensorIdsFixture) { TfLiteParser::ModelPtr model = TfLiteParser::LoadModelFromBinary(m_GraphBinary.data(), m_GraphBinary.size()); BOOST_CHECK_THROW(TfLiteParser::GetInputTensorIds(model, 1, 0), armnn::ParseException); } -BOOST_FIXTURE_TEST_CASE(GetOutputTensorIdsInvalidSubGraph, GetInputOutputTensorIdsFixture) +BOOST_FIXTURE_TEST_CASE(GetOutputTensorIdsInvalidSubgraph, GetInputOutputTensorIdsFixture) { TfLiteParser::ModelPtr model = TfLiteParser::LoadModelFromBinary(m_GraphBinary.data(), m_GraphBinary.size()); BOOST_CHECK_THROW(TfLiteParser::GetOutputTensorIds(model, 1, 0), armnn::ParseException); diff --git a/src/armnnTfLiteParser/test/LoadModel.cpp b/src/armnnTfLiteParser/test/LoadModel.cpp index ea490106c4..dac30efb1e 100644 --- a/src/armnnTfLiteParser/test/LoadModel.cpp +++ b/src/armnnTfLiteParser/test/LoadModel.cpp @@ -10,7 +10,7 @@ using armnnTfLiteParser::TfLiteParser; using ModelPtr = TfLiteParser::ModelPtr; -using SubGraphPtr = TfLiteParser::SubGraphPtr; +using SubgraphPtr = TfLiteParser::SubgraphPtr; using OperatorPtr = TfLiteParser::OperatorPtr; BOOST_AUTO_TEST_SUITE(TensorflowLiteParser) @@ -156,7 +156,7 @@ struct LoadModelFixture : public ParserFlatbuffersFixture } } - void CheckSubgraph(const SubGraphPtr& subgraph, size_t tensors, const std::vector& inputs, + void CheckSubgraph(const SubgraphPtr& subgraph, size_t tensors, const std::vector& inputs, const std::vector& outputs, size_t operators, const std::string& name) { BOOST_CHECK(subgraph); diff --git a/src/backends/README.md b/src/backends/README.md index c269ea08da..90eeed2968 100644 --- a/src/backends/README.md +++ b/src/backends/README.md @@ -116,11 +116,11 @@ The interface functions to be implemented are: virtual IBackendContextPtr CreateBackendContext(const IRuntime::CreationOptions&) const = 0; virtual Optimizations GetOptimizations() const = 0; virtual ILayerSupportSharedPtr GetLayerSupport() const = 0; - virtual SubGraphUniquePtr OptimizeSubGraph(const SubGraph& subGraph, bool& optimizationAttempted) const = 0; + virtual SubgraphUniquePtr OptimizeSubgraph(const SubgraphView& subgraph, bool& optimizationAttempted) const = 0; ``` Note that ```GetOptimizations()``` has been deprecated. -The method ```OptimizeSubGraph(...)``` should be used instead to specific optimizations to a given sub-graph. +The method ```OptimizeSubgraph(...)``` should be used instead to specific optimizations to a given sub-graph. The ArmNN framework then creates instances of the IBackendInternal interface with the help of the [BackendRegistry](backendsCommon/BackendRegistry.hpp) singleton. @@ -191,7 +191,7 @@ mechanism: ## The Optimizations The backends may choose to implement backend-specific optimizations. -This is supported through the method ```OptimizeSubGraph(...)``` to the backend interface +This is supported through the method ```OptimizeSubgraph(...)``` to the backend interface that allows the backends to apply their specific optimizations to a given sub-grah. The way backends had to provide a list optimizations to the Optimizer (through the ```GetOptimizations()``` method) diff --git a/src/backends/backendsCommon/IBackendInternal.hpp b/src/backends/backendsCommon/IBackendInternal.hpp index 6e6d47fc90..108e66e6a8 100644 --- a/src/backends/backendsCommon/IBackendInternal.hpp +++ b/src/backends/backendsCommon/IBackendInternal.hpp @@ -7,8 +7,8 @@ #include #include -#include -#include +#include +#include #include @@ -41,9 +41,9 @@ public: using IMemoryManagerUniquePtr = std::unique_ptr; using IMemoryManagerSharedPtr = std::shared_ptr; - using ISubGraphConverterPtr = std::unique_ptr; + using ISubgraphViewConverterPtr = std::unique_ptr; - using SubGraphUniquePtr = std::unique_ptr; + using SubgraphViewUniquePtr = std::unique_ptr; virtual IMemoryManagerUniquePtr CreateMemoryManager() const = 0; @@ -52,12 +52,14 @@ public: virtual IBackendContextPtr CreateBackendContext(const IRuntime::CreationOptions&) const = 0; - virtual ISubGraphConverterPtr CreateSubGraphConverter(const std::shared_ptr& subGraph) const = 0; + virtual ISubgraphViewConverterPtr CreateSubgraphViewConverter(const std::shared_ptr& subgraph) + const = 0; virtual Optimizations GetOptimizations() const = 0; virtual ILayerSupportSharedPtr GetLayerSupport() const = 0; - virtual SubGraphUniquePtr OptimizeSubGraph(const SubGraph& subGraph, bool& optimizationAttempted) const = 0; + virtual SubgraphViewUniquePtr OptimizeSubgraphView(const SubgraphView& subgraph, bool& optimizationAttempted) + const = 0; }; using IBackendInternalUniquePtr = std::unique_ptr; diff --git a/src/backends/cl/ClBackend.cpp b/src/backends/cl/ClBackend.cpp index dfa5e7c777..25318ecdaa 100644 --- a/src/backends/cl/ClBackend.cpp +++ b/src/backends/cl/ClBackend.cpp @@ -63,10 +63,10 @@ ClBackend::CreateBackendContext(const IRuntime::CreationOptions& options) const return IBackendContextPtr{new ClBackendContext{options}}; } -IBackendInternal::ISubGraphConverterPtr ClBackend::CreateSubGraphConverter( - const std::shared_ptr& subGraph) const +IBackendInternal::ISubgraphViewConverterPtr ClBackend::CreateSubgraphViewConverter( + const std::shared_ptr& subgraph) const { - return ISubGraphConverterPtr{}; + return ISubgraphViewConverterPtr{}; } IBackendInternal::Optimizations ClBackend::GetOptimizations() const @@ -80,13 +80,13 @@ IBackendInternal::ILayerSupportSharedPtr ClBackend::GetLayerSupport() const return layerSupport; } -IBackendInternal::SubGraphUniquePtr ClBackend::OptimizeSubGraph(const SubGraph& subGraph, - bool& optimizationAttempted) const +IBackendInternal::SubgraphViewUniquePtr ClBackend::OptimizeSubgraphView(const SubgraphView& subgraph, + bool& optimizationAttempted) const { // Not trying to optimize the given sub-graph optimizationAttempted = false; - return SubGraphUniquePtr{}; + return SubgraphViewUniquePtr{}; } } // namespace armnn diff --git a/src/backends/cl/ClBackend.hpp b/src/backends/cl/ClBackend.hpp index 8133ce49f6..e50c7e12ca 100644 --- a/src/backends/cl/ClBackend.hpp +++ b/src/backends/cl/ClBackend.hpp @@ -25,14 +25,14 @@ public: IBackendInternal::IBackendContextPtr CreateBackendContext(const IRuntime::CreationOptions&) const override; - IBackendInternal::ISubGraphConverterPtr CreateSubGraphConverter( - const std::shared_ptr& subGraph) const override; + IBackendInternal::ISubgraphViewConverterPtr CreateSubgraphViewConverter( + const std::shared_ptr& subgraph) const override; IBackendInternal::Optimizations GetOptimizations() const override; IBackendInternal::ILayerSupportSharedPtr GetLayerSupport() const override; - IBackendInternal::SubGraphUniquePtr OptimizeSubGraph(const SubGraph& subGraph, - bool& optimizationAttempted) const override; + IBackendInternal::SubgraphViewUniquePtr OptimizeSubgraphView(const SubgraphView& subgraph, + bool& optimizationAttempted) const override; }; } // namespace armnn diff --git a/src/backends/neon/NeonBackend.cpp b/src/backends/neon/NeonBackend.cpp index 830d637b01..096fe26642 100644 --- a/src/backends/neon/NeonBackend.cpp +++ b/src/backends/neon/NeonBackend.cpp @@ -63,10 +63,10 @@ IBackendInternal::IBackendContextPtr NeonBackend::CreateBackendContext(const IRu return IBackendContextPtr{}; } -IBackendInternal::ISubGraphConverterPtr NeonBackend::CreateSubGraphConverter( - const std::shared_ptr& subGraph) const +IBackendInternal::ISubgraphViewConverterPtr NeonBackend::CreateSubgraphViewConverter( + const std::shared_ptr& subgraph) const { - return ISubGraphConverterPtr{}; + return ISubgraphViewConverterPtr{}; } IBackendInternal::Optimizations NeonBackend::GetOptimizations() const @@ -80,13 +80,13 @@ IBackendInternal::ILayerSupportSharedPtr NeonBackend::GetLayerSupport() const return layerSupport; } -IBackendInternal::SubGraphUniquePtr NeonBackend::OptimizeSubGraph(const SubGraph& subGraph, - bool& optimizationAttempted) const +IBackendInternal::SubgraphViewUniquePtr NeonBackend::OptimizeSubgraphView(const SubgraphView& subgraph, + bool& optimizationAttempted) const { // Not trying to optimize the given sub-graph optimizationAttempted = false; - return SubGraphUniquePtr{}; + return SubgraphViewUniquePtr{}; } } // namespace armnn diff --git a/src/backends/neon/NeonBackend.hpp b/src/backends/neon/NeonBackend.hpp index 634704571c..a2949b1e14 100644 --- a/src/backends/neon/NeonBackend.hpp +++ b/src/backends/neon/NeonBackend.hpp @@ -25,14 +25,14 @@ public: IBackendInternal::IBackendContextPtr CreateBackendContext(const IRuntime::CreationOptions&) const override; - IBackendInternal::ISubGraphConverterPtr CreateSubGraphConverter( - const std::shared_ptr& subGraph) const override; + IBackendInternal::ISubgraphViewConverterPtr CreateSubgraphViewConverter( + const std::shared_ptr& subgraph) const override; IBackendInternal::Optimizations GetOptimizations() const override; IBackendInternal::ILayerSupportSharedPtr GetLayerSupport() const override; - IBackendInternal::SubGraphUniquePtr OptimizeSubGraph(const SubGraph& subGraph, - bool& optimizationAttempted) const override; + IBackendInternal::SubgraphViewUniquePtr OptimizeSubgraphView(const SubgraphView& subgraph, + bool& optimizationAttempted) const override; }; } // namespace armnn diff --git a/src/backends/reference/RefBackend.cpp b/src/backends/reference/RefBackend.cpp index d5f5348642..006f2e7af9 100644 --- a/src/backends/reference/RefBackend.cpp +++ b/src/backends/reference/RefBackend.cpp @@ -56,10 +56,10 @@ IBackendInternal::IMemoryManagerUniquePtr RefBackend::CreateMemoryManager() cons return IMemoryManagerUniquePtr{}; } -IBackendInternal::ISubGraphConverterPtr RefBackend::CreateSubGraphConverter( - const std::shared_ptr& subGraph) const +IBackendInternal::ISubgraphViewConverterPtr RefBackend::CreateSubgraphViewConverter( + const std::shared_ptr& subgraph) const { - return ISubGraphConverterPtr{}; + return ISubgraphViewConverterPtr{}; } IBackendInternal::Optimizations RefBackend::GetOptimizations() const @@ -73,13 +73,13 @@ IBackendInternal::ILayerSupportSharedPtr RefBackend::GetLayerSupport() const return layerSupport; } -IBackendInternal::SubGraphUniquePtr RefBackend::OptimizeSubGraph(const SubGraph& subGraph, - bool& optimizationAttempted) const +IBackendInternal::SubgraphViewUniquePtr RefBackend::OptimizeSubgraphView(const SubgraphView& subgraph, + bool& optimizationAttempted) const { // Not trying to optimize the given sub-graph optimizationAttempted = false; - return SubGraphUniquePtr{}; + return SubgraphViewUniquePtr{}; } } // namespace armnn diff --git a/src/backends/reference/RefBackend.hpp b/src/backends/reference/RefBackend.hpp index 6305bf568c..8658e1a87e 100644 --- a/src/backends/reference/RefBackend.hpp +++ b/src/backends/reference/RefBackend.hpp @@ -25,14 +25,14 @@ public: IBackendInternal::IBackendContextPtr CreateBackendContext(const IRuntime::CreationOptions&) const override; - IBackendInternal::ISubGraphConverterPtr CreateSubGraphConverter( - const std::shared_ptr& subGraph) const override; + IBackendInternal::ISubgraphViewConverterPtr CreateSubgraphViewConverter( + const std::shared_ptr& subgraph) const override; IBackendInternal::Optimizations GetOptimizations() const override; IBackendInternal::ILayerSupportSharedPtr GetLayerSupport() const override; - IBackendInternal::SubGraphUniquePtr OptimizeSubGraph(const SubGraph& subGraph, - bool& optimizationAttempted) const override; + IBackendInternal::SubgraphViewUniquePtr OptimizeSubgraphView(const SubgraphView& subgraph, + bool& optimizationAttempted) const override; }; } // namespace armnn -- cgit v1.2.1