Skip to content

Commit cf43e35

Browse files
author
Release Manager
committed
gh-35780: Reduce dependency to rainbow in sage.graphs.graph_coloring
We add a method to format colorings to reduce the number of places in which method rainbow from `sage.plot.colors` is called in `sage.graphs.graph_coloring`. This is a first step toward the resolution of #35777 ### 📝 Checklist - [x] The title is concise, informative, and self-explanatory. - [x] The description explains in detail what this PR is about. - [x] I have linked a relevant issue or discussion. - [x] I have created tests covering the changes. - [x] I have updated the documentation accordingly. URL: #35780 Reported by: David Coudert Reviewer(s): Matthias Köppe
2 parents af6ec5a + e6be1da commit cf43e35

File tree

3 files changed

+117
-91
lines changed

3 files changed

+117
-91
lines changed

build/pkgs/configure/checksums.ini

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
11
tarball=configure-VERSION.tar.gz
2-
sha1=c7c1743bdc3941301764422bef5694849cd1e265
3-
md5=610ce114c46a794ffd70346581012808
4-
cksum=2506747194
2+
sha1=1def339a9f1b9f34ee5fb82f38a95914876618d8
3+
md5=520b4c716ee0ebc94fbb5d81f17efa85
4+
cksum=2018559679
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
464d3d20aaa0a04237ebc98378cf1eec48505e17
1+
c418a82d8985d727cb63c5d3f62c60264b52fd13

src/sage/graphs/graph_coloring.pyx

Lines changed: 113 additions & 87 deletions
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,6 @@ do :
4646
:meth:`acyclic_edge_coloring` | Compute an acyclic edge coloring of the current graph
4747
4848
49-
5049
AUTHORS:
5150
5251
- Tom Boothby (2008-02-21): Initial version
@@ -75,7 +74,69 @@ DLXCPP = LazyImport('sage.combinat.matrices.dlxcpp', 'DLXCPP')
7574
MixedIntegerLinearProgram = LazyImport('sage.numerical.mip', 'MixedIntegerLinearProgram')
7675

7776

