Skip to content

Commit

Permalink
Bron kerbosch (#467)
Browse files Browse the repository at this point in the history
* Bron Kerbosch implementation. No pivots. Needs more tests.

* More tests added. Need a few more.

* Added another test case.

* Formatted relevant files to pass clang-format checks
  • Loading branch information
danrr02 authored Oct 7, 2024
1 parent c747d80 commit 6284c8b
Show file tree
Hide file tree
Showing 9 changed files with 271 additions and 5 deletions.
2 changes: 1 addition & 1 deletion include/CXXGraph/Graph/Algorithm/BestFirstSearch_impl.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -22,12 +22,12 @@

#pragma once

#include <algorithm>
#include <atomic>
#include <condition_variable>
#include <mutex>
#include <queue>
#include <thread>
#include <algorithm>

#include "CXXGraph/Graph/Graph_decl.h"

Expand Down
75 changes: 75 additions & 0 deletions include/CXXGraph/Graph/Algorithm/BronKerbosch_impl.hpp
Original file line number Diff line number Diff line change
@@ -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 <typename T>
const BronKerboschResult<T> Graph<T>::bron_kerbosch() const {
std::vector<T_NodeSet<T>> maximalCliques{};
BronKerboschResult<T> result;

if (!isUndirectedGraph()) {
result.success = false;
result.errorMessage = ERR_DIR_GRAPH;
return result;
}

T_NodeSet<T> _X, _R, _P(getNodeSet());

std::function<void(T_NodeSet<T>, T_NodeSet<T>, T_NodeSet<T>)> rec =
[&maximalCliques, &rec, this](T_NodeSet<T> R, T_NodeSet<T> P,
T_NodeSet<T> X) {
if (P.empty() && X.empty()) {
maximalCliques.push_back(R);
}

auto it = P.begin();
while (it != P.end()) {
const auto v = *it;
T_NodeSet<T> 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
4 changes: 2 additions & 2 deletions include/CXXGraph/Graph/Algorithm/Dijkstra_impl.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ const DijkstraResult Graph<T>::dijkstra(const Node<T>& 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<shared<const Node<T>>, double, nodeHash<T>> dist;
Expand Down Expand Up @@ -177,7 +177,7 @@ const DijkstraResult Graph<T>::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<shared<const Node<T>>, double, nodeHash<T>> dist;
Expand Down
1 change: 1 addition & 0 deletions include/CXXGraph/Graph/Graph.h
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down
5 changes: 4 additions & 1 deletion include/CXXGraph/Graph/Graph_decl.h
Original file line number Diff line number Diff line change
Expand Up @@ -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;

/**
Expand Down Expand Up @@ -703,6 +703,9 @@ class Graph {
* search.
*
*/

virtual const BronKerboschResult<T> bron_kerbosch() const;

virtual const std::vector<Node<T>> breadth_first_search(
const Node<T> &start) const;

Expand Down
2 changes: 1 addition & 1 deletion include/CXXGraph/Graph/Graph_impl.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,8 @@

#pragma once

#include <deque>
#include <algorithm>
#include <deque>

#include "CXXGraph/Graph/Graph_decl.h"
#include "CXXGraph/Utility/ConstString.hpp"
Expand Down
11 changes: 11 additions & 0 deletions include/CXXGraph/Utility/Typedef.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -237,6 +237,17 @@ struct BestFirstSearchResult_struct {
template <typename T>
using BestFirstSearchResult = BestFirstSearchResult_struct<T>;

// Struct that contains information about BronKerbosch algorithm results
template <typename T>
struct BronKerboschResult_struct {
bool success = false;
std::string errorMessage = "";
std::vector<std::unordered_set<shared<const Node<T>>, nodeHash<T>>>
maximalCliques = {};
};
template <typename T>
using BronKerboschResult = BronKerboschResult_struct<T>;

///////////////////////////////////////////////////////////////////////////////////
// Using Definition
// ///////////////////////////////////////////////////////////////
Expand Down
175 changes: 175 additions & 0 deletions test/BronKerboschTest.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,175 @@
#include <memory>

#include "CXXGraph/CXXGraph.hpp"
#include "gtest/gtest.h"

// Smart pointers alias
template <typename T>
using unique = std::unique_ptr<T>;
template <typename T>
using shared = std::shared_ptr<T>;

using std::make_shared;
using std::make_unique;

TEST(BronKerboschTest, test_1) {
CXXGraph::Node<int> node0("0", 0);
CXXGraph::Node<int> node1("1", 1);
CXXGraph::Node<int> node2("2", 2);
CXXGraph::Node<int> node3("3", 3);
CXXGraph::Node<int> node4("4", 4);

CXXGraph::UndirectedEdge<int> edge0(0, node0, node1);
CXXGraph::UndirectedEdge<int> edge1(0, node0, node2);
CXXGraph::UndirectedEdge<int> edge2(0, node2, node1);
CXXGraph::UndirectedEdge<int> edge3(0, node2, node3);
CXXGraph::UndirectedEdge<int> edge4(0, node2, node4);
CXXGraph::UndirectedEdge<int> edge5(0, node3, node4);

CXXGraph::T_EdgeSet<int> edgeSet;
edgeSet.insert(make_shared<CXXGraph::UndirectedEdge<int>>(edge0));
edgeSet.insert(make_shared<CXXGraph::UndirectedEdge<int>>(edge1));
edgeSet.insert(make_shared<CXXGraph::UndirectedEdge<int>>(edge2));
edgeSet.insert(make_shared<CXXGraph::UndirectedEdge<int>>(edge3));
edgeSet.insert(make_shared<CXXGraph::UndirectedEdge<int>>(edge4));
edgeSet.insert(make_shared<CXXGraph::UndirectedEdge<int>>(edge5));

CXXGraph::Graph<int> graph(edgeSet);
CXXGraph::BronKerboschResult<int> 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<int>{
make_shared<CXXGraph::Node<int>>(node0),
make_shared<CXXGraph::Node<int>>(node1),
make_shared<CXXGraph::Node<int>>(node2)}) !=
res.maximalCliques.end());
ASSERT_TRUE(std::find(res.maximalCliques.begin(), res.maximalCliques.end(),
CXXGraph::T_NodeSet<int>{
make_shared<CXXGraph::Node<int>>(node2),
make_shared<CXXGraph::Node<int>>(node3),
make_shared<CXXGraph::Node<int>>(node4)}) !=
res.maximalCliques.end());
}

// Empty graph E_5
TEST(BronKerboschTest, test_2) {
CXXGraph::Node<int> node0("0", 0);
CXXGraph::Node<int> node1("1", 1);
CXXGraph::Node<int> node2("2", 2);
CXXGraph::Node<int> node3("3", 3);
CXXGraph::Node<int> node4("4", 4);

CXXGraph::T_EdgeSet<int> edgeSet;
CXXGraph::Graph<int> graph(edgeSet);

graph.addNode(&node0);
graph.addNode(&node1);
graph.addNode(&node2);
graph.addNode(&node3);
graph.addNode(&node4);

CXXGraph::BronKerboschResult<int> 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<int>{
make_shared<CXXGraph::Node<int>>(node0)}) !=
res.maximalCliques.end());
ASSERT_TRUE(std::find(res.maximalCliques.begin(), res.maximalCliques.end(),
CXXGraph::T_NodeSet<int>{
make_shared<CXXGraph::Node<int>>(node1)}) !=
res.maximalCliques.end());
ASSERT_TRUE(std::find(res.maximalCliques.begin(), res.maximalCliques.end(),
CXXGraph::T_NodeSet<int>{
make_shared<CXXGraph::Node<int>>(node2)}) !=
res.maximalCliques.end());
ASSERT_TRUE(std::find(res.maximalCliques.begin(), res.maximalCliques.end(),
CXXGraph::T_NodeSet<int>{
make_shared<CXXGraph::Node<int>>(node3)}) !=
res.maximalCliques.end());
ASSERT_TRUE(std::find(res.maximalCliques.begin(), res.maximalCliques.end(),
CXXGraph::T_NodeSet<int>{
make_shared<CXXGraph::Node<int>>(node4)}) !=
res.maximalCliques.end());
}

// Complete graph K_5
TEST(BronKerboschTest, test_3) {
CXXGraph::Node<int> node0("0", 0);
CXXGraph::Node<int> node1("1", 1);
CXXGraph::Node<int> node2("2", 2);
CXXGraph::Node<int> node3("3", 3);
CXXGraph::Node<int> node4("4", 4);

CXXGraph::UndirectedEdge<int> edge0(0, node0, node1);
CXXGraph::UndirectedEdge<int> edge1(1, node0, node2);
CXXGraph::UndirectedEdge<int> edge2(2, node0, node3);
CXXGraph::UndirectedEdge<int> edge3(3, node0, node4);
CXXGraph::UndirectedEdge<int> edge4(4, node1, node2);
CXXGraph::UndirectedEdge<int> edge5(5, node1, node3);
CXXGraph::UndirectedEdge<int> edge6(6, node1, node4);
CXXGraph::UndirectedEdge<int> edge7(7, node2, node3);
CXXGraph::UndirectedEdge<int> edge8(8, node2, node4);
CXXGraph::UndirectedEdge<int> edge9(9, node3, node4);

CXXGraph::T_EdgeSet<int> edgeSet;
edgeSet.insert(make_shared<CXXGraph::UndirectedEdge<int>>(edge0));
edgeSet.insert(make_shared<CXXGraph::UndirectedEdge<int>>(edge1));
edgeSet.insert(make_shared<CXXGraph::UndirectedEdge<int>>(edge2));
edgeSet.insert(make_shared<CXXGraph::UndirectedEdge<int>>(edge3));
edgeSet.insert(make_shared<CXXGraph::UndirectedEdge<int>>(edge4));
edgeSet.insert(make_shared<CXXGraph::UndirectedEdge<int>>(edge5));
edgeSet.insert(make_shared<CXXGraph::UndirectedEdge<int>>(edge6));
edgeSet.insert(make_shared<CXXGraph::UndirectedEdge<int>>(edge7));
edgeSet.insert(make_shared<CXXGraph::UndirectedEdge<int>>(edge8));
edgeSet.insert(make_shared<CXXGraph::UndirectedEdge<int>>(edge9));

CXXGraph::Graph<int> graph(edgeSet);
CXXGraph::BronKerboschResult<int> 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<int>{
make_shared<CXXGraph::Node<int>>(node0),
make_shared<CXXGraph::Node<int>>(node1),
make_shared<CXXGraph::Node<int>>(node2),
make_shared<CXXGraph::Node<int>>(node3),
make_shared<CXXGraph::Node<int>>(node4)}) !=
res.maximalCliques.end());
}

