biconnected_components.w 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387
  1. \documentclass[11pt]{report}
  2. \usepackage[leqno]{amsmath}
  3. \usepackage{amsfonts}
  4. \usepackage{amssymb}
  5. \usepackage{amsthm}
  6. \usepackage{latexsym}
  7. \usepackage{jweb}
  8. \usepackage{times}
  9. \usepackage{graphicx}
  10. \usepackage[nolineno]{lgrind}
  11. \newif\ifpdf
  12. \ifx\pdfoutput\undefined
  13. \pdffalse
  14. \else
  15. \pdfoutput=1
  16. \pdftrue
  17. \fi
  18. \ifpdf
  19. \usepackage[
  20. pdftex,
  21. colorlinks=true, % change to true for the electronic version
  22. linkcolor=blue,filecolor=blue,pagecolor=blue,urlcolor=blue
  23. ]{hyperref}
  24. \newcommand{\myhyperref}[2]{\hyperref[#1]{#2}}
  25. \fi
  26. \newcommand{\mtlfig}[2]{\centerline{\includegraphics*[#2]{#1.pdf}}}
  27. \newcommand{\Path}{\rightsquigarrow}
  28. \newcommand{\ancestor}{\overset{T}{\rightsquigarrow}}
  29. \newcommand{\descendant}{\ancestor^{-1}}
  30. \newcommand{\backedge}{\overset{B}{\rightarrow}}
  31. \newcommand{\edge}{\rightarrow}
  32. \DeclareMathOperator{\suchthat}{s.t.}
  33. \newcommand{\code}[1]{{\small{\em \textbf{#1}}}}
  34. \newcommand{\concept}[1]{\textsf{#1}}
  35. \begin{document}
  36. \title{An Implementation of Biconnected Components}
  37. \author{Jeremy Siek}
  38. \maketitle
  39. \section{Introduction}
  40. This paper documents the implementation of the
  41. \code{biconnected\_components()} function of the Boost Graph
  42. Library. The function was implemented by Jeremy Siek.
  43. The algorithm used to implement the \code{biconnected\_components()}
  44. function is the one based on depth-first search described
  45. by Tarjan~\cite{tarjan72:dfs_and_linear_algo}.
  46. An undirected graph $G = (V,E)$ is \emph{biconnected} if for each
  47. triple of distinct vertices $v, w, a \in V$ there is a path $p : v
  48. \Path w$ such that $a$ is not on the path $p$. An \emph{articulation
  49. point} of $G = (V,E)$ is a vertex $a \in V$ where there are two other
  50. distinct vertices $v,w \in V$ such that $a$ is on any path $p:v \Path
  51. w$ and there is at least one such path. If $a$ were to be removed from
  52. $G$, then $v$ and $w$ would no longer be reachable from each other.
  53. So articulation points act as bridges between biconnected components;
  54. the only path from one biconnected component to another is through an
  55. articulation point.
  56. The algorithm finds articulation points based on information provided
  57. by depth-first search. During a DFS, we label each vertex $v \in G$
  58. with its discover time, denoted $d[v]$. During the DFS we also
  59. compute the $lowpt(v)$, which is the smallest (in terms of discover
  60. time) vertex reachable from $v$ by traversing zero or more DFS-tree
  61. edges followed by at most one back edge. Tree edges and back edges can
  62. be identified based on discover time because for tree edge $(u,v)$ we
  63. have $d[u] < d[v]$ and for back edge $(u,v)$ we have $d[u] >
  64. d[v]$. The $lowpt(v)$ is computed for $v$ by taking the minimum
  65. $lowpt$ of the vertices to which $v$ is adjacent. The $lowpt(v)$ is
  66. computed after the recursive DFS call so the $lowpt$ has already been
  67. computed for the adjacent vertices by the time $lowpt(v)$ is computed.
  68. Now it turns out that $lowpt$ can be used to identify articulation
  69. points. Suppose $a,v,w$ are distinct vertices in $G$ such that $(a,v)$
  70. is a tree edge and $w$ is not a descendant of $v$. If $d[lowpt(v)]
  71. \geq d[a]$, then $a$ is an articulation point and removal of $a$
  72. disconnects $v$ and $w$. The reason this works is that if $d[lowpt(v)]
  73. \geq d[a]$, then we know all paths starting from $v$ stay within the
  74. sub-tree $T_v$ rooted at $v$. If a path were to escape from the
  75. sub-tree, then consider the first vertex $w$ in that path outside of
  76. $T_v$. $v \Path w$ must be considered for $lowpt(v)$, so $d[lowpt(v)]
  77. < d[w]$. Now $d[w] < d[a]$ due the structure of the DFS, so
  78. transitively $d[lowpt(v)] < d[a]$.
  79. \section{The Implementation}
  80. The implementation consists of a recursive DFS-like function named
  81. \code{biconnect()} and the public interface function
  82. \code{biconnected\-\_components()}. The \code{Graph} type must be a
  83. model of \concept{VertexListGraph} and of \concept{IncidenceGraph}.
  84. The result of the algorithm is recorded in the \code{ComponentMap},
  85. which maps edges to the biconnected component that they belong to
  86. (components are labeled with integers from zero on up). The
  87. \code{ComponentMap} type must be a model of
  88. \concept{WritablePropertyMap}, which the graph's
  89. \code{edge\-\_descriptor} type as the key type and an unsigned integer
  90. type as the value type. We do not record which component each vertex
  91. belongs to because vertices that are articulation points belong to
  92. multiple biconnected components. The number of biconnected components
  93. is returned in the \code{num\_components} parameter. The
  94. \code{discover\_time} parameter is used internally to keep track of
  95. the DFS discover time for each vertex. It must be a
  96. \concept{ReadWritePropertyMap} with the graph's
  97. \code{vertex\_\-descriptor} type as the key type and an unsigned
  98. integer as the value type. The \code{lowpt} parameter is used
  99. internally to keep track of the $d[lowpt(v)]$ for each vertex $v$. It
  100. must be a \concept{ReadWritePropertyMap} with the graph's
  101. \code{vertex\_\-descriptor} type is the key type and the value type is
  102. the same unsigned integer type as the value type in the
  103. \code{discover\-\_time} map.
  104. @d Biconnected Components Algorithm
  105. @{
  106. namespace detail {
  107. @<Recursive Biconnect Function@>
  108. }
  109. template <typename Graph, typename ComponentMap,
  110. typename DiscoverTimeMap, typename LowPointMap>
  111. void biconnected_components
  112. (typename graph_traits<Graph>::vertex_descriptor v,
  113. typename graph_traits<Graph>::vertex_descriptor u,
  114. const Graph& g,
  115. ComponentMap comp,
  116. std::size_t& num_components,
  117. DiscoverTimeMap discover_time,
  118. LowPointMap lowpt)
  119. {
  120. typedef graph_traits<Graph>::vertex_descriptor vertex_t;
  121. typedef graph_traits<Graph>::edge_descriptor edge_t;
  122. @<Concept checking of type parameters@>
  123. typedef typename property_traits<DiscoverTimeMap>::value_type D;
  124. num_components = 0;
  125. std::size_t dfs_time = 0;
  126. std::stack<edge_t> S;
  127. @<Initialize discover times to infinity@>
  128. @<Process each connected component@>
  129. }
  130. @}
  131. \noindent In the following code we use the Boost Concept Checking
  132. Library to provide better error messages in the event that the user
  133. makes a mistake in the kind of parameter used in the function
  134. template.
  135. @d Concept checking of type parameters
  136. @{
  137. BOOST_CONCEPT_ASSERT(( VertexListGraphConcept<Graph> ));
  138. BOOST_CONCEPT_ASSERT(( IncidenceGraphConcept<Graph> ));
  139. BOOST_CONCEPT_ASSERT(( WritablePropertyMapConcept<ComponentMap, edge_t> ));
  140. BOOST_CONCEPT_ASSERT(( ReadWritePropertyMapConcept<DiscoverTimeMap, vertex_t> ));
  141. BOOST_CONCEPT_ASSERT(( ReadWritePropertyMapConcept<LowPointMap, vertex_t> ));
  142. @}
  143. The first step of the algorithm is to initialize the discover times of
  144. all the vertices to infinity. This marks the vertices as undiscovered.
  145. @d Initialize discover times to infinity
  146. @{
  147. typename graph_traits<Graph>::vertex_iterator wi, wi_end;
  148. std::size_t infinity = std::numeric_limits<std::size_t>::max();
  149. for (tie(wi, wi_end) = vertices(g); wi != wi_end; ++wi)
  150. put(discover_time, *wi, infinity);
  151. @}
  152. \noindent Next we invoke \code{biconnect()} on every vertex in the
  153. graph, making sure that all connected components within the graph are
  154. searched (\code{biconnect()} only processes a single connected
  155. component).
  156. @d Process each connected component
  157. @{
  158. for (tie(wi, wi_end) = vertices(g); wi != wi_end; ++wi)
  159. if (get(discover_time, *wi) == std::numeric_limits<D>::max())
  160. detail::biconnect(*wi, *wi, true,
  161. g, comp, num_components,
  162. discover_time, dfs_time, lowpt, S);
  163. @}
  164. The recursive \code{biconnect()} function is shown below. The
  165. \code{v} parameter is where the DFS is started. The \code{u}
  166. parameter is the parent of \code{v} in the DFS-tree if \code{at\_top
  167. == false} or if \code{at\_top == true} the \code{u} is not used.
  168. \code{S} is a stack of edges that is used to collect all edges in a
  169. biconnected component. The way this works is that on ``the way down''
  170. edges are pushed into the stack. ``On the way back up'', when an
  171. articulation point $v$ is found (identified because $d[lowpt(w)] \geq
  172. d[v]$) we know that a contiguous portion of the stack (starting at the
  173. top) contains the edges in the sub-tree $T_v$ which is the biconnected
  174. component. We therefore pop these edges off of the stack (until we
  175. find an edge $e$ where $d[lowpt(source(e))] < d[w]$) and mark them as
  176. belonging to the same component. The code below also includes the
  177. bookkeeping details such as recording the discover times and computing
  178. $lowpt$. When a back edge $(v,w)$ is encountered, we do not use
  179. $lowpt(w)$ in calculating $lowpt(v)$ since $lowpt(w)$ has not yet been
  180. computed. Also, we ignore the edge $(v,w)$ if $w$ is the parent of $v$
  181. in the DFS-tree, meaning that $(w,v)$ has already been examined and
  182. categorized as a tree edge (not a back edge).
  183. @d Recursive Biconnect Function
  184. @{
  185. template <typename Graph, typename ComponentMap,
  186. typename DiscoverTimeMap, typename LowPointMap, typename Stack>
  187. void biconnect(typename graph_traits<Graph>::vertex_descriptor v,
  188. typename graph_traits<Graph>::vertex_descriptor u,
  189. bool at_top,
  190. const Graph& g,
  191. ComponentMap comp,
  192. std::size_t& c,
  193. DiscoverTimeMap d,
  194. std::size_t& dfs_time,
  195. LowPointMap lowpt,
  196. Stack& S)
  197. {
  198. typedef typename graph_traits<Graph>::vertex_descriptor vertex_t;
  199. typedef typename property_traits<DiscoverTimeMap>::value_type D;
  200. D infinity = std::numeric_limits<D>::max();
  201. put(d, v, ++dfs_time);
  202. put(lowpt, v, get(d, v));
  203. typename graph_traits<Graph>::out_edge_iterator ei, ei_end;
  204. for (tie(ei, ei_end) = out_edges(v, g); ei != ei_end; ++ei) {
  205. vertex_t w = target(*ei, g);
  206. if (get(d, w) == infinity) {
  207. S.push(*ei);
  208. biconnect(w, v, false, g, comp, c, d, dfs_time, lowpt, S);
  209. put(lowpt, v, std::min(get(lowpt, v), get(lowpt, w)));
  210. if (get(lowpt, w) >= get(d, v)) {
  211. @<Record the biconnected component@>
  212. }
  213. } else if (get(d, w) < get(d, v) && (!at_top && w != u)) {
  214. S.push(*ei);
  215. put(lowpt, v, std::min(get(lowpt, v), get(d, w)));
  216. }
  217. }
  218. }
  219. @}
  220. \noindent The following is the code for popping the edges of sub-tree
  221. $T_v$ off of the stack and recording them as being in the same
  222. biconnected component.
  223. @d Record the biconnected component
  224. @{
  225. while (d[source(S.top(), g)] >= d[w]) {
  226. put(comp, S.top(), c);
  227. S.pop();
  228. }
  229. put(comp, S.top(), c);
  230. S.pop();
  231. ++c;
  232. @}
  233. \section{Appendix}
  234. @o biconnected-components.hpp
  235. @{
  236. // Copyright (c) Jeremy Siek 2001
  237. //
  238. // Permission to use, copy, modify, distribute and sell this software
  239. // and its documentation for any purpose is hereby granted without fee,
  240. // provided that the above copyright notice appears in all copies and
  241. // that both that copyright notice and this permission notice appear
  242. // in supporting documentation. Silicon Graphics makes no
  243. // representations about the suitability of this software for any
  244. // purpose. It is provided "as is" without express or implied warranty.
  245. // NOTE: this final is generated by libs/graph/doc/biconnected_components.w
  246. #ifndef BOOST_GRAPH_BICONNECTED_COMPONENTS_HPP
  247. #define BOOST_GRAPH_BICONNECTED_COMPONENTS_HPP
  248. #include <stack>
  249. #include <boost/limits.hpp>
  250. #include <boost/graph/graph_traits.hpp>
  251. #include <boost/graph/graph_concepts.hpp>
  252. #include <boost/property_map/property_map.hpp>
  253. #include <boost/concept/assert.hpp>
  254. namespace boost {
  255. @<Biconnected Components Algorithm@>
  256. } // namespace boost
  257. #endif BOOST_GRAPH_BICONNECTED_COMPONENTS_HPP
  258. @}
  259. Figure~\ref{fig:bcc} shows the graph used in the following example and
  260. the edges are labeled by biconnected component number, as computed by
  261. the algorithm.
  262. \begin{figure}[htbp]
  263. \mtlfig{bcc}{width=3.0in}
  264. \caption{The biconnected components.}
  265. \label{fig:bcc}
  266. \end{figure}
  267. @o biconnected-components.cpp
  268. @{
  269. #include <vector>
  270. #include <list>
  271. #include <boost/graph/biconnected_components.hpp>
  272. #include <boost/graph/adjacency_list.hpp>
  273. namespace boost {
  274. struct edge_component_t {
  275. enum { num = 555 };
  276. typedef edge_property_tag kind;
  277. } edge_component;
  278. }
  279. int main()
  280. {
  281. using namespace boost;
  282. typedef adjacency_list<vecS, vecS, undirectedS,
  283. no_property, property<edge_component_t, std::size_t> > graph_t;
  284. typedef graph_traits<graph_t>::vertex_descriptor vertex_t;
  285. graph_t g(9);
  286. add_edge(0, 5, g); add_edge(0, 1, g); add_edge(0, 6, g);
  287. add_edge(1, 2, g); add_edge(1, 3, g); add_edge(1, 4, g);
  288. add_edge(2, 3, g);
  289. add_edge(4, 5, g);
  290. add_edge(6, 8, g); add_edge(6, 7, g);
  291. add_edge(7, 8, g);
  292. std::size_t c = 0;
  293. std::vector<std::size_t> discover_time(num_vertices(g));
  294. std::vector<vertex_t> lowpt(num_vertices(g));
  295. property_map<graph_t, edge_component_t>::type
  296. component = get(edge_component, g);
  297. biconnected_components(0, 8, g, component,
  298. c, &discover_time[0], &lowpt[0]);
  299. std::cout << "graph A {\n"
  300. << " node[shape=\"circle\"]\n";
  301. graph_traits<graph_t>::edge_iterator ei, ei_end;
  302. for (tie(ei, ei_end) = edges(g); ei != ei_end; ++ei)
  303. std::cout << source(*ei, g) << " -- " << target(*ei, g)
  304. << "[label=\"" << component[*ei] << "\"]\n";
  305. std::cout << "}\n";
  306. return 0;
  307. }
  308. @}
  309. % \paragraph{Definition.} A \emph{palm tree} $P$ is a directed graph that
  310. % consists of two disjoint sets of edges, denoted by $v \rightarrow w$
  311. % and $v \backedge w$ respectively, with the following properties:
  312. % \begin{enumerate}
  313. % \item The subgraph $T$ containing the edges $v \rightarrow w$ is a
  314. % spanning tree of $P$.
  315. % \item $\backedge \; \subseteq \descendant$. That is, each edge of $P$
  316. % that is not in $T$ connects a vertex to one of its ancestors in $T$.
  317. % \end{enumerate}
  318. \bibliographystyle{abbrv}
  319. \bibliography{jtran,ggcl,optimization,generic-programming,cad}
  320. \end{document}
  321. % LocalWords: Biconnected Siek biconnected Tarjan undirected DFS lowpt num dfs
  322. % LocalWords: biconnect VertexListGraph IncidenceGraph ComponentMap namespace
  323. % LocalWords: WritablePropertyMap ReadWritePropertyMap typename LowPointMap wi
  324. % LocalWords: DiscoverTimeMap const comp typedef VertexListGraphConcept max ei
  325. % LocalWords: IncidenceGraphConcept WritablePropertyMapConcept iterator bool
  326. % LocalWords: ReadWritePropertyMapConcept hpp ifndef endif bcc cpp struct enum
  327. % LocalWords: adjacency vecS undirectedS jtran ggcl