Ticket #7527: trac_7527-reviewer.patch
File trac_7527-reviewer.patch, 10.5 KB (added by , 11 years ago) |
---|
-
sage/graphs/graph_coloring.py
# HG changeset patch # User Minh Van Nguyen <nguyenminh2@gmail.com> # Date 1260298074 28800 # Node ID 81b01e7a0099b3e9d9b920bae9e12352500b8c60 # Parent 8ca939870ef50e8f8ec3d94a0b9508d32489867c trac 7527: graph colouring: reviewer patch diff -r 8ca939870ef5 -r 81b01e7a0099 sage/graphs/graph_coloring.py
a b 1 1 """ 2 Graph Coloring Functions2 Graph Coloring 3 3 4 4 AUTHORS: 5 5 … … 28 28 into an implementation of the Dancing Links algorithm described 29 29 by Knuth (who attributes the idea to Hitotumatu and Noshita). 30 30 31 The construction works as follows: 32 (columns) 33 * The first `|V|` columns correspond to a vertex -- a `1` in this 34 column indicates that that vertex has a color. 35 * After those `|V|` columns, we add `n*|E|` columns -- a `1` in 36 these columns indicate that a particular edge is 37 incident to a vertex with a certain color. 31 The construction works as follows. Columns: 38 32 39 (rows) 40 * For each vertex, add `n` rows; one for each color `c`. Place 41 a `1` in the column corresponding to the vertex, and a `1` 42 in the appropriate column for each edge incident to the 43 vertex, indicating that that edge is incident to the 44 color `c`. 45 * If `n > 2`, the above construction cannot be exactly covered 46 since each edge will be incident to only two vertices 47 (and hence two colors) - so we add `n*|E|` rows, each one 48 containing a `1` for each of the `n*|E|` columns. These 49 get added to the cover solutions "for free" during the 50 backtracking. 33 * The first `|V|` columns correspond to a vertex -- a `1` in this 34 column indicates that that vertex has a color. 35 36 * After those `|V|` columns, we add `n*|E|` columns -- a `1` in 37 these columns indicate that a particular edge is 38 incident to a vertex with a certain color. 39 40 Rows: 41 42 * For each vertex, add `n` rows; one for each color `c`. Place 43 a `1` in the column corresponding to the vertex, and a `1` 44 in the appropriate column for each edge incident to the 45 vertex, indicating that that edge is incident to the 46 color `c`. 47 48 * If `n > 2`, the above construction cannot be exactly covered 49 since each edge will be incident to only two vertices 50 (and hence two colors) - so we add `n*|E|` rows, each one 51 containing a `1` for each of the `n*|E|` columns. These 52 get added to the cover solutions "for free" during the 53 backtracking. 51 54 52 55 Note that this construction results in `n*|V| + 2*n*|E| + n*|E|` 53 56 entries in the matrix. The Dancing Links algorithm uses a 54 57 sparse representation, so if the graph is simple, `|E| \leq |V|^2` 55 58 and `n <= |V|`, this construction runs in `O(|V|^3)` time. 56 59 Back-conversion to a coloring solution is a simple scan of the 57 solutions, which will contain `|V| + (n-2)*|E|` entries, so 60 solutions, which will contain `|V| + (n-2)*|E|` entries, so 58 61 runs in `O(|V|^3)` time also. For most graphs, the conversion 59 62 will be much faster -- for example, a planar graph will be 60 63 transformed for `4`-coloring in linear time since `|E| = O(|V|)`. 61 64 62 65 REFERENCES: 63 http://www-cs-staff.stanford.edu/~uno/papers/dancing-color.ps.gz 66 67 http://www-cs-staff.stanford.edu/~uno/papers/dancing-color.ps.gz 64 68 65 69 EXAMPLES:: 66 70 … … 225 229 sage: from sage.graphs.graph_coloring import chromatic_number 226 230 sage: G = Graph({0:[1,2,3],1:[2]}) 227 231 sage: chromatic_number(G) 228 3 232 3 229 233 230 234 sage: G = graphs.PetersenGraph() 231 235 sage: G.chromatic_number() 232 236 3 233 234 237 """ 235 238 o = G.order() 236 239 if o == 0: … … 245 248 return m 246 249 for n in range(m,o+1): 247 250 for C in all_graph_colorings(G,n): 248 return n 251 return n 249 252 250 253 def vertex_coloring(g, k=None, value_only=False, hex_colors=False, log=0): 251 254 r""" 252 255 Computes the chromatic number of the given graph or tests its 253 256 `k`-colorability. See http://en.wikipedia.org/wiki/Graph_coloring for 254 257 further details on graph coloring. 255 258 256 259 INPUT: 257 260 258 261 - ``g`` -- a graph. … … 279 282 be no message printed by the solver. 280 283 281 284 OUTPUT: 282 285 283 286 - If ``k=None`` and ``value_only=False``, then return a partition of the 284 287 vertex set into the minimum possible of independent sets. 285 288 286 289 - If ``k=None`` and ``value_only=True``, return the chromatic number. 287 290 288 291 - If ``k`` is set and ``value_only=None``, return ``False`` if the 289 292 graph is not `k`-colorable, and a partition of the vertex set into 290 293 `k` independent sets otherwise. … … 293 296 `k`-colorable, and return ``True`` or ``False`` accordingly. 294 297 295 298 EXAMPLE:: 296 299 297 300 sage: from sage.graphs.graph_coloring import vertex_coloring 298 301 sage: g = graphs.PetersenGraph() 299 302 sage: vertex_coloring(g, value_only=True) # optional - requires GLPK or CBC … … 376 379 return dict(zip(rainbow(k), value)) 377 380 else: 378 381 return value 379 382 380 383 # Degeneracy 381 384 # Vertices whose degree is less than k are of no importance in 382 385 # the coloring. … … 412 415 return dict(zip(rainbow(k), value)) 413 416 else: 414 417 return value 415 418 416 419 p = MixedIntegerLinearProgram(maximization=True) 417 420 color = p.new_variable(dim=2) 418 421 # a vertex has exactly one color 419 422 [p.add_constraint(sum([color[v][i] for i in xrange(k)]), min=1, max=1) 420 423 for v in g.vertices()] 421 424 # adjacent vertices have different colors 422 [p.add_constraint(color[u][i] + color[v][i], max=1) 425 [p.add_constraint(color[u][i] + color[v][i], max=1) 423 426 for (u, v) in g.edge_iterator(labels=None) 424 427 for i in xrange(k)] 425 428 # anything is good as an objective value as long as it is satisfiable … … 439 442 color = p.get_values(color) 440 443 # builds the color classes 441 444 classes = [[] for i in xrange(k)] 442 [classes[i].append(v) 443 for i in xrange(k) 444 for v in g.vertices() 445 [classes[i].append(v) 446 for i in xrange(k) 447 for v in g.vertices() 445 448 if color[v][i] == 1] 446 449 if hex_colors: 447 450 return dict(zip(rainbow(len(classes)), classes)) … … 453 456 Properly colors the edges of a graph. See the URL 454 457 http://en.wikipedia.org/wiki/Edge_coloring for further details on 455 458 edge coloring. 456 459 457 460 INPUT: 458 461 459 462 - ``g`` -- a graph. … … 489 492 490 493 In the following, `\Delta` is equal to the maximum degree in the graph 491 494 ``g``. 492 495 493 496 - If ``vizing=True`` and ``value_only=False``, return a partition of 494 497 the edge set into `\Delta + 1` matchings. 495 498 … … 497 500 498 501 - If ``vizing=False`` and ``value_only=False``, return a partition of 499 502 the edge set into the minimum number of matchings. 500 503 501 504 - If ``vizing=True`` and ``value_only=True``, should return something, 502 505 but mainly you are just trying to compute the maximum degree of the 503 506 graph, and this is not the easiest way. By Vizing's theorem, a graph 504 507 has a chromatic index equal to `\Delta` or to `\Delta + 1`. 505 508 506 509 EXAMPLE:: 507 510 508 511 sage: from sage.graphs.graph_coloring import edge_coloring 509 512 sage: g = graphs.PetersenGraph() 510 513 sage: edge_coloring(g, value_only=True) # optional - requires GLPK or CBC … … 532 535 if hex_colors: 533 536 return zip(rainbow(len(classes)), classes) 534 537 else: 535 return classes 538 return classes 536 539 537 540 p = MixedIntegerLinearProgram(maximization=True) 538 541 color = p.new_variable(dim=2) … … 556 559 e = g.edge_iterator().next() 557 560 p.set_objective(color[R(e)][0]) 558 561 p.set_binary(color) 559 try: 562 try: 560 563 if value_only: 561 564 p.solve(objective_only=True, log=log) 562 565 else: … … 572 575 # Builds the color classes 573 576 color = p.get_values(color) 574 577 classes = [[] for i in xrange(k)] 575 [classes[i].append(e) 576 for e in g.edge_iterator() 577 for i in xrange(k) 578 [classes[i].append(e) 579 for e in g.edge_iterator() 580 for i in xrange(k) 578 581 if color[R(e)][i] == 1] 579 582 # if needed, builds a dictionary from the color classes adding colors 580 583 if hex_colors: … … 632 635 sage: sum([Set([e[2] for e in g.edges_incident(v)]).cardinality() for v in g]) == 2*g.size() 633 636 True 634 637 sage: Set([e[2] for e in g.edge_iterator()]).cardinality() 635 9 638 9 636 639 """ 637 640 if n <= 1: 638 641 raise ValueError("There must be at least two vertices in the graph.") … … 652 655 class Test: 653 656 r""" 654 657 This class performs randomized testing for all_graph_colorings. 655 Since everything else in this file is derived from 658 Since everything else in this file is derived from 656 659 all_graph_colorings, this is a pretty good randomized tester for 657 660 the entire file. Note that for a graph `G`, ``G.chromatic_polynomial()`` 658 661 uses an entirely different algorithm, so we provide a good, … … 661 664 662 665 def random(self,tests = 1000): 663 666 r""" 664 Calls ``self.random_all_graph_colorings()``. In the future, if 667 Calls ``self.random_all_graph_colorings()``. In the future, if 665 668 other methods are added, it should call them, too. 666 669 667 670 TESTS:: 668 671 669 672 sage: from sage.graphs.graph_coloring import Test … … 673 676 674 677 def random_all_graph_colorings(self,tests = 1000): 675 678 r""" 676 Verifies the results of all_graph_colorings in three ways: 677 1) all colorings are unique 678 2) number of m-colorings is `P(m)` (where `P` is the chromatic 679 polynomial of the graph being tested) 680 3) colorings are valid -- that is, that no two vertices of 681 the same color share an edge. 679 Verifies the results of ``all_graph_colorings()`` in three ways: 680 681 #. all colorings are unique 682 683 #. number of m-colorings is `P(m)` (where `P` is the chromatic 684 polynomial of the graph being tested) 685 686 #. colorings are valid -- that is, that no two vertices of 687 the same color share an edge. 682 688 683 689 TESTS:: 684 690