Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
sagemath
GitHub Repository: sagemath/sagelib
Path: blob/master/sage/graphs/comparability.pyx
4045 views
1
r"""
2
Comparability and permutation graphs
3
4
This module implements method related to :wikipedia:`Comparability graphs
5
<Comparability_graph>` and :wikipedia:`Permutation graphs <Permutation_graph>`,
6
that is, for the moment, only recognition algorithms.
7
8
Most of the information found here can alo be found in [Cleanup]_ or [ATGA]_.
9
10
The following methods are implemented in this module
11
12
.. csv-table::
13
:class: contentstable
14
:widths: 30, 70
15
:delim: |
16
17
:meth:`~is_comparability_MILP` | Tests whether the graph is a comparability graph (MILP)
18
:meth:`~greedy_is_comparability` | Tests whether the graph is a comparability graph (greedy algorithm)
19
:meth:`~greedy_is_comparability_with_certificate` | Tests whether the graph is a comparability graph and returns certificates (greedy algorithm)
20
:meth:`~is_comparability` | Tests whether the graph is a comparability graph
21
:meth:`~is_permutation` | Tests whether the graph is a permutation graph.
22
:meth:`~is_transitive` | Tests whether the digraph is transitive.
23
24
Author:
25
26
- Nathann Cohen 2012-04
27
28
Graph classes
29
-------------
30
31
**Comparability graphs**
32
33
A graph is a comparability graph if it can be obtained from a poset by adding an
34
edge between any two elements that are comparable. Co-comparability graph are
35
complements of such graphs, i.e. graphs built from a poset by adding an edge
36
between any two incomparable elements.
37
38
For more information on comparablity graphs, see the :wikipedia:`corresponding
39
wikipedia page <Comparability_graph>`
40
41
**Permutation graphs**
42
43
Definitions:
44
45
- A permutation `\pi = \pi_1\pi_2\dots\pi_n` defines a graph on `n` vertices
46
such that `i\sim j` when `\pi` reverses `i` and `j` (i.e. when `i<j` and
47
`\pi_j < \pi_i`. A graph is a permutation graph whenever it can be built
48
through this construction.
49
50
- A graph is a permutation graph if it can be build from two parallel lines are
51
the intersection graph of segments intersecting both lines.
52
53
- A graph is a permutation graph if it is both a comparability graph and a
54
co-comparability graph.
55
56
For more information on permutation graphs, see the :wikipedia:`corresponding
57
wikipedia page <Permutation_graph>`.
58
59
60
Recognition algorithm for comparability graphs
61
----------------------------------------------
62
63
**Greedy algorithm**
64
65
This algorithm attempts to build a transitive orientation of a given graph `G`,
66
that is an orientation `D` such that for any directed `uv`-path of `D` there
67
exists in `D` an edge `uv`. This already determines a notion of equivalence
68
between some edges of `G` :
69
70
In `G`, two edges `uv` and `uv'` (incident to a common vertex `u`) such that
71
`vv'\not\in G` need necessarily be oriented *the same way* (that is that they
72
should either both *leave* or both *enter* `u`). Indeed, if one enters `G`
73
while the other leaves it, these two edges form a path of length two, which is
74
not possible in any transitive orientation of `G` as `vv'\not\in G`.
75
76
Hence, we can say that in this case a *directed edge* `uv` is equivalent to a
77
*directed edge* `uv'` (to mean that if one belongs to the transitive
78
orientation, the other one must be present too) in the same way that `vu` is
79
equivalent to `v'u`. We can thus define equivalence classes on oriented edges,
80
to represent set of edges that imply each other. We can thus define `C^G_{uv}`
81
to be the equivalence class in `G` of the oriented edge `uv`.
82
83
Of course, if there exists a transitive orientation of a graph `G`, then no edge
84
`uv` implies its contrary `vu`, i.e. it is necessary to ensure that `\forall
85
uv\in G, vu\not\in C^G_{uv}`. The key result on which the greedy algorithm is
86
built is the following (see [Cleanup]_):
87
88
**Theorem** -- The following statements are equivalent :
89
90
- `G` is a comparability graph
91
- `\forall uv\in G, vu\not\in C^G_{uv}`
92
- The edges of `G` can be partitionned into `B_1,...,B_k` where `B_i` is the
93
equivalence class of some oriented edge in `G-B_1-\dots-B_{i-1}`
94
95
Hence, ensuring that a graph is a comparability graph can be done by checking
96
that no equivalence class is contradictory. Building the orientation, however,
97
requires to build equivalence classes step by step until an orientation has been
98
found for all of them.
99
100
**Mixed Integer Linear Program**
101
102
A MILP formulation is available to check the other methods for correction. It is
103
easily built :
104
105
To each edge are associated two binary variables (one for each possible
106
direction). We then ensure that each triangle is transitively oriented, and
107
that each pair of incident edges `uv, uv'` such that `vv'\not\in G` do not
108
create a 2-path.
109
110
Here is the formulation:
111
112
.. MATH::
113
114
\mbox{Maximize : }&\mbox{Nothing}\\[2mm]
115
\mbox{Such that : }&\\
116
&\forall uv\in G\\
117
&\cdot o_{uv}+o_{vu} = 1\\[2mm]
118
&\forall u\in G, \forall v,v'\in N(v)\text{ such that }vv'\not\in G\\
119
&\cdot o_{uv} + o_{v'u} - o_{v'v} \leq 1\\
120
&\cdot o_{uv'} + o_{vu} - o_{vv'} \leq 1\\[2mm]
121
&\forall u\in G, \forall v,v'\in N(v)\text{ such that }vv'\in G\\
122
&\cdot o_{uv} + o_{v'u} \leq 1\\
123
&\cdot o_{uv'} + o_{vu} \leq 1\\[2mm]
124
&o_{uv}\text{ is a binary variable}\\
125
126
.. NOTE::
127
128
The MILP formulation is usually much slower than the greedy algorithm. This
129
MILP has been implemented to check the results of the greedy algorithm that
130
has been implemented to check the results of a faster algorithm which has not
131
been implemented yet.
132
133
Certificates
134
------------
135
136
**Comparability graphs**
137
138
The *yes*-certificates that a graph is a comparability graphs are transitive
139
orientations of it. The *no*-certificates, on the other hand, are odd cycles of
140
such graph. These odd cycles have the property that around each vertex `v` of
141
the cycle its two incident edges must have the same orientation (toward `v`, or
142
outward `v`) in any transitive orientation of the graph. This is impossible
143
whenever the cycle has odd length. Explanations are given in the "Greedy
144
algorithm" part of the previous section.
145
146
**Permutation graphs**
147
148
Permutation graphs are precisely the intersection of comparability graphs and
149
co-comparability graphs. Hence, negative certificates are precisely negative
150
certificates of comparability or co-comparability. Positive certificates are a
151
pair of permutations that can be used through
152
:meth:`~sage.graphs.graph_generators.GraphGenerators.PermutationGraph` (whose
153
documentation says more about what these permutations represent).
154
155
Implementation details
156
----------------------
157
158
**Test that the equivalence classes are not self-contradictory**
159
160
This is done by a call to :meth:`Graph.is_bipartite`, and here is how :
161
162
Around a vertex `u`, any two edges `uv, uv'` such that `vv'\not\in G` are
163
equivalent. Hence, the equivalence classe of edges around a vertex are
164
precisely the connected components of the complement of the graph induced by
165
the neighbors of `u`.
166
167
In each equivalence class (around a given vertex `u`), the edges should all
168
have the same orientation, i.e. all should go toward `u` at the same time, or
169
leave it at the same time. To represent this, we create a graph with vertices
170
for all equivalent classes around all vertices of `G`, and link `(v, C)` to
171
`(u, C')` if `u\in C` and `v\in C'`.
172
173
A bipartite coloring of this graph with colors 0 and 1 tells us that the
174
edges of an equivalence class `C` around `u` should be directed toward `u` if
175
`(u, C)` is colored with `0`, and outward if `(u, C)` is colored with `1`.
176
177
If the graph is not bipartite, this is the proof that some equivalence class
178
is self-contradictory !
179
180
181
.. NOTE::
182
183
The greedy algorithm implemented here is just there to check the correction
184
of more complicated ones, and it is reaaaaaaaaaaaalllly bad whenever you
185
look at it with performance in mind.
186
187
References
188
----------
189
190
.. [ATGA] Advanced Topics in Graph Algorithms,
191
Ron Shamir,
192
`<http://www.cs.tau.ac.il/~rshamir/atga/atga.html>`_
193
194
.. [Cleanup] A cleanup on transitive orientation,
195
Orders, Algorithms, and Applications, 1994,
196
Simon, K. and Trunz, P.,
197
`<ftp://ftp.inf.ethz.ch/doc/papers/ti/ga/ST94.ps.gz>`_
198
199
Methods
200
-------
201
"""
202
203
################################################################################
204
# Copyright (C) 2012 Nathann Cohen <[email protected]> #
205
# #
206
# Distributed under the terms of the GNU General Public License (GPL) #
207
# http://www.gnu.org/licenses/ #
208
################################################################################
209
210
include '../ext/stdsage.pxi'
211
#####################
212
# Greedy Algorithms #
213
#####################
214
215
def greedy_is_comparability(g, no_certificate = False, equivalence_class = False):
216
r"""
217
Tests whether the graph is a comparability graph (greedy algorithm)
218
219
This method only returns no-certificates.
220
221
To understand how this method works, please consult the documentation of the
222
:mod:`comparability module <sage.graphs.comparability>`.
223
224
INPUT:
225
226
- ``no_certificate`` -- whether to return a *no*-certificate when the graph
227
is not a comparability graph. This certificate is an odd cycle of edges,
228
each of which implies the next. It is set to ``False`` by default.
229
230
- ``equivalence_class`` -- whether to return an equivalence class
231
if the graph is a comparability graph.
232
233
OUTPUT:
234
235
- If the graph is a comparability graph and ``no_certificate = False``, this
236
method returns ``True`` or ``(True, an_equivalence_class)`` according to
237
the value of ``equivalence_class``.
238
239
- If the graph is *not* a comparability graph, this method returns ``False``
240
or ``(False, odd_cycle)`` according to the value of ``no_certificate``.
241
242
EXAMPLE:
243
244
The Petersen Graph is not transitively orientable::
245
246
sage: from sage.graphs.comparability import greedy_is_comparability as is_comparability
247
sage: g = graphs.PetersenGraph()
248
sage: is_comparability(g)
249
False
250
sage: is_comparability(g, no_certificate = True)
251
(False, [9, 4, 3, 2, 7, 9])
252
253
But the Bull graph is::
254
255
sage: g = graphs.BullGraph()
256
sage: is_comparability(g)
257
True
258
"""
259
cdef int i,j
260
261
# Each vertex can partition its neighbors into equivalence classes
262
equivalence_classes = {}
263
for v in g:
264
equivalence_classes[v] = g.subgraph(vertices = g.neighbors(v)).complement().connected_components()
265
266
# We build a graph h with one vertex per (vertex of g + equivalence class)
267
from sage.graphs.graph import Graph
268
h = Graph()
269
h.add_vertices([(v,i) for v in g for i in range(len(equivalence_classes[v]))])
270
271
# We add an edge between two vertices of h if they represent
272
# opposed equivalence classes
273
274
for u,v in g.edges(labels = False):
275
276
for i,s in enumerate(equivalence_classes[v]):
277
if u in s:
278
break
279
280
for j,s in enumerate(equivalence_classes[u]):
281
if v in s:
282
break
283
284
h.add_edge((v,i),(u,j))
285
286
# Is it a comparability graph ?
287
288
cdef int isit
289
isit, certif = h.is_bipartite(certificate = True)
290
291
if isit:
292
if equivalence_class:
293
294
# Returning the largest equivalence class
295
cc = sorted(h.connected_components(), key=len)[-1]
296
297
edges = []
298
for v,sid in cc:
299
s = equivalence_classes[v][sid]
300
301
# For each edge we pick the good orientations
302
if certif[v,sid] == 1:
303
for vv in s:
304
edges.append((v,vv))
305
else:
306
for vv in s:
307
edges.append((vv,v))
308
309
# We return the value but take care of removing edges that were
310
# added twice.
311
return True, sorted(set(edges))
312
313
else:
314
return True
315
else:
316
if no_certificate:
317
certif.append(certif[0])
318
cycle = [v for v,_ in certif]
319
return False, cycle
320
else:
321
return False
322
323
def greedy_is_comparability_with_certificate(g, certificate = False):
324
r"""
325
Tests whether the graph is a comparability graph and returns
326
certificates(greedy algorithm).
327
328
This method can return certificates of both *yes* and *no* answers.
329
330
To understand how this method works, please consult the documentation of the
331
:mod:`comparability module <sage.graphs.comparability>`.
332
333
INPUT:
334
335
- ``certificate`` (boolean) -- whether to return a
336
certificate. *Yes*-answers the certificate is a transitive orientation of
337
`G`, and a *no* certificates is an odd cycle of sequentially forcing
338
edges.
339
340
EXAMPLE:
341
342
The 5-cycle or the Petersen Graph are not transitively orientable::
343
344
sage: from sage.graphs.comparability import greedy_is_comparability_with_certificate as is_comparability
345
sage: is_comparability(graphs.CycleGraph(5), certificate = True)
346
(False, [3, 4, 0, 1, 2, 3])
347
sage: g = graphs.PetersenGraph()
348
sage: is_comparability(g)
349
False
350
sage: is_comparability(g, certificate = True)
351
(False, [9, 4, 3, 2, 7, 9])
352
353
But the Bull graph is::
354
355
sage: g = graphs.BullGraph()
356
sage: is_comparability(g)
357
True
358
sage: is_comparability(g, certificate = True)
359
(True, Digraph on 5 vertices)
360
sage: is_comparability(g, certificate = True)[1].is_transitive()
361
True
362
"""
363
isit, certif = greedy_is_comparability(g, no_certificate = True, equivalence_class = True)
364
if not isit:
365
if certificate:
366
return False, certif
367
else:
368
return False
369
370
elif not certificate:
371
return True
372
373
gg = g.copy()
374
from sage.graphs.digraph import DiGraph
375
h = DiGraph()
376
h.add_vertices(gg.vertices())
377
378
for u,v in certif:
379
gg.delete_edge(u,v)
380
h.add_edge(u,v)
381
382
# While there are some edges left to be oriented
383
while gg.size():
384
385
# We take an equivalence class and orient it
386
isit, certif = greedy_is_comparability(gg, no_certificate = True, equivalence_class = True)
387
388
# Then remove it from the former graph
389
for u,v in certif:
390
gg.delete_edge(u,v)
391
h.add_edge(u,v)
392
393
return True, h
394
395
###################
396
# Integer Program #
397
###################
398
399
def is_comparability_MILP(g, certificate = False):
400
r"""
401
Tests whether the graph is a comparability graph (MILP)
402
403
INPUT:
404
405
- ``certificate`` (boolean) -- whether to return a certificate for
406
yes instances. This method can not return negative certificates.
407
408
EXAMPLE:
409
410
The 5-cycle or the Petersen Graph are not transitively orientable::
411
412
sage: from sage.graphs.comparability import is_comparability_MILP as is_comparability
413
sage: is_comparability(graphs.CycleGraph(5), certificate = True)
414
(False, None)
415
sage: g = graphs.PetersenGraph()
416
sage: is_comparability(g, certificate = True)
417
(False, None)
418
419
But the Bull graph is::
420
421
sage: g = graphs.BullGraph()
422
sage: is_comparability(g)
423
True
424
sage: is_comparability(g, certificate = True)
425
(True, Digraph on 5 vertices)
426
sage: is_comparability(g, certificate = True)[1].is_transitive()
427
True
428
"""
429
from sage.numerical.mip import MixedIntegerLinearProgram, MIPSolverException
430
cdef int i
431
432
p = MixedIntegerLinearProgram()
433
o = p.new_variable(binary = True)
434
435
for u,v in g.edges(labels = False):
436
p.add_constraint( o[u,v] + o[v,u] == 1)
437
438
for u in g:
439
neighbors = g.neighbors(u)
440
441
for i in range(len(neighbors)):
442
v = neighbors[i]
443
for j in range(i+1,len(neighbors)):
444
vv = neighbors[j]
445
446
# If there is an edge between v and vv, we must be
447
# sure it is in the good direction when v-u-vv is a
448
# directed path
449
if g.has_edge(v,vv):
450
p.add_constraint(o[u,v] + o[vv,u] - o[vv,v] <= 1)
451
p.add_constraint(o[u,vv] + o[v,u] - o[v,vv] <= 1)
452
453
# If there is no edge there there are only two
454
# orientation possible (see the module's documentation
455
# about edges which imply each other)
456
else:
457
p.add_constraint(o[u,v] + o[vv,u] <= 1)
458
p.add_constraint(o[u,vv] + o[v,u] <= 1)
459
460
try:
461
p.solve()
462
if not certificate:
463
return True
464
465
# Building the transitive orientation
466
from sage.graphs.digraph import DiGraph
467
d = DiGraph()
468
d.add_vertices(g.vertices())
469
470
o = p.get_values(o)
471
for u,v in g.edges(labels = False):
472
if o[u,v] > .5:
473
d.add_edge(u,v)
474
else:
475
d.add_edge(v,u)
476
477
return True, d
478
479
except MIPSolverException:
480
if certificate:
481
return False, None
482
return False
483
484
###############
485
# Empty shell #
486
###############
487
488
def is_comparability(g, algorithm = "greedy", certificate = False, check = True):
489
r"""
490
Tests whether the graph is a comparability graph
491
492
INPUT:
493
494
- ``algorithm`` -- chose the implementation used to do the test.
495
496
- ``"greedy"`` -- a greedy algorithm (see the documentation of the
497
:mod:`comparability module <sage.graphs.comparability>`).
498
499
- ``"MILP"`` -- a Mixed Integer Linear Program formulation of the
500
problem. Beware, for this implementation is unable to return negative
501
certificates ! When ``certificate = True``, negative certificates are
502
always equal to ``None``. True certificates are valid, though.
503
504
- ``certificate`` (boolean) -- whether to return a
505
certificate. *Yes*-answers the certificate is a transitive orientation of
506
`G`, and a *no* certificates is an odd cycle of sequentially forcing
507
edges.
508
509
- ``check`` (boolean) -- whether to check that the
510
yes-certificates are indeed transitive. As it is very quick
511
compared to the rest of the operation, it is enabled by default.
512
513
EXAMPLE::
514
515
sage: from sage.graphs.comparability import is_comparability
516
sage: g = graphs.PetersenGraph()
517
sage: is_comparability(g)
518
False
519
sage: is_comparability(graphs.CompleteGraph(5), certificate = True)
520
(True, Digraph on 5 vertices)
521
522
TESTS:
523
524
Let us ensure that no exception is raised when we go over all
525
small graphs::
526
527
sage: from sage.graphs.comparability import is_comparability
528
sage: [len([g for g in graphs(i) if is_comparability(g, certificate = True)[0]]) for i in range(7)]
529
[1, 1, 2, 4, 11, 33, 144]
530
"""
531
if g.size() == 0:
532
if certificate:
533
from sage.graphs.digraph import DiGraph
534
return True, DiGraph(g)
535
else:
536
return True
537
538
if algorithm == "greedy":
539
comparability_test = greedy_is_comparability_with_certificate
540
elif algorithm == "MILP":
541
comparability_test = is_comparability_MILP
542
543
if not certificate:
544
return comparability_test(g, certificate = certificate)
545
546
# Checking that the orientation found is indeed transitive. No
547
# reason why it should not, but no reason why we should not check
548
# anyway :-p
549
isit, certif = comparability_test(g, certificate = certificate)
550
551
if check and isit and (not certif.is_transitive()):
552
raise ValueError("Looks like there is a bug somewhere. The "+
553
"algorithm thinks that the orientation is "+
554
"transitive, but we just checked and it is not."+
555
"Please report the bug on sage-devel, and give"+
556
"us the graph that made this method fail !")
557
558
return isit, certif
559
560
def is_permutation(g, algorithm = "greedy", certificate = False, check = True):
561
r"""
562
Tests whether the graph is a permutation graph.
563
564
For more information on permutation graphs, refer to the documentation of
565
the :mod:`comparability module <sage.graphs.comparability>`.
566
567
INPUT:
568
569
- ``algorithm`` -- chose the implementation used for the subcalls to
570
:meth:`is_comparability`.
571
572
- ``"greedy"`` -- a greedy algorithm (see the documentation of the
573
:mod:`comparability module <sage.graphs.comparability>`).
574
575
- ``"MILP"`` -- a Mixed Integer Linear Program formulation of the
576
problem. Beware, for this implementation is unable to return negative
577
certificates ! When ``certificate = True``, negative certificates are
578
always equal to ``None``. True certificates are valid, though.
579
580
- ``certificate`` (boolean) -- whether to return a certificate for the
581
answer given. For ``True`` answers the certificate is a permutation, for
582
``False`` answers it is a no-certificate for the test of comparability or
583
co-comparability.
584
585
- ``check`` (boolean) -- whether to check that the permutations returned
586
indeed create the expected Permutation graph. Pretty cheap compared to the
587
rest, hence a good investment. It is enabled by default.
588
589
.. NOTE::
590
591
As the ``True`` certificate is a :class:`Permutation` object, the
592
segment intersection model of the permutation graph can be visualized
593
through a call to :meth:`Permutation.show
594
<sage.combinat.permutation.Permutation_class.show>`.
595
596
EXAMPLE:
597
598
A permutation realizing the bull graph::
599
600
sage: from sage.graphs.comparability import is_permutation
601
sage: g = graphs.BullGraph()
602
sage: _ , certif = is_permutation(g, certificate = True)
603
sage: h = graphs.PermutationGraph(*certif)
604
sage: h.is_isomorphic(g)
605
True
606
607
Plotting the realization as an intersection graph of segments::
608
609
sage: true, perm = is_permutation(g, certificate = True)
610
sage: p1, p2 = map(Permutation, perm)
611
sage: p = p2 * p1.inverse()
612
sage: p.show(representation = "braid")
613
614
TESTS:
615
616
Trying random permutations, first with the greedy algorithm::
617
618
sage: from sage.graphs.comparability import is_permutation
619
sage: for i in range(20):
620
... p = Permutations(10).random_element()
621
... g1 = graphs.PermutationGraph(p)
622
... isit, certif = is_permutation(g1, certificate = True)
623
... if not isit:
624
... print "Something is wrong here !!"
625
... break
626
... g2 = graphs.PermutationGraph(*certif)
627
... if not g1.is_isomorphic(g2):
628
... print "Something is wrong here !!"
629
... break
630
631
Then with MILP::
632
633
sage: from sage.graphs.comparability import is_permutation
634
sage: for i in range(20):
635
... p = Permutations(10).random_element()
636
... g1 = graphs.PermutationGraph(p)
637
... isit, certif = is_permutation(g1, algorithm = "MILP", certificate = True)
638
... if not isit:
639
... print "Something is wrong here !!"
640
... break
641
... g2 = graphs.PermutationGraph(*certif)
642
... if not g1.is_isomorphic(g2):
643
... print "Something is wrong here !!"
644
... break
645
646
"""
647
from sage.graphs.comparability import is_comparability
648
if certificate:
649
650
# First poset, we stop if it fails
651
isit, certif = is_comparability(g, algorithm = algorithm, certificate = True)
652
if not isit:
653
return False, certif
654
655
# Second poset
656
isit, co_certif = is_comparability(g.complement(), algorithm = algorithm, certificate = True)
657
if not isit:
658
return False, co_certif
659
660
# Building the two orderings
661
tmp = co_certif.edges(labels = False)
662
for u,v in certif.edges(labels = False):
663
co_certif.add_edge(v,u)
664
certif.add_edges(tmp)
665
666
ordering = certif.topological_sort()
667
co_ordering = co_certif.topological_sort()
668
669
# Try to build the Permutation graph from the permutations, just to make
670
# sure nothing weird happened !
671
if check:
672
from sage.graphs.graph_generators import GraphGenerators
673
pg = GraphGenerators().PermutationGraph(ordering, co_ordering)
674
if not pg.is_isomorphic(g):
675
raise ValueError("There is a mistake somewhere ! It looks like "+
676
"the Permutation Graph model computed does "+
677
"not match the input graph !")
678
679
return True, (ordering, co_ordering)
680
681
# No certificate... A piece of cake
682
else:
683
return is_comparability(g) and is_comparability(g.complement())
684
685
from sage.graphs.distances_all_pairs cimport c_distances_all_pairs
686
687
def is_transitive(g, certificate = False):
688
r"""
689
Tests whether the digraph is transitive.
690
691
A digraph is transitive if for any pair of vertices `u,v\in G` linked by a
692
`uv`-path the edge `uv` belongs to `G`.
693
694
INPUT:
695
696
- ``certificate`` -- whether to return a certificate for negative answers.
697
698
- If ``certificate = False`` (default), this method returns ``True`` or
699
``False`` according to the graph.
700
701
- If ``certificate = True``, this method either returns ``True`` answers
702
or yield a pair of vertices `uv` such that there exists a `uv`-path in
703
`G` but `uv\not\in G`.
704
705
EXAMPLE::
706
707
sage: digraphs.Circuit(4).is_transitive()
708
False
709
sage: digraphs.Circuit(4).is_transitive(certificate = True)
710
(0, 2)
711
sage: digraphs.RandomDirectedGNP(30,.2).is_transitive()
712
False
713
sage: digraphs.DeBruijn(5,2).is_transitive()
714
False
715
sage: digraphs.DeBruijn(5,2).is_transitive(certificate = True)
716
('00', '10')
717
sage: digraphs.RandomDirectedGNP(20,.2).transitive_closure().is_transitive()
718
True
719
"""
720
cdef int n = g.order()
721
722
if n <= 2:
723
return True
724
725
cdef unsigned short * distances = c_distances_all_pairs(g)
726
cdef unsigned short * c_distances = distances
727
728
cdef list int_to_vertex = g.vertices()
729
cdef int i, j
730
731
# Only 3 distances can appear in the matrix of all distances : 0, 1, and
732
# infinity. Anything else is a proof of nontransitivity !
733
734
for j in range(n):
735
for i in range(n):
736
if ((c_distances[i] != <unsigned short> -1) and
737
(c_distances[i] > 1)):
738
sage_free(distances)
739
if certificate:
740
741
return int_to_vertex[j], int_to_vertex[i]
742
else:
743
return False
744
745
c_distances += n
746
747
sage_free(distances)
748
return True
749
750