78-
def all_graph_colorings(G, n, count_only=False, hex_colors=False, vertex_color_dict=False):
77+
def format_coloring(data, value_only=False, hex_colors=False, vertex_color_dict=False):
78+
r"""
79+
Helper method for vertex and edge coloring methods.
80+
81+
INPUT:
82+
83+
- ``data`` -- either a number when ``value_only`` is ``True`` or a list of
84+
color classes
85+
86+
- ``value_only`` -- boolean (default: ``False``); when set to ``True``, it
87+
simply returns ``data``
88+
89+
- ``hex_colors`` -- boolean (default: ``False``); when set to ``False``,
90+
colors are labeled [0, 1, ..., `n - 1`], otherwise the RGB Hex labeling
91+
is used
92+
93+
- ``vertex_color_dict`` -- boolean (default: ``False``); when set to
94+
``True``, it returns a dictionary ``{vertex: color}``, otherwise it
95+
returns a dictionary ``{color: [list of vertices]}``
96+
97+
EXAMPLES::
98+
99+
sage: from sage.graphs.graph_coloring import format_coloring
100+
sage: color_classes = [['a', 'b'], ['c'], ['d']]
101+
sage: format_coloring(color_classes, value_only=True)
102+
[['a', 'b'], ['c'], ['d']]
103+
sage: format_coloring(len(color_classes), value_only=True)
104+
3
105+
sage: format_coloring(color_classes, value_only=False)
106+
{0: ['a', 'b'], 1: ['c'], 2: ['d']}
107+
sage: format_coloring(color_classes, value_only=False, hex_colors=True)
108+
{'#0000ff': ['d'], '#00ff00': ['c'], '#ff0000': ['a', 'b']}
109+
sage: format_coloring(color_classes, value_only=False, hex_colors=False, vertex_color_dict=True)
110+
{'a': 0, 'b': 0, 'c': 1, 'd': 2}
111+
sage: format_coloring(color_classes, value_only=False, hex_colors=True, vertex_color_dict=True)
112+
{'a': '#ff0000', 'b': '#ff0000', 'c': '#00ff00', 'd': '#0000ff'}
113+
114+
TESTS::
115+
116+
sage: from sage.graphs.graph_coloring import format_coloring
117+
sage: format_coloring([], value_only=True)
118+
[]
119+
sage: format_coloring([], value_only=False, hex_colors=True)
120+
{}
121+
sage: format_coloring([], value_only=False, hex_colors=True, vertex_color_dict=True)
122+
{}
123+
sage: format_coloring([], value_only=False, hex_colors=False, vertex_color_dict=True)
124+
{}
125+
"""
126+
if value_only:
127+
return data
128+
if hex_colors:
129+
from sage.plot.colors import rainbow
130+
colors = rainbow(len(data))
131+
else:
132+
colors = list(range(len(data)))
133+
if vertex_color_dict:
134+
return {u: col for col, C in zip(colors, data) for u in C}
135+
return {col: C for col, C in zip(colors, data) if C}
136+
137+
138+
def all_graph_colorings(G, n, count_only=False, hex_colors=False,
139+
vertex_color_dict=False, color_classes=False):
79140
r"""
80141
Compute all `n`-colorings of a graph.
81142
@@ -90,7 +151,7 @@ def all_graph_colorings(G, n, count_only=False, hex_colors=False, vertex_color_d
90151
* ``n`` -- a positive integer; the number of colors
91152
92153
* ``count_only`` -- boolean (default: ``False``); when set to ``True``, it
93-
returns 1 for each coloring
154+
returns 1 for each coloring and ignores other parameters
94155
95156
* ``hex_colors`` -- boolean (default: ``False``); when set to ``False``,
96157
colors are labeled [0, 1, ..., `n - 1`], otherwise the RGB Hex labeling
@@ -100,6 +161,10 @@ def all_graph_colorings(G, n, count_only=False, hex_colors=False, vertex_color_d
100161
``True``, it returns a dictionary ``{vertex: color}``, otherwise it
101162
returns a dictionary ``{color: [list of vertices]}``
102163
164+
* ``color_classes`` -- boolean (default: ``False``); when set to ``True``,
165+
the method returns only a list of the color classes and ignores parameters
166+
``hex_colors`` and ``vertex_color_dict``
167+
103168
.. WARNING::
104169
105170
This method considers only colorings using exactly `n` colors, even if a
@@ -170,27 +235,29 @@ def all_graph_colorings(G, n, count_only=False, hex_colors=False, vertex_color_d
170235
sage: G = Graph({0: [1], 1: [2]})
171236
sage: for c in all_graph_colorings(G, 2, vertex_color_dict=True):
172237
....: print(c)
173-
{0: 0, 1: 1, 2: 0}
174-
{0: 1, 1: 0, 2: 1}
238+
{0: 0, 2: 0, 1: 1}
239+
{1: 0, 0: 1, 2: 1}
175240
sage: for c in all_graph_colorings(G, 2, hex_colors=True):
176241
....: print(sorted(c.items()))
177242
[('#00ffff', [1]), ('#ff0000', [0, 2])]
178243
[('#00ffff', [0, 2]), ('#ff0000', [1])]
179244
sage: for c in all_graph_colorings(G, 2, hex_colors=True, vertex_color_dict=True):
180245
....: print(c)
181-
{0: '#ff0000', 1: '#00ffff', 2: '#ff0000'}
182-
{0: '#00ffff', 1: '#ff0000', 2: '#00ffff'}
246+
{0: '#ff0000', 2: '#ff0000', 1: '#00ffff'}
247+
{1: '#ff0000', 0: '#00ffff', 2: '#00ffff'}
183248
sage: for c in all_graph_colorings(G, 2, vertex_color_dict=True):
184249
....: print(c)
185-
{0: 0, 1: 1, 2: 0}
186-
{0: 1, 1: 0, 2: 1}
250+
{0: 0, 2: 0, 1: 1}
251+
{1: 0, 0: 1, 2: 1}
187252
sage: for c in all_graph_colorings(G, 2, count_only=True, vertex_color_dict=True):
188253
....: print(c)
189254
1
190255
1
256+
sage: for c in all_graph_colorings(G, 2, color_classes=True):
257+
....: print(c)
258+
[[0, 2], [1]]
259+
[[1], [0, 2]]
191260
"""
192-
from sage.plot.colors import rainbow
193-
194261
G._scream_if_not_simple(allow_multiple_edges=True)
195262

