Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
sagemath
GitHub Repository: sagemath/sagesmc
Path: blob/master/src/sage/graphs/base/static_sparse_backend.pyx
8815 views
1
r"""
2
Static sparse graph backend
3
4
This module implement a immutable sparse graph backend using the data structure
5
from :mod:`sage.graphs.base.static_sparse_graph`. It supports both directed and
6
undirected graphs, as well as vertex/edge labels, loops and multiple edges. As
7
it uses a very compact C structure it should be very small in memory.
8
9
As it is a sparse data structure, you can expect it to be very efficient when
10
you need to list the graph's edge, or those incident to a vertex, but an
11
adjacency test can be much longer than in a dense data structure (i.e. like in
12
:mod:`sage.graphs.base.static_dense_graph`)
13
14
Two classes
15
-----------
16
17
This module implements two classes
18
19
* :class:`StaticSparseCGraph` extends :class:`~sage.graphs.base.c_graph.CGraph`
20
and is a Cython class that manages the definition/deallocation of the
21
``short_digraph`` structure. It does not know anything about labels on
22
vertices.
23
24
* :class:`StaticSparseBackend` extends
25
:class:`~sage.graphs.base.c_graph.CGraphBackend` and is a Python class that
26
does know about vertex labels and contains an instance of
27
:class:`StaticSparseCGraph` as an internal variable. The input/output of its
28
methods are labeled vertices, which it translates to integer id before
29
forwarding them to the :class:`StaticSparseCGraph` instance.
30
31
Classes and methods
32
-------------------
33
"""
34
from sage.graphs.base.static_sparse_graph cimport (init_short_digraph,
35
init_reverse,
36
out_degree,
37
has_edge,
38
free_short_digraph,
39
edge_label)
40
from c_graph import CGraphBackend
41
from sage.misc.bitset cimport FrozenBitset
42
from libc.stdint cimport uint32_t
43
include 'sage/misc/bitset.pxi'
44
45
cdef class StaticSparseCGraph(CGraph):
46
"""
47
:mod:`CGraph <sage.graphs.base.c_graph>` class based on the sparse graph
48
data structure :mod:`static sparse graphs
49
<sage.graphs.base.static_sparse_graph>`.
50
"""
51
52
def __cinit__(self, G):
53
r"""
54
Cython constructor
55
56
INPUT:
57
58
- ``G`` -- a :class:`Graph` object.
59
60
TEST::
61
62
sage: from sage.graphs.base.static_sparse_backend import StaticSparseCGraph
63
sage: g = StaticSparseCGraph(graphs.PetersenGraph())
64
"""
65
has_labels = any(not l is None for _,_,l in G.edge_iterator())
66
self.directed = G.is_directed()
67
68
init_short_digraph(self.g, G, edge_labelled = has_labels)
69
if self.directed:
70
init_reverse(self.g_rev,self.g)
71
72
# Defining the meaningless set of 'active' vertices. Because of CGraph.
73
bitset_init(self.active_vertices, self.g.n+1)
74
bitset_set_first_n(self.active_vertices, self.g.n)
75
76
def __dealloc__(self):
77
r"""
78
Freeing the memory
79
80
TEST::
81
82
sage: from sage.graphs.base.static_sparse_backend import StaticSparseCGraph
83
sage: g = StaticSparseCGraph(graphs.PetersenGraph())
84
"""
85
bitset_free(self.active_vertices)
86
free_short_digraph(self.g)
87
if self.g_rev != NULL:
88
free_short_digraph(self.g_rev)
89
90
cpdef bint has_vertex(self, int n):
91
r"""
92
Tests if a vertex belongs to the graph
93
94
INPUT:
95
96
- ``n`` -- an integer
97
98
TEST::
99
100
sage: from sage.graphs.base.static_sparse_backend import StaticSparseCGraph
101
sage: g = StaticSparseCGraph(graphs.PetersenGraph())
102
sage: g.has_vertex(1)
103
True
104
sage: g.has_vertex(10)
105
False
106
"""
107
return 0 <= n and n < self.g.n
108
109
cdef int add_vertex_unsafe(self, int k):
110
raise ValueError("Thou shalt not add a vertex to an immutable graph")
111
112
def add_vertex(self, int k):
113
r"""
114
Adds a vertex to the graph. No way.
115
116
TEST::
117
118
sage: from sage.graphs.base.static_sparse_backend import StaticSparseCGraph
119
sage: g = StaticSparseCGraph(graphs.PetersenGraph())
120
sage: g.add_vertex(45)
121
Traceback (most recent call last):
122
...
123
ValueError: Thou shalt not add a vertex to an immutable graph
124
125
"""
126
raise ValueError("Thou shalt not add a vertex to an immutable graph")
127
128
def del_vertex(self, int k):
129
r"""
130
Removes a vertex from the graph. No way.
131
132
TEST::
133
134
sage: from sage.graphs.base.static_sparse_backend import StaticSparseCGraph
135
sage: g = StaticSparseCGraph(graphs.PetersenGraph())
136
sage: g.del_vertex(45)
137
Traceback (most recent call last):
138
...
139
ValueError: Thou shalt not remove a vertex from an immutable graph
140
141
"""
142
raise ValueError("Thou shalt not remove a vertex from an immutable graph")
143
144
cdef int del_vertex_unsafe(self, int v):
145
raise ValueError("Thou shalt not remove a vertex from an immutable graph")
146
147
cpdef list verts(self):
148
r"""
149
Returns the list of vertices
150
151
TEST::
152
153
sage: from sage.graphs.base.static_sparse_backend import StaticSparseCGraph
154
sage: g = StaticSparseCGraph(graphs.PetersenGraph())
155
sage: g.verts()
156
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
157
"""
158
return range(self.g.n)
159
160
cdef int has_arc_unsafe(self, int u, int v):
161
return ((0 <= u) and
162
(0 <= v) and
163
(u < self.g.n) and
164
(v < self.g.n) and
165
has_edge(self.g, u, v) != NULL)
166
167
cpdef bint has_arc(self, int u, int v):
168
r"""
169
Tests if uv is an edge of the graph
170
171
INPUT:
172
173
- ``u,v`` -- integers
174
175
TEST::
176
177
sage: from sage.graphs.base.static_sparse_backend import StaticSparseCGraph
178
sage: g = StaticSparseCGraph(graphs.PetersenGraph())
179
sage: g.has_arc(0,1)
180
True
181
sage: g.has_arc(0,7)
182
False
183
"""
184
return self.has_arc_unsafe(u, v)
185
186
cdef int out_neighbors_unsafe(self, int u, int *neighbors, int size) except? -2:
187
cdef int degree = self.g.neighbors[u+1] - self.g.neighbors[u]
188
cdef int i
189
for i in range(min(degree,size)):
190
neighbors[i] = self.g.neighbors[u][i]
191
return -1 if size < degree else degree
192
193
cdef int in_neighbors_unsafe(self, int u, int *neighbors, int size) except? -2:
194
if not self.directed:
195
return self.out_neighbors_unsafe(u,neighbors,size)
196
197
cdef int degree = self.g_rev.neighbors[u+1] - self.g_rev.neighbors[u]
198
cdef int i
199
for i in range(min(degree,size)):
200
neighbors[i] = self.g_rev.neighbors[u][i]
201
return -1 if size < degree else degree
202
203
cpdef list out_neighbors(self, int u):
204
r"""
205
List the out-neighbors of a vertex
206
207
INPUT:
208
209
- ``u`` -- a vertex
210
211
TEST::
212
213
sage: from sage.graphs.base.static_sparse_backend import StaticSparseCGraph
214
sage: g = StaticSparseCGraph(graphs.PetersenGraph())
215
sage: g.out_neighbors(0)
216
[1, 4, 5]
217
"""
218
if u<0 or u>self.g.n:
219
raise LookupError("The vertex does not belong to the graph")
220
221
cdef int i
222
return [<int> self.g.neighbors[u][i] for i in range(out_degree(self.g,u))]
223
224
cpdef list in_neighbors(self, int u):
225
r"""
226
Returns the in-neighbors of a vertex
227
228
INPUT:
229
230
- ``u`` -- a vertex
231
232
TEST::
233
234
sage: from sage.graphs.base.static_sparse_backend import StaticSparseCGraph
235
sage: g = StaticSparseCGraph(graphs.PetersenGraph())
236
sage: g.in_neighbors(0)
237
[1, 4, 5]
238
"""
239
if not self.directed:
240
return self.out_neighbors(u)
241
242
if u<0 or u>self.g.n:
243
raise LookupError("The vertex does not belong to the graph")
244
245
cdef int i
246
return [<int> self.g_rev.neighbors[u][i] for i in range(out_degree(self.g_rev,u))]
247
248
cpdef int out_degree(self, int u):
249
r"""
250
Returns the out-degree of a vertex
251
252
INPUT:
253
254
- ``u`` -- a vertex
255
256
TEST::
257
258
sage: from sage.graphs.base.static_sparse_backend import StaticSparseCGraph
259
sage: g = StaticSparseCGraph(graphs.PetersenGraph())
260
sage: g.out_degree(0)
261
3
262
"""
263
if u<0 or u>self.g.n:
264
raise LookupError("The vertex does not belong to the graph")
265
266
return self.g.neighbors[u+1] - self.g.neighbors[u]
267
268
cpdef int in_degree(self, int u):
269
r"""
270
Returns the in-degree of a vertex
271
272
INPUT:
273
274
- ``u`` -- a vertex
275
276
TEST::
277
278
sage: from sage.graphs.base.static_sparse_backend import StaticSparseCGraph
279
sage: g = StaticSparseCGraph(graphs.PetersenGraph())
280
sage: g.in_degree(0)
281
3
282
"""
283
if u<0 or u>self.g.n:
284
raise LookupError("The vertex does not belong to the graph")
285
286
if not self.directed:
287
return self.g.neighbors[u+1] - self.g.neighbors[u]
288
else:
289
return self.g_rev.neighbors[u+1] - self.g_rev.neighbors[u]
290
291
class StaticSparseBackend(CGraphBackend):
292
293
def __init__(self, G, loops = False, multiedges=False):
294
"""
295
A graph :mod:`backend <sage.graphs.base.graph_backends>` for static
296
sparse graphs.
297
298
EXAMPLE::
299
300
sage: D = sage.graphs.base.sparse_graph.SparseGraphBackend(9)
301
sage: D.add_edge(0,1,None,False)
302
sage: list(D.iterator_edges(range(9), True))
303
[(0, 1, None)]
304
305
::
306
307
sage: from sage.graphs.base.static_sparse_backend import StaticSparseBackend
308
sage: g = StaticSparseBackend(graphs.PetersenGraph())
309
sage: list(g.iterator_edges([0],1))
310
[(0, 1, None), (0, 4, None), (0, 5, None)]
311
312
::
313
314
sage: g=DiGraph(digraphs.DeBruijn(4,3),data_structure="static_sparse")
315
sage: gi=DiGraph(g,data_structure="static_sparse")
316
sage: gi.edges()[0]
317
('000', '000', '0')
318
sage: gi.edges_incident('111')
319
[('111', '110', '0'), ('111', '111', '1'), ('111', '112', '2'), ('111', '113', '3')]
320
sage: sorted(g.edges()) == sorted(gi.edges())
321
True
322
323
::
324
325
sage: g = graphs.PetersenGraph()
326
sage: gi=Graph(g,data_structure="static_sparse")
327
sage: g == gi
328
True
329
sage: sorted(g.edges()) == sorted(gi.edges())
330
True
331
332
::
333
334
sage: gi = Graph( { 0: {1: 1}, 1: {2: 1}, 2: {3: 1}, 3: {4: 2}, 4: {0: 2} }, data_structure="static_sparse")
335
sage: (0,4,2) in gi.edges()
336
True
337
sage: gi.has_edge(0,4)
338
True
339
340
::
341
342
sage: G = Graph({1:{2:28, 6:10}, 2:{3:16, 7:14}, 3:{4:12}, 4:{5:22, 7:18}, 5:{6:25, 7:24}})
343
sage: GI = Graph({1:{2:28, 6:10}, 2:{3:16, 7:14}, 3:{4:12}, 4:{5:22, 7:18}, 5:{6:25, 7:24}}, data_structure="static_sparse")
344
sage: G == GI
345
True
346
347
::
348
349
sage: G = graphs.OddGraph(4)
350
sage: d = G.diameter()
351
sage: H = G.distance_graph(range(d+1))
352
sage: HI = Graph(H,data_structure="static_sparse")
353
sage: HI.size() == len(HI.edges())
354
True
355
356
::
357
358
sage: g = Graph({1:{1:[1,2,3]}}, data_structure="static_sparse")
359
sage: g.size()
360
3
361
sage: g.order()
362
1
363
sage: g.vertices()
364
[1]
365
sage: g.edges()
366
[(1, 1, 1), (1, 1, 2), (1, 1, 3)]
367
"""
368
cdef StaticSparseCGraph cg = <StaticSparseCGraph> StaticSparseCGraph(G)
369
self._cg = cg
370
371
# .directed and not ._directed. Because of CGraph.
372
self.directed = cg.directed
373
374
vertices = G.vertices()
375
self._order = len(vertices)
376
377
# Does it allow loops/multiedges ?
378
self._loops = loops
379
self._multiedges = multiedges
380
381
# Dictionary translating a vertex int to a label, and the other way around.
382
self._vertex_to_labels = vertices
383
self._vertex_to_int = {v:i for i,v in enumerate(vertices)}
384
385
# Needed by CGraph. The first one is just an alias, and the second is
386
# useless : accessing _vertex_to_labels (which is a list) is faster than
387
# vertex_labels (which is a dictionary)
388
self.vertex_ints = self._vertex_to_int
389
self.vertex_labels = {i:v for i,v in enumerate(vertices)}
390
391
def __reduce__(self):
392
"""
393
Return a tuple used for pickling this graph.
394
395
TESTS:
396
397
Pickling of the static graph backend makes pickling of immutable
398
graphs and digraphs work::
399
400
sage: G = Graph(graphs.PetersenGraph(), immutable=True)
401
sage: G == loads(dumps(G))
402
True
403
sage: uc = [[2,3], [], [1], [1], [1], [3,4]]
404
sage: D = DiGraph(dict([[i,uc[i]] for i in range(len(uc))]), immutable=True)
405
sage: loads(dumps(D)) == D
406
True
407
408
No problems with loops and multiple edges, with Labels::
409
410
sage: g = Graph(multiedges = True, loops = True)
411
sage: g.add_edges(2*graphs.PetersenGraph().edges())
412
sage: g.add_edge(0,0)
413
sage: g.add_edge(1,1, "a label")
414
sage: g.add_edge([(0,1,"labellll"), (0,1,"labellll"), (0,1,"LABELLLL")])
415
sage: g.add_vertex("isolated vertex")
416
sage: gi = g.copy(immutable=True)
417
sage: loads(dumps(gi)) == gi
418
True
419
420
Similar, with a directed graph::
421
422
sage: g = DiGraph(multiedges = True, loops = True)
423
sage: H = 2*(digraphs.Circuit(15)+DiGraph(graphs.PetersenGraph()))
424
sage: g.add_edges(H.edges())
425
sage: g.add_edge(0,0)
426
sage: g.add_edge(1,1, "a label")
427
sage: g.add_edge([(0,1,"labellll"), (0,1,"labellll"), (0,1,"LABELLLL")])
428
sage: g.add_vertex("isolated vertex")
429
sage: gi = g.copy(immutable=True)
430
sage: loads(dumps(gi)) == gi
431
True
432
"""
433
if self.directed:
434
from sage.graphs.digraph import DiGraph
435
G = DiGraph(loops=self._loops, multiedges=self._multiedges)
436
G.add_edges(list(self.iterator_out_edges(self.iterator_verts(None),True)))
437
else:
438
from sage.graphs.graph import Graph
439
G = Graph(loops=self._loops, multiedges=self._multiedges)
440
G.add_edges(list(self.iterator_edges(self.iterator_verts(None),True)))
441
442
G.add_vertices(self.iterator_verts(None))
443
return (StaticSparseBackend, (G, self._loops, self._multiedges))
444
445
def has_vertex(self, v):
446
r"""
447
Tests if the vertex belongs to the graph
448
449
INPUT:
450
451
- ``v`` -- a vertex (or not?)
452
453
TEST::
454
455
sage: from sage.graphs.base.static_sparse_backend import StaticSparseBackend
456
sage: g = StaticSparseBackend(graphs.PetersenGraph())
457
sage: g.has_vertex(0)
458
True
459
sage: g.has_vertex("Hey")
460
False
461
"""
462
return v in self._vertex_to_int
463
464
def relabel(self, perm, directed):
465
r"""
466
Relabel the graphs' vertices. No way.
467
468
TEST::
469
470
sage: from sage.graphs.base.static_sparse_backend import StaticSparseBackend
471
sage: g = StaticSparseBackend(graphs.PetersenGraph())
472
sage: g.relabel([],True)
473
Traceback (most recent call last):
474
...
475
ValueError: Thou shalt not relabel an immutable graph
476
477
"""
478
raise ValueError("Thou shalt not relabel an immutable graph")
479
480
def get_edge_label(self, object u, object v):
481
"""
482
Returns the edge label for ``(u,v)``.
483
484
INPUT:
485
486
- ``u,v`` -- two vertices
487
488
TEST::
489
490
sage: from sage.graphs.base.static_sparse_backend import StaticSparseBackend
491
sage: g = StaticSparseBackend(graphs.PetersenGraph())
492
sage: print g.get_edge_label(0,1)
493
None
494
sage: print g.get_edge_label(0,"Hey")
495
Traceback (most recent call last):
496
...
497
LookupError: One of the two vertices does not belong to the graph
498
sage: print g.get_edge_label(0,7)
499
Traceback (most recent call last):
500
...
501
LookupError: The edge does not exist
502
503
::
504
505
sage: from sage.graphs.base.static_sparse_backend import StaticSparseBackend
506
sage: g = StaticSparseBackend(digraphs.DeBruijn(3,2))
507
sage: g.has_edge('00','01','1')
508
True
509
sage: g.has_edge('00','01','0')
510
False
511
"""
512
try:
513
u = self._vertex_to_int[u]
514
v = self._vertex_to_int[v]
515
except KeyError:
516
raise LookupError("One of the two vertices does not belong to the graph")
517
518
cdef StaticSparseCGraph cg = self._cg
519
cdef list l
520
521
cdef uint32_t * edge = has_edge(cg.g,u,v)
522
if edge == NULL:
523
raise LookupError("The edge does not exist")
524
525
# At this level, edge points toward a edge from u to v in the graph, but
526
# not necessarily to the leftmost edge. Hence, we first decrease edge to
527
# make it point toward the leftmost such edge, then build the list of
528
# all labels.
529
if self.multiple_edges(None):
530
while edge > cg.g.neighbors[u] and (edge-1)[0] == v:
531
edge -= 1
532
l = []
533
while edge < cg.g.neighbors[u+1] and edge[0] == v:
534
l.append(edge_label(cg.g,edge))
535
edge += 1
536
return l
537
538
else:
539
return edge_label(cg.g,edge)
540
541
def has_edge(self, object u, object v, object l):
542
"""
543
Returns whether this graph has edge ``(u,v)`` with label ``l``.
544
545
If ``l`` is ``None``, return whether this graph has an edge ``(u,v)``
546
with any label.
547
548
INPUT:
549
550
- ``u,v`` -- two vertices
551
552
- ``l`` -- a label
553
554
TEST::
555
556
sage: from sage.graphs.base.static_sparse_backend import StaticSparseBackend
557
sage: g = StaticSparseBackend(graphs.PetersenGraph())
558
sage: g.has_edge(0,1,'e')
559
False
560
sage: g.has_edge(0,4,None)
561
True
562
"""
563
cdef uint32_t * edge = NULL
564
cdef StaticSparseCGraph cg = <StaticSparseCGraph> (self._cg)
565
try:
566
u = self._vertex_to_int[u]
567
v = self._vertex_to_int[v]
568
except KeyError:
569
raise LookupError("One of the two vertices does not belong to the graph")
570
571
edge = has_edge(cg.g,u,v)
572
if edge == NULL:
573
return False
574
if l is None:
575
return True
576
577
# At this level, edge points toward a edge from u to v in the graph, but
578
# not necessarily toward the right label. As there may be many uv edges
579
# with different labels, we first make edge point toward the leftmost uv
580
# edge, then scan them all to find the right label.
581
while edge > cg.g.neighbors[u] and (edge-1)[0] == v :
582
edge -= 1
583
584
while edge[0] == v and edge < cg.g.neighbors[u+1]:
585
if edge_label(cg.g,edge) == l:
586
return True
587
edge += 1
588
589
return False
590
591
def iterator_in_edges(self, object vertices, bint labels):
592
"""
593
Iterate over the incoming edges incident to a sequence of vertices.
594
595
INPUT:
596
597
- ``vertices`` -- a list of vertices
598
599
- ``labels`` -- whether to return labels too
600
601
TEST::
602
603
sage: from sage.graphs.base.static_sparse_backend import StaticSparseBackend
604
sage: g = StaticSparseBackend(graphs.PetersenGraph())
605
sage: list(g.iterator_in_edges([0],False))
606
[(0, 1), (0, 4), (0, 5)]
607
sage: list(g.iterator_in_edges([0],True))
608
[(0, 1, None), (0, 4, None), (0, 5, None)]
609
610
::
611
612
sage: DiGraph(digraphs.Path(5),immutable=False).incoming_edges([2])
613
[(1, 2, None)]
614
sage: DiGraph(digraphs.Path(5),immutable=True).incoming_edges([2])
615
[(1, 2, None)]
616
"""
617
cdef StaticSparseCGraph cg = self._cg
618
if not cg.directed:
619
for x in self.iterator_out_edges(vertices, labels):
620
yield x
621
return
622
623
try:
624
vertices = [self._vertex_to_int[x] for x in vertices]
625
except KeyError:
626
raise LookupError("One of the vertices does not belong to the graph")
627
628
cdef int i,j
629
for i in vertices:
630
vi = self._vertex_to_labels[i]
631
for j in range(out_degree(cg.g_rev,i)):
632
if labels:
633
yield (self._vertex_to_labels[cg.g_rev.neighbors[i][j]],
634
vi,
635
edge_label(cg.g_rev,cg.g_rev.neighbors[i]+j))
636
else:
637
yield self._vertex_to_labels[cg.g_rev.neighbors[i][j]], vi
638
639
def iterator_out_edges(self, object vertices, bint labels):
640
"""
641
Iterate over the outbound edges incident to a sequence of vertices.
642
643
INPUT:
644
645
- ``vertices`` -- a list of vertices
646
647
- ``labels`` -- whether to return labels too
648
649
TEST::
650
651
sage: from sage.graphs.base.static_sparse_backend import StaticSparseBackend
652
sage: g = StaticSparseBackend(graphs.PetersenGraph())
653
sage: list(g.iterator_out_edges([0], False))
654
[(0, 1), (0, 4), (0, 5)]
655
sage: list(g.iterator_out_edges([0],True))
656
[(0, 1, None), (0, 4, None), (0, 5, None)]
657
658
"""
659
try:
660
vertices = [self._vertex_to_int[x] for x in vertices]
661
except KeyError:
662
raise LookupError("One of the vertices does not belong to the graph")
663
664
cdef StaticSparseCGraph cg = self._cg
665
cdef int i,j
666
for i in vertices:
667
vi = self._vertex_to_labels[i]
668
for j in range(out_degree(cg.g,i)):
669
if labels:
670
yield (vi,
671
self._vertex_to_labels[cg.g.neighbors[i][j]],
672
edge_label(cg.g,cg.g.neighbors[i]+j))
673
else:
674
yield vi,self._vertex_to_labels[cg.g.neighbors[i][j]]
675
676
def iterator_verts(self, vertices):
677
r"""
678
Returns an iterator over the vertices
679
680
INPUT:
681
682
- ``vertices`` -- a list of objects. The method will only return the
683
elements of the graph which are contained in ``vertices``. It's not
684
very efficient. If ``vertices`` is equal to ``None``, all the vertices
685
are returned.
686
687
TEST::
688
689
sage: from sage.graphs.base.static_sparse_backend import StaticSparseBackend
690
sage: g = StaticSparseBackend(graphs.PetersenGraph())
691
sage: list(g.iterator_verts(None))
692
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
693
sage: list(g.iterator_verts([1,"Hey","I am a french fry"]))
694
[1]
695
"""
696
if vertices is None:
697
return iter(self._vertex_to_labels)
698
else:
699
return (x for x in self._vertex_to_labels if x in vertices)
700
701
def num_verts(self):
702
r"""
703
Returns the number of vertices
704
705
TEST::
706
707
sage: from sage.graphs.base.static_sparse_backend import StaticSparseBackend
708
sage: g = StaticSparseBackend(graphs.PetersenGraph())
709
sage: g.num_verts()
710
10
711
"""
712
return self._order
713
714
def allows_loops(self, value=None):
715
r"""
716
Returns whether the graph allows loops
717
718
INPUT:
719
720
- ``value`` -- only useful for compatibility with other graph backends,
721
where this method can be used to define this boolean. This method
722
raises an exception if ``value`` is not equal to ``None``.
723
724
TEST::
725
726
sage: from sage.graphs.base.static_sparse_backend import StaticSparseBackend
727
sage: g = StaticSparseBackend(graphs.PetersenGraph())
728
sage: g.allows_loops()
729
False
730
sage: g = StaticSparseBackend(graphs.PetersenGraph(), loops=True)
731
sage: g.allows_loops()
732
True
733
"""
734
if value is None:
735
return self._loops
736
else:
737
raise ValueError("The graph is immutable. You cannot change it in any way !")
738
739
def multiple_edges(self, value=None):
740
r"""
741
Returns whether the graph allows multiple edges
742
743
INPUT:
744
745
- ``value`` -- only useful for compatibility with other graph backends,
746
where this method can be used to define this boolean. This method
747
raises an exception if ``value`` is not equal to ``None``.
748
749
TEST::
750
751
sage: from sage.graphs.base.static_sparse_backend import StaticSparseBackend
752
sage: g = StaticSparseBackend(graphs.PetersenGraph())
753
sage: g.multiple_edges()
754
False
755
sage: g = StaticSparseBackend(graphs.PetersenGraph(), multiedges=True)
756
sage: g.multiple_edges()
757
True
758
"""
759
if value is None:
760
return self._multiedges
761
else:
762
raise ValueError("The graph is immutable. You cannot change it in any way !")
763
764
def num_edges(self,directed):
765
r"""
766
Returns the number of edges
767
768
INPUT:
769
770
- ``directed`` (boolean) -- whether to consider the graph as directed or
771
not.
772
773
TEST::
774
775
sage: from sage.graphs.base.static_sparse_backend import StaticSparseBackend
776
sage: g = StaticSparseBackend(graphs.PetersenGraph())
777
sage: g.num_edges(False)
778
15
779
780
Testing the exception::
781
782
sage: g = StaticSparseBackend(digraphs.Circuit(4))
783
sage: g.num_edges(False)
784
Traceback (most recent call last):
785
...
786
NotImplementedError: Sorry, I have no idea what is expected in this situation. I don't think that it is well-defined either, especially for multigraphs.
787
788
:trac:`15491`::
789
790
sage: g=digraphs.RandomDirectedGNP(10,.3)
791
sage: gi=DiGraph(g,data_structure="static_sparse")
792
sage: gi.size() == len(gi.edges())
793
True
794
"""
795
cdef StaticSparseCGraph cg = <StaticSparseCGraph> self._cg
796
797
if directed:
798
if cg.directed:
799
# Returns the real number of directed arcs
800
return int(cg.g.m)
801
else:
802
# Returns twice the number of edges, minus the number of
803
# loops. This is actually equal to the index of
804
# cg.g.neighbors[cg.g.n] in the array `cg.g.edges`
805
return int(cg.g.neighbors[cg.g.n]-cg.g.edges)
806
else:
807
if cg.directed:
808
raise NotImplementedError("Sorry, I have no idea what is expected "
809
"in this situation. I don't think "
810
"that it is well-defined either, "
811
"especially for multigraphs.")
812
else:
813
# Returns the number of edges
814
return int(cg.g.m)
815
816
def iterator_edges(self, vertices, bint labels):
817
r"""
818
Returns an iterator over the graph's edges.
819
820
INPUT:
821
822
- ``vertices`` -- only returns the edges incident to at least one vertex
823
of ``vertices``.
824
825
- ``labels`` -- whether to return edge labels too
826
827
TEST::
828
829
sage: from sage.graphs.base.static_sparse_backend import StaticSparseBackend
830
sage: g = StaticSparseBackend(graphs.PetersenGraph())
831
sage: list(g.iterator_edges(g.iterator_verts(None), False))
832
[(0, 1), (0, 4), (0, 5), (1, 2), (1, 6), (2, 3), (2, 7),
833
(3, 4), (3, 8), (4, 9), (5, 7), (5, 8), (6, 8), (6, 9), (7, 9)]
834
835
:trac:`15665`::
836
837
sage: Graph(immutable=True).edges()
838
[]
839
"""
840
cdef FrozenBitset b_vertices
841
842
if not vertices:
843
return
844
845
if self._directed:
846
raise RuntimeError("This is not meant for directed graphs.")
847
848
try:
849
vertices = [self._vertex_to_int[x] for x in vertices]
850
b_vertices = FrozenBitset(vertices)
851
except KeyError:
852
raise LookupError("One of the vertices does not belong to the graph")
853
854
cdef StaticSparseCGraph cg = self._cg
855
cdef int i,j,tmp
856
857
for i in vertices:
858
vi = self._vertex_to_labels[i]
859
for tmp in range(out_degree(cg.g,i)):
860
j = cg.g.neighbors[i][tmp]
861
if j < i and j in b_vertices:
862
continue
863
if labels:
864
yield (vi,
865
self._vertex_to_labels[j],
866
edge_label(cg.g,cg.g.neighbors[i]+tmp))
867
else:
868
yield vi,self._vertex_to_labels[j]
869
870
def degree(self, v, directed):
871
r"""
872
Returns the degree of a vertex
873
874
INPUT:
875
876
- ``v`` -- a vertex
877
878
- ``directed`` -- boolean; whether to take into account the
879
orientation of this graph in counting the degree of ``v``.
880
881
EXAMPLE::
882
883
sage: g = Graph(graphs.PetersenGraph(), data_structure="static_sparse")
884
sage: g.degree(0)
885
3
886
"""
887
try:
888
v = self._vertex_to_int[v]
889
except KeyError:
890
raise LookupError("The vertex does not belong to the graph")
891
892
cdef StaticSparseCGraph cg = self._cg
893
894
if directed:
895
if cg.directed:
896
return cg.in_degree(v) + cg.out_degree(v)
897
else:
898
return 2*cg.out_degree(v)
899
else:
900
if cg.directed:
901
raise NotImplementedError("Sorry, I have no idea what is expected "
902
"in this situation. I don't think "
903
"that it is well-defined either, "
904
"especially for multigraphs.")
905
else:
906
return cg.out_degree(v)
907
908
def in_degree(self, v):
909
r"""
910
Returns the in-degree of a vertex
911
912
INPUT:
913
914
- ``v`` -- a vertex
915
916
EXAMPLE::
917
918
sage: g = DiGraph(graphs.PetersenGraph(), data_structure="static_sparse")
919
sage: g.in_degree(0)
920
3
921
"""
922
try:
923
v = self._vertex_to_int[v]
924
except KeyError:
925
raise LookupError("The vertex does not belong to the graph")
926
927
cdef StaticSparseCGraph cg = self._cg
928
929
if cg.directed:
930
return cg.in_degree(v)
931
else:
932
return cg.out_degree(v)
933
934
def out_degree(self, v):
935
r"""
936
Returns the out-degree of a vertex
937
938
INPUT:
939
940
- ``v`` -- a vertex
941
942
EXAMPLE::
943
944
sage: g = DiGraph(graphs.PetersenGraph(), data_structure="static_sparse")
945
sage: g.out_degree(0)
946
3
947
"""
948
try:
949
v = self._vertex_to_int[v]
950
except KeyError:
951
raise LookupError("The vertex does not belong to the graph")
952
953
cdef StaticSparseCGraph cg = self._cg
954
955
return cg.out_degree(v)
956
957
def iterator_nbrs(self, v):
958
r"""
959
Returns the neighbors of a vertex
960
961
INPUT:
962
963
- ``v`` -- a vertex
964
965
EXAMPLE::
966
967
sage: g = Graph(graphs.PetersenGraph(), data_structure="static_sparse")
968
sage: g.neighbors(0)
969
[1, 4, 5]
970
"""
971
try:
972
v = self._vertex_to_int[v]
973
except KeyError:
974
raise LookupError("The vertex does not belong to the graph")
975
976
cdef StaticSparseCGraph cg = self._cg
977
cdef int i
978
979
for i in range(out_degree(cg.g,v)):
980
yield self._vertex_to_labels[cg.g.neighbors[v][i]]
981
982
def iterator_out_nbrs(self, v):
983
r"""
984
Returns the out-neighbors of a vertex
985
986
INPUT:
987
988
- ``v`` -- a vertex
989
990
EXAMPLE::
991
992
sage: g = DiGraph(graphs.PetersenGraph(), data_structure="static_sparse")
993
sage: g.neighbors_out(0)
994
[1, 4, 5]
995
"""
996
try:
997
v = self._vertex_to_int[v]
998
except KeyError:
999
raise LookupError("The vertex does not belong to the graph")
1000
1001
cdef StaticSparseCGraph cg = self._cg
1002
cdef int i
1003
1004
for i in range(out_degree(cg.g,v)):
1005
yield self._vertex_to_labels[cg.g.neighbors[v][i]]
1006
1007
def iterator_in_nbrs(self, v):
1008
r"""
1009
Returns the out-neighbors of a vertex
1010
1011
INPUT:
1012
1013
- ``v`` -- a vertex
1014
1015
EXAMPLE::
1016
1017
sage: g = DiGraph(graphs.PetersenGraph(), data_structure="static_sparse")
1018
sage: g.neighbors_in(0)
1019
[1, 4, 5]
1020
"""
1021
try:
1022
v = self._vertex_to_int[v]
1023
except KeyError:
1024
raise LookupError("The vertex does not belong to the graph")
1025
1026
cdef StaticSparseCGraph cg = self._cg
1027
cdef short_digraph g
1028
1029
if cg.directed:
1030
for i in range(out_degree(cg.g_rev,v)):
1031
yield self._vertex_to_labels[cg.g_rev.neighbors[v][i]]
1032
else:
1033
for i in range(out_degree(cg.g,v)):
1034
yield self._vertex_to_labels[cg.g.neighbors[v][i]]
1035
1036
def _run_it_on_static_instead(f):
1037
r"""
1038
A decorator function to force the (Di)Graph functions to compute from a
1039
static sparse graph3
1040
1041
This decorator can be used on methods from (Di)Graph. When it is applied,
1042
the method that was meant to compute something on a graph first converts
1043
this graph to a static sparse graph, then does what it had to do on this new
1044
graph. Of course, it makes no sense to decorate Graph.add_vertex with it as
1045
such a method will never work on an immutable graph. But it can help find
1046
new bugs, from time to time.
1047
1048
EXAMPLE::
1049
1050
sage: from sage.graphs.base.static_sparse_backend import _run_it_on_static_instead
1051
sage: @_run_it_on_static_instead
1052
....: def new_graph_method(g):
1053
....: print "My backend is of type", g._backend
1054
sage: Graph.new_graph_method = new_graph_method
1055
sage: g = Graph(5)
1056
sage: print "My backend is of type", g._backend
1057
My backend is of type <class 'sage.graphs.base.sparse_graph.SparseGraphBackend'>
1058
sage: g.new_graph_method()
1059
My backend is of type <class 'sage.graphs.base.static_sparse_backend.StaticSparseBackend'>
1060
"""
1061
def same_function_on_static_version(*kwd,**kwds):
1062
if not isinstance(kwd[0]._backend,StaticSparseBackend):
1063
gcopy = kwd[0].copy(data_structure="static_sparse")
1064
return getattr(gcopy,f.__name__)(*kwd[1:],**kwds)
1065
else:
1066
return f(*kwd,**kwds)
1067
1068
return same_function_on_static_version
1069
1070