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