// Graph with a directed edge
TEST(BronKerboschTest, test_4) {
CXXGraph::Node<int> node0("0", 0);
CXXGraph::Node<int> node1("1", 1);
CXXGraph::Node<int> node2("2", 2);
CXXGraph::Node<int> node3("3", 3);
CXXGraph::Node<int> node4("4", 4);

CXXGraph::DirectedEdge<int> edge0(0, node0, node1);
CXXGraph::UndirectedEdge<int> edge1(0, node0, node2);
CXXGraph::UndirectedEdge<int> edge2(0, node2, node1);
CXXGraph::UndirectedEdge<int> edge3(0, node2, node3);
CXXGraph::UndirectedEdge<int> edge4(0, node2, node4);
CXXGraph::UndirectedEdge<int> edge5(0, node3, node4);

CXXGraph::T_EdgeSet<int> edgeSet;
edgeSet.insert(make_shared<CXXGraph::DirectedEdge<int>>(edge0));
edgeSet.insert(make_shared<CXXGraph::UndirectedEdge<int>>(edge1));
edgeSet.insert(make_shared<CXXGraph::UndirectedEdge<int>>(edge2));
edgeSet.insert(make_shared<CXXGraph::UndirectedEdge<int>>(edge3));
edgeSet.insert(make_shared<CXXGraph::UndirectedEdge<int>>(edge4));
edgeSet.insert(make_shared<CXXGraph::UndirectedEdge<int>>(edge5));

CXXGraph::Graph<int> graph(edgeSet);
CXXGraph::BronKerboschResult<int> res = graph.bron_kerbosch();

ASSERT_FALSE(res.success);
ASSERT_EQ(res.errorMessage, "Graph is directed");
}
1 change: 1 addition & 0 deletions test/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -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*)
Expand Down

0 comments on commit 6284c8b

Please sign in to comment.