mirror of
https://git.rwth-aachen.de/acs/public/villas/node/
synced 2025-03-09 00:00:00 +01:00
lib/graph: add path-finding with loop detection and corresponding unittest
This commit is contained in:
parent
ec8e9a1cd1
commit
27c67f206e
2 changed files with 114 additions and 2 deletions
|
@ -6,6 +6,7 @@
|
|||
#include <sstream>
|
||||
#include <string>
|
||||
#include <stdexcept>
|
||||
#include <algorithm>
|
||||
|
||||
#include "log.hpp"
|
||||
|
||||
|
@ -168,6 +169,50 @@ public:
|
|||
vertexGetEdges(VertexIdentifier vertexId) const
|
||||
{ return getVertex(vertexId)->edges; }
|
||||
|
||||
bool getPath(VertexIdentifier fromVertexId, VertexIdentifier toVertexId,
|
||||
std::list<EdgeIdentifier>& path)
|
||||
{
|
||||
if(fromVertexId == toVertexId) {
|
||||
// arrived at the destination
|
||||
return true;
|
||||
} else {
|
||||
auto fromVertex = getVertex(fromVertexId);
|
||||
|
||||
for(auto& edgeId : fromVertex->edges) {
|
||||
auto edge = getEdge(edgeId);
|
||||
|
||||
// loop detection
|
||||
bool loop = false;
|
||||
for(auto& edgeIdInPath : path) {
|
||||
auto edgeInPath = getEdge(edgeIdInPath);
|
||||
if(edgeInPath->from == edgeId) {
|
||||
loop = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if(loop) {
|
||||
logger->debug("Loop detected via edge {}", edgeId);
|
||||
continue;
|
||||
}
|
||||
|
||||
// remember the path we're investigating to detect loops
|
||||
path.push_back(edgeId);
|
||||
|
||||
// recursive, depth-first search
|
||||
if(getPath(edge->to, toVertexId, path)) {
|
||||
// path found, we're done
|
||||
return true;
|
||||
} else {
|
||||
// tear down path that didn't lead to the destination
|
||||
path.pop_back();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void dump()
|
||||
{
|
||||
logger->info("Vertices:");
|
||||
|
|
|
@ -2,10 +2,14 @@
|
|||
|
||||
#include <criterion/criterion.h>
|
||||
#include <villas/directed_graph.hpp>
|
||||
#include <villas/log.hpp>
|
||||
|
||||
Test(graph, directed, .description = "DirectedGraph")
|
||||
Test(graph, basic, .description = "DirectedGraph")
|
||||
{
|
||||
villas::graph::DirectedGraph<> g;
|
||||
auto logger = loggerGetOrCreate("unittest:basic");
|
||||
logger->info("Testing basic graph construction and modification");
|
||||
|
||||
villas::graph::DirectedGraph<> g("unittest:basic");
|
||||
|
||||
std::shared_ptr<villas::graph::Vertex> v1(new villas::graph::Vertex);
|
||||
std::shared_ptr<villas::graph::Vertex> v2(new villas::graph::Vertex);
|
||||
|
@ -30,3 +34,66 @@ Test(graph, directed, .description = "DirectedGraph")
|
|||
cr_assert(g.getVertexCount() == 2);
|
||||
cr_assert(g.vertexGetEdges(v2id).size() == 0);
|
||||
}
|
||||
|
||||
Test(graph, path, .description = "Find path")
|
||||
{
|
||||
auto logger = loggerGetOrCreate("unittest:path");
|
||||
logger->info("Testing path finding algorithm");
|
||||
|
||||
villas::graph::DirectedGraph<> g("unittest:path");
|
||||
|
||||
std::shared_ptr<villas::graph::Vertex> v1(new villas::graph::Vertex);
|
||||
std::shared_ptr<villas::graph::Vertex> v2(new villas::graph::Vertex);
|
||||
std::shared_ptr<villas::graph::Vertex> v3(new villas::graph::Vertex);
|
||||
std::shared_ptr<villas::graph::Vertex> v4(new villas::graph::Vertex);
|
||||
std::shared_ptr<villas::graph::Vertex> v5(new villas::graph::Vertex);
|
||||
std::shared_ptr<villas::graph::Vertex> v6(new villas::graph::Vertex);
|
||||
|
||||
auto v1id = g.addVertex(v1);
|
||||
auto v2id = g.addVertex(v2);
|
||||
auto v3id = g.addVertex(v3);
|
||||
|
||||
auto v4id = g.addVertex(v4);
|
||||
auto v5id = g.addVertex(v5);
|
||||
auto v6id = g.addVertex(v6);
|
||||
|
||||
g.addEdge(v1id, v2id);
|
||||
g.addEdge(v2id, v3id);
|
||||
|
||||
// create circular subgraph
|
||||
g.addEdge(v4id, v5id);
|
||||
g.addEdge(v5id, v4id);
|
||||
g.addEdge(v5id, v6id);
|
||||
|
||||
g.dump();
|
||||
|
||||
logger->info("Find simple path via two edges");
|
||||
std::list<villas::graph::EdgeIdentifier> path1;
|
||||
cr_assert(g.getPath(v1id, v3id, path1));
|
||||
|
||||
logger->info(" Path from {} to {} via:", v1id, v3id);
|
||||
for(auto& edge : path1) {
|
||||
logger->info(" -> edge {}", edge);
|
||||
}
|
||||
|
||||
logger->info("Find path between two unconnected sub-graphs");
|
||||
std::list<villas::graph::EdgeIdentifier> path2;
|
||||
cr_assert(not g.getPath(v1id, v4id, path2));
|
||||
logger->info(" no path found -> ok");
|
||||
|
||||
|
||||
logger->info("Find non-existing path in circular sub-graph");
|
||||
std::list<villas::graph::EdgeIdentifier> path3;
|
||||
cr_assert(not g.getPath(v4id, v2id, path3));
|
||||
logger->info(" no path found -> ok");
|
||||
|
||||
|
||||
logger->info("Find path in circular graph");
|
||||
std::list<villas::graph::EdgeIdentifier> path4;
|
||||
cr_assert(g.getPath(v4id, v6id, path4));
|
||||
|
||||
logger->info(" Path from {} to {} via:", v4id, v6id);
|
||||
for(auto& edge : path4) {
|
||||
logger->info(" -> edge {}", edge);
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Add table
Reference in a new issue