196263
if not n or n > G.order():
@@ -230,48 +297,29 @@ def all_graph_colorings(G, n, count_only=False, hex_colors=False, vertex_color_d
230297
for i in range(n * nE):
231298
ones.push_back((k + i, [nV + i]))
232299

233-
cdef list colors = rainbow(n)
234-
cdef dict color_dict = {col: i for i, col in enumerate(colors)}
235-
236300
cdef list ones_second = [ones[i].second for i in range(len(ones))]
237-
cdef dict coloring
301+
cdef list coloring
238302
cdef set used_colors
239303

240304
try:
241305
for a in DLXCPP(ones_second):
242-
coloring = {}
306+
coloring = [[] for _ in range(n)]
243307
used_colors = set()
244308
if count_only:
245309
used_colors = set(colormap[x][1] for x in a if x in colormap)
246-
elif vertex_color_dict:
247-
for x in a:
248-
if x in colormap:
249-
v, c = colormap[x]
250-
used_colors.add(c)
251-
if hex_colors:
252-
coloring[v] = colors[c]
253-
else:
254-
coloring[v] = color_dict[colors[c]]
255310
else:
256311
for x in a:
257312
if x in colormap:
258313
v, c = colormap[x]
259314
used_colors.add(c)
260-
if hex_colors:
261-
if colors[c] in coloring:
262-
coloring[colors[c]].append(v)
263-
else:
264-
coloring[colors[c]] = [v]
265-
else:
266-
if color_dict[colors[c]] in coloring:
267-
coloring[color_dict[colors[c]]].append(v)
268-
else:
269-
coloring[color_dict[colors[c]]] = [v]
315+
coloring[c].append(v)
270316
if len(used_colors) == n:
271317
if count_only:
272318
yield 1
273319
else:
274-
yield coloring
320+
yield format_coloring(coloring, value_only=color_classes,
321+
hex_colors=hex_colors,
322+
vertex_color_dict=vertex_color_dict)
275323
except RuntimeError:
276324
raise RuntimeError("too much recursion, Graph coloring failed")
277325

@@ -310,11 +358,8 @@ cpdef first_coloring(G, n=0, hex_colors=False):
310358
G._scream_if_not_simple(allow_multiple_edges=True)
311359
cdef int o = G.order()
312360
for m in range(n, o + 1):
313-
for C in all_graph_colorings(G, m, hex_colors=True):
314-
if hex_colors:
315-
return C
316-
else:
317-
return list(C.values())
361+
for C in all_graph_colorings(G, m, hex_colors=hex_colors, color_classes=not hex_colors):
362+
return C
318363

319364

320365
cpdef number_of_n_colorings(G, n):
@@ -402,7 +447,7 @@ cpdef chromatic_number(G):
402447
# don't waste our time coloring.
403448
return m
404449
for n in range(m, o + 1):
405-
for C in all_graph_colorings(G, n):
450+
for C in all_graph_colorings(G, n, count_only=True):
406451
return n
407452

408453

@@ -491,7 +536,6 @@ def vertex_coloring(g, k=None, value_only=False, hex_colors=False, solver=None,
491536
4
492537
"""
493538
g._scream_if_not_simple(allow_multiple_edges=True)
494-
from sage.plot.colors import rainbow
495539
cdef list colorings
496540
cdef set vertices
497541
cdef list deg
@@ -514,18 +558,15 @@ def vertex_coloring(g, k=None, value_only=False, hex_colors=False, solver=None,
514558
if not g.size():
515559
if value_only:
516560
return 1
517-
elif hex_colors:
518-
return {rainbow(1)[0]: list(g)}
519-
else:
520-
return [list(g)]
561+
return format_coloring([list(g)], value_only=not hex_colors,
562+
hex_colors=hex_colors)
521563
# - Bipartite set
522564
if g.is_bipartite():
523565
if value_only:
524566
return 2
525-
elif hex_colors:
526-
return dict(zip(rainbow(2), g.bipartite_sets()))
527-
else:
528-
return g.bipartite_sets()
567+
return format_coloring(g.bipartite_sets(),
568+
value_only=not hex_colors,
569+
hex_colors=hex_colors)
529570

530571
# - No need to try any k smaller than the maximum clique in the graph
531572
# - No need to try k less than |G|/alpha(G), as each color
@@ -553,10 +594,9 @@ def vertex_coloring(g, k=None, value_only=False, hex_colors=False, solver=None,
553594
if not g.order():
554595
if value_only:
555596
return True
556-
elif hex_colors:
557-
return {color: [] for color in rainbow(k)}
558-
else:
559-
return [[] for i in range(k)]
597+
return format_coloring([[] for i in range(k)],
598+
value_only=not hex_colors,
599+
hex_colors=hex_colors)
560600
# Is the graph connected?
561601
# This is not so stupid, as the graph could be disconnected
562602
# by the test of degeneracy (as previously).
@@ -585,10 +625,9 @@ def vertex_coloring(g, k=None, value_only=False, hex_colors=False, solver=None,
585625
for color in range(k):
586626
for component in colorings:
587627
value[color].extend(component[color])
588-
if hex_colors:
589-
return dict(zip(rainbow(k), value))
590-
else:
591-
return value
628+
629+
return format_coloring(value, value_only=not hex_colors,
630+
hex_colors=hex_colors)
592631

593632
# Degeneracy
594633
# Vertices whose degree is less than k are of no importance in
@@ -623,10 +662,9 @@ def vertex_coloring(g, k=None, value_only=False, hex_colors=False, solver=None,
623662
classe.append(deg[-1])
624663
deg.pop(-1)
625664
break
626-
if hex_colors:
627-
return dict(zip(rainbow(k), value))
628-
else:
629-
return value
665+
666+
return format_coloring(value, value_only=not hex_colors,
667+
hex_colors=hex_colors)
630668

631669
p = MixedIntegerLinearProgram(maximization=True, solver=solver)
632670
color = p.new_variable(binary=True)
@@ -664,10 +702,8 @@ def vertex_coloring(g, k=None, value_only=False, hex_colors=False, solver=None,
664702
classes[i].append(v)
665703
break
666704

667-
if hex_colors:
668-
return dict(zip(rainbow(len(classes)), classes))
669-
else:
670-
return classes
705+
return format_coloring(classes, value_only=not hex_colors,
706+
hex_colors=hex_colors)
671707

672708

673709
# Fractional relaxations
@@ -1088,9 +1124,9 @@ def b_coloring(g, k, value_only=True, solver=None, verbose=0,
10881124
proper coloring where each color class has a b-vertex.
10891125
10901126
In the worst case, after successive applications of the above procedure, one
1091-
get a proper coloring that uses a number of colors equal to the the
1092-
b-chromatic number of `G` (denoted `\chi_b(G)`): the maximum `k` such that
1093-
`G` admits a b-coloring with `k` colors.
1127+
get a proper coloring that uses a number of colors equal to the b-chromatic
1128+
number of `G` (denoted `\chi_b(G)`): the maximum `k` such that `G` admits a
1129+
b-coloring with `k` colors.
10941130
10951131
A useful upper bound for calculating the b-chromatic number is the
10961132
following. If `G` admits a b-coloring with `k` colors, then there are `k`
@@ -1385,7 +1421,6 @@ def edge_coloring(g, value_only=False, vizing=False, hex_colors=False, solver=No
13851421
{}
13861422
"""
13871423
g._scream_if_not_simple()
1388-
from sage.plot.colors import rainbow
13891424

13901425
if not g.order() or not g.size():
13911426
if value_only:
@@ -1492,10 +1527,7 @@ def edge_coloring(g, value_only=False, vizing=False, hex_colors=False, solver=No
14921527
return chi
14931528

14941529
# if needed, builds a dictionary from the color classes adding colors
1495-
if hex_colors:
1496-
return dict(zip(rainbow(len(classes)), classes))
1497-
else:
1498-
return classes
1530+
return format_coloring(classes, value_only=not hex_colors, hex_colors=hex_colors)
14991531

15001532

15011533
def _vizing_edge_coloring(g):
@@ -1861,8 +1893,6 @@ def linear_arboricity(g, plus_one=None, hex_colors=False, value_only=False,
18611893
else:
18621894
raise ValueError("plus_one must be equal to 0,1, or to None!")
18631895

1864-
from sage.plot.colors import rainbow
1865-
18661896
p = MixedIntegerLinearProgram(solver=solver)
18671897

18681898
# c is a boolean value such that c[i,(u,v)] = 1 if and only if (u,v) is
@@ -1928,10 +1958,7 @@ def linear_arboricity(g, plus_one=None, hex_colors=False, value_only=False,
19281958
if c[i, frozenset((u, v))]:
19291959
add((u, v), i)
19301960

1931-
if hex_colors:
1932-
return dict(zip(rainbow(len(answer)), answer))
1933-
else:
1934-
return answer
1961+
return format_coloring(answer, value_only=not hex_colors, hex_colors=hex_colors)
19351962

19361963

19371964
def acyclic_edge_coloring(g, hex_colors=False, value_only=False, k=0,
@@ -2079,7 +2106,6 @@ def acyclic_edge_coloring(g, hex_colors=False, value_only=False, k=0,
20792106

20802107
from sage.rings.integer import Integer
20812108
from sage.combinat.subset import Subsets
2082-
from sage.plot.colors import rainbow
20832109

20842110
if not g.order() or not g.size():
20852111
if k == 0:
@@ -2089,7 +2115,10 @@ def acyclic_edge_coloring(g, hex_colors=False, value_only=False, k=0,
20892115
else:
20902116
if k is None:
20912117
return {} if hex_colors else []
2092-
return {c: [] for c in rainbow(k)} if hex_colors else [copy(g) for _ in range(k)]
2118+
if hex_colors:
2119+
return format_coloring([[] for _ in range(k)], hex_colors=True)
2120+
else:
2121+
return [copy(g) for _ in range(k)]
20932122

20942123
if k is None:
20952124
k = max(g.degree())
@@ -2181,10 +2210,7 @@ def acyclic_edge_coloring(g, hex_colors=False, value_only=False, k=0,
21812210
if c[i, E(u, v)]:
21822211
add((u, v), i)
21832212

2184-
if hex_colors:
2185-
return dict(zip(rainbow(len(answer)), answer))
2186-
else:
2187-
return answer
2213+
return format_coloring(answer, value_only=not hex_colors, hex_colors=hex_colors)
21882214

21892215

21902216
cdef class Test:

0 commit comments

Comments
 (0)