Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
sagemath
GitHub Repository: sagemath/sagesmc
Path: blob/master/src/sage/graphs/chrompoly.pyx
8815 views
1
"""
2
Chromatic Polynomial Routine
3
4
AUTHORS:
5
-- Gordon Royle - original C implementation
6
-- Robert Miller - transplant
7
8
REFERENCE:
9
Ronald C Read, An improved method for computing the chromatic polynomials of
10
sparse graphs.
11
"""
12
13
#*****************************************************************************
14
# Copyright (C) 2008 Robert Miller and Gordon Royle
15
#
16
# Distributed under the terms of the GNU General Public License (GPL)
17
# http://www.gnu.org/licenses/
18
#*****************************************************************************
19
20
from sage.rings.integer_ring import ZZ
21
from sage.rings.integer cimport Integer
22
from sage.misc.misc import prod
23
include 'sage/ext/interrupt.pxi'
24
include 'sage/ext/cdefs.pxi'
25
include 'sage/ext/stdsage.pxi'
26
27
def chromatic_polynomial(G, return_tree_basis = False):
28
"""
29
Computes the chromatic polynomial of the graph G.
30
31
The algorithm used is a recursive one, based on the following observations
32
of Read:
33
34
- The chromatic polynomial of a tree on n vertices is x(x-1)^(n-1).
35
36
- If e is an edge of G, G' is the result of deleting the edge e, and G''
37
is the result of contracting e, then the chromatic polynomial of G is
38
equal to that of G' minus that of G''.
39
40
EXAMPLES::
41
42
sage: graphs.CycleGraph(4).chromatic_polynomial()
43
x^4 - 4*x^3 + 6*x^2 - 3*x
44
sage: graphs.CycleGraph(3).chromatic_polynomial()
45
x^3 - 3*x^2 + 2*x
46
sage: graphs.CubeGraph(3).chromatic_polynomial()
47
x^8 - 12*x^7 + 66*x^6 - 214*x^5 + 441*x^4 - 572*x^3 + 423*x^2 - 133*x
48
sage: graphs.PetersenGraph().chromatic_polynomial()
49
x^10 - 15*x^9 + 105*x^8 - 455*x^7 + 1353*x^6 - 2861*x^5 + 4275*x^4 - 4305*x^3 + 2606*x^2 - 704*x
50
sage: graphs.CompleteBipartiteGraph(3,3).chromatic_polynomial()
51
x^6 - 9*x^5 + 36*x^4 - 75*x^3 + 78*x^2 - 31*x
52
sage: for i in range(2,7):
53
... graphs.CompleteGraph(i).chromatic_polynomial().factor()
54
(x - 1) * x
55
(x - 2) * (x - 1) * x
56
(x - 3) * (x - 2) * (x - 1) * x
57
(x - 4) * (x - 3) * (x - 2) * (x - 1) * x
58
(x - 5) * (x - 4) * (x - 3) * (x - 2) * (x - 1) * x
59
sage: graphs.CycleGraph(5).chromatic_polynomial().factor()
60
(x - 2) * (x - 1) * x * (x^2 - 2*x + 2)
61
sage: graphs.OctahedralGraph().chromatic_polynomial().factor()
62
(x - 2) * (x - 1) * x * (x^3 - 9*x^2 + 29*x - 32)
63
sage: graphs.WheelGraph(5).chromatic_polynomial().factor()
64
(x - 2) * (x - 1) * x * (x^2 - 5*x + 7)
65
sage: graphs.WheelGraph(6).chromatic_polynomial().factor()
66
(x - 3) * (x - 2) * (x - 1) * x * (x^2 - 4*x + 5)
67
sage: C(x)=graphs.LCFGraph(24, [12,7,-7], 8).chromatic_polynomial() # long time (6s on sage.math, 2011)
68
sage: C(2) # long time
69
0
70
71
By definition, the chromatic number of a graph G is the least integer k such that
72
the chromatic polynomial of G is strictly positive at k::
73
74
sage: G = graphs.PetersenGraph()
75
sage: P = G.chromatic_polynomial()
76
sage: min((i for i in xrange(11) if P(i) > 0)) == G.chromatic_number()
77
True
78
79
sage: G = graphs.RandomGNP(10,0.7)
80
sage: P = G.chromatic_polynomial()
81
sage: min((i for i in xrange(11) if P(i) > 0)) == G.chromatic_number()
82
True
83
"""
84
if not G.is_connected():
85
return prod([chromatic_polynomial(g) for g in G.connected_components_subgraphs()])
86
R = ZZ['x']
87
x = R.gen()
88
if G.is_tree():
89
return x*(x-1)**(G.num_verts()-1)
90
91
cdef int nverts, nedges, i, j, u, v, top, bot, num_chords, next_v
92
cdef int *queue, *chords1, *chords2, *bfs_reorder, *parent
93
cdef mpz_t m, coeff, *tot, *coeffs
94
G = G.relabel(inplace=False)
95
G.remove_multiple_edges()
96
G.remove_loops()
97
nverts = G.num_verts()
98
nedges = G.num_edges()
99
queue = <int *> sage_malloc(nverts * sizeof(int))
100
chords1 = <int *> sage_malloc((nedges - nverts + 1) * sizeof(int))
101
chords2 = <int *> sage_malloc((nedges - nverts + 1) * sizeof(int))
102
parent = <int *> sage_malloc(nverts * sizeof(int))
103
bfs_reorder = <int *> sage_malloc(nverts * sizeof(int))
104
tot = <mpz_t *> sage_malloc((nverts+1) * sizeof(mpz_t))
105
if queue is NULL or \
106
chords1 is NULL or \
107
chords2 is NULL or \
108
parent is NULL or \
109
bfs_reorder is NULL or \
110
tot is NULL:
111
if queue is not NULL:
112
sage_free(queue)
113
if chords1 is not NULL:
114
sage_free(chords1)
115
if chords2 is not NULL:
116
sage_free(chords2)
117
if parent is not NULL:
118
sage_free(parent)
119
if bfs_reorder is not NULL:
120
sage_free(bfs_reorder)
121
if tot is not NULL:
122
sage_free(tot)
123
raise RuntimeError("Error allocating memory for chrompoly.")
124
num_chords = 0
125
126
# Breadth first search from 0:
127
bfs_reorder[0] = 0
128
mpz_init(tot[0]) # sets to 0
129
for i from 0 < i < nverts:
130
bfs_reorder[i] = -1
131
mpz_init(tot[i]) # sets to 0
132
mpz_init(tot[nverts]) # sets to 0
133
queue[0] = 0
134
top = 1
135
bot = 0
136
next_v = 1
137
while top > bot:
138
v = queue[bot]
139
bot += 1
140
for u in G.neighbor_iterator(v):
141
if bfs_reorder[u] == -1: # if u is not yet in tree
142
bfs_reorder[u] = next_v
143
next_v += 1
144
queue[top] = u
145
top += 1
146
parent[bfs_reorder[u]] = bfs_reorder[v]
147
else:
148
if bfs_reorder[u] > bfs_reorder[v]:
149
chords1[num_chords] = bfs_reorder[u]
150
chords2[num_chords] = bfs_reorder[v]
151
else:
152
continue
153
i = num_chords
154
num_chords += 1
155
# bubble sort the chords
156
while i > 0:
157
if chords1[i-1] > chords1[i]:
158
break
159
if chords1[i-1] == chords1[i] and chords2[i-1] > chords2[i]:
160
break
161
j = chords1[i-1]
162
chords1[i-1] = chords1[i]
163
chords1[i] = j
164
j = chords2[i-1]
165
chords2[i-1] = chords2[i]
166
chords2[i] = j
167
i -= 1
168
try:
169
sig_on()
170
contract_and_count(chords1, chords2, num_chords, nverts, tot, parent)
171
sig_off()
172
except RuntimeError:
173
sage_free(queue)
174
sage_free(chords1)
175
sage_free(chords2)
176
sage_free(parent)
177
sage_free(bfs_reorder)
178
for i from 0 <= i <= nverts:
179
mpz_clear(tot[i])
180
sage_free(tot)
181
raise RuntimeError("Error allocating memory for chrompoly.")
182
coeffs = <mpz_t *> sage_malloc((nverts+1) * sizeof(mpz_t))
183
if coeffs is NULL:
184
sage_free(queue)
185
sage_free(chords1)
186
sage_free(chords2)
187
sage_free(parent)
188
sage_free(bfs_reorder)
189
for i from 0 <= i <= nverts:
190
mpz_clear(tot[i])
191
sage_free(tot)
192
raise RuntimeError("Error allocating memory for chrompoly.")
193
for i from 0 <= i <= nverts:
194
mpz_init(coeffs[i]) # also sets them to 0
195
mpz_init(coeff)
196
mpz_init_set_si(m, -1)
197
# start with the zero polynomial: f(x) = 0
198
for i from nverts >= i > 0:
199
if not mpz_sgn(tot[i]):
200
continue
201
mpz_neg(m, m)
202
203
# do this:
204
# f += tot[i]*m*x*(x-1)**(i-1)
205
mpz_addmul(coeffs[i], m, tot[i])
206
mpz_set_si(coeff, 1)
207
for j from 1 <= j < i:
208
# an iterative method for binomial coefficients...
209
mpz_mul_si(coeff, coeff, j-i)
210
mpz_divexact_ui(coeff, coeff, j)
211
# coeffs[i-j] += tot[i]*m*coeff
212
mpz_mul(coeff, coeff, m)
213
mpz_addmul(coeffs[i-j], coeff, tot[i])
214
mpz_mul(coeff, coeff, m)
215
coeffs_ZZ = []
216
cdef Integer c_ZZ
217
for i from 0 <= i <= nverts:
218
c_ZZ = Integer(0)
219
mpz_set(c_ZZ.value, coeffs[i])
220
coeffs_ZZ.append(c_ZZ)
221
f = R(coeffs_ZZ)
222
sage_free(queue)
223
sage_free(chords1)
224
sage_free(chords2)
225
sage_free(parent)
226
sage_free(bfs_reorder)
227
228
for i from 0 <= i <= nverts:
229
mpz_clear(tot[i])
230
mpz_clear(coeffs[i])
231
232
sage_free(tot)
233
sage_free(coeffs)
234
235
mpz_clear(coeff)
236
mpz_clear(m)
237
238
return f
239
240
cdef int contract_and_count(int *chords1, int *chords2, int num_chords, int nverts, \
241
mpz_t *tot, int *parent):
242
if num_chords == 0:
243
mpz_add_ui(tot[nverts], tot[nverts], 1)
244
return 0
245
cdef int *new_chords1 = <int *> sage_malloc(num_chords * sizeof(int))
246
cdef int *new_chords2 = <int *> sage_malloc(num_chords * sizeof(int))
247
cdef int *ins_list1 = <int *> sage_malloc(num_chords * sizeof(int))
248
cdef int *ins_list2 = <int *> sage_malloc(num_chords * sizeof(int))
249
if new_chords1 is NULL or new_chords2 is NULL or ins_list1 is NULL or ins_list2 is NULL:
250
if new_chords1 is not NULL:
251
sage_free(new_chords1)
252
if new_chords2 is not NULL:
253
sage_free(new_chords2)
254
if ins_list1 is not NULL:
255
sage_free(ins_list1)
256
if ins_list2 is not NULL:
257
sage_free(ins_list2)
258
raise RuntimeError("Error allocating memory for chrompoly.")
259
cdef int i, j, k, x1, xj, z, num, insnum, parent_checked
260
for i from 0 <= i < num_chords:
261
# contract chord i, and recurse
262
z = chords1[i]
263
x1 = chords2[i]
264
j = i + 1
265
insnum = 0
266
parent_checked = 0
267
while j < num_chords and chords1[j] == z:
268
xj = chords2[j]
269
if parent[z] > xj:
270
parent_checked = 1
271
# now try adding {x1, parent[z]} to the list
272
if not parent[x1] == parent[z]:
273
if x1 > parent[z]:
274
ins_list1[insnum] = x1
275
ins_list2[insnum] = parent[z]
276
else:
277
ins_list1[insnum] = parent[z]
278
ins_list2[insnum] = x1
279
insnum += 1
280
if not parent[x1] == xj: # then {x1, xj} isn't already a tree edge
281
ins_list1[insnum] = x1
282
ins_list2[insnum] = xj
283
insnum += 1
284
j += 1
285
if not parent_checked:
286
if not parent[x1] == parent[z]:
287
if x1 > parent[z]:
288
ins_list1[insnum] = x1
289
ins_list2[insnum] = parent[z]
290
else:
291
ins_list1[insnum] = parent[z]
292
ins_list2[insnum] = x1
293
insnum += 1
294
295
# now merge new_chords and ins_list
296
num = 0
297
k = 0
298
while k < insnum and j < num_chords:
299
if chords1[j] > ins_list1[k] or \
300
(chords1[j] == ins_list1[k] and chords2[j] > ins_list2[k]):
301
new_chords1[num] = chords1[j]
302
new_chords2[num] = chords2[j]
303
num += 1
304
j += 1
305
elif chords1[j] < ins_list1[k] or \
306
(chords1[j] == ins_list1[k] and chords2[j] < ins_list2[k]):
307
new_chords1[num] = ins_list1[k]
308
new_chords2[num] = ins_list2[k]
309
num += 1
310
k += 1
311
else:
312
new_chords1[num] = chords1[j]
313
new_chords2[num] = chords2[j]
314
num += 1
315
j += 1
316
k += 1
317
if j == num_chords:
318
while k < insnum:
319
new_chords1[num] = ins_list1[k]
320
new_chords2[num] = ins_list2[k]
321
num += 1
322
k += 1
323
elif k == insnum:
324
while j < num_chords:
325
new_chords1[num] = chords1[j]
326
new_chords2[num] = chords2[j]
327
num += 1
328
j += 1
329
try:
330
contract_and_count(new_chords1, new_chords2, num, nverts - 1, tot, parent)
331
except RuntimeError:
332
sage_free(new_chords1)
333
sage_free(new_chords2)
334
sage_free(ins_list1)
335
sage_free(ins_list2)
336
raise RuntimeError("Error allocating memory for chrompoly.")
337
mpz_add_ui(tot[nverts], tot[nverts], 1)
338
sage_free(new_chords1)
339
sage_free(new_chords2)
340
sage_free(ins_list1)
341
sage_free(ins_list2)
342
343
344
345