bron_kerbosch_all_cliques.hpp 12 KB

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