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{An Implementation of the Multiple Minimum Degree Algorithm} 16\author{Jeremy G. Siek and Lie Quan Lee} 17 18\maketitle 19 20\section{Introduction} 21 22The minimum degree ordering algorithm \cite{LIU:MMD,George:evolution} 23reorders a matrix to reduce fill-in. Fill-in is the term used to refer 24to the zero elements of a matrix that become non-zero during the 25gaussian elimination process. Fill-in is bad because it makes the 26matrix less sparse and therefore consume more time and space in later 27stages of the elimintation and in computations that use the resulting 28factorization. Reordering the rows of a matrix can have a dramatic 29affect on the amount of fill-in that occurs. So instead of solving 30\begin{eqnarray} 31A x = b 32\end{eqnarray} 33we instead solve the reordered (but equivalent) system 34\begin{eqnarray} 35(P A P^T)(P x) = P b. 36\end{eqnarray} 37where $P$ is a permutation matrix. 38 39Finding the optimal ordering is an NP-complete problem (e.i., it can 40not be solved in a reasonable amount of time) so instead we just find 41an ordering that is ``good enough'' using heuristics. The minimum 42degree ordering algorithm is one such approach. 43 44A symmetric matrix $A$ is typically represented with an undirected 45graph, however for this function we require the input to be a directed 46graph. Each nonzero matrix entry $A_{ij}$ must be represented by two 47directed edges, $(i,j)$ and $(j,i)$, in $G$. 48 49An \keyword{elimination graph} $G_v$ is formed by removing vertex $v$ 50and all of its incident edges from graph $G$ and then adding edges to 51make the vertices adjacent to $v$ into a clique\footnote{A clique is a 52complete subgraph. That is, it is a subgraph where each vertex is 53adjacent to every other vertex in the subgraph}. 54 55 56quotient graph 57set of cliques in the graph 58 59 60Mass elmination: if $y$ is selected as the minimum degree node then 61the the vertices adjacent to $y$ with degree equal to $degree(y) - 1$ 62can be selected next (in any order). 63 64Two nodes are \keyword{indistinguishable} if they have identical 65neighborhood sets. That is, 66\begin{eqnarray} 67Adj[u] \cup \{ u \} = Adj[v] \bigcup \{ v \} 68\end{eqnarray} 69Nodes that are indistiguishable can be eliminated at the same time. 70 71A representative for a set of indistiguishable nodes is called a 72\keyword{supernode}. 73 74incomplete degree update 75external degree 76 77 78 79 80\section{Algorithm Overview} 81 82@d MMD Algorithm Overview @{ 83 @<Eliminate isolated nodes@> 84 85@} 86 87 88 89 90@d Set up a mapping from integers to vertices @{ 91size_type vid = 0; 92typename graph_traits<Graph>::vertex_iterator v, vend; 93for (tie(v, vend) = vertices(G); v != vend; ++v, ++vid) 94 index_vertex_vec[vid] = *v; 95index_vertex_map = IndexVertexMap(&index_vertex_vec[0]); 96@} 97 98 99@d Insert vertices into bucket sorter (bucket number equals degree) @{ 100for (tie(v, vend) = vertices(G); v != vend; ++v) { 101 put(degree, *v, out_degree(*v, G)); 102 degreelists.push(*v); 103} 104@} 105 106 107@d Eliminate isolated nodes (nodes with zero degree) @{ 108typename DegreeLists::stack list_isolated = degreelists[0]; 109while (!list_isolated.empty()) { 110 vertex_t node = list_isolated.top(); 111 marker.mark_done(node); 112 numbering(node); 113 numbering.increment(); 114 list_isolated.pop(); 115} 116@} 117 118 119@d Find first non-empty degree bucket 120@{ 121size_type min_degree = 1; 122typename DegreeLists::stack list_min_degree = degreelists[min_degree]; 123while (list_min_degree.empty()) { 124 ++min_degree; 125 list_min_degree = degreelists[min_degree]; 126} 127@} 128 129 130 131@d Main Loop 132@{ 133while (!numbering.all_done()) { 134 eliminated_nodes = work_space.make_stack(); 135 if (delta >= 0) 136 while (true) { 137 @<Find next non-empty degree bucket@> 138 @<Select a node of minimum degree for elimination@> 139 eliminate(node); 140 } 141 if (numbering.all_done()) 142 break; 143 update(eliminated_nodes, min_degree); 144} 145@} 146 147 148@d Elimination Function 149@{ 150void eliminate(vertex_t node) 151{ 152 remove out-edges from the node if the target vertex was 153 tagged or if it is numbered 154 add vertices adjacent to node to the clique 155 put all numbered adjacent vertices into the temporary neighbors stack 156 157 @<Perform element absorption optimization@> 158} 159@} 160 161 162@d 163@{ 164bool remove_out_edges_predicate::operator()(edge_t e) 165{ 166 vertex_t v = target(e, *g); 167 bool is_tagged = marker->is_tagged(dist); 168 bool is_numbered = numbering.is_numbered(v); 169 if (is_numbered) 170 neighbors->push(id[v]); 171 if (!is_tagged) 172 marker->mark_tagged(v); 173 return is_tagged || is_numbered; 174} 175@} 176 177 178 179How does this work???? 180 181Does \code{is\_tagged} mean in the clique?? 182 183@d Perform element absorption optimization 184@{ 185while (!neighbors.empty()) { 186 size_type n_id = neighbors.top(); 187 vertex_t neighbor = index_vertex_map[n_id]; 188 adjacency_iterator i, i_end; 189 for (tie(i, i_end) = adjacent_vertices(neighbor, G); i != i_end; ++i) { 190 vertex_t i_node = *i; 191 if (!marker.is_tagged(i_node) && !numbering.is_numbered(i_node)) { 192 marker.mark_tagged(i_node); 193 add_edge(node, i_node, G); 194 } 195 } 196 neighbors.pop(); 197} 198@} 199 200 201 202@d 203@{ 204predicateRemoveEdge1<Graph, MarkerP, NumberingD, 205 typename Workspace::stack, VertexIndexMap> 206 p(G, marker, numbering, element_neighbor, vertex_index_map); 207 208remove_out_edge_if(node, p, G); 209@} 210 211 212\section{The Interface} 213 214 215@d Interface of the MMD function @{ 216template<class Graph, class DegreeMap, 217 class InversePermutationMap, 218 class PermutationMap, 219 class SuperNodeMap, class VertexIndexMap> 220void minimum_degree_ordering 221 (Graph& G, 222 DegreeMap degree, 223 InversePermutationMap inverse_perm, 224 PermutationMap perm, 225 SuperNodeMap supernode_size, 226 int delta, 227 VertexIndexMap vertex_index_map) 228@} 229 230 231 232\section{Representing Disjoint Stacks} 233 234Given a set of $N$ integers (where the integer values range from zero 235to $N-1$), we want to keep track of a collection of stacks of 236integers. It so happens that an integer will appear in at most one 237stack at a time, so the stacks form disjoint sets. Because of these 238restrictions, we can use one big array to store all the stacks, 239intertwined with one another. No allocation/deallocation happens in 240the \code{push()}/\code{pop()} methods so this is faster than using 241\code{std::stack}. 242 243 244\section{Bucket Sorting} 245 246 247 248@d Bucket Sorter Class Interface @{ 249template <typename BucketMap, typename ValueIndexMap> 250class bucket_sorter { 251public: 252 typedef typename property_traits<BucketMap>::value_type bucket_type; 253 typedef typename property_traits<ValueIndex>::key_type value_type; 254 typedef typename property_traits<ValueIndex>::value_type size_type; 255 256 bucket_sorter(size_type length, bucket_type max_bucket, 257 const BucketMap& bucket = BucketMap(), 258 const ValueIndexMap& index_map = ValueIndexMap()); 259 void remove(const value_type& x); 260 void push(const value_type& x); 261 void update(const value_type& x); 262 263 class stack { 264 public: 265 void push(const value_type& x); 266 void pop(); 267 value_type& top(); 268 const value_type& top() const; 269 bool empty() const; 270 private: 271 @<Bucket Stack Constructor@> 272 @<Bucket Stack Data Members@> 273 }; 274 stack operator[](const bucket_type& i); 275private: 276 @<Bucket Sorter Data Members@> 277}; 278@} 279 280\code{BucketMap} is a \concept{LvaluePropertyMap} that converts a 281value object to a bucket number (an integer). The range of the mapping 282must be finite. \code{ValueIndexMap} is a \concept{LvaluePropertyMap} 283that maps from value objects to a unique integer. At the top of the 284definition of \code{bucket\_sorter} we create some typedefs for the 285bucket number type, the value type, and the index type. 286 287@d Bucket Sorter Data Members @{ 288std::vector<size_type> head; 289std::vector<size_type> next; 290std::vector<size_type> prev; 291std::vector<value_type> id_to_value; 292BucketMap bucket_map; 293ValueIndexMap index_map; 294@} 295 296 297\code{N} is the maximum integer that the \code{index\_map} will map a 298value object to (the minimum is assumed to be zero). 299 300@d Bucket Sorter Constructor @{ 301bucket_sorter::bucket_sorter 302 (size_type N, bucket_type max_bucket, 303 const BucketMap& bucket_map = BucketMap(), 304 const ValueIndexMap& index_map = ValueIndexMap()) 305 : head(max_bucket, invalid_value()), 306 next(N, invalid_value()), 307 prev(N, invalid_value()), 308 id_to_value(N), 309 bucket_map(bucket_map), index_map(index_map) { } 310@} 311 312 313 314 315\bibliographystyle{abbrv} 316\bibliography{jtran,ggcl,optimization,generic-programming,cad} 317 318\end{document} 319