Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
sagemath
GitHub Repository: sagemath/sagesmc
Path: blob/master/src/sage/graphs/hyperbolicity.pyx
8815 views
1
r"""
2
Hyperbolicity of a graph
3
4
**Definition** :
5
6
The hyperbolicity `\delta` of a graph `G` has been defined by Gromov
7
[Gromov87]_ as follows (we give here the so-called 4-points condition):
8
9
Let `a, b, c, d` be vertices of the graph, let `S_1`, `S_2` and `S_3` be
10
defined by
11
12
.. MATH::
13
14
S_1 = dist(a, b) + dist(b, c)\\
15
S_2 = dist(a, c) + dist(b, d)\\
16
S_3 = dist(a, d) + dist(b, c)\\
17
18
and let `M_1` and `M_2` be the two largest values among `S_1`, `S_2`, and
19
`S_3`. We define `hyp(a, b, c, d) = M_1 - M_2`, and the hyperbolicity
20
`\delta(G)` of the graph is the maximum of `hyp` over all possible
21
4-tuples `(a, b, c, d)` divided by 2. That is, the graph is said
22
`\delta`-hyperbolic when
23
24
.. MATH::
25
26
\delta(G) = \frac{1}{2}\max_{a,b,c,d\in V(G)}hyp(a, b, c, d)
27
28
(note that `hyp(a, b, c, d)=0` whenever two elements among `a,b,c,d` are equal)
29
30
**Some known results** :
31
32
- Trees and cliques are `0`-hyperbolic
33
34
- `n\times n` grids are `n-1`-hyperbolic
35
36
- Cycles are approximately `n/4`-hyperbolic
37
38
- Chordal graphs are `\leq 1`-hyperbolic
39
40
Besides, the hyperbolicity of a graph is the maximum over all its
41
biconnected components.
42
43
**Algorithms and complexity** :
44
45
The time complexity of the naive implementation (i.e. testing all 4-tuples)
46
is `O( n^4 )`, and an algorithm with time complexity `O(n^{3.69})` has been
47
proposed in [FIV12]_. This remains very long for large-scale graphs, and
48
much harder to implement.
49
50
51
An improvement over the naive algorithm has been proposed in [CCL12]_, and
52
is implemented in the current module. Like the naive algorithm, it has
53
complexity `O(n^4)` but behaves much better in practice. It uses the
54
following fact :
55
56
Assume that `S_1 = dist(a, b) + dist(c, d)` is the largest among
57
`S_1,S_2,S_3`. We have
58
59
.. MATH::
60
61
S_2 + S_3 =& dist(a, c) + dist(b, d) + dist(a, d) + dist(b, c)\\
62
=& [ dist(a, c) + dist(b, c) ] + [ dist(a, d) + dist(b, d)]\\
63
\geq &dist(a,b) + dist(a,b)\\
64
\geq &2dist(a,b)\\
65
66
Now, since `S_1` is the largest sum, we have
67
68
.. MATH::
69
70
hyp(a, b, c, d) =& S_1 - \max\{S_2, S_3\}\\
71
\leq& S_1 - \frac{S_2+ S_3}{2}\\
72
\leq& S_1 - dist(a, b)\\
73
=& dist(c, d)\\
74
75
We obtain similarly that `hyp(a, b, c, d) \leq dist(a, b)`. Consequently,
76
in the implementation, we ensure that `S_1` is larger than `S_2` and `S_3`
77
using an ordering of the pairs by decreasing lengths. Furthermore, we use
78
the best value `h` found so far to cut exploration.
79
80
The worst case time complexity of this algorithm is `O(n^4)`, but it
81
performs very well in practice since it cuts the search space. This
82
algorithm can be turned into an approximation algorithm since at any step of
83
its execution we maintain an upper and a lower bound. We can thus stop
84
execution as soon as a multiplicative approximation factor or an additive
85
one is proven.
86
87
TODO:
88
89
- Add exact methods for the hyperbolicity of chordal graphs
90
91
- Add method for partitioning the graph with clique separators
92
93
**This module contains the following functions**
94
95
At Python level :
96
97
.. csv-table::
98
:class: contentstable
99
:widths: 30, 70
100
:delim: |
101
102
:meth:`~hyperbolicity` | Return the hyperbolicity of the graph or an approximation of this value.
103
:meth:`~hyperbolicity_distribution` | Return the hyperbolicity distribution of the graph or a sampling of it.
104
105
REFERENCES:
106
107
.. [CCL12] N. Cohen, D. Coudert, and A. Lancin. Exact and approximate algorithms
108
for computing the hyperbolicity of large-scale graphs. Research Report
109
RR-8074, Sep. 2012. [`<http://hal.inria.fr/hal-00735481>`_].
110
111
.. [FIV12] H. Fournier, A. Ismail, and A. Vigneron. Computing the Gromov
112
hyperbolicity of a discrete metric space. ArXiv, Tech. Rep. arXiv:1210.3323,
113
Oct. 2012. [`<http://arxiv.org/abs/1210.3323>`_].
114
115
.. [Gromov87] M. Gromov. Hyperbolic groups. Essays in Group Theory, 8:75--263,
116
1987.
117
118
AUTHORS:
119
120
- David Coudert (2012): initial version, exact and approximate algorithm,
121
distribution, sampling
122
123
124
Methods
125
-------
126
"""
127
128
###############################################################################
129
# Copyright (C) 2012 David Coudert <[email protected]>
130
#
131
# Distributed under the terms of the GNU General Public License (GPL)
132
# http://www.gnu.org/licenses/
133
###############################################################################
134
135
# imports
136
from sage.graphs.graph import Graph
137
from sage.graphs.distances_all_pairs cimport c_distances_all_pairs
138
from sage.rings.arith import binomial
139
from sage.rings.integer_ring import ZZ
140
from sage.rings.real_mpfr import RR
141
from sage.functions.other import floor
142
from sage.misc.bitset import Bitset
143
from libc.stdint cimport uint16_t, uint32_t, uint64_t
144
include "sage/ext/stdsage.pxi"
145
146
147
# Defining a pair of vertices as a C struct
148
ctypedef struct pair:
149
uint16_t s
150
uint16_t t
151
152
153
######################################################################
154
# Speedup functions
155
######################################################################
156
157
def _my_subgraph(G, vertices, relabel=False, return_map=False):
158
r"""
159
Return the subgraph containing the given vertices
160
161
This method considers only the connectivity. Therefore, edge labels are
162
ignored as well as any other decoration of the graph (vertex position,
163
etc.).
164
165
If ``relabel`` is ``True``, the vertices of the new graph are relabeled with
166
integers in the range '0\cdots |vertices|-1'. The relabeling map is returned
167
if ``return_map`` is also ``True``.
168
169
TESTS:
170
171
Giving anything else than a Graph::
172
173
sage: from sage.graphs.hyperbolicity import _my_subgraph as mysub
174
sage: mysub([],[])
175
Traceback (most recent call last):
176
...
177
ValueError: The input parameter must be a Graph.
178
179
Subgraph of a PetersenGraph::
180
181
sage: from sage.graphs.hyperbolicity import _my_subgraph as mysub
182
sage: H = mysub(graphs.PetersenGraph(), [0,2,4,6])
183
sage: H.edges(labels=None)
184
[(0, 4)]
185
sage: H.vertices()
186
[0, 2, 4, 6]
187
"""
188
if not isinstance(G,Graph):
189
raise ValueError("The input parameter must be a Graph.")
190
H = Graph()
191
if not vertices:
192
return (H,{}) if (relabel and return_map) else H
193
194
if relabel:
195
map = dict(zip(iter(vertices),xrange(len(vertices))))
196
else:
197
map = dict(zip(iter(vertices),iter(vertices)))
198
199
B = {}
200
for v in G.vertex_iterator():
201
B[v] = False
202
for v in vertices:
203
B[v] = True
204
H.add_vertex(map[v])
205
206
for u in vertices:
207
for v in G.neighbor_iterator(u):
208
if B[v]:
209
H.add_edge(map[u],map[v])
210
211
return (H,map) if (relabel and return_map) else H
212
213
214
######################################################################
215
# Building blocks
216
######################################################################
217
218
cdef inline int __hyp__(unsigned short ** distances, int a, int b, int c, int d):
219
"""
220
Return the hyperbolicity of the given 4-tuple.
221
"""
222
cdef int S1, S2, S3, h
223
S1 = distances[a][b] + distances[c][d]
224
S2 = distances[a][c] + distances[b][d]
225
S3 = distances[a][d] + distances[b][c]
226
if S1 >= S2:
227
if S2 > S3:
228
h = S1-S2
229
else:
230
h = abs(S1-S3)
231
else:
232
if S1 > S3:
233
h = S2-S1
234
else:
235
h = abs(S2-S3)
236
return h
237
238
######################################################################
239
# Basic algorithm for the hyperbolicity
240
######################################################################
241
242
cdef tuple __hyperbolicity_basic_algorithm__(int N,
243
unsigned short ** distances,
244
verbose = False):
245
"""
246
Returns **twice** the hyperbolicity of a graph, and a certificate.
247
248
This method implements the basic algorithm for computing the hyperbolicity
249
of a graph, that is iterating over all 4-tuples. See the module's
250
documentation for more details.
251
252
INPUTS:
253
254
- ``N`` -- number of vertices of the graph.
255
256
- ``distances`` -- path distance matrix (see the distance_all_pairs module).
257
258
- ``verbose`` -- (default: ``False``) is boolean. Set to True to display
259
some information during execution.
260
261
OUTPUTS:
262
263
This function returns a tuple ( h, certificate ), where:
264
265
- ``h`` -- the maximum computed value over all 4-tuples, and so is twice the
266
hyperbolicity of the graph. If no such 4-tuple is found, -1 is returned.
267
268
- ``certificate`` -- 4-tuple of vertices maximizing the value `h`. If no
269
such 4-tuple is found, the empty list [] is returned.
270
"""
271
cdef int a, b, c, d, S1, S2, S3, hh, h_LB
272
cdef list certificate
273
274
h_LB = -1
275
for 0 <= a < N-3:
276
for a < b < N-2:
277
for b < c < N-1:
278
for c < d < N:
279
280
# We compute the hyperbolicity of the 4-tuple
281
hh = __hyp__(distances, a, b, c, d)
282
283
# We compare the value with previously known bound
284
if hh > h_LB:
285
h_LB = hh
286
certificate = [a, b, c, d]
287
288
if verbose:
289
print 'New lower bound:',ZZ(hh)/2
290
291
# Last, we return the computed value and the certificate
292
if h_LB != -1:
293
return ( h_LB, certificate )
294
else:
295
return ( -1, [] )
296
297
298
######################################################################
299
# Decomposition methods
300
######################################################################
301
302
def elimination_ordering_of_simplicial_vertices(G, max_degree=4, verbose=False):
303
r"""
304
Return an elimination ordering of simplicial vertices.
305
306
An elimination ordering of simplicial vertices is an elimination ordering of
307
the vertices of the graphs such that the induced subgraph of their neighbors
308
is a clique. More precisely, as long as the graph has a vertex ``u`` such
309
that the induced subgraph of its neighbors is a clique, we remove ``u`` from
310
the graph, add it to the elimination ordering (list of vertices), and
311
repeat. This method is inspired from the decomposition of a graph by
312
clique-separators.
313
314
INPUTS:
315
316
- ``G`` -- a Graph
317
318
- ``max_degree`` -- (default: 4) maximum degree of the vertices to consider.
319
The running time of this method depends on the value of this parameter.
320
321
- ``verbose`` -- (default: ``False``) is boolean set to ``True`` to display
322
some information during execution.
323
324
OUTPUT:
325
326
- ``elim`` -- A ordered list of vertices such that vertex ``elim[i]`` is
327
removed before vertex ``elim[i+1]``.
328
329
TESTS:
330
331
Giving anything else than a Graph::
332
333
sage: from sage.graphs.hyperbolicity import elimination_ordering_of_simplicial_vertices
334
sage: elimination_ordering_of_simplicial_vertices([])
335
Traceback (most recent call last):
336
...
337
ValueError: The input parameter must be a Graph.
338
339
Giving two small bounds on degree::
340
341
sage: from sage.graphs.hyperbolicity import elimination_ordering_of_simplicial_vertices
342
sage: elimination_ordering_of_simplicial_vertices(Graph(), max_degree=0)
343
Traceback (most recent call last):
344
...
345
ValueError: The parameter max_degree must be > 0.
346
347
Giving a graph built from a bipartite graph plus an edge::
348
349
sage: G = graphs.CompleteBipartiteGraph(2,10)
350
sage: G.add_edge(0,1)
351
sage: from sage.graphs.hyperbolicity import elimination_ordering_of_simplicial_vertices
352
sage: elimination_ordering_of_simplicial_vertices(G)
353
[2, 3, 4, 5, 6, 7, 8, 9, 10, 0, 1, 11]
354
sage: elimination_ordering_of_simplicial_vertices(G,max_degree=1)
355
[]
356
"""
357
if verbose:
358
print 'Entering elimination_ordering_of_simplicial_vertices'
359
360
if not isinstance(G,Graph):
361
raise ValueError("The input parameter must be a Graph.")
362
elif max_degree < 1:
363
raise ValueError("The parameter max_degree must be > 0.")
364
365
# We make a copy of the graph. We use a NetworkX graph since modifications
366
# are a bit faster this way.
367
import networkx
368
ggnx = networkx.empty_graph()
369
for u,v in G.edge_iterator(labels=None):
370
ggnx.add_edge(u,v)
371
372
from sage.combinat.combination import Combinations
373
cdef list elim = []
374
cdef set L = set()
375
376
# We identify vertices of degree at most max_degree
377
for u,d in ggnx.degree_iter():
378
if d<=max_degree:
379
L.add(u)
380
381
while L:
382
# We pick up a vertex and check if the induced subgraph of its neighbors
383
# is a clique. If True, we record it, remove it from the graph, and
384
# update the list of vertices of degree at most max_degree.
385
u = L.pop()
386
X = ggnx.neighbors(u)
387
if all(ggnx.has_edge(v,w) for v,w in Combinations(X,2).list()):
388
elim.append(u)
389
ggnx.remove_node(u)
390
for v,d in ggnx.degree_iter(X):
391
if d<=max_degree:
392
L.add(v)
393
394
if verbose:
395
print 'Propose to eliminate',len(elim),'of the',G.num_verts(),'vertices'
396
print 'End elimination_ordering_of_simplicial_vertices'
397
398
return elim
399
400
401
######################################################################
402
# Greedy dominating set
403
######################################################################
404
405
def _greedy_dominating_set(H):
406
r"""
407
Returns a greedy approximation of a dominating set
408
"""
409
V = sorted([(d,u) for u,d in H.degree_iterator(None,True)],reverse=True)
410
DOM = []
411
seen = set()
412
for _,u in V:
413
if not u in seen:
414
seen.add(u)
415
DOM.append(u)
416
seen.update(H.neighbor_iterator(u))
417
return DOM
418
419
######################################################################
420
# Compute the hyperbolicity using a path decreasing length ordering
421
######################################################################
422
423
cdef inline _invert_cells(pair * tab, uint32_t idxa, uint32_t idxb):
424
cdef pair tmp = tab[idxa]
425
tab[idxa] = tab[idxb]
426
tab[idxb] = tmp
427
428
429
cdef _order_pairs_according_elimination(elim, int N, pair *pairs, uint32_t nb_pairs, uint32_t *last_pair):
430
r"""
431
Re-order the pairs of vertices according the set of eliminated vertices.
432
433
We put pairs of vertices with an extremity in ``elim`` at the end of the
434
array of pairs. We record the positions of the first pair of vertices in
435
``elim``. If ``elim`` is empty, the ordering is unchanged and we set
436
``last_pair=nb_pairs``.
437
"""
438
cdef uint32_t j, jmax
439
440
jmax = nb_pairs-1
441
if nb_pairs<=1 or not elim:
442
last_pair[0] = nb_pairs
443
else:
444
B = Bitset(iter(elim))
445
j = 0
446
while j<jmax:
447
448
if pairs[j].s in B or pairs[j].t in B:
449
while (pairs[jmax].s in B or pairs[jmax].t in B) and (j<jmax):
450
jmax -= 1
451
452
if j<jmax:
453
_invert_cells(pairs, j, jmax)
454
455
if jmax>0:
456
jmax -= 1
457
458
else: # This pair is at a correct position.
459
j += 1
460
461
# We record the position of the first pair of vertices in elim
462
last_pair[0] = jmax+1
463
464
cdef tuple __hyperbolicity__(int N,
465
unsigned short ** distances,
466
int D,
467
int h_LB,
468
float approximation_factor,
469
float additive_gap,
470
elim,
471
verbose = False):
472
"""
473
Return the hyperbolicity of a graph.
474
475
This method implements the exact and the approximate algorithms proposed in
476
[CCL12]_. See the module's documentation for more details.
477
478
INPUTS:
479
480
- ``N`` -- number of vertices of the graph
481
482
- ``distances`` -- path distance matrix
483
484
- ``D`` -- diameter of the graph
485
486
- ``h_LB`` -- lower bound on the hyperbolicity
487
488
- ``approximation_factor`` -- When the approximation factor is set to some
489
value larger than 1.0, the function stop computations as soon as the ratio
490
between the upper bound and the best found solution is less than the
491
approximation factor. When the approximation factor is 1.0, the problem is
492
solved optimaly.
493
494
- ``additive_gap`` -- When sets to a positive number, the function stop
495
computations as soon as the difference between the upper bound and the
496
best found solution is less than additive gap. When the gap is 0.0, the
497
problem is solved optimaly.
498
499
- ``elim`` -- list of vertices that should not be considered during
500
computations.
501
502
- ``verbose`` -- (default: ``False``) is boolean set to ``True`` to display
503
some information during execution
504
505
OUTPUTS:
506
507
This function returns a tuple ( h, certificate, h_UB ), where:
508
509
- ``h`` -- is an integer. When 4-tuples with hyperbolicity larger or equal
510
to `h_LB are found, h is the maximum computed value and so twice the
511
hyperbolicity of the graph. If no such 4-tuple is found, it returns -1.
512
513
- ``certificate`` -- is a list of vertices. When 4-tuples with hyperbolicity
514
larger that h_LB are found, certificate is the list of the 4 vertices for
515
which the maximum value (and so the hyperbolicity of the graph) has been
516
computed. If no such 4-tuple is found, it returns the empty list [].
517
518
- ``h_UB`` -- is an integer equal to the proven upper bound for `h`. When
519
``h == h_UB``, the returned solution is optimal.
520
"""
521
cdef int i, j, l, l1, l2, h, hh, h_UB, a, b, c, d, S1, S2, S3
522
cdef uint32_t x, y
523
cdef dict distr = {}
524
cdef list certificate = []
525
526
# ==> Allocates and fills nb_pairs_of_length
527
#
528
# nb_pairs_of_length[d] is the number of pairs of vertices at distance d
529
cdef uint32_t * nb_pairs_of_length = <uint32_t *>sage_calloc(D+1,sizeof(uint32_t))
530
if nb_pairs_of_length == NULL:
531
sage_free(nb_pairs_of_length)
532
raise MemoryError
533
534
for 0 <= i < N:
535
for i+1 <= j < N:
536
nb_pairs_of_length[ distances[i][j] ] += 1
537
538
# ==> Allocates pairs_of_length
539
#
540
# pairs_of_length[d] is the list of pairs of vertices at distance d
541
cdef pair ** pairs_of_length = <pair **>sage_malloc(sizeof(pair *)*(D+1))
542
if pairs_of_length == NULL:
543
sage_free(nb_pairs_of_length)
544
raise MemoryError
545
546
# ==> Allocates cpt_pairs
547
#
548
# (temporary variable used to fill pairs_of_length)
549
cdef uint32_t * cpt_pairs = <uint32_t *>sage_calloc(D+1,sizeof(uint32_t))
550
if cpt_pairs == NULL:
551
sage_free(nb_pairs_of_length)
552
sage_free(pairs_of_length)
553
raise MemoryError
554
555
# ==> Allocates pairs_of_length[d] for all d
556
for 1 <= i <= D:
557
pairs_of_length[i] = <pair *>sage_malloc(sizeof(pair)*nb_pairs_of_length[i])
558
559
if nb_pairs_of_length[i] > 0 and pairs_of_length[i] == NULL:
560
while i>1:
561
i -= 1
562
sage_free(pairs_of_length[i])
563
sage_free(nb_pairs_of_length)
564
sage_free(pairs_of_length)
565
sage_free(cpt_pairs)
566
raise MemoryError
567
568
# ==> Fills pairs_of_length[d] for all d
569
for 0 <= i < N:
570
for i+1 <= j < N:
571
l = distances[i][j]
572
pairs_of_length[ l ][ cpt_pairs[ l ] ].s = i
573
pairs_of_length[ l ][ cpt_pairs[ l ] ].t = j
574
cpt_pairs[ l ] += 1
575
576
sage_free(cpt_pairs)
577
578
if verbose:
579
print "Current 2 connected component has %d vertices and diameter %d" %(N,D)
580
print "Paths length distribution:", [ (l, nb_pairs_of_length[l]) for l in range(1, D+1) ]
581
582
# ==> Allocates last_pair
583
#
584
# We change the ordering of the pairs to avoid considering pairs touching
585
# eliminated vertices (i.e., vertices in elim). We store in last_pair[l] the
586
# index of the last pair of length l to consider.
587
cdef uint32_t * last_pair = <uint32_t *>sage_malloc(sizeof(uint32_t)*(D+1))
588
if last_pair == NULL:
589
for 1 <= i <= D:
590
sage_free(pairs_of_length[i])
591
sage_free(nb_pairs_of_length)
592
sage_free(pairs_of_length)
593
sage_free(cpt_pairs)
594
raise MemoryError
595
596
for 1 <= l <= D:
597
_order_pairs_according_elimination(elim, N, pairs_of_length[l], nb_pairs_of_length[l], last_pair+l)
598
599
approximation_factor = min(approximation_factor, D)
600
additive_gap = min(additive_gap, D)
601
602
# We create the list of triples (sum,length1,length2) sorted in
603
# co-lexicographic order: decreasing by sum, decreasing by length2,
604
# decreasing length1. This is to ensure a valid ordering for S1, to avoid
605
# some tests, and to ease computation of bounds.
606
cdef list triples = []
607
for i in range(D,0,-1):
608
for j in range(D,i-1,-1):
609
triples.append((i+j,j,i))
610
611
# We use some short-cut variables for efficiency
612
cdef pair * pairs_of_length_l1
613
cdef pair * pairs_of_length_l2
614
cdef uint32_t nb_pairs_of_length_l1, nb_pairs_of_length_l2
615
h = h_LB
616
h_UB = D
617
cdef int STOP = 0
618
for S1, l1, l2 in triples:
619
620
# If we cannot improve further, we stop
621
#
622
# See the module's documentation for an proof that this cut is
623
# valid. Remember that the triples are sorted in a specific order.
624
if l2 <= h:
625
h_UB = h
626
break
627
628
if h_UB > l2:
629
h_UB = l2
630
631
if verbose:
632
print "New upper bound:",ZZ(h_UB)/2
633
634
# Termination if required approximation is found
635
if certificate and ( (h_UB <= h*approximation_factor) or (h_UB-h <= additive_gap) ):
636
break
637
638
pairs_of_length_l1 = pairs_of_length[l1]
639
nb_pairs_of_length_l1 = last_pair[l1]
640
x = 0
641
while x < nb_pairs_of_length_l1:
642
a = pairs_of_length_l1[x].s
643
b = pairs_of_length_l1[x].t
644
645
# If we cannot improve further, we stop
646
#
647
# See the module's documentation for an proof that this cut is
648
# valid.
649
if l2 <= h:
650
STOP = 1
651
break
652
653
# We do not want to test pairs of pairs twice if l1 == l2
654
elif l1 == l2:
655
y = x+1
656
else:
657
y = 0
658
659
pairs_of_length_l2 = pairs_of_length[l2]
660
nb_pairs_of_length_l2 = last_pair[l2]
661
while y < nb_pairs_of_length_l2:
662
c = pairs_of_length_l2[y].s
663
d = pairs_of_length_l2[y].t
664
665
# If two points are equal, the value will be 0, so we skip the
666
# test.
667
if a == c or a == d or b == c or b == d:
668
y += 1
669
continue
670
671
# We compute the hyperbolicity of the 4-tuple. We have S1 = l1 +
672
# l2, and the order in which pairs are visited allow us to claim
673
# that S1 = max( S1, S2, S3 ). If at some point S1 is not the
674
# maximum value, the order ensures that the maximum value has
675
# previously been checked.
676
S2 = distances[a][c] + distances[b][d]
677
S3 = distances[a][d] + distances[b][c]
678
if S2 > S3:
679
hh = S1 - S2
680
else:
681
hh = S1 - S3
682
683
if h < hh or not certificate:
684
# We update current bound on the hyperbolicity and the
685
# search space
686
h = hh
687
certificate = [a, b, c, d]
688
689
if verbose:
690
print "New lower bound:",ZZ(hh)/2
691
692
# Termination if required approximation is found
693
if (h_UB <= h*approximation_factor) or (h_UB-h <= additive_gap):
694
STOP = 1
695
break
696
697
# We go for the next pair c-d
698
y += 1
699
# We cut current exploration if we know we can not improve lower bound
700
#
701
# See the module's documentation for an proof that this cut is
702
# valid.
703
if l2 <= h:
704
STOP = 1
705
h_UB = h
706
break
707
708
if STOP:
709
break
710
711
# We go for the next pair a-b
712
x += 1
713
714
if STOP:
715
break
716
717
# We now free the memory
718
sage_free(nb_pairs_of_length)
719
for 1 <= i <= D:
720
sage_free(pairs_of_length[i])
721
sage_free(pairs_of_length)
722
sage_free(last_pair)
723
724
# Last, we return the computed value and the certificate
725
if len(certificate) == 0:
726
return ( -1, [], h_UB )
727
else:
728
return (h, certificate, h_UB)
729
730
731
def hyperbolicity(G, algorithm='cuts', approximation_factor=None, additive_gap=None, verbose = False):
732
r"""
733
Return the hyperbolicity of the graph or an approximation of this value.
734
735
The hyperbolicity of a graph has been defined by Gromov [Gromov87]_ as
736
follows: Let `a, b, c, d` be vertices of the graph, let `S_1 = dist(a, b) +
737
dist(b, c)`, `S_2 = dist(a, c) + dist(b, d)`, and `S_3 = dist(a, d) +
738
dist(b, c)`, and let `M_1` and `M_2` be the two largest values among `S_1`,
739
`S_2`, and `S_3`. We have `hyp(a, b, c, d) = |M_1 - M_2|`, and the
740
hyperbolicity of the graph is the maximum over all possible 4-tuples `(a, b,
741
c, d)` divided by 2. The worst case time complexity is in `O( n^4 )`.
742
743
See the documentation of :mod:`sage.graphs.hyperbolicity` for more
744
information.
745
746
INPUT:
747
748
- ``G`` -- a Graph
749
750
- ``algorithm`` -- (default: ``'cuts'``) specifies the algorithm to use
751
among:
752
753
- ``'basic'`` is an exhaustive algorithm considering all possible
754
4-tuples and so have time complexity in `O(n^4)`.
755
756
- ``'cuts'`` is an exact algorithm proposed in [CCL12_]. It considers
757
the 4-tuples in an ordering allowing to cut the search space as soon
758
as a new lower bound is found (see the module's documentation). This
759
algorithm can be turned into a approximation algorithm.
760
761
- ``'cuts+'`` is an additive constant approximation algorithm. It
762
proceeds by first removing the simplicial vertices and then applying
763
the ``'cuts'`` algorithm on the remaining graph, as documented in
764
[CCL12_]. By default, the additive constant of the approximation is
765
one. This value can be increased by setting the ``additive_gap`` to
766
the desired value, provide ``additive_gap \geq 1``. In some cases,
767
the returned result is proven optimal. However, this algorithm
768
*cannot* be used to compute an approximation with multiplicative
769
factor, and so the ``approximation_factor`` parameter is just
770
ignored here.
771
772
- ``'dom'`` is an approximation with additive constant four. It
773
computes the hyperbolicity of the vertices of a dominating set of
774
the graph. This is sometimes slower than ``'cuts'`` and sometimes
775
faster. Try it to know if it is interesting for you.
776
The ``additive_gap`` and ``approximation_factor`` parameters cannot
777
be used in combination with this method and so are ignored.
778
779
- ``approximation_factor`` -- (default: None) When the approximation factor
780
is set to some value (larger than 1.0), the function stop computations as
781
soon as the ratio between the upper bound and the best found solution is
782
less than the approximation factor. When the approximation factor is 1.0,
783
the problem is solved optimaly. This parameter is used only when the
784
chosen algorithm is ``'cuts'``.
785
786
- ``additive_gap`` -- (default: None) When sets to a positive number, the
787
function stop computations as soon as the difference between the upper
788
bound and the best found solution is less than additive gap. When the gap
789
is 0.0, the problem is solved optimaly. This parameter is used only when
790
the chosen algorithm is ``'cuts'`` or ``'cuts+'``. The parameter must be
791
``\geq 1`` when used with ``'cuts+'``.
792
793
- ``verbose`` -- (default: ``False``) is a boolean set to True to display
794
some information during execution: new upper and lower bounds, etc.
795
796
OUTPUT:
797
798
This function returns the tuple ( delta, certificate, delta_UB ), where:
799
800
- ``delta`` -- the hyperbolicity of the graph (half-integer value).
801
802
- ``certificate`` -- is the list of the 4 vertices for which the maximum
803
value has been computed, and so the hyperbolicity of the graph.
804
805
- ``delta_UB`` -- is an upper bound for ``delta``. When ``delta ==
806
delta_UB``, the returned solution is optimal. Otherwise, the approximation
807
factor if ``delta_UB/delta``.
808
809
EXAMPLES:
810
811
Hyperbolicity of a `3\times 3` grid::
812
813
sage: from sage.graphs.hyperbolicity import hyperbolicity
814
sage: G = graphs.GridGraph([3,3])
815
sage: hyperbolicity(G,algorithm='cuts')
816
(2, [(0, 0), (0, 2), (2, 0), (2, 2)], 2)
817
sage: hyperbolicity(G,algorithm='basic')
818
(2, [(0, 0), (0, 2), (2, 0), (2, 2)], 2)
819
820
Hyperbolicity of a PetersenGraph::
821
822
sage: from sage.graphs.hyperbolicity import hyperbolicity
823
sage: G = graphs.PetersenGraph()
824
sage: hyperbolicity(G,algorithm='cuts')
825
(1/2, [0, 1, 2, 3], 1/2)
826
sage: hyperbolicity(G,algorithm='basic')
827
(1/2, [0, 1, 2, 3], 1/2)
828
sage: hyperbolicity(G,algorithm='dom')
829
(0, [0, 2, 8, 9], 1)
830
831
Asking for an approximation::
832
833
sage: from sage.graphs.hyperbolicity import hyperbolicity
834
sage: G = graphs.GridGraph([2,10])
835
sage: hyperbolicity(G,algorithm='cuts', approximation_factor=1.5)
836
(1, [(0, 0), (0, 9), (1, 0), (1, 9)], 3/2)
837
sage: hyperbolicity(G,algorithm='cuts', approximation_factor=4)
838
(1, [(0, 0), (0, 9), (1, 0), (1, 9)], 4)
839
sage: hyperbolicity(G,algorithm='cuts', additive_gap=2)
840
(1, [(0, 0), (0, 9), (1, 0), (1, 9)], 3)
841
sage: hyperbolicity(G,algorithm='cuts+')
842
(1, [(0, 0), (0, 9), (1, 0), (1, 9)], 2)
843
sage: hyperbolicity(G,algorithm='cuts+', additive_gap=2)
844
(1, [(0, 0), (0, 9), (1, 0), (1, 9)], 3)
845
sage: hyperbolicity(G,algorithm='dom')
846
(1, [(0, 1), (0, 9), (1, 0), (1, 8)], 5)
847
848
Comparison of results::
849
850
sage: from sage.graphs.hyperbolicity import hyperbolicity
851
sage: for i in xrange(10): # long time
852
... G = graphs.RandomBarabasiAlbert(100,2)
853
... d1,_,_ = hyperbolicity(G,algorithm='basic')
854
... d2,_,_ = hyperbolicity(G,algorithm='cuts')
855
... d3,_,_ = hyperbolicity(G,algorithm='cuts+')
856
... l3,_,u3 = hyperbolicity(G,approximation_factor=2)
857
... if d1!=d2 or d1<d3 or l3>d1 or u3<d1:
858
... print "That's not good!"
859
860
TESTS:
861
862
Giving anything else than a Graph::
863
864
sage: from sage.graphs.hyperbolicity import hyperbolicity
865
sage: hyperbolicity([])
866
Traceback (most recent call last):
867
...
868
ValueError: The input parameter must be a Graph.
869
870
Giving a non connected graph::
871
872
sage: from sage.graphs.hyperbolicity import hyperbolicity
873
sage: G = Graph([(0,1),(2,3)])
874
sage: hyperbolicity(G)
875
Traceback (most recent call last):
876
...
877
ValueError: The input Graph must be connected.
878
879
Giving wrong approximation factor::
880
881
sage: from sage.graphs.hyperbolicity import hyperbolicity
882
sage: G = graphs.PetersenGraph()
883
sage: hyperbolicity(G,algorithm='cuts', approximation_factor=0.1)
884
Traceback (most recent call last):
885
...
886
ValueError: The approximation factor must be >= 1.0.
887
888
Giving negative additive gap::
889
890
sage: from sage.graphs.hyperbolicity import hyperbolicity
891
sage: G = Graph()
892
sage: hyperbolicity(G,algorithm='cuts', additive_gap=-1)
893
Traceback (most recent call last):
894
...
895
ValueError: The additive gap must be >= 0 when using the 'cuts' algorithm.
896
897
Asking for an unknown algorithm::
898
899
sage: from sage.graphs.hyperbolicity import hyperbolicity
900
sage: G = Graph()
901
sage: hyperbolicity(G,algorithm='tip top')
902
Traceback (most recent call last):
903
...
904
ValueError: Algorithm 'tip top' not yet implemented. Please contribute.
905
"""
906
if not isinstance(G,Graph):
907
raise ValueError("The input parameter must be a Graph.")
908
if not algorithm in ['basic', 'cuts', 'cuts+', 'dom']:
909
raise ValueError("Algorithm '%s' not yet implemented. Please contribute." %(algorithm))
910
if approximation_factor is None:
911
approximation_factor = 1.0
912
elif algorithm=='cuts' and (not approximation_factor in RR or approximation_factor < 1.0):
913
raise ValueError("The approximation factor must be >= 1.0.")
914
elif algorithm!='cuts':
915
print "The approximation_factor is ignored when using the '%s' algorithm." %(algorithm)
916
if additive_gap is None:
917
additive_gap = 1.0 if algorithm=='cuts+' else 0.0
918
elif algorithm=='cuts' or algorithm=='cuts+':
919
if not additive_gap in RR:
920
raise ValueError("The additive gap must be a real positive number.")
921
elif algorithm=='cuts' and additive_gap < 0.0:
922
raise ValueError("The additive gap must be >= 0 when using the '%s' algorithm." %(algorithm))
923
elif algorithm=='cuts+' and additive_gap < 1.0:
924
raise ValueError("The additive gap must be >= 1 when using the '%s' algorithm." %(algorithm))
925
else:
926
print "The additive_gap is ignored when using the '%s' algorithm." %(algorithm)
927
928
# The hyperbolicity is defined on connected graphs
929
if not G.is_connected():
930
raise ValueError("The input Graph must be connected.")
931
932
# The hyperbolicity of some classes of graphs is known. If it is easy and
933
# fast to test that a graph belongs to one of these classes, we do it.
934
if G.num_verts() <= 3:
935
# The hyperbolicity of a graph with 3 vertices is 0.
936
# The certificate is the set of vertices.
937
return 0, G.vertices(), 0
938
939
elif G.num_verts() == G.num_edges() + 1:
940
# G is a tree
941
# Any set of 4 vertices is a valid certificate
942
return 0, G.vertices()[:4], 0
943
944
elif G.is_clique():
945
# Any set of 4 vertices is a valid certificate
946
return 0, G.vertices()[:4], 0
947
948
cdef unsigned short * _distances_
949
cdef unsigned short ** distances
950
cdef int i, j, k, N, hyp, hyp_UB, hh, hh_UB, D
951
cdef dict distr = {}
952
cdef list certificate = []
953
cdef list certif
954
cdef dict mymap, myinvmap
955
956
# We search for the largest 2-connected component. Indeed, the hyperbolicity
957
# of a graph is the maximum over its 2-connected components.
958
B,C = G.blocks_and_cut_vertices()
959
960
if verbose:
961
# we compute the distribution of size of the blocks
962
for V in B:
963
i = len(V)
964
if i in distr:
965
distr[ i ] += 1
966
else:
967
distr[ i ] = 1
968
print "Graph with %d blocks" %(len(B))
969
print "Blocks size distribution:", distr
970
971
hyp = 0
972
for V in B:
973
974
# The hyperbolicity of a graph with 3 vertices is 0, and a graph cannot
975
# have hyperbolicity larger than N/2. So we consider only larger
976
# 2-connected subgraphs.
977
if len(V) > max( 3, 2*hyp) :
978
979
# We build the subgraph and we relabel the vertices to ensure
980
# integer vertex names in the range [0..N-1] since the
981
# c_distances_all_pairs uses integer labels in the range [0..N-1].
982
H, mymap = _my_subgraph(G, V, relabel=True, return_map=True)
983
N = H.num_verts()
984
985
# We test if the block belongs to a graph class with known
986
# hyperbolicity and a fast test.
987
if H.is_clique():
988
continue
989
990
# We compute the distances and store the results in a 2D array, and
991
# the diameter
992
_distances_ = c_distances_all_pairs(H)
993
distances = <unsigned short **>sage_malloc(sizeof(unsigned short *)*N)
994
if distances == NULL:
995
sage_free(_distances_)
996
raise MemoryError
997
998
D = 0
999
for 0 <= i < N:
1000
distances[i] = _distances_+i*N
1001
for 0 <= j < N:
1002
if distances[i][j] > D:
1003
D = distances[i][j]
1004
1005
# We call the cython function for computing the hyperbolicity with
1006
# the required parameters.
1007
if algorithm == 'cuts':
1008
hh, certif, hh_UB = __hyperbolicity__(N, distances, D, hyp, approximation_factor, 2*additive_gap, [], verbose)
1009
hh_UB = min( hh_UB, D)
1010
1011
elif algorithm == 'cuts+':
1012
# We compute the elimination ordering of simplicial vertices of H
1013
elim = elimination_ordering_of_simplicial_vertices(H, max(2,floor(N**(1/2.0))), verbose)
1014
if len(elim) > N-4:
1015
# We know that this component has hyperbolicity <= 1
1016
certif = H.vertices()[:4]
1017
hh = __hyp__(distances,certif[0],certif[1],certif[2],certif[3])
1018
hh_UB = 2
1019
else:
1020
hh, certif, hh_UB = __hyperbolicity__(N, distances, D, hyp, 1.0, 2*additive_gap-2, elim, verbose)
1021
hh_UB = min( hh_UB+2, D)
1022
1023
elif algorithm == 'dom':
1024
# Computes a dominating set DOM of H, and computes the
1025
# hyperbolicity considering only vertices in DOM
1026
DOM = _greedy_dominating_set(H)
1027
elim = [u for u in H.vertex_iterator() if not u in DOM]
1028
# We need at least 4 vertices
1029
while len(DOM)<4:
1030
DOM.append(elim.pop())
1031
hh, certif, hh_UB = __hyperbolicity__(N, distances, D, hyp, 1.0, 0.0, elim, verbose)
1032
hh_UB = min( hh+8, D)
1033
1034
elif algorithm == 'basic':
1035
hh, certif = __hyperbolicity_basic_algorithm__(N, distances, verbose)
1036
hh_UB = hh
1037
1038
# We test if the new computed value improves upon previous value.
1039
if hh > hyp or (hh==hyp and not certificate):
1040
hyp = hh
1041
hyp_UB = hh_UB
1042
1043
# We construct the inverse mapping of the relabeling of the
1044
# vertices of the subgraph
1045
myinvmap = dict([(mymap[x],x) for x in mymap])
1046
1047
# We then construct the correct certificate
1048
certificate = [myinvmap[u] for u in certif]
1049
1050
# We now release the memory
1051
sage_free(distances)
1052
sage_free(_distances_)
1053
1054
# Last, we return the computed value and the certificate
1055
return ZZ(hyp)/2, sorted(certificate), ZZ(hyp_UB)/2
1056
1057
1058
######################################################################
1059
# Distribution of the hyperbolicity of 4-tuples
1060
######################################################################
1061
1062
cdef dict __hyperbolicity_distribution__(int N, unsigned short ** distances):
1063
"""
1064
Return the distribution of the hyperbolicity of the 4-tuples of the graph.
1065
1066
The hyperbolicity of a graph has been defined by Gromov [Gromov87]_ as
1067
follows: Let `a, b, c, d` be vertices of the graph, let `S_1 = dist(a, b) +
1068
dist(b, c)`, `S_2 = dist(a, c) + dist(b, d)`, and `S_3 = dist(a, d) +
1069
dist(b, c)`, and let `M_1` and `M_2` be the two largest values among `S_1`,
1070
`S_2`, and `S_3`. We have `hyp(a, b, c, d) = |M_1 - M_2|`, and the
1071
hyperbolicity of the graph is the maximum over all possible 4-tuples `(a, b,
1072
c, d)` divided by 2.
1073
1074
The computation of the hyperbolicity of each 4-tuple, and so the
1075
hyperbolicity distribution, takes time in `O( n^4 )`.
1076
1077
We use ``unsigned long int`` on 64 bits, so ``uint64_t``, to count the
1078
number of 4-tuples of given hyperbolicity. So we cannot exceed `2^64-1`.
1079
This value should be sufficient for most users.
1080
1081
INPUT:
1082
1083
- ``N`` -- number of vertices of the graph (and side of the matrix)
1084
1085
- ``distances`` -- matrix of distances in the graph
1086
1087
OUTPUT:
1088
1089
- ``hdict`` -- A dictionnary such that hdict[i] is the number of 4-tuples of
1090
hyperbolicity i among the considered 4-tuples.
1091
"""
1092
# We initialize the table of hyperbolicity. We use an array of unsigned long
1093
# int instead of a dictionnary since it is much faster.
1094
cdef int i
1095
1096
cdef uint64_t * hdistr = <uint64_t *>sage_calloc(N+1,sizeof(uint64_t))
1097
if hdistr == NULL:
1098
raise MemoryError
1099
1100
# We now compute the hyperbolicity of each 4-tuple
1101
cdef int a, b, c, d
1102
for 0 <= a < N-3:
1103
for a < b < N-2:
1104
for b < c < N-1:
1105
for c < d < N:
1106
hdistr[ __hyp__(distances, a, b, c, d) ] += 1
1107
1108
# We prepare the dictionnary of hyperbolicity distribution to return
1109
Nchoose4 = binomial(N,4)
1110
cdef dict hdict = {ZZ(i)/2: (ZZ(hdistr[i])/Nchoose4) for 0 <= i <= N if hdistr[i] > 0}
1111
1112
sage_free(hdistr)
1113
1114
return hdict
1115
1116
1117
# We use this trick since it is way faster than using the sage randint function.
1118
cdef extern from "stdlib.h":
1119
long c_libc_random "random"()
1120
void c_libc_srandom "srandom"(unsigned int seed)
1121
1122
cdef dict __hyperbolicity_sampling__(int N, unsigned short ** distances, uint64_t sampling_size):
1123
"""
1124
Return a sampling of the hyperbolicity distribution of the graph.
1125
1126
The hyperbolicity of a graph has been defined by Gromov [Gromov87]_ as
1127
follows: Let `a, b, c, d` be vertices of the graph, let `S_1 = dist(a, b) +
1128
dist(b, c)`, `S_2 = dist(a, c) + dist(b, d)`, and `S_3 = dist(a, d) +
1129
dist(b, c)`, and let `M_1` and `M_2` be the two largest values among `S_1`,
1130
`S_2`, and `S_3`. We have `hyp(a, b, c, d) = |M_1 - M_2|`, and the
1131
hyperbolicity of the graph is the maximum over all possible 4-tuples `(a, b,
1132
c, d)` divided by 2.
1133
1134
We use ``unsigned long int`` on 64 bits, so ``uint64_t``, to count the
1135
number of 4-tuples of given hyperbolicity. So we cannot exceed `2^64-1`.
1136
This value should be sufficient for most users.
1137
1138
INPUT:
1139
1140
- ``N`` -- number of vertices of the graph (and side of the matrix)
1141
1142
- ``distances`` -- matrix of distances in the graph
1143
1144
- ``sampling_size`` -- number of 4-tuples considered. Default value is 1000.
1145
1146
OUTPUT:
1147
1148
- ``hdict`` -- A dictionnary such that hdict[i] is the number of 4-tuples of
1149
hyperbolicity i among the considered 4-tuples.
1150
"""
1151
cdef int i, a, b, c, d
1152
cdef uint64_t j
1153
1154
if N < 4:
1155
raise ValueError("N must be at least 4")
1156
1157
# We initialize the table of hyperbolicity. We use an array of unsigned long
1158
# int instead of a dictionnary since it is much faster.
1159
cdef uint64_t * hdistr = <uint64_t *>sage_calloc(N+1,sizeof(uint64_t))
1160
if hdistr == NULL:
1161
raise MemoryError
1162
1163
# We now compute the hyperbolicity of each quadruple
1164
for 0 <= j < sampling_size:
1165
a = c_libc_random() % N
1166
b = c_libc_random() % N
1167
c = c_libc_random() % N
1168
d = c_libc_random() % N
1169
while a == b:
1170
b = c_libc_random() % N
1171
while a == c or b == c:
1172
c = c_libc_random() % N
1173
while a == d or b == d or c == d:
1174
d = c_libc_random() % N
1175
1176
hdistr[ __hyp__(distances, a, b, c, d) ] += 1
1177
1178
# We prepare the dictionnary of hyperbolicity distribution from sampling
1179
cdef dict hdict = dict( [ (ZZ(i)/2, ZZ(hdistr[i])/ZZ(sampling_size)) for 0 <= i <= N if hdistr[i] > 0 ] )
1180
1181
sage_free(hdistr)
1182
1183
return hdict
1184
1185
1186
def hyperbolicity_distribution(G, algorithm='sampling', sampling_size=10**6):
1187
r"""
1188
Return the hyperbolicity distribution of the graph or a sampling of it.
1189
1190
The hyperbolicity of a graph has been defined by Gromov [Gromov87]_ as
1191
follows: Let `a, b, c, d` be vertices of the graph, let `S_1 = dist(a, b) +
1192
dist(b, c)`, `S_2 = dist(a, c) + dist(b, d)`, and `S_3 = dist(a, d) +
1193
dist(b, c)`, and let `M_1` and `M_2` be the two largest values among `S_1`,
1194
`S_2`, and `S_3`. We have `hyp(a, b, c, d) = |M_1 - M_2|`, and the
1195
hyperbolicity of the graph is the maximum over all possible 4-tuples `(a, b,
1196
c, d)` divided by 2.
1197
1198
The computation of the hyperbolicity of each 4-tuple, and so the
1199
hyperbolicity distribution, takes time in `O( n^4 )`.
1200
1201
INPUT:
1202
1203
- ``G`` -- a Graph.
1204
1205
- ``algorithm`` -- (default: 'sampling') When algorithm is 'sampling', it
1206
returns the distribution of the hyperbolicity over a sample of
1207
``sampling_size`` 4-tuples. When algorithm is 'exact', it computes the
1208
distribution of the hyperbolicity over all 4-tuples. Be aware that the
1209
computation time can be HUGE.
1210
1211
- ``sampling_size`` -- (default: `10^6`) number of 4-tuples considered in
1212
the sampling. Used only when ``algorithm == 'sampling'``.
1213
1214
OUTPUT:
1215
1216
- ``hdict`` -- A dictionnary such that hdict[i] is the number of 4-tuples of
1217
hyperbolicity i.
1218
1219
EXAMPLES:
1220
1221
Exact hyperbolicity distribution of the Petersen Graph::
1222
1223
sage: from sage.graphs.hyperbolicity import hyperbolicity_distribution
1224
sage: G = graphs.PetersenGraph()
1225
sage: hyperbolicity_distribution(G,algorithm='exact')
1226
{0: 3/7, 1/2: 4/7}
1227
1228
Exact hyperbolicity distribution of a `3\times 3` grid::
1229
1230
sage: from sage.graphs.hyperbolicity import hyperbolicity_distribution
1231
sage: G = graphs.GridGraph([3,3])
1232
sage: hyperbolicity_distribution(G,algorithm='exact')
1233
{0: 11/18, 1: 8/21, 2: 1/126}
1234
1235
TESTS:
1236
1237
Giving anythin else than a Graph::
1238
1239
sage: from sage.graphs.hyperbolicity import hyperbolicity_distribution
1240
sage: hyperbolicity_distribution([])
1241
Traceback (most recent call last):
1242
...
1243
ValueError: The input parameter must be a Graph.
1244
1245
Giving a non connected graph::
1246
1247
sage: from sage.graphs.hyperbolicity import hyperbolicity_distribution
1248
sage: G = Graph([(0,1),(2,3)])
1249
sage: hyperbolicity_distribution(G)
1250
Traceback (most recent call last):
1251
...
1252
ValueError: The input Graph must be connected.
1253
"""
1254
if not isinstance(G,Graph):
1255
raise ValueError("The input parameter must be a Graph.")
1256
# The hyperbolicity is defined on connected graphs
1257
if not G.is_connected():
1258
raise ValueError("The input Graph must be connected.")
1259
1260
# The hyperbolicity distribution of some classes of graphs is known. If it
1261
# is easy and fast to test that a graph belongs to one of these classes, we
1262
# do it.
1263
if (G.num_verts()==G.num_edges()+1) or G.is_clique():
1264
return {0: sampling_size if algorithm=='sampling' else binomial(G.num_verts(),4)}
1265
1266
cdef int N = G.num_verts()
1267
cdef int i, j
1268
cdef unsigned short ** distances
1269
cdef unsigned short * _distances_
1270
cdef dict hdict
1271
1272
# We compute the all pairs shortest path and store the result in a 2D array
1273
# for faster access.
1274
H = G.relabel( inplace = False )
1275
_distances_ = c_distances_all_pairs(H)
1276
distances = <unsigned short **>sage_malloc(sizeof(unsigned short *)*N)
1277
if distances == NULL:
1278
sage_free(_distances_)
1279
raise MemoryError
1280
1281
for 0 <= i < N:
1282
distances[i] = _distances_+i*N
1283
1284
if algorithm == 'exact':
1285
hdict = __hyperbolicity_distribution__(N, distances)
1286
elif algorithm == 'sampling':
1287
hdict = __hyperbolicity_sampling__(N, distances, sampling_size)
1288
else:
1289
raise ValueError("Algorithm '%s' not yet implemented. Please contribute." %(algorithm))
1290
1291
# We release memory
1292
sage_free(distances)
1293
sage_free(_distances_)
1294
1295
return hdict
1296
1297