I have implemented an algorithm which computes a maximum clique via a heuristic. The pseudo-code can be found in this Paper (see Algorithm 2).
Maximum Clique Problem
Given an undirected, simple Graph \$G=(V,E)\$, a clique \$C \subseteq V\$ is a subset of vertices such that all vertices in \$C\$ are connected, i.e. \$\forall v,w \in C : vw \in E\$. A clique \$\hat{C}\$ is maximum if and only if for all cliques \$C': |\hat{C}| \geq |C'|\$.
Problem: Find a maximum clique in \$G\$.
Furthermore, I want the following property to be fulfilled: Let \$C_1, C_2, \dots, C_k\$ be maximum cliques in \$G\$. Find \$ \hat{C} = {arg\,max}_{i=1,\dots,k} \,\, \sum_{v \in C_i} degree(v) \$
I want to compute a maximum clique which with the highest sum of degrees.
Heuristic
Some aliases which are located in a header file.
// boost.hpp
using Graph = boost::adjacency_list<boost::hash_setS, boost::vecS, boost::undirectedS, VertexProperties, EdgeProperties>;
using Vertex = boost::graph_traits<Graph>::vertex_descriptor;
using VertexIt = boost::graph_traits<Graph>::vertex_iterator;
using AdjacentIt = boost::graph_traits<Graph>::adjacency_iterator;
The heuristic:
// Clique.cpp
// graph is a class member and is of type Graph
std::vector<Vertex> Coloring::findMaxClique() const noexcept {
auto maxCliqueSize = 0;
std::vector<Vertex> maxClique;
std::vector<Vertex> tmpClique;
VertexIt vertex, v_end;
for (tie(vertex, v_end) = boost::vertices(graph); vertex != v_end; ++vertex) {
if (boost::degree(*vertex, graph) >= maxCliqueSize) {
tmpClique = findMaxCliqueWithVertex(*vertex, maxCliqueSize);
if (tmpClique.size() >= 2 && static_cast<int>(tmpClique.size()) > maxCliqueSize) {
maxClique = std::move(tmpClique);
maxCliqueSize = static_cast<int>(maxClique.size());
} else if (tmpClique.size() == maxClique.size()) {
if (calculateDegreeSum(tmpClique) > calculateDegreeSum(maxClique)) {
maxClique = std::move(tmpClique);
}
}
}
}
return maxClique;
}
inline int Coloring::calculateDegreeSum(const std::vector<Vertex> &clique) const noexcept {
return std::accumulate(clique.begin(), clique.end(), 0, [this](int accumulator, const Vertex vertex) {
return accumulator + boost::degree(vertex, graph);
});
}
std::vector<Vertex> Coloring::findMaxCliqueWithVertex(const Vertex vertex, const int maxCliqueSize) const noexcept {
std::vector<Vertex> clique;
clique.reserve(maxCliqueSize);
clique.emplace_back(vertex);
std::vector<Vertex> candidateNeighbors;
candidateNeighbors.reserve(maxCliqueSize);
AdjacentIt adjVertex, adjVertEnd;
for (tie(adjVertex, adjVertEnd) = boost::adjacent_vertices(vertex, graph); adjVertex != adjVertEnd; ++adjVertex) {
candidateNeighbors.emplace_back(*adjVertex);
}
std::vector<Vertex> tmp;
tmp.reserve(maxCliqueSize);
while (!candidateNeighbors.empty()) {
const auto highestDegNeighborIt = std::max_element(candidateNeighbors.begin(), candidateNeighbors.end(), [this](const Vertex &lhs, const Vertex &rhs) {
return boost::degree(lhs,graph) < boost::degree(rhs,graph);
});
const auto highestDegVert = *highestDegNeighborIt;
clique.emplace_back(highestDegVert);
for (tie(adjVertex, adjVertEnd) = boost::adjacent_vertices(highestDegVert, graph_); adjVertex != adjVertEnd; ++adjVertex) {
if (std::find(candidateNeighbors.begin(), candidateNeighbors.end(), *adjVertex) != candidateNeighbors.end()) {
tmp.emplace_back(*adjVertex);
}
}
candidateNeighbors = std::move(tmp);
}
return clique;
}
How it works
- for every vertex \$v \in V\$: try find a clique which includes \$v\$
- add \$v\$ to new clique \$C'\$
- consider neighbor-vertices of \$v\$. Let \$N_v := \{w \in V : vw \in E\}\$ be the neighborhood of a vertex \$v\$.
- select \$\hat{w} = {arg\,max}_{w\in N_v} degree(w)\$
- add \$\hat{w}\$ to \$C'\$
- now consider \$M := N_{\hat{w}} \cap N_v\$ and repeat the procedure of finding a vertex in \$M\$ with highest degree like above until no candidate-vertices exist
- return \$C'\$
- if we find a new clique \$C'\$ which has the same size as our current maximum clique \$\Rightarrow\$ compute sum of degrees and save the one which has the higher sum value
Question/Concerns
- I am fairly new to C++ and just started to get to know C++11 features
- Because this algorithm is used several thousands of times for a graph
- I want this procedure to be fast as possible
- readable and clean coding style?
maxCliqueSize
can be removed as usingmaxClique.size()
can be done in constant time (usingstd::vector<Vertex>
)- Did I use
std::move
correctly or should I usestd::swap
instead? - Is there any way to alleviate the
std::find
as it is not a \$O(1)\$ operation. I could try to use astd::vector<bool>
of length \$|V|\$ which represents candidate vertices when an entry is set totrue
.
So I would get this algorithm; however I need additional std::move
operations and vector-emplacements.
std::vector<Vertex> Coloring::findMaxCliqueWithVertex(const Vertex vertex, const int maxCliqueSize) const noexcept {
std::vector<Vertex> clique;
clique.reserve(maxCliqueSize);
clique.emplace_back(vertex);
std::vector<Vertex> candidateNeighbors;
std::vector<bool> candidateNeighborsVec(boost::num_vertices(graph));
candidateNeighbors.reserve(maxCliqueSize);
AdjacentIt adjVertex, adjVertEnd;
for (tie(adjVertex, adjVertEnd) = boost::adjacent_vertices(vertex, graph); adjVertex != adjVertEnd; ++adjVertex) {
candidateNeighbors.emplace_back(*adjVertex);
candidateNeighborsVec[*adjVertex] = true;
}
std::vector<Vertex> tmp;
tmp.reserve(maxCliqueSize);
while (!candidateNeighbors.empty()) {
const auto highestDegNeighborIt = std::max_element(candidateNeighbors.begin(), candidateNeighbors.end(), [this](const Vertex &lhs, const Vertex &rhs) {
return boost::degree(lhs,graph) < boost::degree(rhs,graph);
});
const auto highestDegVert = *highestDegNeighborIt;
clique.emplace_back(highestDegVert);
std::vector<bool> newCandidates(boost::num_vertices(graph));
for (tie(adjVertex, adjVertEnd) = boost::adjacent_vertices(highestDegVert, graph); adjVertex != adjVertEnd; ++adjVertex) {
if (candidageNeighborsVec[*adjVertex])
tmp.emplace_back(*adjVertex);
newCandidatesVec[*adjVertex] = true;
}
}
candidateNeighbors = std::move(tmp);
candidateNeighborsVec = std::move(newCandidatesVec);
}
return clique;
}