Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
sagemath
GitHub Repository: sagemath/sagelib
Path: blob/master/sage/graphs/graph_coloring.py
4048 views
1
"""
2
Graph coloring
3
4
This module gathers all methods related to graph coloring. Here is what it can
5
do :
6
7
**Proper vertex coloring**
8
9
.. csv-table::
10
:class: contentstable
11
:widths: 30, 70
12
:delim: |
13
14
:meth:`all_graph_colorings` | Computes all `n`-colorings a graph
15
:meth:`first_coloring` | Returns the first vertex coloring found
16
:meth:`number_of_n_colorings` | Computes the number of `n`-colorings of a graph
17
:meth:`numbers_of_colorings` | Computes the number of colorings of a graph
18
:meth:`chromatic_number` | Returns the chromatic number of the graph
19
:meth:`vertex_coloring` | Computes Vertex colorings and chromatic numbers
20
21
22
**Other colorings**
23
24
.. csv-table::
25
:class: contentstable
26
:widths: 30, 70
27
:delim: |
28
29
:meth:`grundy_coloring` | Computes Grundy numbers and Grundy colorings
30
:meth:`b_coloring` | Computes a b-chromatic numbers and b-colorings
31
:meth:`edge_coloring` | Compute chromatic index and edge colorings
32
:meth:`round_robin` | Computes a round-robin coloring of the complete graph on `n` vertices
33
:meth:`linear_arboricity` | Computes the linear arboricity of the given graph
34
:meth:`acyclic_edge_coloring` | Computes an acyclic edge coloring of the current graph
35
36
37
38
AUTHORS:
39
40
- Tom Boothby (2008-02-21): Initial version
41
- Carlo Hamalainen (2009-03-28): minor change: switch to C++ DLX solver
42
- Nathann Cohen (2009-10-24): Coloring methods using linear programming
43
44
Methods
45
-------
46
"""
47
48
#*****************************************************************************
49
# Copyright (C) 2008 Tom Boothby <[email protected]>
50
#
51
# Distributed under the terms of the GNU General Public License (GPL)
52
# http://www.gnu.org/licenses/
53
#*****************************************************************************
54
55
from sage.combinat.matrices.dlxcpp import DLXCPP
56
from sage.plot.colors import rainbow
57
from graph_generators import GraphGenerators
58
59
def all_graph_colorings(G,n,count_only=False):
60
r"""
61
Computes all `n`-colorings of the graph `G` by casting the graph
62
coloring problem into an exact cover problem, and passing this
63
into an implementation of the Dancing Links algorithm described
64
by Knuth (who attributes the idea to Hitotumatu and Noshita).
65
66
The construction works as follows. Columns:
67
68
* The first `|V|` columns correspond to a vertex -- a `1` in this
69
column indicates that that vertex has a color.
70
71
* After those `|V|` columns, we add `n*|E|` columns -- a `1` in
72
these columns indicate that a particular edge is
73
incident to a vertex with a certain color.
74
75
Rows:
76
77
* For each vertex, add `n` rows; one for each color `c`. Place
78
a `1` in the column corresponding to the vertex, and a `1`
79
in the appropriate column for each edge incident to the
80
vertex, indicating that that edge is incident to the
81
color `c`.
82
83
* If `n > 2`, the above construction cannot be exactly covered
84
since each edge will be incident to only two vertices
85
(and hence two colors) - so we add `n*|E|` rows, each one
86
containing a `1` for each of the `n*|E|` columns. These
87
get added to the cover solutions "for free" during the
88
backtracking.
89
90
Note that this construction results in `n*|V| + 2*n*|E| + n*|E|`
91
entries in the matrix. The Dancing Links algorithm uses a
92
sparse representation, so if the graph is simple, `|E| \leq |V|^2`
93
and `n <= |V|`, this construction runs in `O(|V|^3)` time.
94
Back-conversion to a coloring solution is a simple scan of the
95
solutions, which will contain `|V| + (n-2)*|E|` entries, so
96
runs in `O(|V|^3)` time also. For most graphs, the conversion
97
will be much faster -- for example, a planar graph will be
98
transformed for `4`-coloring in linear time since `|E| = O(|V|)`.
99
100
REFERENCES:
101
102
http://www-cs-staff.stanford.edu/~uno/papers/dancing-color.ps.gz
103
104
EXAMPLES::
105
106
sage: from sage.graphs.graph_coloring import all_graph_colorings
107
sage: G = Graph({0:[1,2,3],1:[2]})
108
sage: n = 0
109
sage: for C in all_graph_colorings(G,3):
110
... parts = [C[k] for k in C]
111
... for P in parts:
112
... l = len(P)
113
... for i in range(l):
114
... for j in range(i+1,l):
115
... if G.has_edge(P[i],P[j]):
116
... raise RuntimeError, "Coloring Failed."
117
... n+=1
118
sage: print "G has %s 3-colorings."%n
119
G has 12 3-colorings.
120
121
TESTS::
122
123
sage: G = Graph({0:[1,2,3],1:[2]})
124
sage: for C in all_graph_colorings(G,0): print C
125
sage: for C in all_graph_colorings(G,-1): print C
126
Traceback (most recent call last):
127
...
128
ValueError: n must be non-negative.
129
"""
130
131
if n == 0: return
132
if n < 0: raise ValueError, "n must be non-negative."
133
134
V = G.vertices()
135
E = G.edges()
136
137
nV=len(V)
138
nE=len(E)
139
140
ones = []
141
N = xrange(n)
142
Vd= {}
143
colormap = {}
144
k = 0
145
for i in range(nV):
146
v = V[i]
147
Vd[v] = i
148
for c in N:
149
ones.append([k, [i]])
150
colormap[k] = (v,c)
151
k+=1
152
153
kk = nV
154
for e in E:
155
for c in N:
156
v0 = n*Vd[e[0]]+c
157
v1 = n*Vd[e[1]]+c
158
ones[v0][1].append(kk+c)
159
ones[v1][1].append(kk+c)
160
kk+=n
161
162
if n > 2:
163
for i in range(n*nE):
164
ones.append([k+i, [nV+i]])
165
166
colors = rainbow(n)
167
168
for i in range(len(ones)): ones[i] = ones[i][1]
169
170
try:
171
for a in DLXCPP(ones):
172
if count_only:
173
yield 1
174
continue
175
coloring = {}
176
for x in a:
177
if colormap.has_key(x):
178
v,c = colormap[x]
179
if coloring.has_key(colors[c]):
180
coloring[colors[c]].append(v)
181
else:
182
coloring[colors[c]] = [v]
183
yield coloring
184
except RuntimeError:
185
raise RuntimeError, "Too much recursion! Graph coloring failed."
186
187
def first_coloring(G, n=0, hex_colors=False):
188
r"""
189
Given a graph, and optionally a natural number `n`, returns
190
the first coloring we find with at least `n` colors.
191
192
INPUT:
193
194
- ``hex_colors`` -- (default: ``False``) when set to ``True``, the
195
partition returned is a dictionary whose keys are colors and whose
196
values are the color classes (ideal for plotting).
197
198
- ``n`` -- The minimal number of colors to try.
199
200
EXAMPLES::
201
202
sage: from sage.graphs.graph_coloring import first_coloring
203
sage: G = Graph({0: [1, 2, 3], 1: [2]})
204
sage: first_coloring(G, 3)
205
[[1, 3], [0], [2]]
206
"""
207
o = G.order()
208
for m in xrange(n, o + 1):
209
for C in all_graph_colorings(G, m):
210
if hex_colors:
211
return C
212
else:
213
return C.values()
214
215
def number_of_n_colorings(G,n):
216
r"""
217
Computes the number of `n`-colorings of a graph
218
219
EXAMPLES::
220
221
sage: from sage.graphs.graph_coloring import number_of_n_colorings
222
sage: G = Graph({0:[1,2,3],1:[2]})
223
sage: number_of_n_colorings(G,3)
224
12
225
"""
226
#Take care of the stupid stuff
227
if n == 1:
228
return int(len(G.edges()) == 0)
229
if n < 1:
230
if n == 0:
231
return int(len(G.vertices()) == 0)
232
else:
233
#negative colors?? what does that even mean?
234
return 0
235
236
m = 0
237
for C in all_graph_colorings(G,n,count_only=True):
238
m+=1
239
return m
240
241
def numbers_of_colorings(G):
242
r"""
243
Returns the number of `n`-colorings of the graph `G` for `n` from
244
`0` to `|V|`.
245
246
EXAMPLES::
247
248
sage: from sage.graphs.graph_coloring import numbers_of_colorings
249
sage: G = Graph({0:[1,2,3],1:[2]})
250
sage: numbers_of_colorings(G)
251
[0, 0, 0, 12, 72]
252
"""
253
o = G.order()
254
return [number_of_n_colorings(G,i) for i in range(0,o+1)]
255
256
def chromatic_number(G):
257
r"""
258
Returns the minimal number of colors needed to color the
259
vertices of the graph `G`.
260
261
EXAMPLES::
262
263
sage: from sage.graphs.graph_coloring import chromatic_number
264
sage: G = Graph({0:[1,2,3],1:[2]})
265
sage: chromatic_number(G)
266
3
267
268
sage: G = graphs.PetersenGraph()
269
sage: G.chromatic_number()
270
3
271
"""
272
o = G.order()
273
if o == 0:
274
return 0
275
if len(G.edges()) == 0:
276
return 1
277
elif G.is_bipartite(): #can we do it in linear time?
278
return 2
279
else: #counting cliques is faster than our brute-force method...
280
m = G.clique_number()
281
if m >= o-1: #marginal improvement... if there's an o-1 clique and not an o clique, don't waste our time coloring.
282
return m
283
for n in range(m,o+1):
284
for C in all_graph_colorings(G,n):
285
return n
286
287
from sage.numerical.mip import MIPSolverException
288
289
def vertex_coloring(g, k=None, value_only=False, hex_colors=False, solver = None, verbose = 0):
290
r"""
291
Computes the chromatic number of the given graph or tests its
292
`k`-colorability. See http://en.wikipedia.org/wiki/Graph_coloring for
293
further details on graph coloring.
294
295
INPUT:
296
297
- ``g`` -- a graph.
298
299
- ``k`` -- (default: ``None``) tests whether the graph is `k`-colorable.
300
The function returns a partition of the vertex set in `k` independent
301
sets if possible and ``False`` otherwise.
302
303
- ``value_only`` -- (default: ``False``):
304
305
- When set to ``True``, only the chromatic number is returned.
306
307
- When set to ``False`` (default), a partition of the vertex set into
308
independent sets is returned if possible.
309
310
- ``hex_colors`` -- (default: ``False``) when set to ``True``, the
311
partition returned is a dictionary whose keys are colors and whose
312
values are the color classes (ideal for plotting).
313
314
- ``solver`` -- (default: ``None``) Specify a Linear Program (LP)
315
solver to be used. If set to ``None``, the default one is
316
used. For more information on LP solvers and which default
317
solver is used, see the method :meth:`solve
318
<sage.numerical.mip.MixedIntegerLinearProgram.solve>` of the
319
class :class:`MixedIntegerLinearProgram
320
<sage.numerical.mip.MixedIntegerLinearProgram>`.
321
322
- ``verbose`` -- integer (default: ``0``). Sets the level of
323
verbosity. Set to 0 by default, which means quiet.
324
325
326
OUTPUT:
327
328
- If ``k=None`` and ``value_only=False``, then return a partition of the
329
vertex set into the minimum possible of independent sets.
330
331
- If ``k=None`` and ``value_only=True``, return the chromatic number.
332
333
- If ``k`` is set and ``value_only=None``, return ``False`` if the
334
graph is not `k`-colorable, and a partition of the vertex set into
335
`k` independent sets otherwise.
336
337
- If ``k`` is set and ``value_only=True``, test whether the graph is
338
`k`-colorable, and return ``True`` or ``False`` accordingly.
339
340
EXAMPLE::
341
342
sage: from sage.graphs.graph_coloring import vertex_coloring
343
sage: g = graphs.PetersenGraph()
344
sage: vertex_coloring(g, value_only=True)
345
3
346
"""
347
from sage.numerical.mip import MixedIntegerLinearProgram, Sum
348
from sage.plot.colors import rainbow
349
350
# If k==None, tries to find an optimal coloring
351
if k is None:
352
# No need to start a linear program if the graph is an
353
# independent set or bipartite.
354
# - Independent set
355
if g.size() == 0:
356
if value_only:
357
return 1
358
elif hex_colors:
359
return {rainbow(1)[0]: g.vertices()}
360
else:
361
return g.vertices()
362
# - Bipartite set
363
if g.is_bipartite():
364
if value_only:
365
return 2
366
elif hex_colors:
367
return dict(zip(rainbow(2), g.bipartite_sets()))
368
else:
369
return g.bipartite_sets()
370
371
# - No need to try any k smaller than the maximum clique in
372
# - the graph No need to try k less than |G|/alpha(G), as each
373
# color class is at most alpha(G)
374
# - max, because we know it is not bipartite
375
from math import ceil
376
k = int(max([3, g.clique_number(),ceil(g.order()/len(g.independent_set()))]))
377
378
while True:
379
# tries to color the graph, increasing k each time it fails.
380
tmp = vertex_coloring(g, k=k, value_only=value_only,
381
hex_colors=hex_colors, verbose=verbose)
382
if tmp is not False:
383
if value_only:
384
return k
385
else:
386
return tmp
387
k += 1
388
else:
389
# Is the graph empty?
390
# If the graph is empty, something should be returned.
391
# This is not so stupid, as the graph could be emptied
392
# by the test of degeneracy.
393
if g.order() == 0:
394
if value_only:
395
return True
396
elif hex_colors:
397
return dict([(color, []) for color in rainbow(k)])
398
else:
399
return [[] for i in xrange(k)]
400
# Is the graph connected?
401
# This is not so stupid, as the graph could be disconnected
402
# by the test of degeneracy (as previously).
403
if not g.is_connected():
404
if value_only:
405
for component in g.connected_components():
406
tmp = vertex_coloring(g.subgraph(component), k=k,
407
value_only=value_only,
408
hex_colors=hex_colors,
409
verbose=verbose)
410
if tmp is False:
411
return False
412
return True
413
colorings = []
414
for component in g.connected_components():
415
tmp = vertex_coloring(g.subgraph(component), k=k,
416
value_only=value_only,
417
hex_colors=False, verbose=verbose)
418
if tmp is False:
419
return False
420
colorings.append(tmp)
421
value = [[] for color in xrange(k)]
422
for color in xrange(k):
423
for component in colorings:
424
value[color].extend(component[color])
425
if hex_colors:
426
return dict(zip(rainbow(k), value))
427
else:
428
return value
429
430
# Degeneracy
431
# Vertices whose degree is less than k are of no importance in
432
# the coloring.
433
if min(g.degree()) < k:
434
vertices = set(g.vertices())
435
deg = []
436
tmp = [v for v in vertices if g.degree(v) < k]
437
while len(tmp) > 0:
438
v = tmp.pop(0)
439
neighbors = list(set(g.neighbors(v)) & vertices)
440
if v in vertices and len(neighbors) < k:
441
vertices.remove(v)
442
tmp.extend(neighbors)
443
deg.append(v)
444
if value_only:
445
return vertex_coloring(g.subgraph(list(vertices)), k=k,
446
value_only=value_only,
447
hex_colors=hex_colors,
448
verbose=verbose)
449
value = vertex_coloring(g.subgraph(list(vertices)), k=k,
450
value_only=value_only,
451
hex_colors=False,
452
verbose=verbose)
453
if value is False:
454
return False
455
while len(deg) > 0:
456
for classe in value:
457
if len(list(set(classe) & set(g.neighbors(deg[-1])))) == 0:
458
classe.append(deg[-1])
459
deg.pop(-1)
460
break
461
if hex_colors:
462
return dict(zip(rainbow(k), value))
463
else:
464
return value
465
466
p = MixedIntegerLinearProgram(maximization=True, solver = solver)
467
color = p.new_variable(binary = True)
468
469
# a vertex has exactly one color
470
for v in g.vertices():
471
p.add_constraint(Sum([color[v,i] for i in range(k)]), min=1, max=1)
472
473
# adjacent vertices have different colors
474
for (u, v) in g.edge_iterator(labels=None):
475
for i in xrange(k):
476
p.add_constraint(color[u,i] + color[v,i], max=1)
477
478
# The first vertex is colored with 1. It costs nothing to say
479
# it, and it can help.
480
p.add_constraint(color[g.vertex_iterator().next(),0], max=1, min=1)
481
482
try:
483
if value_only:
484
p.solve(objective_only=True, log=verbose)
485
return True
486
else:
487
chi = p.solve(log=verbose)
488
except MIPSolverException:
489
return False
490
491
color = p.get_values(color)
492
# builds the color classes
493
classes = [[] for i in xrange(k)]
494
495
for v in g.vertices():
496
for i in xrange(k):
497
if color[v,i] == 1:
498
classes[i].append(v)
499
break
500
501
if hex_colors:
502
return dict(zip(rainbow(len(classes)), classes))
503
else:
504
return classes
505
506
def grundy_coloring(g, k, value_only = True, solver = None, verbose = 0):
507
r"""
508
Computes the worst-case of a first-fit coloring with less than `k`
509
colors.
510
511
Definition :
512
513
A first-fit coloring is obtained by sequentially coloring the
514
vertices of a graph, assigning them the smallest color not already
515
assigned to one of its neighbors. The result is clearly a proper
516
coloring, which usually requires much more colors than an optimal
517
vertex coloring of the graph, and heavily depends on the ordering
518
of the vertices.
519
520
The number of colors required by the worst-case application of
521
this algorithm on a graph `G` is called the Grundy number, written
522
`\Gamma (G)`.
523
524
Equivalent formulation :
525
526
Equivalently, a Grundy coloring is a proper vertex coloring such
527
that any vertex colored with `i` has, for every `j<i`, a neighbor
528
colored with `j`. This can define a Linear Program, which is used
529
here to compute the Grundy number of a graph.
530
531
.. NOTE:
532
533
This method computes a grundy coloring using at *MOST* `k`
534
colors. If this method returns a value equal to `k`, it can not
535
be assumed that `k` is equal to `\Gamma(G)`. Meanwhile, if it
536
returns any value `k' < k`, this is a certificate that the
537
Grundy number of the given graph is `k'`.
538
539
As `\Gamma(G)\leq \Delta(G)+1`, it can also be assumed that
540
`\Gamma(G) = k` if ``grundy_coloring(g, k)`` returns `k` when
541
`k = \Delta(G) +1`.
542
543
INPUT:
544
545
- ``k`` (integer) -- Maximum number of colors
546
547
- ``solver`` -- (default: ``None``) Specify a Linear Program (LP)
548
solver to be used. If set to ``None``, the default one is used. For
549
more information on LP solvers and which default solver is used, see
550
the method
551
:meth:`solve <sage.numerical.mip.MixedIntegerLinearProgram.solve>`
552
of the class
553
:class:`MixedIntegerLinearProgram <sage.numerical.mip.MixedIntegerLinearProgram>`.
554
555
- ``value_only`` -- boolean (default: ``True``). When set to
556
``True``, only the number of colors is returned. Otherwise, the
557
pair ``(nb_colors, coloring)`` is returned, where ``coloring``
558
is a dictionary associating its color (integer) to each vertex
559
of the graph.
560
561
- ``verbose`` -- integer (default: ``0``). Sets the level of
562
verbosity. Set to 0 by default, which means quiet.
563
564
ALGORITHM:
565
566
Integer Linear Program.
567
568
EXAMPLES:
569
570
The Grundy number of a `P_4` is equal to 3::
571
572
sage: from sage.graphs.graph_coloring import grundy_coloring
573
sage: g = graphs.PathGraph(4)
574
sage: grundy_coloring(g, 4)
575
3
576
577
The Grundy number of the PetersenGraph is equal to 4::
578
579
sage: g = graphs.PetersenGraph()
580
sage: grundy_coloring(g, 5)
581
4
582
583
It would have been sufficient to set the value of ``k`` to 4 in
584
this case, as `4 = \Delta(G)+1`.
585
"""
586
from sage.numerical.mip import MixedIntegerLinearProgram
587
from sage.numerical.mip import MIPSolverException, Sum
588
589
p = MixedIntegerLinearProgram(solver = solver)
590
591
# List of colors
592
classes = range(k)
593
594
# b[v][i] is set to 1 if and only if v is colored with i
595
b = p.new_variable(dim=2)
596
597
# is_used[i] is set to 1 if and only if color [i] is used by some
598
# vertex
599
is_used = p.new_variable()
600
601
# Each vertex is in exactly one class
602
for v in g:
603
p.add_constraint(Sum( b[v][i] for i in classes ), max = 1, min = 1)
604
605
# Two adjacent vertices have different classes
606
for u,v in g.edges(labels = None):
607
for i in classes:
608
p.add_constraint(b[v][i] + b[u][i], max = 1)
609
610
# The following constraints ensure that if v is colored with i,
611
# then it has a neighbor colored with j for every j<i
612
613
for i in range(k):
614
for j in range(i):
615
for v in g:
616
617
# If b[v][i] == 0, then the following constraint is
618
# always satisfied, as a sum of binary variables is
619
# always positive. If it is equal to 1, then at least
620
# one of fthe other variables must be set to 1 too.
621
622
p.add_constraint( Sum( b[u][j] for u in g.neighbors(v) ) - b[v][i] ,min = 0)
623
624
# is_used[i] can be set to 1 only if the color is used
625
for i in classes:
626
p.add_constraint( Sum( b[v][i] for v in g ) - is_used[i], min = 0)
627
628
# Both variables are binary
629
p.set_binary(b)
630
p.set_binary(is_used)
631
632
# Trying to use as many colors as possible
633
p.set_objective( Sum( is_used[i] for i in classes ) )
634
635
try:
636
obj = p.solve(log = verbose, objective_only = value_only)
637
from sage.rings.integer import Integer
638
obj = Integer(obj)
639
640
except MIPSolverException:
641
raise ValueError("This graph can not be colored with k colors")
642
643
if value_only:
644
return obj
645
646
# Building the dictionary associating its color to every vertex
647
648
b = p.get_values(b)
649
coloring = {}
650
651
for v in g:
652
for i in classes:
653
if b[v][i] == 1:
654
coloring[v] = i
655
break
656
657
return obj, coloring
658
659
660
def b_coloring(g, k, value_only = True, solver = None, verbose = 0):
661
r"""
662
Computes a b-coloring with at most k colors that maximizes the
663
number of colors, if such a coloring exists
664
665
Definition :
666
667
Given a proper coloring of a graph `G` and a color class `C` such
668
that none of its vertices have neighbors in all the other color
669
classes, one can eliminate color class `C` assigning to each of
670
its elements a missing color in its neighborhood.
671
672
Let a b-vertex be a vertex with neighbors in all other colorings.
673
Then, one can repeat the above procedure until a coloring
674
is obtained where every color class contains a b-vertex,
675
in which case none of the color classes can be eliminated
676
with the same ideia. So, one can define a b-coloring as a
677
proper coloring where each color class has a b-vertex.
678
679
In the worst case, after successive applications of the above procedure,
680
one get a proper coloring that uses a number of colors equal to the
681
the b-chromatic number of `G` (denoted `\chi_b(G)`):
682
the maximum `k` such that `G` admits a b-coloring with `k` colors.
683
684
An useful upper bound for calculating the b-chromatic number is
685
the following. If G admits a b-coloring with k colors, then there
686
are `k` vertices of degree at least `k - 1` (the b-vertices of
687
each color class). So, if we set `m(G) = max` \{`k | `there are
688
k vertices of degree at least `k - 1`\}, we have that `\chi_b(G)
689
\leq m(G)`.
690
691
692
.. NOTE::
693
694
This method computes a b-coloring that uses at *MOST* `k`
695
colors. If this method returns a value equal to `k`, it can not
696
be assumed that `k` is equal to `\chi_b(G)`. Meanwhile, if it
697
returns any value `k' < k`, this is a certificate that the
698
Grundy number of the given graph is `k'`.
699
700
As `\chi_b(G)\leq m(G)`, it can be assumed that
701
`\chi_b(G) = k` if ``b_coloring(g, k)`` returns `k` when
702
`k = m(G)`.
703
704
INPUT:
705
706
- ``k`` (integer) -- Maximum number of colors
707
708
- ``solver`` -- (default: ``None``) Specify a Linear Program (LP)
709
solver to be used. If set to ``None``, the default one is used. For
710
more information on LP solvers and which default solver is used, see
711
the method
712
:meth:`solve <sage.numerical.mip.MixedIntegerLinearProgram.solve>`
713
of the class
714
:class:`MixedIntegerLinearProgram <sage.numerical.mip.MixedIntegerLinearProgram>`.
715
716
- ``value_only`` -- boolean (default: ``True``). When set to
717
``True``, only the number of colors is returned. Otherwise, the
718
pair ``(nb_colors, coloring)`` is returned, where ``coloring``
719
is a dictionary associating its color (integer) to each vertex
720
of the graph.
721
722
- ``verbose`` -- integer (default: ``0``). Sets the level of
723
verbosity. Set to 0 by default, which means quiet.
724
725
ALGORITHM:
726
727
Integer Linear Program.
728
729
EXAMPLES:
730
731
The b-chromatic number of a `P_5` is equal to 3::
732
733
sage: from sage.graphs.graph_coloring import b_coloring
734
sage: g = graphs.PathGraph(5)
735
sage: b_coloring(g, 5)
736
3
737
738
The b-chromatic number of the Petersen Graph is equal to 3::
739
740
sage: g = graphs.PetersenGraph()
741
sage: b_coloring(g, 5)
742
3
743
744
It would have been sufficient to set the value of ``k`` to 4 in
745
this case, as `4 = m(G)`.
746
"""
747
748
from sage.numerical.mip import MixedIntegerLinearProgram
749
from sage.numerical.mip import MIPSolverException, Sum
750
751
752
# Calculate the upper bound m(G)
753
# To do so, it takes the list of degrees in
754
# non-increasing order and computes the largest
755
# i, such that the ith degree on the list is
756
# at least i - 1 (note that in the code we need
757
# to take in consideration that the indices
758
# of the list starts with 0)
759
760
deg = g.degree()
761
deg.sort(reverse = True)
762
for i in xrange(g.order()):
763
if deg[i] < i:
764
break
765
if i != (g.order() - 1):
766
m = i
767
else:
768
m = g.order()
769
770
# In case the k specified by the user is greater than m(G), make k = m(G)
771
if k > m:
772
k = m
773
774
775
p = MixedIntegerLinearProgram(solver = solver)
776
777
# List of possible colors
778
classes = range(k)
779
780
#color[v][i] is set to 1 if and only if v is colored i
781
color = p.new_variable(dim=2)
782
783
#b[v][i] is set to 1 if and only if v is a b-vertex from color class i
784
b = p.new_variable(dim=2)
785
786
#is_used[i] is set to 1 if and only if color [i] is used by some vertex
787
is_used = p.new_variable()
788
789
# Each vertex is in exactly one class
790
for v in g.vertices():
791
p.add_constraint(Sum(color[v][i] for i in xrange(k)), min=1, max=1)
792
793
# Adjacent vertices have distinct colors
794
for (u, v) in g.edge_iterator(labels=None):
795
for i in classes:
796
p.add_constraint(color[u][i] + color[v][i], max=1)
797
798
# The following constraints ensure that if v is a b-vertex of color i
799
# then it has a neighbor colored j for every j != i
800
801
802
for v in g.vertices():
803
for i in classes:
804
for j in classes:
805
if j != i:
806
# If v is not a b-vertex of color i, the constraint
807
# is always satisfied, since the only possible
808
# negative term in this case is -is_used[j] which is
809
# cancelled by + 1. If v is a b-vertex of color i
810
# then we MUST have sum(color[w][j] for w in g.neighbors(v))
811
# valued at least 1, which means that v has a neighbour in
812
# color j, as desired.
813
p.add_constraint(Sum(color[w][j] for w in g.neighbors(v)) - b[v][i]
814
+ 1 - is_used[j], min=0)
815
816
#if color i is used, there is a vertex colored i
817
for i in classes:
818
p.add_constraint(Sum(color[v][i] for v in g.vertices()) - is_used[i], min = 0)
819
820
#if there is a vertex colored with color i, then i is used
821
for v in g.vertices():
822
for i in classes:
823
p.add_constraint(color[v][i] - is_used[i], max = 0)
824
825
826
#a color class is used if and only if it has one b-vertex
827
for i in classes:
828
p.add_constraint(Sum(b[w][i] for w in g.vertices()) - is_used[i], min = 0, max = 0)
829
830
831
#All variables are binary
832
p.set_binary(color)
833
p.set_binary(b)
834
p.set_binary(is_used)
835
836
#We want to maximize the number of used colors
837
p.set_objective(Sum(is_used[i] for i in classes))
838
839
840
try:
841
obj = p.solve(log = verbose, objective_only = value_only)
842
from sage.rings.integer import Integer
843
obj = Integer(obj)
844
845
except MIPSolverException:
846
raise ValueError("This graph can not be colored with k colors")
847
848
if value_only:
849
return obj
850
851
852
# Building the dictionary associating its color to every vertex
853
854
c = p.get_values(color)
855
coloring = {}
856
857
for v in g:
858
for i in classes:
859
if c[v][i] == 1:
860
coloring[v] = i
861
break
862
863
return obj, coloring
864
865
def edge_coloring(g, value_only=False, vizing=False, hex_colors=False, solver = None,verbose = 0):
866
r"""
867
Properly colors the edges of a graph. See the URL
868
http://en.wikipedia.org/wiki/Edge_coloring for further details on
869
edge coloring.
870
871
INPUT:
872
873
- ``g`` -- a graph.
874
875
- ``value_only`` -- (default: ``False``):
876
877
- When set to ``True``, only the chromatic index is returned.
878
879
- When set to ``False``, a partition of the edge set into
880
matchings is returned if possible.
881
882
- ``vizing`` -- (default: ``False``):
883
884
- When set to ``True``, tries to find a `\Delta + 1`-edge-coloring,
885
where `\Delta` is equal to the maximum degree in the graph.
886
887
- When set to ``False``, tries to find a `\Delta`-edge-coloring,
888
where `\Delta` is equal to the maximum degree in the graph. If
889
impossible, tries to find and returns a `\Delta + 1`-edge-coloring.
890
This implies that ``value_only=False``.
891
892
- ``hex_colors`` -- (default: ``False``) when set to ``True``, the
893
partition returned is a dictionary whose keys are colors and whose
894
values are the color classes (ideal for plotting).
895
896
- ``solver`` -- (default: ``None``) Specify a Linear Program (LP)
897
solver to be used. If set to ``None``, the default one is
898
used. For more information on LP solvers and which default
899
solver is used, see the method :meth:`solve
900
<sage.numerical.mip.MixedIntegerLinearProgram.solve>` of the
901
class :class:`MixedIntegerLinearProgram
902
<sage.numerical.mip.MixedIntegerLinearProgram>`.
903
904
- ``verbose`` -- integer (default: ``0``). Sets the level of
905
verbosity. Set to 0 by default, which means quiet.
906
907
OUTPUT:
908
909
In the following, `\Delta` is equal to the maximum degree in the graph
910
``g``.
911
912
- If ``vizing=True`` and ``value_only=False``, return a partition of
913
the edge set into `\Delta + 1` matchings.
914
915
- If ``vizing=False`` and ``value_only=True``, return the chromatic index.
916
917
- If ``vizing=False`` and ``value_only=False``, return a partition of
918
the edge set into the minimum number of matchings.
919
920
- If ``vizing=True`` and ``value_only=True``, should return something,
921
but mainly you are just trying to compute the maximum degree of the
922
graph, and this is not the easiest way. By Vizing's theorem, a graph
923
has a chromatic index equal to `\Delta` or to `\Delta + 1`.
924
925
.. NOTE::
926
927
In a few cases, it is possible to find very quickly the chromatic
928
index of a graph, while it remains a tedious job to compute
929
a corresponding coloring. For this reason, ``value_only = True``
930
can sometimes be much faster, and it is a bad idea to compute
931
the whole coloring if you do not need it !
932
933
EXAMPLE::
934
935
sage: from sage.graphs.graph_coloring import edge_coloring
936
sage: g = graphs.PetersenGraph()
937
sage: edge_coloring(g, value_only=True)
938
4
939
940
Complete graphs are colored using the linear-time round-robin coloring::
941
942
sage: from sage.graphs.graph_coloring import edge_coloring
943
sage: len(edge_coloring(graphs.CompleteGraph(20)))
944
19
945
"""
946
from sage.numerical.mip import MixedIntegerLinearProgram
947
from sage.plot.colors import rainbow
948
from sage.numerical.mip import MIPSolverException, Sum
949
950
if g.is_clique():
951
if value_only:
952
return g.order()-1 if g.order() % 2 == 0 else g.order()
953
vertices = g.vertices()
954
r = round_robin(g.order())
955
classes = [[] for v in g]
956
if g.order() % 2 == 0 and not vizing:
957
classes.pop()
958
for (u, v, c) in r.edge_iterator():
959
classes[c].append((vertices[u], vertices[v]))
960
if hex_colors:
961
return dict(zip(rainbow(len(classes)), classes))
962
else:
963
return classes
964
965
if value_only and g.is_overfull():
966
return max(g.degree())+1
967
968
p = MixedIntegerLinearProgram(maximization=True, solver = solver)
969
color = p.new_variable(dim=2)
970
obj = {}
971
k = max(g.degree())
972
# reorders the edge if necessary...
973
R = lambda x: x if (x[0] <= x[1]) else (x[1], x[0])
974
# Vizing's coloring uses Delta + 1 colors
975
if vizing:
976
value_only = False
977
k += 1
978
# A vertex can not have two incident edges with the same color.
979
[p.add_constraint(
980
Sum([color[R(e)][i] for e in g.edges_incident(v, labels=False)]), max=1)
981
for v in g.vertex_iterator()
982
for i in xrange(k)]
983
# an edge must have a color
984
[p.add_constraint(Sum([color[R(e)][i] for i in xrange(k)]), max=1, min=1)
985
for e in g.edge_iterator(labels=False)]
986
# anything is good as an objective value as long as it is satisfiable
987
e = g.edge_iterator(labels=False).next()
988
p.set_objective(color[R(e)][0])
989
p.set_binary(color)
990
try:
991
if value_only:
992
p.solve(objective_only=True, log=verbose)
993
else:
994
chi = p.solve(log=verbose)
995
except MIPSolverException:
996
if value_only:
997
return k + 1
998
else:
999
# if the coloring with Delta colors fails, tries Delta + 1
1000
return edge_coloring(g,
1001
vizing=True,
1002
hex_colors=hex_colors,
1003
verbose=verbose,
1004
solver = solver)
1005
if value_only:
1006
return k
1007
# Builds the color classes
1008
color = p.get_values(color)
1009
classes = [[] for i in xrange(k)]
1010
[classes[i].append(e)
1011
for e in g.edge_iterator(labels=False)
1012
for i in xrange(k)
1013
if color[R(e)][i] == 1]
1014
# if needed, builds a dictionary from the color classes adding colors
1015
if hex_colors:
1016
return dict(zip(rainbow(len(classes)), classes))
1017
else:
1018
return classes
1019
1020
def round_robin(n):
1021
r"""
1022
Computes a round-robin coloring of the complete graph on `n` vertices.
1023
1024
A round-robin coloring of the complete graph `G` on `2n` vertices
1025
(`V = [0, \dots, 2n - 1]`) is a proper coloring of its edges such that
1026
the edges with color `i` are all the `(i + j, i - j)` plus the
1027
edge `(2n - 1, i)`.
1028
1029
If `n` is odd, one obtain a round-robin coloring of the complete graph
1030
through the round-robin coloring of the graph with `n + 1` vertices.
1031
1032
INPUT:
1033
1034
- ``n`` -- the number of vertices in the complete graph.
1035
1036
OUTPUT:
1037
1038
- A ``CompleteGraph`` with labelled edges such that the label of each
1039
edge is its color.
1040
1041
EXAMPLES::
1042
1043
sage: from sage.graphs.graph_coloring import round_robin
1044
sage: round_robin(3).edges()
1045
[(0, 1, 2), (0, 2, 1), (1, 2, 0)]
1046
1047
::
1048
1049
sage: round_robin(4).edges()
1050
[(0, 1, 2), (0, 2, 1), (0, 3, 0), (1, 2, 0), (1, 3, 1), (2, 3, 2)]
1051
1052
1053
For higher orders, the coloring is still proper and uses the expected
1054
number of colors.
1055
1056
::
1057
1058
sage: g = round_robin(9)
1059
sage: sum([Set([e[2] for e in g.edges_incident(v)]).cardinality() for v in g]) == 2*g.size()
1060
True
1061
sage: Set([e[2] for e in g.edge_iterator()]).cardinality()
1062
9
1063
1064
::
1065
1066
sage: g = round_robin(10)
1067
sage: sum([Set([e[2] for e in g.edges_incident(v)]).cardinality() for v in g]) == 2*g.size()
1068
True
1069
sage: Set([e[2] for e in g.edge_iterator()]).cardinality()
1070
9
1071
"""
1072
if n <= 1:
1073
raise ValueError("There must be at least two vertices in the graph.")
1074
mod = lambda x, y: x - y*(x // y)
1075
if n % 2 == 0:
1076
g = GraphGenerators().CompleteGraph(n)
1077
for i in xrange(n - 1):
1078
g.set_edge_label(n - 1, i, i)
1079
for j in xrange(1, (n - 1) // 2 + 1):
1080
g.set_edge_label(mod(i - j, n - 1), mod(i + j, n - 1), i)
1081
return g
1082
else:
1083
g = round_robin(n + 1)
1084
g.delete_vertex(n)
1085
return g
1086
1087
def linear_arboricity(g, k=1, hex_colors=False, value_only=False, solver = None, verbose = 0):
1088
r"""
1089
Computes the linear arboricity of the given graph.
1090
1091
The linear arboricity of a graph `G` is the least
1092
number `la(G)` such that the edges of `G` can be
1093
partitioned into linear forests (i.e. into forests
1094
of paths).
1095
1096
Obviously, `la(G)\geq \lceil \frac {\Delta(G)} 2 \rceil`.
1097
1098
It is conjectured in [Aki80]_ that
1099
`la(G)\leq \lceil \frac {\Delta(G)+1} 2 \rceil`.
1100
1101
INPUT:
1102
1103
- ``hex_colors`` (boolean)
1104
1105
- If ``hex_colors = True``, the function returns a
1106
dictionary associating to each color a list
1107
of edges (meant as an argument to the ``edge_colors``
1108
keyword of the ``plot`` method).
1109
1110
- If ``hex_colors = False`` (default value), returns
1111
a list of graphs corresponding to each color class.
1112
1113
- ``value_only`` (boolean)
1114
1115
- If ``value_only = True``, only returns the linear
1116
arboricity as an integer value.
1117
1118
- If ``value_only = False``, returns the color classes
1119
according to the value of ``hex_colors``
1120
1121
- ``k`` (integer) -- the number of colors to use.
1122
1123
- If ``0``, computes a decomposition of `G` into
1124
`\lceil \frac {\Delta(G)} 2 \rceil`
1125
forests of paths
1126
1127
- If ``1`` (default), computes a decomposition of `G` into
1128
`\lceil \frac {\Delta(G)+1} 2 \rceil` colors,
1129
which is the conjectured general bound.
1130
1131
- If ``k=None``, computes a decomposition using the
1132
least possible number of colors.
1133
1134
- ``solver`` -- (default: ``None``) Specify a Linear Program (LP)
1135
solver to be used. If set to ``None``, the default one is
1136
used. For more information on LP solvers and which default
1137
solver is used, see the method :meth:`solve
1138
<sage.numerical.mip.MixedIntegerLinearProgram.solve>` of the
1139
class :class:`MixedIntegerLinearProgram
1140
<sage.numerical.mip.MixedIntegerLinearProgram>`.
1141
1142
- ``verbose`` -- integer (default: ``0``). Sets the level of
1143
verbosity. Set to 0 by default, which means quiet.
1144
1145
ALGORITHM:
1146
1147
Linear Programming
1148
1149
COMPLEXITY:
1150
1151
NP-Hard
1152
1153
EXAMPLE:
1154
1155
Obviously, a square grid has a linear arboricity of 2, as
1156
the set of horizontal lines and the set of vertical lines
1157
are an admissible partition::
1158
1159
sage: from sage.graphs.graph_coloring import linear_arboricity
1160
sage: g = graphs.GridGraph([4,4])
1161
sage: g1,g2 = linear_arboricity(g, k=0)
1162
1163
Each graph is of course a forest::
1164
1165
sage: g1.is_forest() and g2.is_forest()
1166
True
1167
1168
Of maximum degree 2::
1169
1170
sage: max(g1.degree()) <= 2 and max(g2.degree()) <= 2
1171
True
1172
1173
Which constitutes a partition of the whole edge set::
1174
1175
sage: all([g1.has_edge(e) or g2.has_edge(e) for e in g.edges(labels = None)])
1176
True
1177
1178
REFERENCES:
1179
1180
.. [Aki80] Akiyama, J. and Exoo, G. and Harary, F.
1181
Covering and packing in graphs. III: Cyclic and acyclic invariants
1182
Mathematical Institute of the Slovak Academy of Sciences
1183
Mathematica Slovaca vol30, n4, pages 405--417, 1980
1184
"""
1185
1186
from sage.rings.integer import Integer
1187
1188
if k is None:
1189
try:
1190
return linear_arboricity(g,
1191
k = (Integer(max(g.degree()))/2).ceil(),
1192
value_only = value_only,
1193
hex_colors = hex_colors,
1194
solver = solver,
1195
verbose = verbose)
1196
except ValueError:
1197
return linear_arboricity(g,
1198
k = 0,
1199
value_only = value_only,
1200
hex_colors = hex_colors,
1201
solver = solver,
1202
verbose = verbose)
1203
elif k==1:
1204
k = (Integer(1+max(g.degree()))/2).ceil()
1205
elif k==0:
1206
k = (Integer(max(g.degree()))/2).ceil()
1207
1208
from sage.numerical.mip import MixedIntegerLinearProgram, MIPSolverException, Sum
1209
from sage.plot.colors import rainbow
1210
1211
p = MixedIntegerLinearProgram(solver = solver)
1212
1213
1214
# c is a boolean value such that c[i][(u,v)] = 1 if and only if (u,v) is colored with i
1215
c = p.new_variable(dim=2)
1216
1217
# relaxed value
1218
r = p.new_variable(dim=2)
1219
1220
E = lambda x,y : (x,y) if x<y else (y,x)
1221
1222
MAD = 1-1/(Integer(g.order())*2)
1223
1224
# Partition of the edges
1225
for u,v in g.edges(labels=None):
1226
p.add_constraint(Sum([c[i][E(u,v)] for i in range(k)]), max=1, min=1)
1227
1228
for i in range(k):
1229
1230
# r greater than c
1231
for u,v in g.edges(labels=None):
1232
p.add_constraint(r[i][(u,v)] + r[i][(v,u)] - c[i][E(u,v)], max=0, min=0)
1233
1234
1235
# Maximum degree 2
1236
for u in g.vertices():
1237
p.add_constraint(Sum([c[i][E(u,v)] for v in g.neighbors(u)]),max = 2)
1238
1239
# no cycles
1240
p.add_constraint(Sum([r[i][(u,v)] for v in g.neighbors(u)]),max = MAD)
1241
1242
1243
p.set_objective(None)
1244
p.set_binary(c)
1245
1246
try:
1247
if value_only:
1248
return p.solve(objective_only = True, log = verbose)
1249
else:
1250
p.solve(log = verbose)
1251
1252
except MIPSolverException:
1253
if k == (Integer(max(g.degree()))/2).ceil():
1254
raise Exception("It looks like you have found a counterexample to a very old conjecture. Please do not loose it ! Please publish it, and send a post to sage-devel to warn us. I implore you ! Nathann Cohen ")
1255
else:
1256
raise ValueError("This graph can not be colored with the given number of colors.")
1257
1258
c = p.get_values(c)
1259
1260
if hex_colors:
1261
answer = [[] for i in range(k)]
1262
add = lambda (u,v),i : answer[i].append((u,v))
1263
else:
1264
gg = g.copy()
1265
gg.delete_edges(g.edges())
1266
answer = [gg.copy() for i in range(k)]
1267
add = lambda (u,v),i : answer[i].add_edge((u,v))
1268
1269
for i in range(k):
1270
for u,v in g.edges(labels=None):
1271
if c[i][E(u,v)] == 1:
1272
add((u,v),i)
1273
1274
if hex_colors:
1275
return dict(zip(rainbow(len(classes)),classes))
1276
else:
1277
return answer
1278
1279
def acyclic_edge_coloring(g, hex_colors=False, value_only=False, k=0, solver = None, verbose = 0):
1280
r"""
1281
Computes an acyclic edge coloring of the current graph.
1282
1283
An edge coloring of a graph is a assignment of colors
1284
to the edges of a graph such that :
1285
1286
- the coloring is proper (no adjacent edges share a
1287
color)
1288
- For any two colors `i,j`, the union of the edges
1289
colored with `i` or `j` is a forest.
1290
1291
The least number of colors such that such a coloring
1292
exists for a graph `G` is written `\chi'_a(G)`, also
1293
called the acyclic chromatic index of `G`.
1294
1295
It is conjectured that this parameter can not be too different
1296
from the obvious lower bound `\Delta(G)\leq \chi'_a(G)`,
1297
`\Delta(G)` being the maximum degree of `G`, which is given
1298
by the first of the two constraints. Indeed, it is conjectured
1299
that `\Delta(G)\leq \chi'_a(G) \leq \Delta(G) + 2`.
1300
1301
INPUT:
1302
1303
- ``hex_colors`` (boolean)
1304
1305
- If ``hex_colors = True``, the function returns a
1306
dictionary associating to each color a list
1307
of edges (meant as an argument to the ``edge_colors``
1308
keyword of the ``plot`` method).
1309
1310
- If ``hex_colors = False`` (default value), returns
1311
a list of graphs corresponding to each color class.
1312
1313
- ``value_only`` (boolean)
1314
1315
- If ``value_only = True``, only returns the acyclic
1316
chromatic index as an integer value
1317
1318
- If ``value_only = False``, returns the color classes
1319
according to the value of ``hex_colors``
1320
1321
- ``k`` (integer) -- the number of colors to use.
1322
1323
- If ``k>0``, computes an acyclic edge coloring using
1324
`k` colors.
1325
1326
- If ``k=0`` (default), computes a coloring of `G` into
1327
`\Delta(G) + 2` colors,
1328
which is the conjectured general bound.
1329
1330
- If ``k=None``, computes a decomposition using the
1331
least possible number of colors.
1332
1333
- ``solver`` -- (default: ``None``) Specify a Linear Program (LP)
1334
solver to be used. If set to ``None``, the default one is
1335
used. For more information on LP solvers and which default
1336
solver is used, see the method :meth:`solve
1337
<sage.numerical.mip.MixedIntegerLinearProgram.solve>` of the
1338
class :class:`MixedIntegerLinearProgram
1339
<sage.numerical.mip.MixedIntegerLinearProgram>`.
1340
1341
- ``verbose`` -- integer (default: ``0``). Sets the level of
1342
verbosity. Set to 0 by default, which means quiet.
1343
1344
ALGORITHM:
1345
1346
Linear Programming
1347
1348
EXAMPLE:
1349
1350
The complete graph on 8 vertices can not be acyclically
1351
edge-colored with less `\Delta+1` colors, but it can be
1352
colored with `\Delta+2=9`::
1353
1354
sage: from sage.graphs.graph_coloring import acyclic_edge_coloring
1355
sage: g = graphs.CompleteGraph(8)
1356
sage: colors = acyclic_edge_coloring(g)
1357
1358
Each color class is of course a matching ::
1359
1360
sage: all([max(gg.degree())<=1 for gg in colors])
1361
True
1362
1363
These matchings being a partition of the edge set::
1364
1365
sage: all([ any([gg.has_edge(e) for gg in colors]) for e in g.edges(labels = False)])
1366
True
1367
1368
Besides, the union of any two of them is a forest ::
1369
1370
sage: all([g1.union(g2).is_forest() for g1 in colors for g2 in colors])
1371
True
1372
1373
If one wants to acyclically color a cycle on `4` vertices,
1374
at least 3 colors will be necessary. The function raises
1375
an exception when asked to color it with only 2::
1376
1377
sage: g = graphs.CycleGraph(4)
1378
sage: acyclic_edge_coloring(g, k=2)
1379
Traceback (most recent call last):
1380
...
1381
ValueError: This graph can not be colored with the given number of colors.
1382
1383
The optimal coloring give us `3` classes::
1384
1385
sage: colors = acyclic_edge_coloring(g, k=None)
1386
sage: len(colors)
1387
3
1388
1389
"""
1390
1391
from sage.rings.integer import Integer
1392
from sage.combinat.subset import Subsets
1393
1394
if k is None:
1395
k = max(g.degree())
1396
1397
while True:
1398
try:
1399
return acyclic_edge_coloring(g,
1400
value_only = value_only,
1401
hex_colors = hex_colors,
1402
k = k,
1403
solver = solver,
1404
verbose = verbose)
1405
except ValueError:
1406
k = k+1
1407
1408
raise Exception("This should not happen. Please report a bug !")
1409
1410
elif k==0:
1411
k = max(g.degree())+2
1412
1413
from sage.numerical.mip import MixedIntegerLinearProgram, MIPSolverException, Sum
1414
from sage.plot.colors import rainbow
1415
1416
p = MixedIntegerLinearProgram(solver = solver)
1417
1418
# c is a boolean value such that c[i][(u,v)] = 1 if and only if (u,v) is colored with i
1419
c = p.new_variable(dim=2)
1420
1421
# relaxed value
1422
r = p.new_variable(dim=2)
1423
1424
E = lambda x,y : (x,y) if x<y else (y,x)
1425
1426
MAD = 1-1/(Integer(g.order())*2)
1427
1428
# Partition of the edges
1429
for u,v in g.edges(labels=None):
1430
p.add_constraint(Sum([c[i][E(u,v)] for i in range(k)]), max=1, min=1)
1431
1432
1433
for i in range(k):
1434
1435
# Maximum degree 1
1436
for u in g.vertices():
1437
p.add_constraint(Sum([c[i][E(u,v)] for v in g.neighbors(u)]),max = 1)
1438
1439
for i,j in Subsets(range(k),2):
1440
# r is greater than c
1441
for u in g.vertices():
1442
p.add_constraint(Sum([r[(i,j)][(u,v)] for v in g.neighbors(u)]),max = MAD)
1443
1444
# r greater than c
1445
for u,v in g.edges(labels=None):
1446
p.add_constraint(r[(i,j)][(u,v)] + r[(i,j)][(v,u)] - c[i][E(u,v)] - c[j][E(u,v)], max=0, min=0)
1447
1448
p.set_objective(None)
1449
p.set_binary(c)
1450
1451
try:
1452
if value_only:
1453
return p.solve(objective_only = True, log = verbose)
1454
else:
1455
p.solve(log = verbose)
1456
1457
except MIPSolverException:
1458
if k == max(g.degree()) + 2:
1459
raise Exception("It looks like you have found a counterexample to a very old conjecture. Please do not loose it ! Please publish it, and send a post to sage-devel to warn us. I implore you ! Nathann Cohen ")
1460
else:
1461
raise ValueError("This graph can not be colored with the given number of colors.")
1462
1463
c = p.get_values(c)
1464
1465
if hex_colors:
1466
answer = [[] for i in range(k)]
1467
add = lambda (u,v),i : answer[i].append((u,v))
1468
else:
1469
gg = g.copy()
1470
gg.delete_edges(g.edges())
1471
answer = [gg.copy() for i in range(k)]
1472
add = lambda (u,v),i : answer[i].add_edge((u,v))
1473
1474
for i in range(k):
1475
for u,v in g.edges(labels=None):
1476
if c[i][E(u,v)] == 1:
1477
add((u,v),i)
1478
1479
if hex_colors:
1480
return dict(zip(rainbow(len(classes)),classes))
1481
else:
1482
return answer
1483
1484
1485
class Test:
1486
r"""
1487
This class performs randomized testing for all_graph_colorings.
1488
Since everything else in this file is derived from
1489
all_graph_colorings, this is a pretty good randomized tester for
1490
the entire file. Note that for a graph `G`, ``G.chromatic_polynomial()``
1491
uses an entirely different algorithm, so we provide a good,
1492
independent test.
1493
"""
1494
1495
def random(self,tests = 1000):
1496
r"""
1497
Calls ``self.random_all_graph_colorings()``. In the future, if
1498
other methods are added, it should call them, too.
1499
1500
TESTS::
1501
1502
sage: from sage.graphs.graph_coloring import Test
1503
sage: Test().random(1)
1504
"""
1505
self.random_all_graph_colorings(tests)
1506
1507
def random_all_graph_colorings(self,tests = 1000):
1508
r"""
1509
Verifies the results of ``all_graph_colorings()`` in three ways:
1510
1511
#. all colorings are unique
1512
1513
#. number of m-colorings is `P(m)` (where `P` is the chromatic
1514
polynomial of the graph being tested)
1515
1516
#. colorings are valid -- that is, that no two vertices of
1517
the same color share an edge.
1518
1519
TESTS::
1520
1521
sage: from sage.graphs.graph_coloring import Test
1522
sage: Test().random_all_graph_colorings(1)
1523
"""
1524
from sage.all import Set
1525
1526
G = GraphGenerators().RandomGNP(10,.5)
1527
Q = G.chromatic_polynomial()
1528
N = G.chromatic_number()
1529
m = N
1530
1531
S = Set([])
1532
1533
for C in all_graph_colorings(G, m):
1534
parts = [C[k] for k in C]
1535
for P in parts:
1536
l = len(P)
1537
for i in range(l):
1538
for j in range(i+1,l):
1539
if G.has_edge(P[i],P[j]):
1540
raise RuntimeError, "Coloring Failed."
1541
1542
#make the dict into a set for quick uniqueness checking
1543
S+= Set([Set([(k,tuple(C[k])) for k in C])])
1544
1545
if len(S) != Q(m):
1546
raise RuntimeError, "Incorrect number of unique colorings!"
1547
1548