bron_kerbosch_all_cliques.hpp 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312
  1. // (C) Copyright 2007-2009 Andrew Sutton
  2. //
  3. // Use, modification and distribution are subject to the
  4. // Boost Software License, Version 1.0 (See accompanying file
  5. // LICENSE_1_0.txt or http://www.boost.org/LICENSE_1_0.txt)
  6. #ifndef BOOST_GRAPH_CLIQUE_HPP
  7. #define BOOST_GRAPH_CLIQUE_HPP
  8. #include <vector>
  9. #include <deque>
  10. #include <boost/config.hpp>
  11. #include <boost/concept/assert.hpp>
  12. #include <boost/graph/graph_concepts.hpp>
  13. #include <boost/graph/lookup_edge.hpp>
  14. #include <boost/concept/detail/concept_def.hpp>
  15. namespace boost
  16. {
  17. namespace concepts
  18. {
  19. BOOST_concept(CliqueVisitor, (Visitor)(Clique)(Graph))
  20. {
  21. BOOST_CONCEPT_USAGE(CliqueVisitor) { vis.clique(k, g); }
  22. private:
  23. Visitor vis;
  24. Graph g;
  25. Clique k;
  26. };
  27. } /* namespace concepts */
  28. using concepts::CliqueVisitorConcept;
  29. } /* namespace boost */
  30. #include <boost/concept/detail/concept_undef.hpp>
  31. namespace boost
  32. {
  33. // The algorithm implemented in this paper is based on the so-called
  34. // Algorithm 457, published as:
  35. //
  36. // @article{362367,
  37. // author = {Coen Bron and Joep Kerbosch},
  38. // title = {Algorithm 457: finding all cliques of an undirected graph},
  39. // journal = {Communications of the ACM},
  40. // volume = {16},
  41. // number = {9},
  42. // year = {1973},
  43. // issn = {0001-0782},
  44. // pages = {575--577},
  45. // doi = {http://doi.acm.org/10.1145/362342.362367},
  46. // publisher = {ACM Press},
  47. // address = {New York, NY, USA},
  48. // }
  49. //
  50. // Sort of. This implementation is adapted from the 1st version of the
  51. // algorithm and does not implement the candidate selection optimization
  52. // described as published - it could, it just doesn't yet.
  53. //
  54. // The algorithm is given as proportional to (3.14)^(n/3) power. This is
  55. // not the same as O(...), but based on time measures and approximation.
  56. //
  57. // Unfortunately, this implementation may be less efficient on non-
  58. // AdjacencyMatrix modeled graphs due to the non-constant implementation
  59. // of the edge(u,v,g) functions.
  60. //
  61. // TODO: It might be worthwhile to provide functionality for passing
  62. // a connectivity matrix to improve the efficiency of those lookups
  63. // when needed. This could simply be passed as a BooleanMatrix
  64. // s.t. edge(u,v,B) returns true or false. This could easily be
  65. // abstracted for adjacency matricies.
  66. //
  67. // The following paper is interesting for a number of reasons. First,
  68. // it lists a number of other such algorithms and second, it describes
  69. // a new algorithm (that does not appear to require the edge(u,v,g)
  70. // function and appears fairly efficient. It is probably worth investigating.
  71. //
  72. // @article{DBLP:journals/tcs/TomitaTT06,
  73. // author = {Etsuji Tomita and Akira Tanaka and Haruhisa Takahashi},
  74. // title = {The worst-case time complexity for generating all maximal
  75. // cliques and computational experiments}, journal = {Theor. Comput.
  76. // Sci.}, volume = {363}, number = {1}, year = {2006}, pages = {28-42}
  77. // ee = {https://doi.org/10.1016/j.tcs.2006.06.015}
  78. // }
  79. /**
  80. * The default clique_visitor supplies an empty visitation function.
  81. */
  82. struct clique_visitor
  83. {
  84. template < typename VertexSet, typename Graph >
  85. void clique(const VertexSet&, Graph&)
  86. {
  87. }
  88. };
  89. /**
  90. * The max_clique_visitor records the size of the maximum clique (but not the
  91. * clique itself).
  92. */
  93. struct max_clique_visitor
  94. {
  95. max_clique_visitor(std::size_t& max) : maximum(max) {}
  96. template < typename Clique, typename Graph >
  97. inline void clique(const Clique& p, const Graph& g)
  98. {
  99. BOOST_USING_STD_MAX();
  100. maximum = max BOOST_PREVENT_MACRO_SUBSTITUTION(maximum, p.size());
  101. }
  102. std::size_t& maximum;
  103. };
  104. inline max_clique_visitor find_max_clique(std::size_t& max)
  105. {
  106. return max_clique_visitor(max);
  107. }
  108. namespace detail
  109. {
  110. template < typename Graph >
  111. inline bool is_connected_to_clique(const Graph& g,
  112. typename graph_traits< Graph >::vertex_descriptor u,
  113. typename graph_traits< Graph >::vertex_descriptor v,
  114. typename graph_traits< Graph >::undirected_category)
  115. {
  116. return lookup_edge(u, v, g).second;
  117. }
  118. template < typename Graph >
  119. inline bool is_connected_to_clique(const Graph& g,
  120. typename graph_traits< Graph >::vertex_descriptor u,
  121. typename graph_traits< Graph >::vertex_descriptor v,
  122. typename graph_traits< Graph >::directed_category)
  123. {
  124. // Note that this could alternate between using an || to determine
  125. // full connectivity. I believe that this should produce strongly
  126. // connected components. Note that using && instead of || will
  127. // change the results to a fully connected subgraph (i.e., symmetric
  128. // edges between all vertices s.t., if a->b, then b->a.
  129. return lookup_edge(u, v, g).second && lookup_edge(v, u, g).second;
  130. }
  131. template < typename Graph, typename Container >
  132. inline void filter_unconnected_vertices(const Graph& g,
  133. typename graph_traits< Graph >::vertex_descriptor v,
  134. const Container& in, Container& out)
  135. {
  136. BOOST_CONCEPT_ASSERT((GraphConcept< Graph >));
  137. typename graph_traits< Graph >::directed_category cat;
  138. typename Container::const_iterator i, end = in.end();
  139. for (i = in.begin(); i != end; ++i)
  140. {
  141. if (is_connected_to_clique(g, v, *i, cat))
  142. {
  143. out.push_back(*i);
  144. }
  145. }
  146. }
  147. template < typename Graph,
  148. typename Clique, // compsub type
  149. typename Container, // candidates/not type
  150. typename Visitor >
  151. void extend_clique(const Graph& g, Clique& clique, Container& cands,
  152. Container& nots, Visitor vis, std::size_t min)
  153. {
  154. BOOST_CONCEPT_ASSERT((GraphConcept< Graph >));
  155. BOOST_CONCEPT_ASSERT((CliqueVisitorConcept< Visitor, Clique, Graph >));
  156. typedef typename graph_traits< Graph >::vertex_descriptor Vertex;
  157. // Is there vertex in nots that is connected to all vertices
  158. // in the candidate set? If so, no clique can ever be found.
  159. // This could be broken out into a separate function.
  160. {
  161. typename Container::iterator ni, nend = nots.end();
  162. typename Container::iterator ci, cend = cands.end();
  163. for (ni = nots.begin(); ni != nend; ++ni)
  164. {
  165. for (ci = cands.begin(); ci != cend; ++ci)
  166. {
  167. // if we don't find an edge, then we're okay.
  168. if (!lookup_edge(*ni, *ci, g).second)
  169. break;
  170. }
  171. // if we iterated all the way to the end, then *ni
  172. // is connected to all *ci
  173. if (ci == cend)
  174. break;
  175. }
  176. // if we broke early, we found *ni connected to all *ci
  177. if (ni != nend)
  178. return;
  179. }
  180. // TODO: the original algorithm 457 describes an alternative
  181. // (albeit really complicated) mechanism for selecting candidates.
  182. // The given optimizaiton seeks to bring about the above
  183. // condition sooner (i.e., there is a vertex in the not set
  184. // that is connected to all candidates). unfortunately, the
  185. // method they give for doing this is fairly unclear.
  186. // basically, for every vertex in not, we should know how many
  187. // vertices it is disconnected from in the candidate set. if
  188. // we fix some vertex in the not set, then we want to keep
  189. // choosing vertices that are not connected to that fixed vertex.
  190. // apparently, by selecting fix point with the minimum number
  191. // of disconnections (i.e., the maximum number of connections
  192. // within the candidate set), then the previous condition wil
  193. // be reached sooner.
  194. // there's some other stuff about using the number of disconnects
  195. // as a counter, but i'm jot really sure i followed it.
  196. // TODO: If we min-sized cliques to visit, then theoretically, we
  197. // should be able to stop recursing if the clique falls below that
  198. // size - maybe?
  199. // otherwise, iterate over candidates and and test
  200. // for maxmimal cliquiness.
  201. typename Container::iterator i, j;
  202. for (i = cands.begin(); i != cands.end();)
  203. {
  204. Vertex candidate = *i;
  205. // add the candidate to the clique (keeping the iterator!)
  206. // typename Clique::iterator ci = clique.insert(clique.end(),
  207. // candidate);
  208. clique.push_back(candidate);
  209. // remove it from the candidate set
  210. i = cands.erase(i);
  211. // build new candidate and not sets by removing all vertices
  212. // that are not connected to the current candidate vertex.
  213. // these actually invert the operation, adding them to the new
  214. // sets if the vertices are connected. its semantically the same.
  215. Container new_cands, new_nots;
  216. filter_unconnected_vertices(g, candidate, cands, new_cands);
  217. filter_unconnected_vertices(g, candidate, nots, new_nots);
  218. if (new_cands.empty() && new_nots.empty())
  219. {
  220. // our current clique is maximal since there's nothing
  221. // that's connected that we haven't already visited. If
  222. // the clique is below our radar, then we won't visit it.
  223. if (clique.size() >= min)
  224. {
  225. vis.clique(clique, g);
  226. }
  227. }
  228. else
  229. {
  230. // recurse to explore the new candidates
  231. extend_clique(g, clique, new_cands, new_nots, vis, min);
  232. }
  233. // we're done with this vertex, so we need to move it
  234. // to the nots, and remove the candidate from the clique.
  235. nots.push_back(candidate);
  236. clique.pop_back();
  237. }
  238. }
  239. } /* namespace detail */
  240. template < typename Graph, typename Visitor >
  241. inline void bron_kerbosch_all_cliques(
  242. const Graph& g, Visitor vis, std::size_t min)
  243. {
  244. BOOST_CONCEPT_ASSERT((IncidenceGraphConcept< Graph >));
  245. BOOST_CONCEPT_ASSERT((VertexListGraphConcept< Graph >));
  246. BOOST_CONCEPT_ASSERT(
  247. (AdjacencyMatrixConcept< Graph >)); // Structural requirement only
  248. typedef typename graph_traits< Graph >::vertex_descriptor Vertex;
  249. typedef typename graph_traits< Graph >::vertex_iterator VertexIterator;
  250. typedef std::vector< Vertex > VertexSet;
  251. typedef std::deque< Vertex > Clique;
  252. BOOST_CONCEPT_ASSERT((CliqueVisitorConcept< Visitor, Clique, Graph >));
  253. // NOTE: We're using a deque to implement the clique, because it provides
  254. // constant inserts and removals at the end and also a constant size.
  255. VertexIterator i, end;
  256. boost::tie(i, end) = vertices(g);
  257. VertexSet cands(i, end); // start with all vertices as candidates
  258. VertexSet nots; // start with no vertices visited
  259. Clique clique; // the first clique is an empty vertex set
  260. detail::extend_clique(g, clique, cands, nots, vis, min);
  261. }
  262. // NOTE: By default the minimum number of vertices per clique is set at 2
  263. // because singleton cliques aren't really very interesting.
  264. template < typename Graph, typename Visitor >
  265. inline void bron_kerbosch_all_cliques(const Graph& g, Visitor vis)
  266. {
  267. bron_kerbosch_all_cliques(g, vis, 2);
  268. }
  269. template < typename Graph >
  270. inline std::size_t bron_kerbosch_clique_number(const Graph& g)
  271. {
  272. std::size_t ret = 0;
  273. bron_kerbosch_all_cliques(g, find_max_clique(ret));
  274. return ret;
  275. }
  276. } /* namespace boost */
  277. #endif