diff --git a/fpga/include/villas/directed_graph.hpp b/fpga/include/villas/directed_graph.hpp index 71716d49f..d7752a9f0 100644 --- a/fpga/include/villas/directed_graph.hpp +++ b/fpga/include/villas/directed_graph.hpp @@ -6,6 +6,7 @@ #include #include #include +#include #include "log.hpp" @@ -168,6 +169,50 @@ public: vertexGetEdges(VertexIdentifier vertexId) const { return getVertex(vertexId)->edges; } + bool getPath(VertexIdentifier fromVertexId, VertexIdentifier toVertexId, + std::list& 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:"); diff --git a/fpga/tests/graph.cpp b/fpga/tests/graph.cpp index 92f81cc74..4eb25ac75 100644 --- a/fpga/tests/graph.cpp +++ b/fpga/tests/graph.cpp @@ -2,10 +2,14 @@ #include #include +#include -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 v1(new villas::graph::Vertex); std::shared_ptr 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 v1(new villas::graph::Vertex); + std::shared_ptr v2(new villas::graph::Vertex); + std::shared_ptr v3(new villas::graph::Vertex); + std::shared_ptr v4(new villas::graph::Vertex); + std::shared_ptr v5(new villas::graph::Vertex); + std::shared_ptr 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 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 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 path3; + cr_assert(not g.getPath(v4id, v2id, path3)); + logger->info(" no path found -> ok"); + + + logger->info("Find path in circular graph"); + std::list path4; + cr_assert(g.getPath(v4id, v6id, path4)); + + logger->info(" Path from {} to {} via:", v4id, v6id); + for(auto& edge : path4) { + logger->info(" -> edge {}", edge); + } +}