Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
sagemath
GitHub Repository: sagemath/sagesmc
Path: blob/master/src/sage/graphs/weakly_chordal.pyx
8815 views
1
r"""
2
Weakly chordal graphs
3
4
This module deals with everything related to weakly chordal graphs. It currently
5
contains the following functions:
6
7
.. csv-table::
8
:class: contentstable
9
:widths: 30, 70
10
:delim: |
11
12
:meth:`~sage.graphs.weakly_chordal.is_long_hole_free` | Tests whether ``g`` contains an induced cycle of length at least 5.
13
:meth:`~sage.graphs.weakly_chordal.is_long_antihole_free` | Tests whether ``g`` contains an induced anticycle of length at least 5.
14
:meth:`~sage.graphs.weakly_chordal.is_weakly_chordal` | Tests whether ``g`` is weakly chordal.
15
16
Author:
17
18
- Birk Eisermann (initial implementation)
19
- Nathann Cohen (some doc and optimization)
20
21
REFERENCES:
22
23
.. [NikolopoulosPalios07] Nikolopoulos, S.D. and Palios, L.
24
Detecting holes and antiholes in graphs
25
Algorithmica, 2007
26
Vol. 47, number 2, pages 119--138
27
http://www.cs.uoi.gr/~stavros/C-Papers/C-2004-SODA.pdf
28
29
30
31
Methods
32
-------
33
"""
34
35
##############################################################################
36
# Copyright (C) 2012 Birk Eisermann <[email protected]>
37
# Distributed under the terms of the GNU General Public License (GPL)
38
# The full text of the GPL is available at:
39
# http://www.gnu.org/licenses/
40
##############################################################################
41
42
include "sage/misc/bitset.pxi"
43
44
cdef inline int has_edge(bitset_t bs, int u, int v, int n):
45
return bitset_in(bs, u*n+v)
46
47
48
def is_long_hole_free(g, certificate=False):
49
r"""
50
Tests whether ``g`` contains an induced cycle of length at least 5.
51
52
INPUT:
53
54
- ``certificate`` -- boolean (default: ``False``)
55
56
Whether to return a certificate. When ``certificate = True``, then
57
the function returns
58
59
* ``(True, [])`` if ``g`` does not contain such a cycle.
60
For this case, it is not known how to provide a certificate.
61
* ``(False, Hole)`` if ``g`` contains an induced cycle of length at
62
least 5. ``Hole`` returns this cycle.
63
64
If ``certificate = False``, the function returns just ``True`` or
65
``False`` accordingly.
66
67
ALGORITHM:
68
69
This algorithm tries to find a cycle in the graph of all induced `P_4` of
70
`g`, where two copies `P` and `P'` of `P_4` are adjacent if there exists a
71
(not necessarily induced) copy of `P_5=u_1u_2u_3u_4u_5` such that
72
`P=u_1u_2u_3u_4` and `P'=u_2u_3u_4u_5`.
73
74
This is done through a depth-first-search. For efficiency, the auxiliary
75
graph is constructed on-the-fly and never stored in memory.
76
77
The run time of this algorithm is `O(m^2)` [NikolopoulosPalios07]_ ( where
78
`m` is the number of edges of the graph ) .
79
80
EXAMPLES:
81
82
The Petersen Graph contains a hole::
83
84
sage: g = graphs.PetersenGraph()
85
sage: g.is_long_hole_free()
86
False
87
88
The following graph contains a hole, which we want to display::
89
90
sage: g = graphs.FlowerSnark()
91
sage: r,h = g.is_long_hole_free(certificate=True)
92
sage: r
93
False
94
sage: Graph(h).is_isomorphic(graphs.CycleGraph(h.order()))
95
True
96
97
TESTS:
98
99
Another graph with vertices 2, ..., 8, 10::
100
101
sage: g = Graph({2:[3,8],3:[2,4],4:[3,8,10],5:[6,10],6:[5,7],7:[6,8],8:[2,4,7,10],10:[4,5,8]})
102
sage: r,hole = g.is_long_hole_free(certificate=True)
103
sage: r
104
False
105
sage: hole
106
Subgraph of (): Graph on 5 vertices
107
sage: hole.is_isomorphic(graphs.CycleGraph(hole.order()))
108
True
109
"""
110
g._scream_if_not_simple()
111
cdef int a,b,c,i,u,v,d
112
113
# relabel the graph on 0...n-1
114
cdef dict label_id = g.relabel(return_map = True)
115
cdef dict id_label = {idd:label for label, idd in label_id.iteritems()}
116
117
# A dense copy of our graph
118
cdef bitset_t dense_graph
119
cdef int n = g.order()
120
bitset_init(dense_graph, n*n)
121
bitset_set_first_n(dense_graph, 0)
122
for u,v in g.edges(labels = False):
123
bitset_add(dense_graph,u*n+v)
124
bitset_add(dense_graph,v*n+u)
125
126
InPath = {} #vertices of the current path with their position (InPath[v] = i)
127
VisitedP3 = {} #stores triples (u,v,w) which represent visited paths of length 3
128
129
130
def process(a,b,c,i):
131
InPath[c] = i # c is the (i+1)-th vertex at position i
132
VisitedP3[a,b,c] = True
133
VisitedP3[c,b,a] = True
134
135
for d in g.neighbor_iterator(c):
136
if not has_edge(dense_graph,d,a,n) and not has_edge(dense_graph,d,b,n):
137
# a-b-c-d form an induced path P_4
138
139
if d in InPath:
140
# d is already contained in InPath
141
# HOLE FOUND !!!
142
if certificate:
143
j = InPath[d]
144
C = [v for v,vj in InPath.iteritems() if vj >= j]
145
C.sort(key = lambda x: InPath[x])
146
C_index = {u:i for i,u in enumerate(C)}
147
148
# At this step C[0]C[1]..... is a cycle such that any 4
149
# consecutive vertices induce a P4. C may not be an
150
# induced cycle, so we extract one from it.
151
152
# To do so, we look for the *shortest* edge C[i]C[j]
153
# between two nonconsecutive vertices of C, where the
154
# length is the difference |i-j|.
155
#
156
# C[i]...C[j] is necessarily an induced cycle.⇧
157
158
gg = g.subgraph(C)
159
gg.delete_edges(zip(C[:-1],C[1:]))
160
161
abs = lambda x : x if x>0 else -x
162
dist = lambda X : abs(C_index[X[0]]-C_index[X[1]])
163
164
u,v = min(gg.edges(labels = False), key = dist)
165
u,v = C_index[u], C_index[v]
166
167
# Return the answer, and relabel it on-the-fly with the
168
# vertices' real name
169
return False, map(lambda x:id_label[x],C[min(u,v): max(u,v)+1])
170
171
else:
172
return False, None
173
174
elif not VisitedP3.has_key((b,c,d)):
175
# search for another P_4
176
res, hole_vertices = process(b,c,d,i+1)
177
if not res:
178
return False, hole_vertices
179
180
del InPath[c]
181
return True, []
182
183
184
# main algorithm
185
# For all triples u,v,w of vertices such that uvw is a P_3
186
for u in g:
187
InPath[u] = 0 # u is the first vertex at position 0
188
for vv,ww in g.edge_iterator(labels = False):
189
for v,w in [(vv,ww),(ww,vv)]:
190
if has_edge(dense_graph,u,v,n) and u!=w and not has_edge(dense_graph,u,w,n) and not VisitedP3.has_key((u,v,w)):
191
InPath[v] = 1 # v is the second vertex at position 1
192
res,hole = process(u, v, w, 2)
193
if not res:
194
# We relabel the graph before returning the result
195
g.relabel(id_label)
196
# Free the dense graph
197
bitset_free(dense_graph)
198
199
if certificate:
200
return False, g.subgraph(hole)
201
else:
202
return False
203
del InPath[v]
204
del InPath[u]
205
206
# We relabel the graph before returning the result
207
g.relabel(id_label)
208
# Free the dense graph
209
bitset_free(dense_graph)
210
211
if certificate:
212
return True, []
213
else:
214
return True
215
216
def is_long_antihole_free(g, certificate = False):
217
r"""
218
Tests whether the given graph contains an induced subgraph that is
219
isomorphic to the complement of a cycle of length at least 5.
220
221
INPUT:
222
223
- ``certificate`` -- boolean (default: ``False``)
224
225
Whether to return a certificate. When ``certificate = True``, then
226
the function returns
227
228
* ``(False, Antihole)`` if ``g`` contains an induced complement
229
of a cycle of length at least 5 returned as ``Antihole``.
230
* ``(True, [])`` if ``g`` does not contain an induced complement of
231
a cycle of length at least 5.
232
For this case it is not known how to provide a certificate.
233
234
When ``certificate = False``, the function returns just ``True`` or
235
``False`` accordingly.
236
237
ALGORITHM:
238
239
This algorithm tries to find a cycle in the graph of all induced
240
`\overline{P_4}` of `g`, where two copies `\overline{P}` and `\overline{P'}`
241
of `\overline{P_4}` are adjacent if there exists a (not necessarily induced)
242
copy of `\overline{P_5}=u_1u_2u_3u_4u_5` such that
243
`\overline{P}=u_1u_2u_3u_4` and `\overline{P'}=u_2u_3u_4u_5`.
244
245
This is done through a depth-first-search. For efficiency, the auxiliary
246
graph is constructed on-the-fly and never stored in memory.
247
248
The run time of this algorithm is `O(m^2)` [NikolopoulosPalios07]_ ( where
249
`m` is the number of edges of the graph ) .
250
251
EXAMPLES:
252
253
The Petersen Graph contains an antihole::
254
255
sage: g = graphs.PetersenGraph()
256
sage: g.is_long_antihole_free()
257
False
258
259
The complement of a cycle is an antihole::
260
261
sage: g = graphs.CycleGraph(6).complement()
262
sage: r,a = g.is_long_antihole_free(certificate=True)
263
sage: r
264
False
265
sage: a.complement().is_isomorphic( graphs.CycleGraph(6) )
266
True
267
268
TESTS:
269
270
Further tests::
271
272
sage: g = Graph({0:[6,7],1:[7,8],2:[8,9],3:[9,10],4:[10,11],5:[11,6],6:[0,5,7],7:[0,1,6],8:[1,2,9],9:[2,3,8],10:[3,4,11],11:[4,5,10]}).complement()
273
sage: r,a = g.is_long_antihole_free(certificate=True)
274
sage: r
275
False
276
sage: a.complement().is_isomorphic( graphs.CycleGraph(9) )
277
True
278
"""
279
g._scream_if_not_simple()
280
cdef int a,b,c,i,u,v,d
281
282
# relabel the graph on 0...n-1
283
cdef dict label_id = g.relabel(return_map = True)
284
cdef dict id_label = {idd:label for label, idd in label_id.iteritems()}
285
286
# A dense copy of our graph
287
cdef bitset_t dense_graph
288
cdef int n = g.order()
289
bitset_init(dense_graph, n*n)
290
bitset_set_first_n(dense_graph, 0)
291
for u,v in g.edges(labels = False):
292
bitset_add(dense_graph,u*n+v)
293
bitset_add(dense_graph,v*n+u)
294
295
InPath = {} #vertices of the current path with their position (InPath[v] = i)
296
VisitedP3 = {} #stores triples (u,v,w) which represent visited paths of length 3
297
298
299
def process(a,b,c,k):
300
InPath[c] = k # c is the (i+1)-th vertex at position i
301
VisitedP3[a,c,b] = True
302
VisitedP3[c,a,b] = True
303
for d in g.neighbor_iterator(b):
304
if has_edge(dense_graph,d,a,n) and not has_edge(dense_graph,d,c,n):
305
if InPath.has_key(d):
306
if certificate: #calculation of induced cycle in complement
307
j = InPath[d]
308
309
C = [v for v,vj in InPath.iteritems() if vj >= j]
310
C.sort(key = lambda x: InPath[x])
311
C_index = {u:i for i,u in enumerate(C)}
312
313
# At this step C[0]C[1]..... is an anticycle such that
314
# any 4 consecutive vertices induce the complement of a
315
# P_4. C may not be an induced anticycle, so we extract one
316
# from it.
317
318
# To do so, we look for the *shortest* nonedge C[i]C[j]
319
# between two nonconsecutive vertices of C, where the
320
# length is the difference |i-j|.
321
#
322
# C[i]...C[j] is necessarily an induced anticycle.⇧
323
324
gg = g.subgraph(C).complement()
325
gg.delete_edges(zip(C[:-1],C[1:]))
326
327
abs = lambda x : x if x>0 else -x
328
dist = lambda X : abs(C_index[X[0]]-C_index[X[1]])
329
330
u,v = min(gg.edges(labels = False), key = dist)
331
u,v = C_index[u], C_index[v]
332
333
# Return the answer, and relabel it on-the-fly with the
334
# vertices' real name
335
return False, map(lambda x:id_label[x],C[min(u,v): max(u,v)+1])
336
337
else:
338
return False, []
339
340
elif not VisitedP3.has_key((b,d,c)):
341
r,antihole = process(b,c,d,k+1)
342
if not r:
343
return False, antihole
344
345
del InPath[c]
346
return True, []
347
348
349
# main algorithm
350
# For all triples u,v,w of vertices such that uvw is a complement of P_3
351
for u in g:
352
InPath[u] = 1
353
for v,w in g.edge_iterator(labels = False):
354
if not has_edge(dense_graph,u,v,n) and not has_edge(dense_graph,u,w,n) and not VisitedP3.has_key((v,w,u)):
355
InPath[v] = 0
356
r,antihole = process(v, u, w, 2)
357
if not r:
358
# We relabel the graph before returning the result
359
g.relabel(id_label)
360
# Free the dense graph
361
bitset_free(dense_graph)
362
363
if certificate:
364
return False, g.subgraph(antihole)
365
else:
366
return False
367
del InPath[v]
368
del InPath[u]
369
370
# We relabel the graph before returning the result
371
g.relabel(id_label)
372
# Free the dense graph
373
bitset_free(dense_graph)
374
375
if certificate:
376
return True, []
377
else:
378
return True
379
380
def is_weakly_chordal(g, certificate = False):
381
r"""
382
Tests whether the given graph is weakly chordal, i.e., the graph and its
383
complement have no induced cycle of length at least 5.
384
385
INPUT:
386
387
- ``certificate`` -- Boolean value (default: ``False``) whether to
388
return a certificate. If ``certificate = False``, return ``True`` or
389
``False`` according to the graph. If ``certificate = True``, return
390
391
* ``(False, forbidden_subgraph)`` when the graph contains a
392
forbidden subgraph H, this graph is returned.
393
* ``(True, [])`` when the graph is weakly chordal.
394
For this case, it is not known how to provide a certificate.
395
396
ALGORITHM:
397
398
This algorithm checks whether the graph ``g`` or its complement
399
contain an induced cycle of length at least 5.
400
401
Using is_long_hole_free() and is_long_antihole_free() yields a run time
402
of `O(m^2)` (where `m` is the number of edges of the graph).
403
404
EXAMPLES:
405
406
The Petersen Graph is not weakly chordal and contains a hole::
407
408
sage: g = graphs.PetersenGraph()
409
sage: r,s = g.is_weakly_chordal(certificate = True)
410
sage: r
411
False
412
sage: l = len(s.vertices())
413
sage: s.is_isomorphic( graphs.CycleGraph(l) )
414
True
415
"""
416
417
if certificate:
418
r,forbid_subgr = g.is_long_hole_free(certificate=True)
419
if not r:
420
return False, forbid_subgr
421
422
return g.is_long_antihole_free(certificate=True)
423
else:
424
return g.is_long_hole_free() and g.is_long_antihole_free()
425
426
427