1\documentclass[11pt]{report} 2 3\input{defs} 4 5 6\setlength\overfullrule{5pt} 7\tolerance=10000 8\sloppy 9\hfuzz=10pt 10 11\makeindex 12 13\begin{document} 14 15\title{A Generic Programming Implementation of Transitive Closure} 16\author{Jeremy G. Siek} 17 18\maketitle 19 20\section{Introduction} 21 22This paper documents the implementation of the 23\code{transitive\_closure()} function of the Boost Graph Library. The 24function was implemented by Vladimir Prus and some editing was done by 25Jeremy Siek. 26 27The algorithm used to implement the \code{transitive\_closure()} 28function is based on the detection of strong components 29\cite{nuutila95, purdom70}. The following discussion describes the 30main ideas of the algorithm and some relevant background theory. 31 32The \keyword{transitive closure} of a graph $G = (V,E)$ is a graph $G^+ 33= (V,E^+)$ such that $E^+$ contains an edge $(u,v)$ if and only if $G$ 34contains a path (of at least one edge) from $u$ to $v$. A 35\keyword{successor set} of a vertex $v$, denoted by $Succ(v)$, is the 36set of vertices that are reachable from vertex $v$. The set of 37vertices adjacent to $v$ in the transitive closure $G^+$ is the same as 38the successor set of $v$ in the original graph $G$. Computing the 39transitive closure is equivalent to computing the successor set for 40every vertex in $G$. 41 42All vertices in the same strong component have the same successor set 43(because every vertex is reachable from all the other vertices in the 44component). Therefore, it is redundant to compute the successor set 45for every vertex in a strong component; it suffices to compute it for 46just one vertex per component. 47 48A \keyword{condensation graph} is a a graph $G'=(V',E')$ based on the 49graph $G=(V,E)$ where each vertex in $V'$ corresponds to a strongly 50connected component in $G$ and the edge $(s,t)$ is in $E'$ if and only 51if there exists an edge in $E$ connecting any of the vertices in the 52component of $s$ to any of the vertices in the component of $t$. 53 54\section{The Implementation} 55 56The following is the interface and outline of the function: 57 58@d Transitive Closure Function 59@{ 60template <typename Graph, typename GraphTC, 61 typename G_to_TC_VertexMap, 62 typename VertexIndexMap> 63void transitive_closure(const Graph& g, GraphTC& tc, 64 G_to_TC_VertexMap g_to_tc_map, 65 VertexIndexMap index_map) 66{ 67 if (num_vertices(g) == 0) return; 68 @<Some type definitions@> 69 @<Concept checking@> 70 @<Compute strongly connected components of the graph@> 71 @<Construct the condensation graph (version 2)@> 72 @<Compute transitive closure on the condensation graph@> 73 @<Build transitive closure of the original graph@> 74} 75@} 76 77The parameter \code{g} is the input graph and the parameter \code{tc} 78is the output graph that will contain the transitive closure of 79\code{g}. The \code{g\_to\_tc\_map} maps vertices in the input graph 80to the new vertices in the output transitive closure. The 81\code{index\_map} maps vertices in the input graph to the integers 82zero to \code{num\_vertices(g) - 1}. 83 84There are two alternate interfaces for the transitive closure 85function. The following is the version where defaults are used for 86both the \code{g\_to\_tc\_map} and the \code{index\_map}. 87 88@d The All Defaults Interface 89@{ 90template <typename Graph, typename GraphTC> 91void transitive_closure(const Graph& g, GraphTC& tc) 92{ 93 if (num_vertices(g) == 0) return; 94 typedef typename property_map<Graph, vertex_index_t>::const_type 95 VertexIndexMap; 96 VertexIndexMap index_map = get(vertex_index, g); 97 98 typedef typename graph_traits<GraphTC>::vertex_descriptor tc_vertex; 99 std::vector<tc_vertex> to_tc_vec(num_vertices(g)); 100 iterator_property_map<tc_vertex*, VertexIndexMap> 101 g_to_tc_map(&to_tc_vec[0], index_map); 102 103 transitive_closure(g, tc, g_to_tc_map, index_map); 104} 105@} 106 107\noindent The following alternate interface uses the named parameter 108trick for specifying the parameters. The named parameter functions to 109use in creating the \code{params} argument are 110\code{vertex\_index(VertexIndexMap index\_map)} and 111\code{orig\_to\_copy(G\_to\_TC\_VertexMap g\_to\_tc\_map)}. 112 113@d The Named Parameter Interface 114@{ 115template <typename Graph, typename GraphTC, 116 typename P, typename T, typename R> 117void transitive_closure(const Graph& g, GraphTC& tc, 118 const bgl_named_params<P, T, R>& params) 119{ 120 if (num_vertices(g) == 0) return; 121 detail::transitive_closure_dispatch(g, tc, 122 get_param(params, orig_to_copy), 123 choose_const_pmap(get_param(params, vertex_index), g, vertex_index) 124 ); 125} 126@} 127 128\noindent This dispatch function is used to handle the logic for 129deciding between a user-provided graph to transitive closure vertex 130mapping or to use the default, a vector, to map between the two. 131 132@d Construct Default G to TC Vertex Mapping 133@{ 134namespace detail { 135 template <typename Graph, typename GraphTC, 136 typename G_to_TC_VertexMap, 137 typename VertexIndexMap> 138 void transitive_closure_dispatch 139 (const Graph& g, GraphTC& tc, 140 G_to_TC_VertexMap g_to_tc_map, 141 VertexIndexMap index_map) 142 { 143 typedef typename graph_traits<GraphTC>::vertex_descriptor tc_vertex; 144 typename std::vector<tc_vertex>::size_type 145 n = is_default_param(g_to_tc_map) ? num_vertices(g) : 1; 146 std::vector<tc_vertex> to_tc_vec(n); 147 148 transitive_closure 149 (g, tc, 150 choose_param(g_to_tc_map, make_iterator_property_map 151 (to_tc_vec.begin(), index_map, to_tc_vec[0])), 152 index_map); 153 } 154} // namespace detail 155@} 156 157The following statements check to make sure that the template 158parameters \emph{model} the concepts that are required for this 159algorithm. 160 161@d Concept checking 162@{ 163BOOST_CONCEPT_ASSERT(( VertexListGraphConcept<Graph> )); 164BOOST_CONCEPT_ASSERT(( AdjacencyGraphConcept<Graph> )); 165BOOST_CONCEPT_ASSERT(( VertexMutableGraphConcept<GraphTC> )); 166BOOST_CONCEPT_ASSERT(( EdgeMutableGraphConcept<GraphTC> )); 167BOOST_CONCEPT_ASSERT(( ReadablePropertyMapConcept<VertexIndexMap, vertex> )); 168@} 169 170\noindent To simplify the code in the rest of the function we make the 171following typedefs. 172 173@d Some type definitions 174@{ 175typedef typename graph_traits<Graph>::vertex_descriptor vertex; 176typedef typename graph_traits<Graph>::edge_descriptor edge; 177typedef typename graph_traits<Graph>::vertex_iterator vertex_iterator; 178typedef typename property_traits<VertexIndexMap>::value_type size_type; 179typedef typename graph_traits<Graph>::adjacency_iterator adjacency_iterator; 180@} 181 182The first step of the algorithm is to compute which vertices are in 183each strongly connected component (SCC) of the graph. This is done 184with the \code{strong\_components()} function. The result of this 185function is stored in the \code{component\_number} array which maps 186each vertex to the number of the SCC to which it belongs (the 187components are numbered zero through \code{num\_scc}). We will use 188the SCC numbers for vertices in the condensation graph (CG), so we use 189the same integer type \code{cg\_vertex} for both. 190 191@d Compute strongly connected components of the graph 192@{ 193typedef size_type cg_vertex; 194std::vector<cg_vertex> component_number_vec(num_vertices(g)); 195iterator_property_map<cg_vertex*, VertexIndexMap> 196 component_number(&component_number_vec[0], index_map); 197 198int num_scc = strong_components(g, component_number, 199 vertex_index_map(index_map)); 200 201std::vector< std::vector<vertex> > components; 202build_component_lists(g, num_scc, component_number, components); 203@} 204 205\noindent Later we will need efficient access to all vertices in the 206same SCC so we create a \code{std::vector} of vertices for each SCC 207and fill it in with the \code{build\_components\_lists()} function 208from \code{strong\_components.hpp}. 209 210The next step is to construct the condensation graph. There will be 211one vertex in the CG for every strongly connected component in the 212original graph. We will add an edge to the CG whenever there is one or 213more edges in the original graph that has its source in one SCC and 214its target in another SCC. The data structure we will use for the CG 215is an adjacency-list with a \code{std::set} for each out-edge list. We 216use \code{std::set} because it will automatically discard parallel 217edges. This makes the code simpler since we can just call 218\code{insert()} every time there is an edge connecting two SCCs in the 219original graph. 220 221@d Construct the condensation graph (version 1) 222@{ 223typedef std::vector< std::set<cg_vertex> > CG_t; 224CG_t CG(num_scc); 225for (cg_vertex s = 0; s < components.size(); ++s) { 226 for (size_type i = 0; i < components[s].size(); ++i) { 227 vertex u = components[s][i]; 228 adjacency_iterator vi, vi_end; 229 for (tie(vi, vi_end) = adjacent_vertices(u, g); vi != vi_end; ++vi) { 230 cg_vertex t = component_number[*vi]; 231 if (s != t) // Avoid loops in the condensation graph 232 CG[s].insert(t); // add edge (s,t) to the condensation graph 233 } 234 } 235} 236@} 237 238Inserting into a \code{std::set} and iterator traversal for 239\code{std::set} is a bit slow. We can get better performance if we use 240\code{std::vector} and then explicitly remove duplicated vertices from 241the out-edge lists. Here is the construction of the condensation graph 242rewritten to use \code{std::vector}. 243 244@d Construct the condensation graph (version 2) 245@{ 246typedef std::vector< std::vector<cg_vertex> > CG_t; 247CG_t CG(num_scc); 248for (cg_vertex s = 0; s < components.size(); ++s) { 249 std::vector<cg_vertex> adj; 250 for (size_type i = 0; i < components[s].size(); ++i) { 251 vertex u = components[s][i]; 252 adjacency_iterator v, v_end; 253 for (tie(v, v_end) = adjacent_vertices(u, g); v != v_end; ++v) { 254 cg_vertex t = component_number[*v]; 255 if (s != t) // Avoid loops in the condensation graph 256 adj.push_back(t); 257 } 258 } 259 std::sort(adj.begin(), adj.end()); 260 std::vector<cg_vertex>::iterator di = std::unique(adj.begin(), adj.end()); 261 if (di != adj.end()) 262 adj.erase(di, adj.end()); 263 CG[s] = adj; 264} 265@} 266 267Next we compute the transitive closure of the condensation graph. The 268basic outline of the algorithm is below. The vertices are considered 269in reverse topological order to ensure that the when computing the 270successor set for a vertex $u$, the successor set for each vertex in 271$Adj[u]$ has already been computed. The successor set for a vertex $u$ 272can then be constructed by taking the union of the successor sets for 273all of its adjacent vertices together with the adjacent vertices 274themselves. 275 276\begin{tabbing} 277\textbf{for} \= ea\=ch \= vertex $u$ in $G'$ in reverse topological order \\ 278\>\textbf{for} each vertex $v$ in $Adj[u]$ \\ 279\>\>if ($v \notin Succ(u)$) \\ 280\>\>\>$Succ(u)$ := $Succ(u) \cup \{ v \} \cup Succ(v)$ 281\end{tabbing} 282 283An optimized implementation of the set union operation improves the 284performance of the algorithm. Therefore this implementation uses 285\keyword{chain decomposition}\cite{goral79,simon86}. The vertices of 286$G$ are partitioned into chains $Z_1, ..., Z_k$, where each chain 287$Z_i$ is a path in $G$ and the vertices in a chain have increasing 288topological number. A successor set $S$ is then represented by a 289collection of intersections with the chains, i.e., $S = 290\bigcup_{i=1 \ldots k} (Z_i \cap S)$. Each intersection can be represented 291by the first vertex in the path $Z_i$ that is also in $S$, since the 292rest of the path is guaranteed to also be in $S$. The collection of 293intersections is therefore represented by a vector of length $k$ where 294the $i$th element of the vector stores the first vertex in the 295intersection of $S$ with $Z_i$. 296 297Computing the union of two successor sets, $S_3 = S_1 \cup S_2$, can 298then be computed in $O(k)$ time with the below operation. We will 299represent the successor sets by vectors of integers where the integers 300are the topological numbers for the vertices in the set. 301 302@d Union of successor sets 303@{ 304namespace detail { 305 inline void 306 union_successor_sets(const std::vector<std::size_t>& s1, 307 const std::vector<std::size_t>& s2, 308 std::vector<std::size_t>& s3) 309 { 310 for (std::size_t k = 0; k < s1.size(); ++k) 311 s3[k] = std::min(s1[k], s2[k]); 312 } 313} // namespace detail 314@} 315 316So to compute the transitive closure we must first sort the graph by 317topological number and then decompose the graph into chains. Once 318that is accomplished we can enter the main loop and begin computing 319the successor sets. 320 321@d Compute transitive closure on the condensation graph 322@{ 323 @<Compute topological number for each vertex@> 324 @<Sort the out-edge lists by topological number@> 325 @<Decompose the condensation graph into chains@> 326 @<Compute successor sets@> 327 @<Build the transitive closure of the condensation graph@> 328@} 329 330The \code{topological\_sort()} function is called to obtain a list of 331vertices in topological order and then we use this ordering to assign 332topological numbers to the vertices. 333 334@d Compute topological number for each vertex 335@{ 336std::vector<cg_vertex> topo_order; 337std::vector<cg_vertex> topo_number(num_vertices(CG)); 338topological_sort(CG, std::back_inserter(topo_order), 339 vertex_index_map(identity_property_map())); 340std::reverse(topo_order.begin(), topo_order.end()); 341size_type n = 0; 342for (std::vector<cg_vertex>::iterator i = topo_order.begin(); 343 i != topo_order.end(); ++i) 344 topo_number[*i] = n++; 345@} 346 347Next we sort the out-edge lists of the condensation graph by 348topological number. This is needed for computing the chain 349decomposition, for each the vertices in a chain must be in topological 350order and we will be adding vertices to the chains from the out-edge 351lists. The \code{subscript()} function creates a function object that 352returns the topological number of its input argument. 353 354@d Sort the out-edge lists by topological number 355@{ 356for (size_type i = 0; i < num_vertices(CG); ++i) 357 std::sort(CG[i].begin(), CG[i].end(), 358 compose_f_gx_hy(std::less<cg_vertex>(), 359 detail::subscript(topo_number), 360 detail::subscript(topo_number))); 361@} 362 363Here is the code that defines the \code{subscript\_t} function object 364and its associated helper object generation function. 365 366@d Subscript function object 367@{ 368namespace detail { 369 template <typename Container, typename ST = std::size_t, 370 typename VT = typename Container::value_type> 371 struct subscript_t : public std::unary_function<ST, VT> { 372 subscript_t(Container& c) : container(&c) { } 373 VT& operator()(const ST& i) const { return (*container)[i]; } 374 protected: 375 Container *container; 376 }; 377 template <typename Container> 378 subscript_t<Container> subscript(Container& c) 379 { return subscript_t<Container>(c); } 380} // namespace detail 381@} 382 383 384Now we are ready to decompose the condensation graph into chains. The 385idea is that we want to form lists of vertices that are in a path and 386that the vertices in the list should be ordered by topological number. 387These lists will be stored in the \code{chains} vector below. To 388create the chains we consider each vertex in the graph in topological 389order. If the vertex is not already in a chain then it will be the 390start of a new chain. We then follow a path from this vertex to extend 391the chain. 392 393@d Decompose the condensation graph into chains 394@{ 395std::vector< std::vector<cg_vertex> > chains; 396{ 397 std::vector<cg_vertex> in_a_chain(num_vertices(CG)); 398 for (std::vector<cg_vertex>::iterator i = topo_order.begin(); 399 i != topo_order.end(); ++i) { 400 cg_vertex v = *i; 401 if (!in_a_chain[v]) { 402 chains.resize(chains.size() + 1); 403 std::vector<cg_vertex>& chain = chains.back(); 404 for (;;) { 405 @<Extend the chain until the path dead-ends@> 406 } 407 } 408 } 409} 410@<Record the chain number and chain position for each vertex@> 411@} 412 413\noindent To extend the chain we pick an adjacent vertex that is not 414already in a chain. Also, the adjacent vertex chosen will be the one 415with lowest topological number since the out-edges of \code{CG} are in 416topological order. 417 418@d Extend the chain until the path dead-ends 419@{ 420chain.push_back(v); 421in_a_chain[v] = true; 422graph_traits<CG_t>::adjacency_iterator adj_first, adj_last; 423tie(adj_first, adj_last) = adjacent_vertices(v, CG); 424graph_traits<CG_t>::adjacency_iterator next 425 = std::find_if(adj_first, adj_last, not1(detail::subscript(in_a_chain))); 426if (next != adj_last) 427 v = *next; 428else 429 break; // end of chain, dead-end 430@} 431 432In the next steps of the algorithm we will need to efficiently find 433the chain for a vertex and the position in the chain for a vertex, so 434here we compute this information and store it in two vectors: 435\code{chain\_number} and \code{pos\_in\_chain}. 436 437@d Record the chain number and chain position for each vertex 438@{ 439std::vector<size_type> chain_number(num_vertices(CG)); 440std::vector<size_type> pos_in_chain(num_vertices(CG)); 441for (size_type i = 0; i < chains.size(); ++i) 442 for (size_type j = 0; j < chains[i].size(); ++j) { 443 cg_vertex v = chains[i][j]; 444 chain_number[v] = i; 445 pos_in_chain[v] = j; 446 } 447@} 448 449Now that we have completed the chain decomposition we are ready to 450write the main loop for computing the transitive closure of the 451condensation graph. The output of this will be a successor set for 452each vertex. Remember that the successor set is stored as a collection 453of intersections with the chains. Each successor set is represented by 454a vector where the $i$th element is the representative vertex for the 455intersection of the set with the $i$th chain. We compute the successor 456sets for every vertex in decreasing topological order. The successor 457set for each vertex is the union of the successor sets of the adjacent 458vertex plus the adjacent vertices themselves. 459 460@d Compute successor sets 461@{ 462cg_vertex inf = std::numeric_limits<cg_vertex>::max(); 463std::vector< std::vector<cg_vertex> > successors(num_vertices(CG), 464 std::vector<cg_vertex>(chains.size(), inf)); 465for (std::vector<cg_vertex>::reverse_iterator i = topo_order.rbegin(); 466 i != topo_order.rend(); ++i) { 467 cg_vertex u = *i; 468 graph_traits<CG_t>::adjacency_iterator adj, adj_last; 469 for (tie(adj, adj_last) = adjacent_vertices(u, CG); 470 adj != adj_last; ++adj) { 471 cg_vertex v = *adj; 472 if (topo_number[v] < successors[u][chain_number[v]]) { 473 // Succ(u) = Succ(u) U Succ(v) 474 detail::union_successor_sets(successors[u], successors[v], 475 successors[u]); 476 // Succ(u) = Succ(u) U {v} 477 successors[u][chain_number[v]] = topo_number[v]; 478 } 479 } 480} 481@} 482 483We now rebuild the condensation graph, adding in edges to connect each 484vertex to every vertex in its successor set, thereby obtaining the 485transitive closure. The successor set vectors contain topological 486numbers, so we map back to vertices using the \code{topo\_order} 487vector. 488 489@d Build the transitive closure of the condensation graph 490@{ 491for (size_type i = 0; i < CG.size(); ++i) 492 CG[i].clear(); 493for (size_type i = 0; i < CG.size(); ++i) 494 for (size_type j = 0; j < chains.size(); ++j) { 495 size_type topo_num = successors[i][j]; 496 if (topo_num < inf) { 497 cg_vertex v = topo_order[topo_num]; 498 for (size_type k = pos_in_chain[v]; k < chains[j].size(); ++k) 499 CG[i].push_back(chains[j][k]); 500 } 501 } 502@} 503 504The last stage is to create the transitive closure graph $G^+$ based on 505the transitive closure of the condensation graph $G'^+$. We do this in 506two steps. First we add edges between all the vertices in one SCC to 507all the vertices in another SCC when the two SCCs are adjacent in the 508condensation graph. Second we add edges to connect each vertex in a 509SCC to every other vertex in the SCC. 510 511@d Build transitive closure of the original graph 512@{ 513// Add vertices to the transitive closure graph 514typedef typename graph_traits<GraphTC>::vertex_descriptor tc_vertex; 515{ 516 vertex_iterator i, i_end; 517 for (tie(i, i_end) = vertices(g); i != i_end; ++i) 518 g_to_tc_map[*i] = add_vertex(tc); 519} 520// Add edges between all the vertices in two adjacent SCCs 521graph_traits<CG_t>::vertex_iterator si, si_end; 522for (tie(si, si_end) = vertices(CG); si != si_end; ++si) { 523 cg_vertex s = *si; 524 graph_traits<CG_t>::adjacency_iterator i, i_end; 525 for (tie(i, i_end) = adjacent_vertices(s, CG); i != i_end; ++i) { 526 cg_vertex t = *i; 527 for (size_type k = 0; k < components[s].size(); ++k) 528 for (size_type l = 0; l < components[t].size(); ++l) 529 add_edge(g_to_tc_map[components[s][k]], 530 g_to_tc_map[components[t][l]], tc); 531 } 532} 533// Add edges connecting all vertices in a SCC 534for (size_type i = 0; i < components.size(); ++i) 535 if (components[i].size() > 1) 536 for (size_type k = 0; k < components[i].size(); ++k) 537 for (size_type l = 0; l < components[i].size(); ++l) { 538 vertex u = components[i][k], v = components[i][l]; 539 add_edge(g_to_tc_map[u], g_to_tc_map[v], tc); 540 } 541 542// Find loopbacks in the original graph. 543// Need to add it to transitive closure. 544{ 545 vertex_iterator i, i_end; 546 for (tie(i, i_end) = vertices(g); i != i_end; ++i) 547 { 548 adjacency_iterator ab, ae; 549 for (boost::tie(ab, ae) = adjacent_vertices(*i, g); ab != ae; ++ab) 550 { 551 if (*ab == *i) 552 if (components[component_number[*i]].size() == 1) 553 add_edge(g_to_tc_map[*i], g_to_tc_map[*i], tc); 554 } 555 } 556} 557@} 558 559\section{Appendix} 560 561@d Warshall Transitive Closure 562@{ 563template <typename G> 564void warshall_transitive_closure(G& g) 565{ 566 typedef typename graph_traits<G>::vertex_descriptor vertex; 567 typedef typename graph_traits<G>::vertex_iterator vertex_iterator; 568 569 BOOST_CONCEPT_ASSERT(( AdjacencyMatrixConcept<G> )); 570 BOOST_CONCEPT_ASSERT(( EdgeMutableGraphConcept<G> )); 571 572 // Matrix form: 573 // for k 574 // for i 575 // if A[i,k] 576 // for j 577 // A[i,j] = A[i,j] | A[k,j] 578 vertex_iterator ki, ke, ii, ie, ji, je; 579 for (tie(ki, ke) = vertices(g); ki != ke; ++ki) 580 for (tie(ii, ie) = vertices(g); ii != ie; ++ii) 581 if (edge(*ii, *ki, g).second) 582 for (tie(ji, je) = vertices(g); ji != je; ++ji) 583 if (!edge(*ii, *ji, g).second && 584 edge(*ki, *ji, g).second) 585 { 586 add_edge(*ii, *ji, g); 587 } 588} 589@} 590 591@d Warren Transitive Closure 592@{ 593template <typename G> 594void warren_transitive_closure(G& g) 595{ 596 using namespace boost; 597 typedef typename graph_traits<G>::vertex_descriptor vertex; 598 typedef typename graph_traits<G>::vertex_iterator vertex_iterator; 599 600 BOOST_CONCEPT_ASSERT(( AdjacencyMatrixConcept<G> )); 601 BOOST_CONCEPT_ASSERT(( EdgeMutableGraphConcept<G> )); 602 603 // Make sure second loop will work 604 if (num_vertices(g) == 0) 605 return; 606 607 // for i = 2 to n 608 // for k = 1 to i - 1 609 // if A[i,k] 610 // for j = 1 to n 611 // A[i,j] = A[i,j] | A[k,j] 612 613 vertex_iterator ic, ie, jc, je, kc, ke; 614 for (tie(ic, ie) = vertices(g), ++ic; ic != ie; ++ic) 615 for (tie(kc, ke) = vertices(g); *kc != *ic; ++kc) 616 if (edge(*ic, *kc, g).second) 617 for (tie(jc, je) = vertices(g); jc != je; ++jc) 618 if (!edge(*ic, *jc, g).second && 619 edge(*kc, *jc, g).second) 620 { 621 add_edge(*ic, *jc, g); 622 } 623 624 // for i = 1 to n - 1 625 // for k = i + 1 to n 626 // if A[i,k] 627 // for j = 1 to n 628 // A[i,j] = A[i,j] | A[k,j] 629 630 for (tie(ic, ie) = vertices(g), --ie; ic != ie; ++ic) 631 for (kc = ic, ke = ie, ++kc; kc != ke; ++kc) 632 if (edge(*ic, *kc, g).second) 633 for (tie(jc, je) = vertices(g); jc != je; ++jc) 634 if (!edge(*ic, *jc, g).second && 635 edge(*kc, *jc, g).second) 636 { 637 add_edge(*ic, *jc, g); 638 } 639} 640@} 641 642 643The following indent command was run on the output files before 644they were checked into the Boost CVS repository. 645 646@e indentation 647@{ 648indent -nut -npcs -i2 -br -cdw -ce transitive_closure.hpp 649@} 650 651@o transitive_closure.hpp 652@{ 653// Copyright (C) 2001 Vladimir Prus <ghost@@cs.msu.su> 654// Copyright (C) 2001 Jeremy Siek <jsiek@@cs.indiana.edu> 655// Permission to copy, use, modify, sell and distribute this software is 656// granted, provided this copyright notice appears in all copies and 657// modified version are clearly marked as such. This software is provided 658// "as is" without express or implied warranty, and with no claim as to its 659// suitability for any purpose. 660 661// NOTE: this final is generated by libs/graph/doc/transitive_closure.w 662 663#ifndef BOOST_GRAPH_TRANSITIVE_CLOSURE_HPP 664#define BOOST_GRAPH_TRANSITIVE_CLOSURE_HPP 665 666#include <vector> 667#include <functional> 668#include <boost/compose.hpp> 669#include <boost/graph/vector_as_graph.hpp> 670#include <boost/graph/strong_components.hpp> 671#include <boost/graph/topological_sort.hpp> 672#include <boost/graph/graph_concepts.hpp> 673#include <boost/graph/named_function_params.hpp> 674#include <boost/concept/assert.hpp> 675 676namespace boost { 677 678 @<Union of successor sets@> 679 @<Subscript function object@> 680 @<Transitive Closure Function@> 681 @<The All Defaults Interface@> 682 @<Construct Default G to TC Vertex Mapping@> 683 @<The Named Parameter Interface@> 684 685 @<Warshall Transitive Closure@> 686 687 @<Warren Transitive Closure@> 688 689} // namespace boost 690 691#endif // BOOST_GRAPH_TRANSITIVE_CLOSURE_HPP 692@} 693 694@o transitive_closure.cpp 695@{ 696// Copyright (c) Jeremy Siek 2001 697// 698// Permission to use, copy, modify, distribute and sell this software 699// and its documentation for any purpose is hereby granted without fee, 700// provided that the above copyright notice appears in all copies and 701// that both that copyright notice and this permission notice appear 702// in supporting documentation. Silicon Graphics makes no 703// representations about the suitability of this software for any 704// purpose. It is provided "as is" without express or implied warranty. 705 706// NOTE: this final is generated by libs/graph/doc/transitive_closure.w 707 708#include <boost/graph/transitive_closure.hpp> 709#include <boost/graph/graphviz.hpp> 710 711int main(int, char*[]) 712{ 713 using namespace boost; 714 typedef property<vertex_name_t, char> Name; 715 typedef property<vertex_index_t, std::size_t, 716 Name> Index; 717 typedef adjacency_list<listS, listS, directedS, Index> graph_t; 718 typedef graph_traits<graph_t>::vertex_descriptor vertex_t; 719 graph_t G; 720 std::vector<vertex_t> verts(4); 721 for (int i = 0; i < 4; ++i) 722 verts[i] = add_vertex(Index(i, Name('a' + i)), G); 723 add_edge(verts[1], verts[2], G); 724 add_edge(verts[1], verts[3], G); 725 add_edge(verts[2], verts[1], G); 726 add_edge(verts[3], verts[2], G); 727 add_edge(verts[3], verts[0], G); 728 729 std::cout << "Graph G:" << std::endl; 730 print_graph(G, get(vertex_name, G)); 731 732 adjacency_list<> TC; 733 transitive_closure(G, TC); 734 735 std::cout << std::endl << "Graph G+:" << std::endl; 736 char name[] = "abcd"; 737 print_graph(TC, name); 738 std::cout << std::endl; 739 740 std::ofstream out("tc-out.dot"); 741 write_graphviz(out, TC, make_label_writer(name)); 742 743 return 0; 744} 745@} 746 747\bibliographystyle{abbrv} 748\bibliography{jtran,ggcl,optimization,generic-programming,cad} 749 750\end{document} 751% LocalWords: Siek Prus Succ typename GraphTC VertexIndexMap const tc typedefs 752% LocalWords: typedef iterator adjacency SCC num scc CG cg resize SCCs di ch 753% LocalWords: traversal ith namespace topo inserter gx hy struct pos inf max 754% LocalWords: rbegin vec si hpp ifndef endif jtran ggcl 755