| | 1 | r""" |
| | 2 | Linear orderings of graphs and digraphs |
| | 3 | |
| | 4 | This module implements several algorithms for computing and evaluating linear |
| | 5 | vertex ordering of graphs and digraphs. The <width> of a linear ordering is one |
| | 6 | of the following values. |
| | 7 | |
| | 8 | +--------------+----------------------------+ |
| | 9 | | short name | long name | |
| | 10 | +==============+============================+ |
| | 11 | | vs | vertex_separation | |
| | 12 | +--------------+----------------------------+ |
| | 13 | | pw | pathwidth | |
| | 14 | +--------------+----------------------------+ |
| | 15 | | tw | treewidth | |
| | 16 | +--------------+----------------------------+ |
| | 17 | | bw | bandwidth | |
| | 18 | +--------------+----------------------------+ |
| | 19 | | pn | process_number | |
| | 20 | +--------------+----------------------------+ |
| | 21 | | cw | cutwidth | |
| | 22 | +--------------+----------------------------+ |
| | 23 | | mcw | modified_cutwidth | |
| | 24 | +--------------+----------------------------+ |
| | 25 | | ola | optimal_linear_arrangement | |
| | 26 | +--------------+----------------------------+ |
| | 27 | | sc | sum_cut | |
| | 28 | +--------------+----------------------------+ |
| | 29 | | mfi | minimun_fill_in | |
| | 30 | +--------------+----------------------------+ |
| | 31 | |
| | 32 | Some of these widths are defined for both graphs and digraphs, and others only |
| | 33 | for graphs or for digraphs. |
| | 34 | |
| | 35 | We follow the formulations proposed in [Bod98]_ and [BFK+11]_ for the evaluation |
| | 36 | of the widths induced by a linear vertex ordering `L=[v_1, ..., v_n]` of the |
| | 37 | vertices of a (di)graph `G=(V,E)`. Thus, we consider that we have a function |
| | 38 | `f` associating an integer to each 3-tuple, consisting of a (di)graph `G=(V,E)`, |
| | 39 | a vertex set `S\subseteq V`, and a vertex `v\in V`. Let `L[:i]=[v_1, ..., v_i]` |
| | 40 | be a prefix of the ordering and so a subset of `V`. We consider the problems |
| | 41 | that can be formulated as the computation of: |
| | 42 | |
| | 43 | .. MATH:: |
| | 44 | |
| | 45 | \text{cost}(G, L) = \max_{0\leq i<|V(G)|} f(G, L[:i]\setminus\{v\}, v) |
| | 46 | |
| | 47 | or |
| | 48 | |
| | 49 | .. MATH:: |
| | 50 | |
| | 51 | \text{cost}(G, L) = \sum_{0\leq i<|V(G)|} f(G, L[:i]\setminus\{v\}, v) |
| | 52 | |
| | 53 | |
| | 54 | |
| | 55 | Given an ordering `v_1, ..., v_n` of the vertices of `V(G)`, its *cost* is |
| | 56 | defined as: |
| | 57 | |
| | 58 | |
| | 59 | **Vertex separation** |
| | 60 | |
| | 61 | The vertex separation of a linear ordering `L` of the vertices of a digraph `D` |
| | 62 | is measured as the maximum number of out-neighbors with position `k>i` in the |
| | 63 | ordering of the vertices with position `j<=i` in the ordering. That is, |
| | 64 | |
| | 65 | .. MATH:: |
| | 66 | vs_L(D) = \max_{1\leq i\leq |V|-1} |\{N^+(L[:i])\setminus L[:i]\}| |
| | 67 | |
| | 68 | |
| | 69 | **Path decomposition** |
| | 70 | |
| | 71 | The cost of a path decomposition induced by a linear ordering `L` of the |
| | 72 | vertices of a graph `G` is measured as the maximum number of neighbors with |
| | 73 | position `k>i` in the ordering of the vertices with position `j<=i` in the |
| | 74 | ordering. That is, |
| | 75 | |
| | 76 | .. MATH:: |
| | 77 | pw_L(G) = \max_{1\leq i\leq |V|-1} |\{N(L[:i])\setminus L[:i]\}| |
| | 78 | |
| | 79 | |
| | 80 | |
| | 81 | **Tree decomposition** |
| | 82 | |
| | 83 | The value ``tw_L(G)`` of the tree decomposition induced by the ordering `L` of |
| | 84 | the vertices of `G`. |
| | 85 | |
| | 86 | The cost of a tree decomposition is mesured by the maximum number of vertices |
| | 87 | with position `j > i` in the vertex ordering that can be reached from a vertex |
| | 88 | `v` at position `i` through a path with internal vertices at positions `<= |
| | 89 | i`. Let `Z(i)` be the set of vertices that can be reached from the vertex at |
| | 90 | position `i`. We have |
| | 91 | |
| | 92 | .. MATH:: |
| | 93 | |
| | 94 | tw_L(G) = \max_{1\leq i\leq |V|-1} | Z( i ) | |
| | 95 | |
| | 96 | |
| | 97 | |
| | 98 | **Bandwidth** |
| | 99 | |
| | 100 | The bandwidth minimization problem is to find a linear vertex ordering that |
| | 101 | minimizes the maximum dilation among all the edges, where the dilatation of an |
| | 102 | edge is the distance between its endpoints in the vertex ordering. We can extend |
| | 103 | the notion to digraphs where the dilatation of an arc `(u,v)` is the distance |
| | 104 | between its endpoints if `v` has a higher index that `u` in the ordering, and 0 |
| | 105 | otherwise. |
| | 106 | |
| | 107 | So, the bandwidth `bw_L(G)` of a graph `G=(V,E)`, or the bandwidth `bw_L(D)` for |
| | 108 | a digraph `D=(V,A)`, induced by a given vertex ordering `L` are measured as |
| | 109 | follows, |
| | 110 | |
| | 111 | .. MATH:: |
| | 112 | |
| | 113 | bw_L(G) = \max_{\{u,v\}\in E} | L[u] - L[v] | |
| | 114 | |
| | 115 | bw_L(D) = \max_{\{u,v\}\in A} max( 0, L[v] - L[u] ) |
| | 116 | |
| | 117 | |
| | 118 | |
| | 119 | **Process number** |
| | 120 | |
| | 121 | The process number of a linear ordering `L` of the vertices of a digraph `D` is |
| | 122 | measured as the maximum number of out-neighbors with position `k>=i` (`i` |
| | 123 | included) in the ordering of the vertices with position `j<=i` (`i` included) in |
| | 124 | the ordering. Thus, loops on vertices of the digraph are also taken into |
| | 125 | account. This notion can also be formulated for undirected graphs (see [CoSe11]_ |
| | 126 | for more details on this notion). We have, |
| | 127 | |
| | 128 | .. MATH:: |
| | 129 | |
| | 130 | pn_L(D) = \max_{1\leq i\leq |V|} |\{N^+(L[:i])\setminus L[:i-1]\}| |
| | 131 | |
| | 132 | pn_L(G) = \max_{1\leq i\leq |V|} |\{N(L[:i])\setminus L[:i-1]\}| |
| | 133 | |
| | 134 | |
| | 135 | |
| | 136 | **Cutwidth and modified cutwidth** |
| | 137 | |
| | 138 | The cutwidth minimization problem is to find an ordering that minimizes the |
| | 139 | maximum number of edges between vertices with index `j<=i` in the ordering and |
| | 140 | vertices with index `k>i` in the ordering. When considering digraphs, only arcs |
| | 141 | from `j<=i` to `k>i` are considered. For the modified cutwidth, only edges (or |
| | 142 | arcs) from vertices with indexes `j<i` to vertices with indexes `k>i` are |
| | 143 | considered. So we count the number of edges (or arcs) passing over the vertex |
| | 144 | with index `i`. |
| | 145 | |
| | 146 | So, the cutwidth `cw_L(G)` of a graph `G=(V,E)`, or the cutwidth `cw_L(D)` for a |
| | 147 | digraph `D=(V,A)`, or the modified cutwidth `mcw_L(G)` of a graph `G=(V,E)`, or |
| | 148 | the modified cutwidth `mcw_L(D)` for a digraph `D=(V,A)`, induced by a given |
| | 149 | vertex ordering `L` are measured as follows, |
| | 150 | |
| | 151 | .. MATH:: |
| | 152 | |
| | 153 | cw_L(D) = \max_{1\leq i\leq |V|-1}|\{(u,v)\in A,\ L[u]\leq i<L[v]\}| |
| | 154 | |
| | 155 | mcw_L(D) = \max_{1\leq i\leq |V|-1}|\{(u,v)\in A,\ L[u]< i<L[v]\}| |
| | 156 | |
| | 157 | |
| | 158 | **Optimal linear arrangement** |
| | 159 | |
| | 160 | The optimal linear arrangement minimization problem is to find an ordering of |
| | 161 | the vertices that minimizes the sum over all intervals `[i,i+1]` of the number |
| | 162 | of edges between vertices with index `j<=i` in the ordering and vertices with |
| | 163 | index `k>i` in the ordering. When considering digraphs, only arcs from `j<=i` to |
| | 164 | `k>i` are considered. So, the cost of a linear arrangement `ola_L(G)` of a graph |
| | 165 | `G=(V,E)`, or `ola_L(D)` for a digraph `D=(V,A)`, induced by a given vertex |
| | 166 | ordering `L` are measured as follows, |
| | 167 | |
| | 168 | .. MATH:: |
| | 169 | |
| | 170 | ola_L(G) = \sum_{1\leq i\leq |V|-1} |\{(u,v)\in E,\ L[u]\leq i<L[v]\}| |
| | 171 | |
| | 172 | ola_L(D) = \sum_{1\leq i\leq |V|-1} |\{(u,v)\in A,\ L[u]\leq i<L[v]\}| |
| | 173 | |
| | 174 | However, realizing that in the sum edge `(u,v)` counts for the distance between |
| | 175 | `u` and `v` in the ordering, we obtain a better formulation. Indeed, the optimal |
| | 176 | linear arrangement minimization problem is to find an ordering that minimizes |
| | 177 | the sum of the dilation of the edges, where the dilatation of an edge is the |
| | 178 | distance between its endpoints in the vertex ordering. We can extend the notion |
| | 179 | to digraphs where the dilatation of an arc `(u,v)` is the distance between its |
| | 180 | endpoints if `v` has a higher index that `u` in the ordering, and 0 otherwise. |
| | 181 | So, the cost of a linear arrangement `ola_L(G)` of a graph `G=(V,E)`, or |
| | 182 | `ola_L(D)` for a digraph `D=(V,A)`, induced by a given vertex ordering `L` are |
| | 183 | measured as follows, |
| | 184 | |
| | 185 | .. MATH:: |
| | 186 | |
| | 187 | ola_L(G) = \sum_{\{u,v\}\in E} | L[u] - L[v] | |
| | 188 | |
| | 189 | ola_L(D) = \sum_{\{u,v\}\in A} max( 0, L[v] - L[u] ) |
| | 190 | |
| | 191 | |
| | 192 | **Sum Cut** |
| | 193 | |
| | 194 | The sum cut problem as a linear vertex ordering problem is to minimize the sum |
| | 195 | of the vertex cuts induced by the ordering. A cut is the size of the |
| | 196 | neighborhood in `L[i+1:]` of the vertices in `L[:i]`. This notion naturally |
| | 197 | extend to digraphs. So the sum of the cost of the cuts induced by an ordering is |
| | 198 | measured as, |
| | 199 | |
| | 200 | .. MATH:: |
| | 201 | sc_L(D) = \sum_{1\leq i\leq |V|-1} |\{N^+(L[:i])\setminus L[:i]\}| |
| | 202 | |
| | 203 | sc_L(G) = \sum_{1\leq i\leq |V|-1} |\{N(L[:i])\setminus L[:i]\}| |
| | 204 | |
| | 205 | |
| | 206 | |
| | 207 | **Minimum fill-in** |
| | 208 | |
| | 209 | The cost of a fill-in is mesured by the sum of the number of vertices with |
| | 210 | position `j > i` in the vertex ordering that can be reached from a vertex `v` at |
| | 211 | position `i` through a path with internal vertices at positions `<= i` (see tree |
| | 212 | decompositions). Let `Z(i)` be the set of vertices that can be reached from the |
| | 213 | vertex at position `i`. We have |
| | 214 | |
| | 215 | .. MATH:: |
| | 216 | |
| | 217 | mfi_L(G) = \sum_{1\leq i\leq |V|-1} | Z( i ) | |
| | 218 | |
| | 219 | |
| | 220 | |
| | 221 | REFERENCES: |
| | 222 | |
| | 223 | .. [BFK+06] *On exact algorithms for treewidth*, Hans L. Bodlaender, Fedor |
| | 224 | V. Fomin, Arie M.C.A. Koster, Dieter Kratsch, and Dimitrios M. Thilikos, |
| | 225 | Proceedings 14th Annual European Symposium on Algorithms (ESA), volume 4168 of |
| | 226 | Lecture Notes in Computer Science, pages 672-683. Springer, 2006. |
| | 227 | |
| | 228 | .. [BFK+11] *A note on exact algorithms for vertex ordering problems on graphs*, |
| | 229 | Hans L. Bodlaender, Fedor V. Fomin, Arie M.C.A. Koster, Dieter Kratsch, and |
| | 230 | Dimitrios M. Thilikos, Theory of Computing Systems, 2011 to appear. |
| | 231 | http://dx.doi.org/10.1007/s00224-011-9312-0. |
| | 232 | |
| | 233 | .. [Bod98] *A partial k-arboretum of graphs with bounded treewidth*, Hans |
| | 234 | L. Bodlaender, Theoretical Computer Science, Volume 209, Issues 1-2, Pages |
| | 235 | 1-45, 6 December 1998 |
| | 236 | |
| | 237 | .. [CoSe11] *Characterization of graphs and digraphs with small process number*, |
| | 238 | D. Coudert and J-S. Sereni, Discrete Applied Mathematics (DAM), |
| | 239 | 159(11):1094-1109, July 2011. |
| | 240 | |
| | 241 | .. [Kin92] *The vertex separation number of a graph equals its path-width*, |
| | 242 | Nancy G. Kinnersley, Information Processing Letters, Volume 42, Issue 6, Pages |
| | 243 | 345-350, 24 July 1992. |
| | 244 | |
| | 245 | |
| | 246 | |
| | 247 | AUTHORS: |
| | 248 | |
| | 249 | - David Coudert (2012-03-20): initial version |
| | 250 | |
| | 251 | |
| | 252 | |
| | 253 | .. TODO:: |
| | 254 | |
| | 255 | * implement methods for computing linear orderings |
| | 256 | |
| | 257 | |
| | 258 | |
| | 259 | Methods |
| | 260 | ------- |
| | 261 | """ |
| | 262 | |
| | 263 | |
| | 264 | #***************************************************************************** |
| | 265 | # Copyright (C) 2012 David Coudert <david.coudert@inria.fr> |
| | 266 | # |
| | 267 | # Distributed under the terms of the GNU General Public License (GPL) |
| | 268 | # http://www.gnu.org/licenses/ |
| | 269 | #***************************************************************************** |
| | 270 | |
| | 271 | |
| | 272 | class LinearOrdering(): |
| | 273 | r""" |
| | 274 | Class gathering methods for manipulating linear orderings of (di)graphs. |
| | 275 | |
| | 276 | This module implements several algorithms for computing and evaluating |
| | 277 | linear vertex ordering of graphs and digraphs. The <width> of a linear |
| | 278 | ordering is one of the following values. |
| | 279 | |
| | 280 | """ |
| | 281 | |
| | 282 | def _repr_(self): |
| | 283 | r""" |
| | 284 | Returns a string representation of the class LinearOrdering. |
| | 285 | """ |
| | 286 | return "Linear Ordering Class" |
| | 287 | |
| | 288 | def _latex_(self): |
| | 289 | r""" |
| | 290 | Returns the LaTeX representation of the class LinearOrdering. |
| | 291 | """ |
| | 292 | return 'Linear Ordering Class' |
| | 293 | |
| | 294 | |
| | 295 | ########################################################## |
| | 296 | # Function for testing the validity of a vertex ordering # |
| | 297 | ########################################################## |
| | 298 | |
| | 299 | def is_valid(self, G, L): |
| | 300 | r""" |
| | 301 | Test if a given linear ordering `L` is valid for a given (di)graph `G`. |
| | 302 | |
| | 303 | Returns True if `L` is a valid vertex ordering for `G`, that is if all |
| | 304 | vertices of `G` are in `L`, and `L` contains no other vertex and no |
| | 305 | duplicated vertices. |
| | 306 | |
| | 307 | INPUT: |
| | 308 | |
| | 309 | - ``G`` -- a graph or a digraph |
| | 310 | |
| | 311 | - ``L`` -- an ordering of the vertices of ``G`` |
| | 312 | |
| | 313 | |
| | 314 | OUTPUT: |
| | 315 | |
| | 316 | Returns True if `L` is a valid vertex ordering for `G`, and False |
| | 317 | oterwise. |
| | 318 | |
| | 319 | |
| | 320 | EXAMPLE: |
| | 321 | |
| | 322 | Path decomposition of a cycle:: |
| | 323 | |
| | 324 | sage: from sage.graphs.graph_decompositions.linear_ordering import LinearOrdering |
| | 325 | sage: LO = LinearOrdering() |
| | 326 | sage: G = graphs.CycleGraph(6) |
| | 327 | sage: L = [u for u in G.vertices()] |
| | 328 | sage: LO.is_valid(G, L) |
| | 329 | True |
| | 330 | sage: LO.is_valid(G, [1,2]) |
| | 331 | False |
| | 332 | |
| | 333 | TEST: |
| | 334 | |
| | 335 | Giving anything else than a Graph or a DiGraph:: |
| | 336 | |
| | 337 | sage: from sage.graphs.graph_decompositions.linear_ordering import LinearOrdering |
| | 338 | sage: LO = LinearOrdering() |
| | 339 | sage: LO.is_valid(2, []) |
| | 340 | Traceback (most recent call last): |
| | 341 | ... |
| | 342 | ValueError: The input parameter must be a Graph or a DiGraph. |
| | 343 | """ |
| | 344 | from sage.graphs.graph import Graph |
| | 345 | from sage.graphs.digraph import DiGraph |
| | 346 | if not isinstance(G, Graph) and not isinstance(G, DiGraph): |
| | 347 | raise ValueError("The input parameter must be a Graph or a DiGraph.") |
| | 348 | |
| | 349 | A = set( G.vertices() ) |
| | 350 | B = set( L ) |
| | 351 | return len( A.symmetric_difference(B) ) == 0 |
| | 352 | |
| | 353 | |
| | 354 | ############################################################################ |
| | 355 | # Front end function for the evaluation of linear vertex orderings with # |
| | 356 | # different widths # |
| | 357 | ############################################################################ |
| | 358 | |
| | 359 | def width_of(self, G, L, width = 'vertex_separation'): |
| | 360 | r""" |
| | 361 | Returns the <width> of the linear ordering `L` for `G`. |
| | 362 | |
| | 363 | This function returns the <width> of the vertex ordering L, where |
| | 364 | width is one of the following values |
| | 365 | |
| | 366 | +--------------+----------------------------+ |
| | 367 | | short name | long name | |
| | 368 | +==============+============================+ |
| | 369 | | vs | vertex_separation | |
| | 370 | +--------------+----------------------------+ |
| | 371 | | pw | pathwidth | |
| | 372 | +--------------+----------------------------+ |
| | 373 | | tw | treewidth | |
| | 374 | +--------------+----------------------------+ |
| | 375 | | bw | bandwidth | |
| | 376 | +--------------+----------------------------+ |
| | 377 | | pn | process_number | |
| | 378 | +--------------+----------------------------+ |
| | 379 | | cw | cutwidth | |
| | 380 | +--------------+----------------------------+ |
| | 381 | | mcw | modified_cutwidth | |
| | 382 | +--------------+----------------------------+ |
| | 383 | | ola | optimal_linear_arrangement | |
| | 384 | +--------------+----------------------------+ |
| | 385 | | sc | sum_cut | |
| | 386 | +--------------+----------------------------+ |
| | 387 | | mfi | minimun_fill_in | |
| | 388 | +--------------+----------------------------+ |
| | 389 | |
| | 390 | See the module's documentation for more details on these widths. |
| | 391 | |
| | 392 | INPUT: |
| | 393 | |
| | 394 | - ``G`` -- a graph or a digraph (possibly with multi-edges) |
| | 395 | |
| | 396 | - ``L`` -- an ordering of the vertices of ``G`` |
| | 397 | |
| | 398 | - ``width`` -- is the graph invariant to consider (vertex_separation by |
| | 399 | default) |
| | 400 | |
| | 401 | |
| | 402 | OUTPUT: |
| | 403 | |
| | 404 | The value of the measured graph invariants for this vertex ordering. |
| | 405 | |
| | 406 | |
| | 407 | NOTES: |
| | 408 | |
| | 409 | All width are defined for undirected graphs. |
| | 410 | |
| | 411 | For directed graphs, only vertex_separation, bandwidth, |
| | 412 | process_number, cutwidth, modified_cutwidth, |
| | 413 | optimal_linear_arrangement, and sum_cut are defined. Measurements |
| | 414 | are performed using arcs from u=L[i] to v=L[j], with i<=j. |
| | 415 | |
| | 416 | EXAMPLE: |
| | 417 | |
| | 418 | Path decomposition of a cycle:: |
| | 419 | |
| | 420 | sage: from sage.graphs.graph_decompositions.linear_ordering import LinearOrdering |
| | 421 | sage: LO = LinearOrdering() |
| | 422 | sage: G = graphs.CycleGraph(6) |
| | 423 | sage: LO.width_of(G, [0, 1, 2, 3, 4, 5], 'pw') |
| | 424 | 2 |
| | 425 | sage: LO.width_of(G, [0, 2, 4, 1, 5, 3], 'pw') |
| | 426 | 3 |
| | 427 | |
| | 428 | TEST: |
| | 429 | |
| | 430 | Giving anything else than a Graph or a DiGraph:: |
| | 431 | |
| | 432 | sage: from sage.graphs.graph_decompositions.linear_ordering import LinearOrdering |
| | 433 | sage: LO = LinearOrdering() |
| | 434 | sage: LO.width_of(2, [], 'pw') |
| | 435 | Traceback (most recent call last): |
| | 436 | ... |
| | 437 | ValueError: The input parameter must be a Graph or a DiGraph. |
| | 438 | |
| | 439 | Giving a vertex ordering on a different set of vertices:: |
| | 440 | |
| | 441 | sage: from sage.graphs.graph_decompositions.linear_ordering import LinearOrdering |
| | 442 | sage: LO = LinearOrdering() |
| | 443 | sage: G = graphs.CycleGraph(6) |
| | 444 | sage: LO.width_of(G, [1, 2, 3], 'pw') |
| | 445 | Traceback (most recent call last): |
| | 446 | ... |
| | 447 | ValueError: The input parameter is not a valid vertex ordering. |
| | 448 | |
| | 449 | Giving a not implemented width parameter:: |
| | 450 | |
| | 451 | sage: from sage.graphs.graph_decompositions.linear_ordering import LinearOrdering |
| | 452 | sage: LO = LinearOrdering() |
| | 453 | sage: G = graphs.CycleGraph(6) |
| | 454 | sage: L = [u for u in G.vertices()] |
| | 455 | sage: LO.width_of(G, L, 'frite') |
| | 456 | Traceback (most recent call last): |
| | 457 | ... |
| | 458 | ValueError: The desired width evaluation function has not been implemented so far. Feel free to add it. |
| | 459 | """ |
| | 460 | from sage.graphs.graph import Graph |
| | 461 | from sage.graphs.digraph import DiGraph |
| | 462 | if not isinstance(G, Graph) and not isinstance(G, DiGraph): |
| | 463 | raise ValueError("The input parameter must be a Graph or a DiGraph.") |
| | 464 | |
| | 465 | if not self.is_valid(G, L): |
| | 466 | raise ValueError("The input parameter is not a valid vertex ordering.") |
| | 467 | |
| | 468 | if width in ['pathwidth','pw']: |
| | 469 | return self.width_of_path_decomposition(G, L) |
| | 470 | elif width in ['vertex_separation','vs']: |
| | 471 | return self.width_of_vertex_separation(G, L) |
| | 472 | elif width in ['process_number','pn']: |
| | 473 | return self.width_of_process_number(G, L) |
| | 474 | elif width in ['treewidth','tw']: |
| | 475 | return self.width_of_tree_decomposition(G, L) |
| | 476 | elif width in ['cutwidth','cw']: |
| | 477 | return self.width_of_cutwidth(G, L) |
| | 478 | elif width in ['modified_cutwidth','mcw']: |
| | 479 | return self.width_of_modified_cutwidth(G, L) |
| | 480 | elif width in ['bandwidth','bw']: |
| | 481 | return self.width_of_bandwidth(G, L) |
| | 482 | elif width in ['sum_cut','sc']: |
| | 483 | return self.width_of_sum_cut(G, L) |
| | 484 | elif width in ['minimum_fill_in','mfi']: |
| | 485 | return self.width_of_fill_in(G, L) |
| | 486 | elif width in ['optimal_linear_arrangement','ola']: |
| | 487 | return self.width_of_linear_arrangement(G, L) |
| | 488 | else: |
| | 489 | raise ValueError("The desired width evaluation function has not been implemented so far. Feel free to add it.") |
| | 490 | |
| | 491 | |
| | 492 | |
| | 493 | |
| | 494 | ############################################### |
| | 495 | # Methods for pathwidth and vertex separation # |
| | 496 | ############################################### |
| | 497 | |
| | 498 | def width_of_path_decomposition(self, G, L): |
| | 499 | r""" |
| | 500 | Returns the value `pw_L(G)` of the path decomposition induced by the |
| | 501 | vertex ordering `L` for `G`, where |
| | 502 | |
| | 503 | ..MATH:: |
| | 504 | |
| | 505 | pw_L(G) = \max_{0\leq i< |V|-1} | N(L[:i])\setminus L[:i] | |
| | 506 | |
| | 507 | INPUT: |
| | 508 | |
| | 509 | - ``G`` -- a Graph |
| | 510 | |
| | 511 | - ``L`` -- an ordering of the vertices of ``G`` |
| | 512 | |
| | 513 | |
| | 514 | OUTPUT: |
| | 515 | |
| | 516 | The value ``pw_L(G)`` of the path decomposition induced by the ordering |
| | 517 | `L` of the vertices of `G`. |
| | 518 | |
| | 519 | EXAMPLE: |
| | 520 | |
| | 521 | Path decomposition of a cycle:: |
| | 522 | |
| | 523 | sage: from sage.graphs.graph_decompositions.linear_ordering import LinearOrdering |
| | 524 | sage: LO = LinearOrdering() |
| | 525 | sage: G = graphs.CycleGraph(6) |
| | 526 | sage: LO.width_of_path_decomposition(G, [0, 1, 2, 3, 4, 5]) |
| | 527 | 2 |
| | 528 | sage: LO.width_of_path_decomposition(G, [0, 2, 4, 1, 5, 3]) |
| | 529 | 3 |
| | 530 | |
| | 531 | TEST: |
| | 532 | |
| | 533 | Giving anything else than a Graph:: |
| | 534 | |
| | 535 | sage: from sage.graphs.graph_decompositions.linear_ordering import LinearOrdering |
| | 536 | sage: LO = LinearOrdering() |
| | 537 | sage: G = digraphs.Circuit(4) |
| | 538 | sage: LO.width_of_path_decomposition(G, []) |
| | 539 | Traceback (most recent call last): |
| | 540 | ... |
| | 541 | ValueError: The input parameter must be a Graph. |
| | 542 | |
| | 543 | Giving a vertex ordering on a different set of vertices:: |
| | 544 | |
| | 545 | sage: from sage.graphs.graph_decompositions.linear_ordering import LinearOrdering |
| | 546 | sage: LO = LinearOrdering() |
| | 547 | sage: G = graphs.CycleGraph(6) |
| | 548 | sage: LO.width_of_path_decomposition(G, [1, 2, 3]) |
| | 549 | Traceback (most recent call last): |
| | 550 | ... |
| | 551 | ValueError: The input parameter is not a valid vertex ordering. |
| | 552 | """ |
| | 553 | # Path decompositions are defined only for graphs |
| | 554 | from sage.graphs.graph import Graph |
| | 555 | if not isinstance(G, Graph): |
| | 556 | raise ValueError("The input parameter must be a Graph.") |
| | 557 | |
| | 558 | if not self.is_valid(G, L): |
| | 559 | raise ValueError("The input parameter is not a valid vertex ordering.") |
| | 560 | |
| | 561 | pwL = 0 |
| | 562 | S = [] |
| | 563 | neighbors_of_S_in_V_minus_S = [] |
| | 564 | |
| | 565 | for u in L: |
| | 566 | |
| | 567 | # We remove u from the list of neighbors of S |
| | 568 | if u in neighbors_of_S_in_V_minus_S: |
| | 569 | neighbors_of_S_in_V_minus_S.remove(u) |
| | 570 | |
| | 571 | # We add vertex u to the set S |
| | 572 | S += [u] |
| | 573 | |
| | 574 | # We add the neighbors of u to the list of neighbors of S |
| | 575 | for v in G.neighbors(u): |
| | 576 | if (not v in S) and (not v in neighbors_of_S_in_V_minus_S): |
| | 577 | neighbors_of_S_in_V_minus_S += [v] |
| | 578 | |
| | 579 | # We update the cost of the path decomposition |
| | 580 | pwL = max( pwL, len(neighbors_of_S_in_V_minus_S) ) |
| | 581 | |
| | 582 | return pwL |
| | 583 | |
| | 584 | |
| | 585 | def width_of_vertex_separation(self, G, L): |
| | 586 | r""" |
| | 587 | Returns the value `vs_L(G)` of the vertex separation induced by the |
| | 588 | vertex ordering `L` for `G`, where |
| | 589 | |
| | 590 | ..MATH:: |
| | 591 | |
| | 592 | vs_L(G) = \max_{0\leq i< |V|-1} | N^+(L[:i])\setminus L[:i] | |
| | 593 | |
| | 594 | INPUT: |
| | 595 | |
| | 596 | - ``G`` -- a graph or a digraph |
| | 597 | |
| | 598 | - ``L`` -- an ordering of the vertices of ``G`` |
| | 599 | |
| | 600 | |
| | 601 | OUTPUT: |
| | 602 | |
| | 603 | The value ``vs_L(G)`` of the vertex separation induced by the ordering |
| | 604 | `L` of the vertices of `G`. |
| | 605 | |
| | 606 | NOTES: |
| | 607 | |
| | 608 | The vertex separation is defined for both graphs and digraphs. The |
| | 609 | vertex separation of a graph is also the cost of the path |
| | 610 | decomposition induced by the vertex ordering (see the module's |
| | 611 | documentation). |
| | 612 | |
| | 613 | EXAMPLE: |
| | 614 | |
| | 615 | Vertex separation of a cycle:: |
| | 616 | |
| | 617 | sage: from sage.graphs.graph_decompositions.linear_ordering import LinearOrdering |
| | 618 | sage: LO = LinearOrdering() |
| | 619 | sage: G = graphs.CycleGraph(6) |
| | 620 | sage: L = [u for u in G.vertices()] |
| | 621 | sage: LO.width_of_vertex_separation(G, L) |
| | 622 | 2 |
| | 623 | |
| | 624 | Vertex separation of a cicuit:: |
| | 625 | |
| | 626 | sage: from sage.graphs.graph_decompositions.linear_ordering import LinearOrdering |
| | 627 | sage: LO = LinearOrdering() |
| | 628 | sage: G = digraphs.Circuit(6) |
| | 629 | sage: L = [u for u in G.vertices()] |
| | 630 | sage: LO.width_of_vertex_separation(G, L) |
| | 631 | 1 |
| | 632 | |
| | 633 | |
| | 634 | TEST: |
| | 635 | |
| | 636 | Giving anything else than a Graph or a DiGraph:: |
| | 637 | |
| | 638 | sage: from sage.graphs.graph_decompositions.linear_ordering import LinearOrdering |
| | 639 | sage: LO = LinearOrdering() |
| | 640 | sage: LO.width_of_vertex_separation(2, []) |
| | 641 | Traceback (most recent call last): |
| | 642 | ... |
| | 643 | ValueError: The input parameter must be a Graph or a DiGraph. |
| | 644 | |
| | 645 | Giving a vertex ordering on a different set of vertices:: |
| | 646 | |
| | 647 | sage: from sage.graphs.graph_decompositions.linear_ordering import LinearOrdering |
| | 648 | sage: LO = LinearOrdering() |
| | 649 | sage: G = digraphs.Circuit(6) |
| | 650 | sage: LO.width_of_vertex_separation(G, [1, 2, 3]) |
| | 651 | Traceback (most recent call last): |
| | 652 | ... |
| | 653 | ValueError: The input parameter is not a valid vertex ordering. |
| | 654 | """ |
| | 655 | # If G is a graph, we instead evaluate the path decomposition |
| | 656 | from sage.graphs.graph import Graph |
| | 657 | if isinstance(G, Graph): |
| | 658 | return self.width_of_path_decomposition(G, L) |
| | 659 | |
| | 660 | if not self.is_valid(G, L): |
| | 661 | raise ValueError("The input parameter is not a valid vertex ordering.") |
| | 662 | |
| | 663 | vsL = 0 |
| | 664 | S = [] |
| | 665 | neighbors_of_S_in_V_minus_S = [] |
| | 666 | |
| | 667 | for u in L: |
| | 668 | |
| | 669 | # We remove u from the list of neighbors of S |
| | 670 | if u in neighbors_of_S_in_V_minus_S: |
| | 671 | neighbors_of_S_in_V_minus_S.remove(u) |
| | 672 | |
| | 673 | # We add vertex u to the set S |
| | 674 | S += [u] |
| | 675 | |
| | 676 | # We add the out-neighbors of u to the list of neighbors of S |
| | 677 | for v in G.neighbors_out(u): |
| | 678 | if (not v in S) and (not v in neighbors_of_S_in_V_minus_S): |
| | 679 | neighbors_of_S_in_V_minus_S += [v] |
| | 680 | |
| | 681 | # We update the cost of the vertex separation |
| | 682 | vsL = max( vsL, len(neighbors_of_S_in_V_minus_S) ) |
| | 683 | |
| | 684 | return vsL |
| | 685 | |
| | 686 | |
| | 687 | def __vertex_separation_MILP__(self, D, integrality = False, verbosity = 0): |
| | 688 | r""" |
| | 689 | Implements an MILP for the vertex separation |
| | 690 | |
| | 691 | INPUTS: |
| | 692 | |
| | 693 | - ``D`` -- a digraph |
| | 694 | |
| | 695 | OUTPUT: |
| | 696 | |
| | 697 | A pair ``(cost, ordering)`` representing the optimal ordering of the |
| | 698 | vertices and its cost. |
| | 699 | |
| | 700 | |
| | 701 | VARIABLES: |
| | 702 | |
| | 703 | x[v,t] = 1 if and only if the working lightpath of connection v is torn down at epoch t |
| | 704 | So if an agent is put on vertex v at step t |
| | 705 | |
| | 706 | y[v,t] = 1 if and only if the new lightpath of connection v is set up at epoch t |
| | 707 | So if v is processed at step t |
| | 708 | |
| | 709 | u[v,t] = 1 if and only if the connection corresponding to v is disrupted at epoch t |
| | 710 | So if there is an agent on v at step t |
| | 711 | |
| | 712 | z Objective = max number of concurrently disrupted connections |
| | 713 | |
| | 714 | CONSTANT: |
| | 715 | T maximum number of epoch/steps. T <= 2.N |
| | 716 | |
| | 717 | TERMINOLOGY: |
| | 718 | An epoch is the period of time between the establishment of 2 new lightpaths |
| | 719 | So between the processing of 2 nodes |
| | 720 | => 1 epoch per connection => #epochs = T = N |
| | 721 | |
| | 722 | CONSTRAINTS: |
| | 723 | (1) Minimize z |
| | 724 | (2) x[v][t] <= x[v][t+1] for all v in V, and for t:=0..T-2 |
| | 725 | (3) y[v][t] <= y[v][t+1] for all v in V, and for t:=0..T-2 |
| | 726 | (miss) y[v][t] <= x[v][t] for all v in V, and for all t:=0..T-1 |
| | 727 | to ensure that once a vertex is processed, it has already been covered.... |
| | 728 | (4) y[v][t] <= x[w][t] for all v in V, for all w in N^+(v), and for all t:=0..T-1 |
| | 729 | (5) sum_{v in V} y[v][0] <= 1 |
| | 730 | (6) sum_{v in V} y[v][t+1] <= sum_{v in V} y[v][t] + 1 for t:=0..T-2 |
| | 731 | (7) sum_{v in V} y[v][T-1] = |V| |
| | 732 | (8) u[v][t] >= x[v][t]-y[v][t] for all v in V, and for all t:=0..T-1 |
| | 733 | (9) z >= sum_{v in V} u[v][t] for all t:=0..T-1 |
| | 734 | (10) 0 <= x[v][t] and u[v][t] <= 1 |
| | 735 | Meaning both are between 0 and 1 |
| | 736 | (11) y[v][t] in {0,1} |
| | 737 | (12) 0 <= z <= |V| |
| | 738 | """ |
| | 739 | from sage.numerical.mip import MixedIntegerLinearProgram, Sum, MIPSolverException |
| | 740 | p = MixedIntegerLinearProgram( maximization = False ) |
| | 741 | |
| | 742 | if integrality: |
| | 743 | x = p.new_variable( integer = True, dim = 2 ) |
| | 744 | u = p.new_variable( integer = True, dim = 2 ) |
| | 745 | else: |
| | 746 | x = p.new_variable( dim = 2 ) |
| | 747 | u = p.new_variable( dim = 2 ) |
| | 748 | y = p.new_variable( integer = True, dim = 2 ) |
| | 749 | z = p.new_variable( integer = True, dim = 1 ) |
| | 750 | |
| | 751 | N = D.num_verts() |
| | 752 | T=N |
| | 753 | |
| | 754 | # (2) x[v][t] <= x[v][t+1] for all v in V, and for t:=0..T-2 |
| | 755 | # (3) y[v][t] <= y[v][t+1] for all v in V, and for t:=0..T-2 |
| | 756 | for v in D.vertices(): |
| | 757 | for t in xrange(T-1): |
| | 758 | p.add_constraint( x[v][t] - x[v][t+1], max = 0 ) |
| | 759 | p.add_constraint( y[v][t] - y[v][t+1], max = 0 ) |
| | 760 | |
| | 761 | # (miss) y[v][t] <= x[v][t] for all v in V, and for all t:=0..T-1 |
| | 762 | # for v in D.vertices(): |
| | 763 | # for t in range(T): |
| | 764 | # p.add_constraint(y[v][t]-x[v][t],max=0) |
| | 765 | # |
| | 766 | # (4) y[v][t] <= x[w][t] for all v in V, for all w in N^+(v), and for all t:=0..T-1 |
| | 767 | for v in D.vertices(): |
| | 768 | for w in D.neighbors_out(v): |
| | 769 | for t in xrange(T): |
| | 770 | p.add_constraint( y[v][t] - x[w][t], max = 0 ) |
| | 771 | |
| | 772 | # (5) sum_{v in V} y[v][0] <= 1 |
| | 773 | p.add_constraint( Sum( y[v][0] for v in D.vertices() ), max = 1 ) |
| | 774 | |
| | 775 | # (6) sum_{v in V} y[v][t+1] <= sum_{v in V} y[v][t] + 1 for t:=0..T-2 |
| | 776 | for t in xrange(T-1): |
| | 777 | p.add_constraint( Sum( y[v][t+1] - y[v][t] for v in D.vertices() ), max = 1 ) |
| | 778 | |
| | 779 | # (7) sum_{v in V} y[v][T-1] = |V| |
| | 780 | p.add_constraint( Sum( y[v][T-1] for v in D.vertices() ), min = N ) |
| | 781 | p.add_constraint( Sum( y[v][T-1] for v in D.vertices() ), max = N ) |
| | 782 | |
| | 783 | # (8) u[v][t] >= x[v][t]-y[v][t] for all v in V, and for all t:=0..T-1 |
| | 784 | for v in D.vertices(): |
| | 785 | for t in xrange(T): |
| | 786 | p.add_constraint( x[v][t] - y[v][t] - u[v][t], max = 0 ) |
| | 787 | |
| | 788 | # (9) z >= sum_{v in V} u[v][t] for all t:=0..T-1 |
| | 789 | for t in xrange(T): |
| | 790 | p.add_constraint( Sum( u[v][t] for v in D.vertices() ) - z['z'], max = 0 ) |
| | 791 | |
| | 792 | # (10) 0 <= x[v][t] and u[v][t] <= 1 |
| | 793 | # (11) y[v][t] in {0,1} |
| | 794 | for v in D.vertices(): |
| | 795 | for t in xrange(T): |
| | 796 | p.add_constraint( x[v][t], min = 0 ) |
| | 797 | p.add_constraint( x[v][t], max = 1 ) |
| | 798 | p.add_constraint( u[v][t], min = 0 ) |
| | 799 | p.add_constraint( u[v][t], max = 1 ) |
| | 800 | p.set_binary( y[v][t] ) |
| | 801 | # (12) 0 <= z <= |V| |
| | 802 | p.add_constraint( z['z'], min = 0 ) |
| | 803 | p.add_constraint( z['z'], max = N ) |
| | 804 | |
| | 805 | # (1) Minimize z |
| | 806 | p.set_objective( z['z'] ) |
| | 807 | |
| | 808 | try: |
| | 809 | obj = p.solve( log=verbosity ) |
| | 810 | |
| | 811 | taby = p.get_values( y ) |
| | 812 | tabz = p.get_values( z ) |
| | 813 | # since exactly one vertex is processed per epoch, we can reconstruct the sequence |
| | 814 | seq = [] |
| | 815 | for t in xrange(T): |
| | 816 | for v in D.vertices(): |
| | 817 | if (taby[v][t] > 0) and (not v in seq): |
| | 818 | seq += [v] |
| | 819 | vs = int(round( tabz['z'] )); |
| | 820 | |
| | 821 | except MIPSolverException: |
| | 822 | print "VSC NOT working with fractional stuff\n",D.edges(labels=False) |
| | 823 | print " => try with integral version" |
| | 824 | (vs,seq)=self.__vertex_separation_MILP__(D, True, verbosity) |
| | 825 | print vs,seq |
| | 826 | raise ValueError("Unbounded or unexpected error") |
| | 827 | |
| | 828 | del p |
| | 829 | return vs,seq; |
| | 830 | |
| | 831 | |
| | 832 | |
| | 833 | |
| | 834 | |
| | 835 | def vertex_separation(self, G, method = 'exp'): |
| | 836 | r""" |
| | 837 | Returns an optimal ordering of the vertices and its cost for |
| | 838 | vertex-separation. |
| | 839 | |
| | 840 | INPUT: |
| | 841 | |
| | 842 | - ``G`` -- a digraph |
| | 843 | |
| | 844 | - ``method`` (string) -- the method to use |
| | 845 | |
| | 846 | - 'exp' (default) -- Uses an algorithm with time and space |
| | 847 | complexity in ``2^n``. Because of its current implementation, this |
| | 848 | algorithm only works on graphs on less than 32 vertices. This can |
| | 849 | be changed to 54 if necessary, but 32 vertices already require 4GB |
| | 850 | of memory. |
| | 851 | |
| | 852 | - 'MILP' -- Uses an MILP implementation. |
| | 853 | |
| | 854 | OUTPUT: |
| | 855 | |
| | 856 | A pair ``(cost, ordering)`` representing the optimal ordering of the |
| | 857 | vertices and its cost. |
| | 858 | |
| | 859 | EXAMPLE: |
| | 860 | |
| | 861 | The vertex separation of a circuit is equal to 1:: |
| | 862 | |
| | 863 | sage: from sage.graphs.graph_decompositions.linear_ordering import LinearOrdering |
| | 864 | sage: LO = LinearOrdering() |
| | 865 | sage: g = digraphs.Circuit(6) |
| | 866 | sage: LO.vertex_separation(g) |
| | 867 | (1, [0, 1, 2, 3, 4, 5]) |
| | 868 | |
| | 869 | TEST: |
| | 870 | |
| | 871 | Given anything else than a DiGraph:: |
| | 872 | |
| | 873 | sage: from sage.graphs.graph_decompositions.linear_ordering import LinearOrdering |
| | 874 | sage: LO = LinearOrdering() |
| | 875 | sage: g = graphs.CycleGraph(6) |
| | 876 | sage: LO.vertex_separation(g) |
| | 877 | Traceback (most recent call last): |
| | 878 | ... |
| | 879 | ValueError: The input parameter must be a DiGraph. |
| | 880 | """ |
| | 881 | from sage.graphs.graph import DiGraph |
| | 882 | if not isinstance(G, DiGraph): |
| | 883 | raise ValueError("The input parameter must be a DiGraph.") |
| | 884 | |
| | 885 | if method == 'MILP': |
| | 886 | return self.__vertex_separation_MILP__(G) |
| | 887 | else: |
| | 888 | # default method |
| | 889 | from sage.graphs.graph_decompositions import vertex_separation |
| | 890 | return vertex_separation.vertex_separation(G) |
| | 891 | |
| | 892 | def path_decomposition(self, G, method = 'exp'): |
| | 893 | r""" |
| | 894 | Returns the pathwidth of the given graph and the ordering of the |
| | 895 | vertices resulting in a corresponding path decomposition. |
| | 896 | |
| | 897 | INPUT: |
| | 898 | |
| | 899 | - ``G`` -- a graph |
| | 900 | |
| | 901 | - ``method`` (string) -- the method to use |
| | 902 | |
| | 903 | - 'exp' (default) -- Uses an algorithm with time and space |
| | 904 | complexity in ``2^n``. Because of its current implementation, this |
| | 905 | algorithm only works on graphs on less than 32 vertices. This can |
| | 906 | be changed to 54 if necessary, but 32 vertices already require 4GB |
| | 907 | of memory. |
| | 908 | |
| | 909 | - 'MILP' -- Uses an MILP implementation. |
| | 910 | |
| | 911 | OUTPUT: |
| | 912 | |
| | 913 | A pair ``(cost, ordering)`` representing the optimal ordering of the |
| | 914 | vertices and its cost. |
| | 915 | |
| | 916 | EXAMPLE: |
| | 917 | |
| | 918 | The pathwidth of a circuit is equal to 2:: |
| | 919 | |
| | 920 | sage: from sage.graphs.graph_decompositions.linear_ordering import LinearOrdering |
| | 921 | sage: LO = LinearOrdering() |
| | 922 | sage: g = graphs.CycleGraph(6) |
| | 923 | sage: LO.path_decomposition(g) |
| | 924 | (2, [0, 1, 2, 3, 4, 5]) |
| | 925 | |
| | 926 | TEST: |
| | 927 | |
| | 928 | Given anything else than a Graph:: |
| | 929 | |
| | 930 | sage: from sage.graphs.graph_decompositions.linear_ordering import LinearOrdering |
| | 931 | sage: LO = LinearOrdering() |
| | 932 | sage: g = digraphs.Circuit(6) |
| | 933 | sage: LO.path_decomposition(g) |
| | 934 | Traceback (most recent call last): |
| | 935 | ... |
| | 936 | ValueError: The input parameter must be a Graph. |
| | 937 | """ |
| | 938 | from sage.graphs.graph import Graph |
| | 939 | if not isinstance(G, Graph): |
| | 940 | raise ValueError("The input parameter must be a Graph.") |
| | 941 | |
| | 942 | if method == 'MILP': |
| | 943 | from sage.graphs.graph import DiGraph |
| | 944 | D = DiGraph( G.edges(labels = None) + [(v,u) for u,v in G.edges(labels = None)] ) |
| | 945 | return self.__vertex_separation_MILP__(D) |
| | 946 | else: |
| | 947 | # default method |
| | 948 | from sage.graphs.graph_decompositions import vertex_separation |
| | 949 | return vertex_separation.path_decomposition(G) |
| | 950 | |
| | 951 | |