diff --git a/include/CXXGraph/Graph/Algorithm/BestFirstSearch_impl.hpp b/include/CXXGraph/Graph/Algorithm/BestFirstSearch_impl.hpp index d8967622..873a6333 100644 --- a/include/CXXGraph/Graph/Algorithm/BestFirstSearch_impl.hpp +++ b/include/CXXGraph/Graph/Algorithm/BestFirstSearch_impl.hpp @@ -22,12 +22,12 @@ #pragma once +#include #include #include #include #include #include -#include #include "CXXGraph/Graph/Graph_decl.h" diff --git a/include/CXXGraph/Graph/Algorithm/BronKerbosch_impl.hpp b/include/CXXGraph/Graph/Algorithm/BronKerbosch_impl.hpp new file mode 100644 index 00000000..95cf617f --- /dev/null +++ b/include/CXXGraph/Graph/Algorithm/BronKerbosch_impl.hpp @@ -0,0 +1,75 @@ +/***********************************************************/ +/*** ______ ____ ______ _ ***/ +/*** / ___\ \/ /\ \/ / ___|_ __ __ _ _ __ | |__ ***/ +/*** | | \ / \ / | _| '__/ _` | '_ \| '_ \ ***/ +/*** | |___ / \ / \ |_| | | | (_| | |_) | | | | ***/ +/*** \____/_/\_\/_/\_\____|_| \__,_| .__/|_| |_| ***/ +/*** |_| ***/ +/***********************************************************/ +/*** Header-Only C++ Library for Graph ***/ +/*** Representation and Algorithms ***/ +/***********************************************************/ +/*** Author: ZigRazor ***/ +/*** E-Mail: zigrazor@gmail.com ***/ +/***********************************************************/ +/*** Collaboration: ----------- ***/ +/***********************************************************/ +/*** License: AGPL v3.0 ***/ +/***********************************************************/ + +#ifndef __CXXGRAPH_BRONKERBOSCH_IMPL_H__ +#define __CXXGRAPH_BRONKERBOSCH_IMPL_H__ + +#pragma once + +#include "CXXGraph/Graph/Graph_decl.h" + +namespace CXXGraph { + +template +const BronKerboschResult Graph::bron_kerbosch() const { + std::vector> maximalCliques{}; + BronKerboschResult result; + + if (!isUndirectedGraph()) { + result.success = false; + result.errorMessage = ERR_DIR_GRAPH; + return result; + } + + T_NodeSet _X, _R, _P(getNodeSet()); + + std::function, T_NodeSet, T_NodeSet)> rec = + [&maximalCliques, &rec, this](T_NodeSet R, T_NodeSet P, + T_NodeSet X) { + if (P.empty() && X.empty()) { + maximalCliques.push_back(R); + } + + auto it = P.begin(); + while (it != P.end()) { + const auto v = *it; + T_NodeSet X2, R2(R), P2, nbd(inOutNeighbors(v)); + R2.insert(v); + for (const auto &u : nbd) { + if (X.count(u) > 0 && u != v) { + X2.insert(u); + } + if (P.count(u) > 0 && u != v) { + P2.insert(u); + } + } + rec(R2, P2, X2); + X.insert(v); + it = P.erase(it); + } + }; + + rec(_R, _P, _X); + result.success = true; + result.maximalCliques = maximalCliques; + return result; +} + +} // namespace CXXGraph +#endif \ No newline at end of file diff --git a/include/CXXGraph/Graph/Algorithm/Dijkstra_impl.hpp b/include/CXXGraph/Graph/Algorithm/Dijkstra_impl.hpp index cb779221..bb706cc9 100644 --- a/include/CXXGraph/Graph/Algorithm/Dijkstra_impl.hpp +++ b/include/CXXGraph/Graph/Algorithm/Dijkstra_impl.hpp @@ -52,7 +52,7 @@ const DijkstraResult Graph::dijkstra(const Node& source, } // n denotes the number of vertices in graph // unused - //auto n = cachedAdjMatrix->size(); + // auto n = cachedAdjMatrix->size(); // setting all the distances initially to INF_DOUBLE std::unordered_map>, double, nodeHash> dist; @@ -177,7 +177,7 @@ const DijkstraResult Graph::dijkstra_deterministic( } // n denotes the number of vertices in graph // unused - //auto n = cachedAdjMatrix->size(); + // auto n = cachedAdjMatrix->size(); // setting all the distances initially to INF_DOUBLE std::unordered_map>, double, nodeHash> dist; diff --git a/include/CXXGraph/Graph/Graph.h b/include/CXXGraph/Graph/Graph.h index aedb55ee..dd515436 100644 --- a/include/CXXGraph/Graph/Graph.h +++ b/include/CXXGraph/Graph/Graph.h @@ -27,6 +27,7 @@ #include "CXXGraph/Graph/Algorithm/BestFirstSearch_impl.hpp" #include "CXXGraph/Graph/Algorithm/Boruvka_impl.hpp" #include "CXXGraph/Graph/Algorithm/BreadthFirstSearch_impl.hpp" +#include "CXXGraph/Graph/Algorithm/BronKerbosch_impl.hpp" #include "CXXGraph/Graph/Algorithm/Connectivity_impl.hpp" #include "CXXGraph/Graph/Algorithm/CycleDetection_impl.hpp" #include "CXXGraph/Graph/Algorithm/DepthFirstSearch_impl.hpp" diff --git a/include/CXXGraph/Graph/Graph_decl.h b/include/CXXGraph/Graph/Graph_decl.h index dcb74b5a..a6c0b3f8 100644 --- a/include/CXXGraph/Graph/Graph_decl.h +++ b/include/CXXGraph/Graph/Graph_decl.h @@ -663,7 +663,7 @@ class Graph { * mst: vector containing id of nodes in minimum spanning tree & cost of MST * mstCost: Cost of MST * errorMessage: "" if no error ELSE report the encountered error - */ + */ virtual const MstResult boruvka_deterministic() const; /** @@ -703,6 +703,9 @@ class Graph { * search. * */ + + virtual const BronKerboschResult bron_kerbosch() const; + virtual const std::vector> breadth_first_search( const Node &start) const; diff --git a/include/CXXGraph/Graph/Graph_impl.hpp b/include/CXXGraph/Graph/Graph_impl.hpp index e48d4f91..b356c365 100644 --- a/include/CXXGraph/Graph/Graph_impl.hpp +++ b/include/CXXGraph/Graph/Graph_impl.hpp @@ -22,8 +22,8 @@ #pragma once -#include #include +#include #include "CXXGraph/Graph/Graph_decl.h" #include "CXXGraph/Utility/ConstString.hpp" diff --git a/include/CXXGraph/Utility/Typedef.hpp b/include/CXXGraph/Utility/Typedef.hpp index b26440b5..3a605c13 100644 --- a/include/CXXGraph/Utility/Typedef.hpp +++ b/include/CXXGraph/Utility/Typedef.hpp @@ -237,6 +237,17 @@ struct BestFirstSearchResult_struct { template using BestFirstSearchResult = BestFirstSearchResult_struct; +// Struct that contains information about BronKerbosch algorithm results +template +struct BronKerboschResult_struct { + bool success = false; + std::string errorMessage = ""; + std::vector>, nodeHash>> + maximalCliques = {}; +}; +template +using BronKerboschResult = BronKerboschResult_struct; + /////////////////////////////////////////////////////////////////////////////////// // Using Definition // /////////////////////////////////////////////////////////////// diff --git a/test/BronKerboschTest.cpp b/test/BronKerboschTest.cpp new file mode 100644 index 00000000..923e0fe4 --- /dev/null +++ b/test/BronKerboschTest.cpp @@ -0,0 +1,175 @@ +#include + +#include "CXXGraph/CXXGraph.hpp" +#include "gtest/gtest.h" + +// Smart pointers alias +template +using unique = std::unique_ptr; +template +using shared = std::shared_ptr; + +using std::make_shared; +using std::make_unique; + +TEST(BronKerboschTest, test_1) { + CXXGraph::Node node0("0", 0); + CXXGraph::Node node1("1", 1); + CXXGraph::Node node2("2", 2); + CXXGraph::Node node3("3", 3); + CXXGraph::Node node4("4", 4); + + CXXGraph::UndirectedEdge edge0(0, node0, node1); + CXXGraph::UndirectedEdge edge1(0, node0, node2); + CXXGraph::UndirectedEdge edge2(0, node2, node1); + CXXGraph::UndirectedEdge edge3(0, node2, node3); + CXXGraph::UndirectedEdge edge4(0, node2, node4); + CXXGraph::UndirectedEdge edge5(0, node3, node4); + + CXXGraph::T_EdgeSet edgeSet; + edgeSet.insert(make_shared>(edge0)); + edgeSet.insert(make_shared>(edge1)); + edgeSet.insert(make_shared>(edge2)); + edgeSet.insert(make_shared>(edge3)); + edgeSet.insert(make_shared>(edge4)); + edgeSet.insert(make_shared>(edge5)); + + CXXGraph::Graph graph(edgeSet); + CXXGraph::BronKerboschResult res = graph.bron_kerbosch(); + + ASSERT_TRUE(res.success); + ASSERT_EQ(res.maximalCliques.size(), 2); + ASSERT_EQ(res.errorMessage, ""); + ASSERT_TRUE(std::find(res.maximalCliques.begin(), res.maximalCliques.end(), + CXXGraph::T_NodeSet{ + make_shared>(node0), + make_shared>(node1), + make_shared>(node2)}) != + res.maximalCliques.end()); + ASSERT_TRUE(std::find(res.maximalCliques.begin(), res.maximalCliques.end(), + CXXGraph::T_NodeSet{ + make_shared>(node2), + make_shared>(node3), + make_shared>(node4)}) != + res.maximalCliques.end()); +} + +// Empty graph E_5 +TEST(BronKerboschTest, test_2) { + CXXGraph::Node node0("0", 0); + CXXGraph::Node node1("1", 1); + CXXGraph::Node node2("2", 2); + CXXGraph::Node node3("3", 3); + CXXGraph::Node node4("4", 4); + + CXXGraph::T_EdgeSet edgeSet; + CXXGraph::Graph graph(edgeSet); + + graph.addNode(&node0); + graph.addNode(&node1); + graph.addNode(&node2); + graph.addNode(&node3); + graph.addNode(&node4); + + CXXGraph::BronKerboschResult res = graph.bron_kerbosch(); + ASSERT_TRUE(res.success); + ASSERT_EQ(res.maximalCliques.size(), 5); + ASSERT_EQ(res.errorMessage, ""); + ASSERT_TRUE(std::find(res.maximalCliques.begin(), res.maximalCliques.end(), + CXXGraph::T_NodeSet{ + make_shared>(node0)}) != + res.maximalCliques.end()); + ASSERT_TRUE(std::find(res.maximalCliques.begin(), res.maximalCliques.end(), + CXXGraph::T_NodeSet{ + make_shared>(node1)}) != + res.maximalCliques.end()); + ASSERT_TRUE(std::find(res.maximalCliques.begin(), res.maximalCliques.end(), + CXXGraph::T_NodeSet{ + make_shared>(node2)}) != + res.maximalCliques.end()); + ASSERT_TRUE(std::find(res.maximalCliques.begin(), res.maximalCliques.end(), + CXXGraph::T_NodeSet{ + make_shared>(node3)}) != + res.maximalCliques.end()); + ASSERT_TRUE(std::find(res.maximalCliques.begin(), res.maximalCliques.end(), + CXXGraph::T_NodeSet{ + make_shared>(node4)}) != + res.maximalCliques.end()); +} + +// Complete graph K_5 +TEST(BronKerboschTest, test_3) { + CXXGraph::Node node0("0", 0); + CXXGraph::Node node1("1", 1); + CXXGraph::Node node2("2", 2); + CXXGraph::Node node3("3", 3); + CXXGraph::Node node4("4", 4); + + CXXGraph::UndirectedEdge edge0(0, node0, node1); + CXXGraph::UndirectedEdge edge1(1, node0, node2); + CXXGraph::UndirectedEdge edge2(2, node0, node3); + CXXGraph::UndirectedEdge edge3(3, node0, node4); + CXXGraph::UndirectedEdge edge4(4, node1, node2); + CXXGraph::UndirectedEdge edge5(5, node1, node3); + CXXGraph::UndirectedEdge edge6(6, node1, node4); + CXXGraph::UndirectedEdge edge7(7, node2, node3); + CXXGraph::UndirectedEdge edge8(8, node2, node4); + CXXGraph::UndirectedEdge edge9(9, node3, node4); + + CXXGraph::T_EdgeSet edgeSet; + edgeSet.insert(make_shared>(edge0)); + edgeSet.insert(make_shared>(edge1)); + edgeSet.insert(make_shared>(edge2)); + edgeSet.insert(make_shared>(edge3)); + edgeSet.insert(make_shared>(edge4)); + edgeSet.insert(make_shared>(edge5)); + edgeSet.insert(make_shared>(edge6)); + edgeSet.insert(make_shared>(edge7)); + edgeSet.insert(make_shared>(edge8)); + edgeSet.insert(make_shared>(edge9)); + + CXXGraph::Graph graph(edgeSet); + CXXGraph::BronKerboschResult res = graph.bron_kerbosch(); + + ASSERT_TRUE(res.success); + ASSERT_EQ(res.maximalCliques.size(), 1); + ASSERT_EQ(res.errorMessage, ""); + ASSERT_TRUE(std::find(res.maximalCliques.begin(), res.maximalCliques.end(), + CXXGraph::T_NodeSet{ + make_shared>(node0), + make_shared>(node1), + make_shared>(node2), + make_shared>(node3), + make_shared>(node4)}) != + res.maximalCliques.end()); +} + +// Graph with a directed edge +TEST(BronKerboschTest, test_4) { + CXXGraph::Node node0("0", 0); + CXXGraph::Node node1("1", 1); + CXXGraph::Node node2("2", 2); + CXXGraph::Node node3("3", 3); + CXXGraph::Node node4("4", 4); + + CXXGraph::DirectedEdge edge0(0, node0, node1); + CXXGraph::UndirectedEdge edge1(0, node0, node2); + CXXGraph::UndirectedEdge edge2(0, node2, node1); + CXXGraph::UndirectedEdge edge3(0, node2, node3); + CXXGraph::UndirectedEdge edge4(0, node2, node4); + CXXGraph::UndirectedEdge edge5(0, node3, node4); + + CXXGraph::T_EdgeSet edgeSet; + edgeSet.insert(make_shared>(edge0)); + edgeSet.insert(make_shared>(edge1)); + edgeSet.insert(make_shared>(edge2)); + edgeSet.insert(make_shared>(edge3)); + edgeSet.insert(make_shared>(edge4)); + edgeSet.insert(make_shared>(edge5)); + + CXXGraph::Graph graph(edgeSet); + CXXGraph::BronKerboschResult res = graph.bron_kerbosch(); + + ASSERT_FALSE(res.success); + ASSERT_EQ(res.errorMessage, "Graph is directed"); +} diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 38845433..f4193531 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -89,6 +89,7 @@ if(TEST) add_test(test_bellmanford test_exe --gtest_filter=BellmanFord*) add_test(test_boruvka test_exe --gtest_filter=Boruvka*) + add_test(test_bron_kerbosch test_exe --gtest_filter=BronKerbosch*) add_test(test_connectivity test_exe --gtest_filter=Connectivity*) add_test(test_euler_path test_exe --gtest_filter=EulerPath*) add_test(test_fordfulkerson test_exe --gtest_filter=FordFulkerson*)