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);
617 TEST_CASE(
"IConnectableLayerConstantTensorsByRef")
619 using namespace armnn;
622 std::vector<uint8_t> falseData = {3};
630 auto weightValue =
reinterpret_cast<const uint8_t*
>(weightData);
631 CHECK(weightValue[0] == 3);
637 const auto depthwiseLayer = net->AddDepthwiseConvolution2dLayer(desc, weights,
EmptyOptional(),
"Depthwise");
639 const void* resultData = depthwiseLayer->GetConstantTensorsByRef()[0].get()->GetConstTensor<
void>();
640 auto resultValue =
reinterpret_cast<const uint8_t*
>(resultData);
641 CHECK(resultValue[0] == 3);
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.
Interface for a layer that is connectable to other layers via InputSlots and OutputSlots.
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.
#define ARMNN_NO_DEPRECATE_WARN_BEGIN
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
virtual void SetTensorInfo(const TensorInfo &tensorInfo)=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).
#define ARMNN_NO_DEPRECATE_WARN_END
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.
EmptyOptional is used to initialize the Optional class in case we want to have default value for an O...
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 const TensorInfo & GetTensorInfo() const =0
virtual const IOutputSlot & GetOutputSlot(unsigned int index) const =0
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.
std::unique_ptr< INetwork, void(*)(INetwork *network)> INetworkPtr
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...
A DepthwiseConvolution2dDescriptor for the DepthwiseConvolution2dLayer.
virtual ConstantTensors GetConstantTensorsByRef()=0
LayerType
When adding a new layer, adapt also the LastLayer enum value in the enum class LayerType below...