20 #include <doctest/doctest.h> 24 TEST_CASE(
"ClassGraph")
31 TEST_CASE(
"TopologicalSort")
74 TEST_CASE(
"InsertNewLayerBefore")
79 std::vector<armnn::Layer*> order;
160 TEST_CASE(
"InsertNewLayerAfter")
165 std::vector<armnn::Layer*> order;
249 using Edge = std::pair<const armnn::Layer*, const armnn::Layer*>;
252 static std::vector<Edge> GetEdgeList(
const armnn::Graph& graph)
254 std::vector<Edge> edges;
256 for (
auto&& srcLayer: graph)
258 const unsigned int numOutputSlots = srcLayer->GetNumOutputSlots();
259 for (
unsigned int s = 0; s < numOutputSlots; ++s)
263 for (
unsigned int c = 0; c < numConnections; ++c)
265 auto inputSlot = armnn::PolymorphicDowncast<const armnn::InputSlot*>(outputSlot.
GetConnection(c));
266 edges.emplace_back(srcLayer, &inputSlot->GetOwningLayer());
276 std::vector<Edge> origEdges = GetEdgeList(origGraph);
277 std::vector<Edge> newEdges = GetEdgeList(graph);
281 std::vector<Edge> sortedNewEdges = newEdges;
282 std::sort(sortedNewEdges.begin(), sortedNewEdges.end());
284 auto last = std::unique(sortedNewEdges.begin(), sortedNewEdges.end());
285 CHECK_MESSAGE(last == sortedNewEdges.end(),
"New graph contains duplicate edges!");
289 while (!newEdges.empty())
291 const Edge edge = std::move(newEdges.back());
295 int originalEdge = -1;
296 for (
unsigned int i = 0; i < origEdges.size(); i++)
298 const Edge& origEdge = origEdges[i];
299 if (origEdge.first->GetNameStr() == edge.first->GetNameStr() &&
300 origEdge.second->GetNameStr() == edge.second->GetNameStr())
306 if (originalEdge != -1)
315 if (srcLayer && dstLayer)
321 origEdges.erase(origEdges.begin() + originalEdge);
330 if (srcLayer ==
nullptr || dstLayer ==
nullptr)
332 FAIL(
"At least one of the two ends of a new edge (" << edge.first <<
", " << edge.second
333 <<
") introduced after adding copy layers to a graph " 334 "correspond to a layer not known to the graph");
342 if (srcLayerInOrigGraph == dstLayerInOrigGraph)
345 << edge.first->GetName()
347 << edge.second->GetName()
348 <<
") introduced after adding copy " 349 "layers to a graph is invalid. One of the ends should be present in the original " 350 "graph and the other should not, but " 351 << (srcLayerInOrigGraph ?
"both are" :
"none are"));
355 const armnn::Layer* copyLayer = srcLayerInOrigGraph ? dstLayer : srcLayer;
356 const armnn::Layer* nonCopyLayer = srcLayerInOrigGraph ? srcLayer : dstLayer;
359 std::vector<Edge> adjEdges;
360 auto it = newEdges.begin();
361 while (it != newEdges.end())
364 if (copyLayer == (srcLayerInOrigGraph ? newEdge.first : newEdge.second))
366 adjEdges.push_back(newEdge);
369 it = newEdges.erase(it);
377 if (adjEdges.empty())
379 FAIL(
"An edge connecting a layer and a copy layer exists, (" << edge.first <<
", " <<
380 edge.second <<
"), but no other edges connecting the copy layer '" << copyLayer->
GetName()
381 <<
"' to other layers could be found");
386 for (
const Edge& adjEdge : adjEdges)
389 const armnn::Layer* adjLayer = srcLayerInOrigGraph ? adjEdge.second : adjEdge.first;
393 FAIL(
"An edge (" << adjEdge.first <<
", " << adjEdge.second <<
") is adjacent to an " 394 "edge connecting a layer and a copy layer, (" << edge.first <<
", " << edge.second <<
395 "), but the non-copy layer in the former does not correspond to a layer");
404 const armnn::Layer* origEdgeSrc = srcLayerInOrigGraph ? nonCopyLayer : adjLayer;
405 const armnn::Layer* origEdgeDst = srcLayerInOrigGraph ? adjLayer : nonCopyLayer;
407 auto origEdgeIter = origEdges.begin();
408 for (; origEdgeIter != origEdges.end(); origEdgeIter++)
410 if (origEdgeIter->first->GetNameStr() == origEdgeSrc->
GetNameStr() &&
411 origEdgeIter->second->GetNameStr() == origEdgeDst->
GetNameStr())
417 if (origEdgeIter != origEdges.end())
419 origEdges.erase(origEdgeIter);
423 FAIL(
"An edge (" << adjEdge.first <<
", " << adjEdge.second <<
") is adjacent to " 424 "an edge connecting a layer and a copy layer, (" << edge.first <<
", " << edge.second <<
425 "), but there is no edge connecting the layers in the original graph");
432 CHECK_MESSAGE(origEdges.empty(),
"Not all of the edges in the original graph correspond to paths in the new graph");
435 struct CopyLayersFixture
441 void InitialiseTestGraph()
443 using namespace armnn;
446 Layer*
const inputLayer = AddLayer<InputLayer>(0,
"input");
450 Layer*
const convLayer1 = AddLayer<Convolution2dLayer>(convolutionDefaults,
"conv1");
455 Layer*
const convLayer2 = AddLayer<Convolution2dLayer>(convolutionDefaults,
"conv2");
461 Layer*
const concatLayer = AddLayer<ConcatLayer>(concatDefaults,
"concat");
468 Layer*
const actLayer = AddLayer<ActivationLayer>(activationDefaults,
"act");
474 Layer*
const softmaxLayer = AddLayer<SoftmaxLayer>(softmaxDefaults,
"softmax");
479 Layer*
const outputLayer = AddLayer<OutputLayer>(0,
"output");
497 std::map<armnn::BackendId, std::unique_ptr<armnn::IBackendInternal>> m_Backends;
502 template <
typename LayerType,
typename... Args>
507 for (
auto slot = layer->BeginOutputSlots(); slot != layer->EndOutputSlots(); ++slot)
509 slot->SetTensorInfo(m_TensorDesc);
518 InitialiseTestGraph();
520 m_Graph.AddCompatibilityLayers(m_Backends, m_FactoryRegistry);
522 TestGraphAfterAddingCopyLayers(m_Graph, origGraph);
527 InitialiseTestGraph();
528 m_Graph.AddCompatibilityLayers(m_Backends, m_FactoryRegistry);
531 const std::vector<Edge> edges = GetEdgeList(m_Graph);
532 for (
int i = 0; i < 4; ++i)
534 m_Graph.AddCompatibilityLayers(m_Backends, m_FactoryRegistry);
535 const std::vector<Edge> otherEdges = GetEdgeList(m_Graph);
536 CHECK((edges == otherEdges));
540 TEST_CASE_FIXTURE(CopyLayersFixture,
"CopyLayersAddedBetweenSameLayersHaveDifferentNames")
569 std::vector<Edge> edges = GetEdgeList(graph);
570 CHECK(edges.size() == 6u);
571 std::sort(edges.begin(), edges.end());
572 auto last = std::unique(edges.begin(), edges.end());
573 CHECK_MESSAGE(last == edges.end(),
"Found duplicated edges after AddCompatibilityLayers()");
576 TEST_CASE(
"DuplicateLayerNames")
593 TEST_CASE(
"CheckGraphConstTensorSharing")
596 const float* sharedWeightPtr;
605 constantLayer->
m_LayerOutput = std::make_shared<armnn::ScopedTensorHandle>(constTensor);;
608 sharedWeightPtr = constantLayer->
m_LayerOutput->GetConstTensor<
float>();
614 CHECK(*sharedWeightPtr == 1);
A layer that the constant data can be bound to.
Iterator begin()
Returns iterator pointing to the beginning of the list. Lowercase for range-based for loops...
This layer represents a split operation.
A ViewsDescriptor for the SplitterLayer.
void SetEdgeStrategy(unsigned int connectionIndex, EdgeStrategy strategy)
No strategy has been defined. Used internally to verify integrity of optimizations.
CPU Execution: Reference C++ kernels.
armnn::Layer * GetFirstLayerWithName(armnn::Graph &graph, const std::string &name)
bool CheckOrder(const armnn::Graph &graph, const armnn::Layer *first, const armnn::Layer *second)
Checks that first comes before second in the order.
std::shared_ptr< ConstTensorHandle > m_LayerOutput
LayerT * AddLayer(Args &&... args)
Adds a new layer, of type LayerType, to the graph constructed with the arguments passed.
A Convolution2dDescriptor for the Convolution2dLayer.
Source backends tensor data can be exported to destination backend tensor without copy...
int Connect(InputSlot &destination)
bool GraphHasNamedLayer(const armnn::Graph &graph, const std::string &name)
This layer represents an activation operation with the specified activation function.
Copyright (c) 2021 ARM Limited and Contributors.
void SetBackendId(const BackendId &id)
virtual const IInputSlot * GetConnection(unsigned int index) const =0
TEST_CASE_FIXTURE(ClContextControlFixture, "CopyBetweenNeonAndGpu")
const InputSlot & GetInputSlot(unsigned int index) const override
Get a const input slot handle by slot index.
A layer user-provided data can be bound to (e.g. inputs, outputs).
An output connection slot for a layer.
An OriginsDescriptor for the ConcatLayer.
A tensor defined by a TensorInfo (shape and data type) and an immutable backing store.
const std::string & GetNameStr() const
GPU Execution: OpenCL: ArmCompute.
An ActivationDescriptor for the ActivationLayer.
const BackendId & GetBackendId() const
This layer represents an addition operation.
CPU Execution: NEON: ArmCompute.
void SetTensorInfo(const TensorInfo &tensorInfo) override
std::enable_if_t< std::is_unsigned< Source >::value &&std::is_unsigned< Dest >::value, Dest > numeric_cast(Source source)
const OutputSlot & GetOutputSlot(unsigned int index=0) const override
Get the const output slot handle by slot index.
virtual unsigned int GetNumConnections() const =0
const char * GetName() const override
Returns the name of the layer.
Graph & TopologicalSort()
Sorts layers in topological order and return this.
LayerT * InsertNewLayer(InputSlot &insertBefore, Args &&... args)
Inserts a new layer between the output slot currently connected to insertBefore and insertBefore itse...
A SoftmaxDescriptor for the SoftmaxLayer.
void AddCompatibilityLayers(std::map< BackendId, std::unique_ptr< class IBackendInternal >> &backends, TensorHandleFactoryRegistry ®istry)
Modifies the graph in-place, removing edges connecting layers using different compute devices...
LayerType
When adding a new layer, adapt also the LastLayer enum value in the enum class LayerType below...