1 // Philipp Klaus Krause, philipp@informatik.uni-frankfurt.de, pkk@spth.de, 2010 - 2011
3 // (c) 2010-2011 Goethe-Universität Frankfurt
5 // This program is free software; you can redistribute it and/or modify it
6 // under the terms of the GNU General Public License as published by the
7 // Free Software Foundation; either version 2, or (at your option) any
10 // This program is distributed in the hope that it will be useful,
11 // but WITHOUT ANY WARRANTY; without even the implied warranty of
12 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 // GNU General Public License for more details.
15 // You should have received a copy of the GNU General Public License
16 // along with this program; if not, write to the Free Software
17 // Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
20 // Some routines for tree-decompositions.
22 // A tree decomposition is a graph that has a set of vertex indices as bundled property, e.g.:
24 // struct tree_dec_node
26 // std::set<unsigned int> bag;
28 // typedef boost::adjacency_list<boost::vecS, boost::vecS, boost::bidirectionalS, tree_dec_node> tree_dec_t;
30 // The following are the routines that are most likely to be interesting for outside use:
32 // void nicify(T_t &T)
33 // Transforms a tree decomposition T into a nice tree decomposition
35 // void thorup_tree_decomposition(T_t &tree_decomposition, const G_t &cfg)
36 // Creates a tree decomposition T from a graph cfg using Thorup's heuristic.
38 // void tree_decomposition_from_elimination_ordering(T_t &T, std::list<unsigned int>& l, const G_t &G)
39 // Creates a tree decomposition T of a graph G from an elimination ordering l.
41 // void thorup_elimination_ordering(l_t &l, const J_t &J)
42 // Creates an elimination ordering l of a graph J using Thorup's heuristic.
51 #include <boost/tuple/tuple_io.hpp>
52 #include <boost/graph/graph_traits.hpp>
53 #include <boost/graph/graph_utility.hpp>
54 #include <boost/graph/properties.hpp>
55 #include <boost/graph/copy.hpp>
56 #include <boost/graph/adjacency_list.hpp>
61 struct forget_properties
63 template<class T1
, class T2
>
64 void operator()(const T1
&, const T2
&) const
69 // Thorup algorithm D.
70 // The use of the multimap makes the complexity of this O(|I|log|I|), which could be reduced to O(|I|).
72 void thorup_D(l_t
&l
, const std::multimap
<unsigned int, unsigned int> &MJ
, const std::multimap
<unsigned int, unsigned int> &MS
, const unsigned int n
)
74 std::map
<unsigned int, unsigned int> m
;
79 for (unsigned int j
= n
; j
> 0;)
82 if (m
.find(j
) == m
.end())
85 std::multimap
<unsigned int, unsigned int>::const_iterator k
, k_end
;
87 for (boost::tie(k
, k_end
) = MS
.equal_range(j
); k
!= k_end
; ++k
)
88 if (m
.find(k
->second
) == m
.end())
91 for (boost::tie(k
, k_end
) = MJ
.equal_range(j
); k
!= k_end
; ++k
)
92 if (m
.find(k
->second
) == m
.end())
96 std::vector
<unsigned int> v(n
);
98 std::map
<unsigned int, unsigned int>::iterator mi
;
100 for (mi
= m
.begin(); mi
!= m
.end(); ++mi
)
101 v
[mi
->second
] = mi
->first
;
103 for (i
= 0; i
< n
; i
++)
107 // Thorup algorithm E.
108 // The use of the multimap makes the complexity of this O(|I|log|I|), which could be reduced to O(|I|).
110 void thorup_E(std::multimap
<unsigned int, unsigned int> &M
, const I_t
&I
)
112 typedef typename
boost::graph_traits
<I_t
>::adjacency_iterator adjacency_iter_t
;
113 typedef typename
boost::property_map
<I_t
, boost::vertex_index_t
>::type index_map
;
114 index_map index
= boost::get(boost::vertex_index
, I
);
116 std::stack
<std::pair
<int, unsigned int> > s
;
120 s
.push(std::pair
<int, unsigned int>(-1, boost::num_vertices(I
)));
122 for (unsigned int i
= 0; i
< boost::num_vertices(I
); i
++)
125 adjacency_iter_t j_curr
, j_end
;
127 for (boost::tie(j_curr
, j_end
) = boost::adjacent_vertices(i
, I
); j_curr
!= j_end
; ++j_curr
)
128 if (index
[*j_curr
] > j
)
134 while (s
.top().second
<= i
)
136 M
.insert(std::pair
<unsigned int, unsigned int>(s
.top().second
, s
.top().first
));
141 while (j
>= s
.top().second
&& s
.top().second
> i2
)
147 s
.push(std::pair
<int, unsigned int>(i2
, j
));
150 // Thorup forgot this in his paper. Without it, some maximal chains are omitted.
153 M
.insert(std::pair
<unsigned int, unsigned int>(s
.top().second
, s
.top().first
));
158 // Heuristically give an elimination ordering for a directed graph.
159 // For a description of this, including algorithms D and E, see
160 // Mikkel Thorup, "All Structured Programs have Small Tree-Width and Good Register Allocation", Appendix A.
161 // The use of the multimap makes the complexity of this O(|I|log|I|), could be reduced to O(|I|).
162 template <class l_t
, class G_t
>
163 void thorup_elimination_ordering(l_t
&l
, const G_t
&G
)
165 // Remove edges to immediately following instruction. By "each statement can have at most obne jump" in the last paragraph of Appendix A it is clear that Thorup does not consider the implicit next-instruction-edges as jumps.
166 boost::adjacency_list
<boost::vecS
, boost::vecS
, boost::directedS
> J
;
167 boost::copy_graph(G
, J
, boost::vertex_copy(forget_properties()).edge_copy(forget_properties()));
168 for (unsigned int i
= 0; i
< boost::num_vertices(J
) - 1; i
++)
169 remove_edge(i
, i
+ 1, J
);
171 // Todo: Implement a graph adaptor for boost that allows to treat directed graphs as undirected graphs.
172 boost::adjacency_list
<boost::vecS
, boost::vecS
, boost::undirectedS
> S
;
173 boost::copy_graph(J
, S
);
175 std::multimap
<unsigned int, unsigned int> MJ
, MS
;
181 thorup_D(l
, MJ
, MS
, num_vertices(J
));
184 // Finds a (the newest) bag that contains all vertices in X in the tree decomposition T.
185 template <class X_t
, class T_t
>
186 typename
boost::graph_traits
<T_t
>::vertex_iterator
find_bag(const X_t
&X
, const T_t
&T
)
188 typedef typename
boost::graph_traits
<T_t
>::vertex_iterator T_vertex_iter_t
;
189 typedef typename
X_t::const_iterator vertex_index_iter_t
;
191 T_vertex_iter_t t
, t_end
, t_found
;
192 vertex_index_iter_t v
;
194 for (boost::tie(t
, t_end
) = vertices(T
), t_found
= t_end
; t
!= t_end
; ++t
)
196 for (v
= X
.begin(); v
!= X
.end(); ++v
)
197 if (T
[*t
].bag
.find(*v
) == T
[*t
].bag
.end())
204 if (t_found
== t_end
) // Todo: Better error handling (throw exception?)
206 std::cerr
<< "find_bag() failed.\n";
213 // Add edges to make the vertices in X a clique in G.
214 template <class X_t
, class G_t
>
215 void make_clique(const X_t
&X
, G_t
&G
)
217 typename
X_t::const_iterator n1
, n2
;
218 for (n1
= X
.begin(); n1
!= X
.end(); n1
++)
219 for (n2
= n1
, ++n2
; n2
!= X
.end(); ++n2
)
220 add_edge(*n1
, *n2
, G
);
223 template <class T_t
, class v_t
, class G_t
>
224 void add_vertices_to_tree_decomposition(T_t
&T
, const v_t v
, const v_t v_end
, G_t
&G
, std::vector
<bool> &active
)
226 // Base case: Empty graph. Create an empty bag.
229 boost::add_vertex(T
);
233 // Todo: A more elegant solution, e.g. using subgraphs or filtered graphs.
235 typedef typename
boost::graph_traits
<G_t
>::adjacency_iterator adjacency_iter_t
;
236 typedef typename
boost::property_map
<G_t
, boost::vertex_index_t
>::type index_map
;
237 index_map index
= boost::get(boost::vertex_index
, G
);
240 adjacency_iter_t n
, n_end
;
241 decltype(T
[0].bag
) neighbours
;
242 for (boost::tie(n
, n_end
) = boost::adjacent_vertices(*v
, G
); n
!= n_end
; ++n
)
243 if (active
[index
[*n
]])
244 neighbours
.insert(index
[*n
]);
248 make_clique(neighbours
, G
);
250 add_vertices_to_tree_decomposition(T
, ++v_next
, v_end
, G
, active
);
253 typename
boost::graph_traits
<T_t
>::vertex_iterator t
;
254 typename
boost::graph_traits
<T_t
>::vertex_descriptor s
;
255 t
= find_bag(neighbours
, T
);
256 s
= boost::add_vertex(T
);
257 boost::add_edge(*t
, s
, T
);
258 T
[s
].bag
= neighbours
;
262 // Create a tree decomposition from an elimination ordering.
263 template <class T_t
, class G_t
>
264 void tree_decomposition_from_elimination_ordering(T_t
&T
, const std::list
<unsigned int>& l
, const G_t
&G
)
266 std::list
<unsigned int>::const_reverse_iterator v
, v_end
;
267 v
= l
.rbegin(), v_end
= l
.rend();
269 // Todo: Implement a graph adaptor for boost that allows to treat directed graphs as undirected graphs.
270 boost::adjacency_list
<boost::vecS
, boost::vecS
, boost::undirectedS
> G_sym
;
271 boost::copy_graph(G
, G_sym
, boost::vertex_copy(forget_properties()).edge_copy(forget_properties()));
273 std::vector
<bool> active(boost::num_vertices(G
), true);
275 add_vertices_to_tree_decomposition(T
, v
, v_end
, G_sym
, active
);
278 template <class T_t
, class G_t
>
279 void thorup_tree_decomposition(T_t
&tree_decomposition
, const G_t
&cfg
)
281 std::list
<unsigned int> elimination_ordering
;
283 thorup_elimination_ordering(elimination_ordering
, cfg
);
285 tree_decomposition_from_elimination_ordering(tree_decomposition
, elimination_ordering
, cfg
);
288 // Ensure that all joins are at proper join nodes: Each node that has two children has the same bag as its children.
289 // Complexity: Linear in the number of vertices of T.
291 void nicify_joins(T_t
&T
, typename
boost::graph_traits
<T_t
>::vertex_descriptor t
)
293 typedef typename
boost::graph_traits
<T_t
>::adjacency_iterator adjacency_iter_t
;
295 adjacency_iter_t c
, c_end
;
296 typename
boost::graph_traits
<T_t
>::vertex_descriptor c0
, c1
;
298 boost::tie(c
, c_end
) = boost::adjacent_vertices(t
, T
);
300 switch (out_degree(t
, T
))
312 typename
boost::graph_traits
<T_t
>::vertex_descriptor d
;
313 d
= boost::add_vertex(T
);
316 boost::remove_edge(t
, c0
, T
);
317 boost::remove_edge(t
, c1
, T
);
319 boost::add_edge(t
, d
, T
);
327 if (T
[t
].bag
!= T
[c0
].bag
)
329 typename
boost::graph_traits
<T_t
>::vertex_descriptor d
;
330 d
= boost::add_vertex(T
);
331 boost::add_edge(d
, c0
, T
);
332 boost::remove_edge(t
, c0
, T
);
334 boost::add_edge(t
, d
, T
);
337 if (T
[t
].bag
!= T
[c1
].bag
)
339 typename
boost::graph_traits
<T_t
>::vertex_descriptor d
= boost::add_vertex(T
);
340 boost::add_edge(d
, c1
, T
);
341 boost::remove_edge(t
, c1
, T
);
343 boost::add_edge(t
, d
, T
);
347 // Ensure that all nodes' bags are either a subset or a superset of their successors'.
348 // Complexity: Linear in the number of vertices of T.
350 void nicify_diffs(T_t
&T
, typename
boost::graph_traits
<T_t
>::vertex_descriptor t
)
352 typedef typename
boost::graph_traits
<T_t
>::adjacency_iterator adjacency_iter_t
;
354 adjacency_iter_t c
, c_end
;
355 typename
boost::graph_traits
<T_t
>::vertex_descriptor c0
, c1
;
357 boost::tie(c
, c_end
) = adjacent_vertices(t
, T
);
359 switch (boost::out_degree(t
, T
))
363 boost::add_edge(t
, boost::add_vertex(T
), T
);
374 std::cerr
<< "nicify_diffs error.\n";
381 // Redundant bags are isolated, and thus marked for later removal.
382 if (T
[t
].bag
== T
[c0
].bag
)
385 boost::remove_edge(t
, c0
, T
);
386 adjacency_iter_t c
, c_end
;
387 for(boost::tie(c
, c_end
) = adjacent_vertices(c0
, T
); c
!= c_end
; ++c
)
388 boost::add_edge(t
, *c
, T
);
389 boost::clear_vertex(c0
, T
);
392 if (std::includes(T
[t
].bag
.begin(), T
[t
].bag
.end(), T
[c0
].bag
.begin(), T
[c0
].bag
.end()) || std::includes(T
[c0
].bag
.begin(), T
[c0
].bag
.end(), T
[t
].bag
.begin(), T
[t
].bag
.end()))
395 typename
boost::graph_traits
<T_t
>::vertex_descriptor d
= boost::add_vertex(T
);
397 boost::add_edge(d
, c0
, T
);
398 boost::remove_edge(t
, c0
, T
);
399 std::set_intersection(T
[t
].bag
.begin(), T
[t
].bag
.end(), T
[c0
].bag
.begin(), T
[c0
].bag
.end(), std::inserter(T
[d
].bag
, T
[d
].bag
.begin()));
400 boost::add_edge(t
, d
, T
);
403 // Ensure that all nodes' bags' sizes differ by at most one to their successors'.
405 void nicify_diffs_more(T_t
&T
, typename
boost::graph_traits
<T_t
>::vertex_descriptor t
)
407 typedef typename
boost::graph_traits
<T_t
>::adjacency_iterator adjacency_iter_t
;
409 adjacency_iter_t c
, c_end
;
410 typename
boost::graph_traits
<T_t
>::vertex_descriptor c0
, c1
;
412 boost::tie(c
, c_end
) = adjacent_vertices(t
, T
);
414 switch (boost::out_degree(t
, T
))
417 if (T
[t
].bag
.size() > 1)
419 typename
boost::graph_traits
<T_t
>::vertex_descriptor d
= boost::add_vertex(T
);
421 T
[d
].bag
.erase(T
[d
].bag
.begin());
423 boost::add_edge(t
, d
, T
);
424 nicify_diffs_more(T
, t
);
434 nicify_diffs_more(T
, c0
);
435 nicify_diffs_more(T
, c1
);
437 const unsigned l
= T
[c0
].weight
;
438 const unsigned r
= T
[c1
].weight
;
439 T
[t
].weight
= (l
== r
) ? l
+ 1 : std::max(l
, r
);
443 std::cerr
<< "nicify_diffs_more error.\n";
449 size_t c0_size
, t_size
;
450 t_size
= T
[t
].bag
.size();
451 c0_size
= T
[c0
].bag
.size();
453 if (t_size
<= c0_size
+ 1 && t_size
+ 1 >= c0_size
)
455 nicify_diffs_more(T
, c0
);
456 T
[t
].weight
= T
[c0
].weight
;
460 typename
boost::graph_traits
<T_t
>::vertex_descriptor d
= add_vertex(T
);
461 boost::add_edge(d
, c0
, T
);
462 boost::remove_edge(t
, c0
, T
);
463 T
[d
].bag
= T
[t_size
> c0_size
? t
: c0
].bag
;
464 typename
decltype(T
[d
].bag
)::iterator i
;
465 for (i
= T
[d
].bag
.begin(); T
[t_size
< c0_size
? t
: c0
].bag
.find(*i
) != T
[t_size
< c0_size
? t
: c0
].bag
.end(); ++i
);
467 boost::add_edge(t
, d
, T
);
469 nicify_diffs_more(T
, t
);
472 #ifdef HAVE_TREEDEC_COMBINATIONS_HPP
473 #include <treedec/treedec_traits.hpp>
474 #include <boost/graph/adjacency_list.hpp>
475 #include <boost/tuple/tuple.hpp>
476 #include <boost/graph/graph_utility.hpp>
477 #include <treedec/nice_decomposition.hpp>
479 using treedec::find_root
;
481 // Find a root of an acyclic graph T
482 // Complexity: Linear in the number of vertices of T.
484 typename
boost::graph_traits
<T_t
>::vertex_descriptor
find_root(T_t
&T
)
486 typename
boost::graph_traits
<T_t
>::vertex_descriptor t
;
487 typename
boost::graph_traits
<T_t
>::in_edge_iterator e
, e_end
;
489 t
= *(boost::vertices(T
).first
);
491 for (boost::tie(e
, e_end
) = boost::in_edges(t
, T
); e
!= e_end
; boost::tie(e
, e_end
) = boost::in_edges(t
, T
))
492 t
= boost::source(*e
, T
);
498 // Remove isolated vertices possibly introduced by nicify_diffs(). Complicated, since boost does not support removing more than one vertex at a time.
500 void remove_isolated_vertices(T_t
&T
)
506 if(boost::num_vertices(T
) <= 1)
509 for (unsigned int i
= 0; i
< boost::num_vertices(T
); i
++)
510 if(!boost::out_degree(i
, T
) && !boost::in_degree(i
, T
))
519 // Transform a tree decomposition into a nice tree decomposition.
523 typename
boost::graph_traits
<T_t
>::vertex_descriptor t
;
527 // Ensure we have an empty bag at the root.
530 typename
boost::graph_traits
<T_t
>::vertex_descriptor d
= t
;
532 boost::add_edge(t
, d
, T
);
537 nicify_diffs_more(T
, t
);
538 remove_isolated_vertices(T
);
541 class cfg_titlewriter
544 explicit cfg_titlewriter(const std::string
& f
, const std::string
& p
) : function(f
), purpose(p
)
547 void operator()(std::ostream
& out
) const
549 out
<< "graph [label=\"Control-flow-graph for " << purpose
<< " (function " << function
<< ")\"]\n";
552 std::string function
;
556 class dec_titlewriter
559 explicit dec_titlewriter(unsigned int w
, const std::string
& f
, const std::string
& p
) : function(f
), purpose(p
)
563 void operator()(std::ostream
& out
) const
565 out
<< "graph [label=\"Tree-decomposition of width " << width
<< " for " << purpose
<< " (function " << function
<< ")\"]\n";
569 std::string function
;
573 #ifdef HAVE_TREEDEC_COMBINATIONS_HPP
575 #include <treedec/graph.hpp>
576 #include <treedec/preprocessing.hpp>
577 #include <boost/graph/copy.hpp>
579 #include <treedec/thorup.hpp>
580 #include <treedec/combinations.hpp>
581 #include <treedec/misc.hpp>
583 template <typename Gd_t
, typename Gs_t
>
584 void copy_undir(Gd_t
&dst
, const Gs_t
&src
)
586 for(unsigned i
= 0; i
< boost::num_vertices(src
); i
++)
587 boost::add_vertex(dst
);
589 typename
boost::graph_traits
<Gs_t
>::edge_iterator e
, e_end
;
590 for(boost::tie(e
, e_end
) = boost::edges(src
); e
!= e_end
; ++e
)
592 wassert (boost::source(*e
, src
) != boost::target(*e
, src
));
593 if (!boost::edge(boost::source(*e
, src
), boost::target(*e
, src
), dst
).second
)
594 boost::add_edge(boost::source(*e
, src
), boost::target(*e
, src
), dst
);
600 #undef USE_THORUP // Thorup's classic algorithm (default in SDCC pre-3.7.0). Substantially worse width than the others.
601 #define USE_PP_FI_TM 1 // A good trade-off between width and runtime
602 #undef USE_EX17 // Slightly better width than PP_FI_TM, but no polynomial runtime bound.
603 #undef USE_PP_MD // Slightly worse width than PP_FI_TM.
604 #undef USE_PP_FI // Slightly worse width than PP_FI_TM.
606 // Get a nice tree decomposition for a cfg.
607 template <class T_t
, class G_t
>
608 void get_nice_tree_decomposition(T_t
&tree_dec
, const G_t
&cfg
)
610 thorup_tree_decomposition(tree_dec
, cfg
);
612 #ifdef HAVE_TREEDEC_COMBINATIONS_HPP
615 treedec::thorup
<G_t
> a2(cfg
);
617 typedef boost::adjacency_list
<boost::setS
, boost::vecS
, boost::undirectedS
> cfg2_t
;
619 copy_undir(cfg2
, cfg
);
621 treedec::comb::PP_FI_TM
<cfg2_t
> a2(cfg2
);
623 treedec::comb::ex17
<cfg2_t
> a2(cfg2
);
625 treedec::comb::PP_MD
<cfg2_t
> a2(cfg2
);
627 treedec::comb::PP_FI
<cfg2_t
> a2(cfg2
);
629 #error No algorithm selected
635 a2
.get_tree_decomposition(tree_dec2
);
636 wassert(treedec::is_valid_treedecomposition(cfg
, tree_dec2
));
638 if (treedec::get_width(tree_dec2
) < treedec::get_width(tree_dec
))
639 std::swap(tree_dec
, tree_dec2
);
644 #ifdef HAVE_TREEDEC_COMBINATIONS_HPP
645 wassert(treedec::is_valid_treedecomposition(cfg
, tree_dec
));