Path: blob/master/src/sage/graphs/base/static_sparse_backend.pyx
8815 views
r"""1Static sparse graph backend23This module implement a immutable sparse graph backend using the data structure4from :mod:`sage.graphs.base.static_sparse_graph`. It supports both directed and5undirected graphs, as well as vertex/edge labels, loops and multiple edges. As6it uses a very compact C structure it should be very small in memory.78As it is a sparse data structure, you can expect it to be very efficient when9you need to list the graph's edge, or those incident to a vertex, but an10adjacency test can be much longer than in a dense data structure (i.e. like in11:mod:`sage.graphs.base.static_dense_graph`)1213Two classes14-----------1516This module implements two classes1718* :class:`StaticSparseCGraph` extends :class:`~sage.graphs.base.c_graph.CGraph`19and is a Cython class that manages the definition/deallocation of the20``short_digraph`` structure. It does not know anything about labels on21vertices.2223* :class:`StaticSparseBackend` extends24:class:`~sage.graphs.base.c_graph.CGraphBackend` and is a Python class that25does know about vertex labels and contains an instance of26:class:`StaticSparseCGraph` as an internal variable. The input/output of its27methods are labeled vertices, which it translates to integer id before28forwarding them to the :class:`StaticSparseCGraph` instance.2930Classes and methods31-------------------32"""33from sage.graphs.base.static_sparse_graph cimport (init_short_digraph,34init_reverse,35out_degree,36has_edge,37free_short_digraph,38edge_label)39from c_graph import CGraphBackend40from sage.misc.bitset cimport FrozenBitset41from libc.stdint cimport uint32_t42include 'sage/misc/bitset.pxi'4344cdef class StaticSparseCGraph(CGraph):45"""46:mod:`CGraph <sage.graphs.base.c_graph>` class based on the sparse graph47data structure :mod:`static sparse graphs48<sage.graphs.base.static_sparse_graph>`.49"""5051def __cinit__(self, G):52r"""53Cython constructor5455INPUT:5657- ``G`` -- a :class:`Graph` object.5859TEST::6061sage: from sage.graphs.base.static_sparse_backend import StaticSparseCGraph62sage: g = StaticSparseCGraph(graphs.PetersenGraph())63"""64has_labels = any(not l is None for _,_,l in G.edge_iterator())65self.directed = G.is_directed()6667init_short_digraph(self.g, G, edge_labelled = has_labels)68if self.directed:69init_reverse(self.g_rev,self.g)7071# Defining the meaningless set of 'active' vertices. Because of CGraph.72bitset_init(self.active_vertices, self.g.n+1)73bitset_set_first_n(self.active_vertices, self.g.n)7475def __dealloc__(self):76r"""77Freeing the memory7879TEST::8081sage: from sage.graphs.base.static_sparse_backend import StaticSparseCGraph82sage: g = StaticSparseCGraph(graphs.PetersenGraph())83"""84bitset_free(self.active_vertices)85free_short_digraph(self.g)86if self.g_rev != NULL:87free_short_digraph(self.g_rev)8889cpdef bint has_vertex(self, int n):90r"""91Tests if a vertex belongs to the graph9293INPUT:9495- ``n`` -- an integer9697TEST::9899sage: from sage.graphs.base.static_sparse_backend import StaticSparseCGraph100sage: g = StaticSparseCGraph(graphs.PetersenGraph())101sage: g.has_vertex(1)102True103sage: g.has_vertex(10)104False105"""106return 0 <= n and n < self.g.n107108cdef int add_vertex_unsafe(self, int k):109raise ValueError("Thou shalt not add a vertex to an immutable graph")110111def add_vertex(self, int k):112r"""113Adds a vertex to the graph. No way.114115TEST::116117sage: from sage.graphs.base.static_sparse_backend import StaticSparseCGraph118sage: g = StaticSparseCGraph(graphs.PetersenGraph())119sage: g.add_vertex(45)120Traceback (most recent call last):121...122ValueError: Thou shalt not add a vertex to an immutable graph123124"""125raise ValueError("Thou shalt not add a vertex to an immutable graph")126127def del_vertex(self, int k):128r"""129Removes a vertex from the graph. No way.130131TEST::132133sage: from sage.graphs.base.static_sparse_backend import StaticSparseCGraph134sage: g = StaticSparseCGraph(graphs.PetersenGraph())135sage: g.del_vertex(45)136Traceback (most recent call last):137...138ValueError: Thou shalt not remove a vertex from an immutable graph139140"""141raise ValueError("Thou shalt not remove a vertex from an immutable graph")142143cdef int del_vertex_unsafe(self, int v):144raise ValueError("Thou shalt not remove a vertex from an immutable graph")145146cpdef list verts(self):147r"""148Returns the list of vertices149150TEST::151152sage: from sage.graphs.base.static_sparse_backend import StaticSparseCGraph153sage: g = StaticSparseCGraph(graphs.PetersenGraph())154sage: g.verts()155[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]156"""157return range(self.g.n)158159cdef int has_arc_unsafe(self, int u, int v):160return ((0 <= u) and161(0 <= v) and162(u < self.g.n) and163(v < self.g.n) and164has_edge(self.g, u, v) != NULL)165166cpdef bint has_arc(self, int u, int v):167r"""168Tests if uv is an edge of the graph169170INPUT:171172- ``u,v`` -- integers173174TEST::175176sage: from sage.graphs.base.static_sparse_backend import StaticSparseCGraph177sage: g = StaticSparseCGraph(graphs.PetersenGraph())178sage: g.has_arc(0,1)179True180sage: g.has_arc(0,7)181False182"""183return self.has_arc_unsafe(u, v)184185cdef int out_neighbors_unsafe(self, int u, int *neighbors, int size) except? -2:186cdef int degree = self.g.neighbors[u+1] - self.g.neighbors[u]187cdef int i188for i in range(min(degree,size)):189neighbors[i] = self.g.neighbors[u][i]190return -1 if size < degree else degree191192cdef int in_neighbors_unsafe(self, int u, int *neighbors, int size) except? -2:193if not self.directed:194return self.out_neighbors_unsafe(u,neighbors,size)195196cdef int degree = self.g_rev.neighbors[u+1] - self.g_rev.neighbors[u]197cdef int i198for i in range(min(degree,size)):199neighbors[i] = self.g_rev.neighbors[u][i]200return -1 if size < degree else degree201202cpdef list out_neighbors(self, int u):203r"""204List the out-neighbors of a vertex205206INPUT:207208- ``u`` -- a vertex209210TEST::211212sage: from sage.graphs.base.static_sparse_backend import StaticSparseCGraph213sage: g = StaticSparseCGraph(graphs.PetersenGraph())214sage: g.out_neighbors(0)215[1, 4, 5]216"""217if u<0 or u>self.g.n:218raise LookupError("The vertex does not belong to the graph")219220cdef int i221return [<int> self.g.neighbors[u][i] for i in range(out_degree(self.g,u))]222223cpdef list in_neighbors(self, int u):224r"""225Returns the in-neighbors of a vertex226227INPUT:228229- ``u`` -- a vertex230231TEST::232233sage: from sage.graphs.base.static_sparse_backend import StaticSparseCGraph234sage: g = StaticSparseCGraph(graphs.PetersenGraph())235sage: g.in_neighbors(0)236[1, 4, 5]237"""238if not self.directed:239return self.out_neighbors(u)240241if u<0 or u>self.g.n:242raise LookupError("The vertex does not belong to the graph")243244cdef int i245return [<int> self.g_rev.neighbors[u][i] for i in range(out_degree(self.g_rev,u))]246247cpdef int out_degree(self, int u):248r"""249Returns the out-degree of a vertex250251INPUT:252253- ``u`` -- a vertex254255TEST::256257sage: from sage.graphs.base.static_sparse_backend import StaticSparseCGraph258sage: g = StaticSparseCGraph(graphs.PetersenGraph())259sage: g.out_degree(0)2603261"""262if u<0 or u>self.g.n:263raise LookupError("The vertex does not belong to the graph")264265return self.g.neighbors[u+1] - self.g.neighbors[u]266267cpdef int in_degree(self, int u):268r"""269Returns the in-degree of a vertex270271INPUT:272273- ``u`` -- a vertex274275TEST::276277sage: from sage.graphs.base.static_sparse_backend import StaticSparseCGraph278sage: g = StaticSparseCGraph(graphs.PetersenGraph())279sage: g.in_degree(0)2803281"""282if u<0 or u>self.g.n:283raise LookupError("The vertex does not belong to the graph")284285if not self.directed:286return self.g.neighbors[u+1] - self.g.neighbors[u]287else:288return self.g_rev.neighbors[u+1] - self.g_rev.neighbors[u]289290class StaticSparseBackend(CGraphBackend):291292def __init__(self, G, loops = False, multiedges=False):293"""294A graph :mod:`backend <sage.graphs.base.graph_backends>` for static295sparse graphs.296297EXAMPLE::298299sage: D = sage.graphs.base.sparse_graph.SparseGraphBackend(9)300sage: D.add_edge(0,1,None,False)301sage: list(D.iterator_edges(range(9), True))302[(0, 1, None)]303304::305306sage: from sage.graphs.base.static_sparse_backend import StaticSparseBackend307sage: g = StaticSparseBackend(graphs.PetersenGraph())308sage: list(g.iterator_edges([0],1))309[(0, 1, None), (0, 4, None), (0, 5, None)]310311::312313sage: g=DiGraph(digraphs.DeBruijn(4,3),data_structure="static_sparse")314sage: gi=DiGraph(g,data_structure="static_sparse")315sage: gi.edges()[0]316('000', '000', '0')317sage: gi.edges_incident('111')318[('111', '110', '0'), ('111', '111', '1'), ('111', '112', '2'), ('111', '113', '3')]319sage: sorted(g.edges()) == sorted(gi.edges())320True321322::323324sage: g = graphs.PetersenGraph()325sage: gi=Graph(g,data_structure="static_sparse")326sage: g == gi327True328sage: sorted(g.edges()) == sorted(gi.edges())329True330331::332333sage: gi = Graph( { 0: {1: 1}, 1: {2: 1}, 2: {3: 1}, 3: {4: 2}, 4: {0: 2} }, data_structure="static_sparse")334sage: (0,4,2) in gi.edges()335True336sage: gi.has_edge(0,4)337True338339::340341sage: 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}})342sage: 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")343sage: G == GI344True345346::347348sage: G = graphs.OddGraph(4)349sage: d = G.diameter()350sage: H = G.distance_graph(range(d+1))351sage: HI = Graph(H,data_structure="static_sparse")352sage: HI.size() == len(HI.edges())353True354355::356357sage: g = Graph({1:{1:[1,2,3]}}, data_structure="static_sparse")358sage: g.size()3593360sage: g.order()3611362sage: g.vertices()363[1]364sage: g.edges()365[(1, 1, 1), (1, 1, 2), (1, 1, 3)]366"""367cdef StaticSparseCGraph cg = <StaticSparseCGraph> StaticSparseCGraph(G)368self._cg = cg369370# .directed and not ._directed. Because of CGraph.371self.directed = cg.directed372373vertices = G.vertices()374self._order = len(vertices)375376# Does it allow loops/multiedges ?377self._loops = loops378self._multiedges = multiedges379380# Dictionary translating a vertex int to a label, and the other way around.381self._vertex_to_labels = vertices382self._vertex_to_int = {v:i for i,v in enumerate(vertices)}383384# Needed by CGraph. The first one is just an alias, and the second is385# useless : accessing _vertex_to_labels (which is a list) is faster than386# vertex_labels (which is a dictionary)387self.vertex_ints = self._vertex_to_int388self.vertex_labels = {i:v for i,v in enumerate(vertices)}389390def __reduce__(self):391"""392Return a tuple used for pickling this graph.393394TESTS:395396Pickling of the static graph backend makes pickling of immutable397graphs and digraphs work::398399sage: G = Graph(graphs.PetersenGraph(), immutable=True)400sage: G == loads(dumps(G))401True402sage: uc = [[2,3], [], [1], [1], [1], [3,4]]403sage: D = DiGraph(dict([[i,uc[i]] for i in range(len(uc))]), immutable=True)404sage: loads(dumps(D)) == D405True406407No problems with loops and multiple edges, with Labels::408409sage: g = Graph(multiedges = True, loops = True)410sage: g.add_edges(2*graphs.PetersenGraph().edges())411sage: g.add_edge(0,0)412sage: g.add_edge(1,1, "a label")413sage: g.add_edge([(0,1,"labellll"), (0,1,"labellll"), (0,1,"LABELLLL")])414sage: g.add_vertex("isolated vertex")415sage: gi = g.copy(immutable=True)416sage: loads(dumps(gi)) == gi417True418419Similar, with a directed graph::420421sage: g = DiGraph(multiedges = True, loops = True)422sage: H = 2*(digraphs.Circuit(15)+DiGraph(graphs.PetersenGraph()))423sage: g.add_edges(H.edges())424sage: g.add_edge(0,0)425sage: g.add_edge(1,1, "a label")426sage: g.add_edge([(0,1,"labellll"), (0,1,"labellll"), (0,1,"LABELLLL")])427sage: g.add_vertex("isolated vertex")428sage: gi = g.copy(immutable=True)429sage: loads(dumps(gi)) == gi430True431"""432if self.directed:433from sage.graphs.digraph import DiGraph434G = DiGraph(loops=self._loops, multiedges=self._multiedges)435G.add_edges(list(self.iterator_out_edges(self.iterator_verts(None),True)))436else:437from sage.graphs.graph import Graph438G = Graph(loops=self._loops, multiedges=self._multiedges)439G.add_edges(list(self.iterator_edges(self.iterator_verts(None),True)))440441G.add_vertices(self.iterator_verts(None))442return (StaticSparseBackend, (G, self._loops, self._multiedges))443444def has_vertex(self, v):445r"""446Tests if the vertex belongs to the graph447448INPUT:449450- ``v`` -- a vertex (or not?)451452TEST::453454sage: from sage.graphs.base.static_sparse_backend import StaticSparseBackend455sage: g = StaticSparseBackend(graphs.PetersenGraph())456sage: g.has_vertex(0)457True458sage: g.has_vertex("Hey")459False460"""461return v in self._vertex_to_int462463def relabel(self, perm, directed):464r"""465Relabel the graphs' vertices. No way.466467TEST::468469sage: from sage.graphs.base.static_sparse_backend import StaticSparseBackend470sage: g = StaticSparseBackend(graphs.PetersenGraph())471sage: g.relabel([],True)472Traceback (most recent call last):473...474ValueError: Thou shalt not relabel an immutable graph475476"""477raise ValueError("Thou shalt not relabel an immutable graph")478479def get_edge_label(self, object u, object v):480"""481Returns the edge label for ``(u,v)``.482483INPUT:484485- ``u,v`` -- two vertices486487TEST::488489sage: from sage.graphs.base.static_sparse_backend import StaticSparseBackend490sage: g = StaticSparseBackend(graphs.PetersenGraph())491sage: print g.get_edge_label(0,1)492None493sage: print g.get_edge_label(0,"Hey")494Traceback (most recent call last):495...496LookupError: One of the two vertices does not belong to the graph497sage: print g.get_edge_label(0,7)498Traceback (most recent call last):499...500LookupError: The edge does not exist501502::503504sage: from sage.graphs.base.static_sparse_backend import StaticSparseBackend505sage: g = StaticSparseBackend(digraphs.DeBruijn(3,2))506sage: g.has_edge('00','01','1')507True508sage: g.has_edge('00','01','0')509False510"""511try:512u = self._vertex_to_int[u]513v = self._vertex_to_int[v]514except KeyError:515raise LookupError("One of the two vertices does not belong to the graph")516517cdef StaticSparseCGraph cg = self._cg518cdef list l519520cdef uint32_t * edge = has_edge(cg.g,u,v)521if edge == NULL:522raise LookupError("The edge does not exist")523524# At this level, edge points toward a edge from u to v in the graph, but525# not necessarily to the leftmost edge. Hence, we first decrease edge to526# make it point toward the leftmost such edge, then build the list of527# all labels.528if self.multiple_edges(None):529while edge > cg.g.neighbors[u] and (edge-1)[0] == v:530edge -= 1531l = []532while edge < cg.g.neighbors[u+1] and edge[0] == v:533l.append(edge_label(cg.g,edge))534edge += 1535return l536537else:538return edge_label(cg.g,edge)539540def has_edge(self, object u, object v, object l):541"""542Returns whether this graph has edge ``(u,v)`` with label ``l``.543544If ``l`` is ``None``, return whether this graph has an edge ``(u,v)``545with any label.546547INPUT:548549- ``u,v`` -- two vertices550551- ``l`` -- a label552553TEST::554555sage: from sage.graphs.base.static_sparse_backend import StaticSparseBackend556sage: g = StaticSparseBackend(graphs.PetersenGraph())557sage: g.has_edge(0,1,'e')558False559sage: g.has_edge(0,4,None)560True561"""562cdef uint32_t * edge = NULL563cdef StaticSparseCGraph cg = <StaticSparseCGraph> (self._cg)564try:565u = self._vertex_to_int[u]566v = self._vertex_to_int[v]567except KeyError:568raise LookupError("One of the two vertices does not belong to the graph")569570edge = has_edge(cg.g,u,v)571if edge == NULL:572return False573if l is None:574return True575576# At this level, edge points toward a edge from u to v in the graph, but577# not necessarily toward the right label. As there may be many uv edges578# with different labels, we first make edge point toward the leftmost uv579# edge, then scan them all to find the right label.580while edge > cg.g.neighbors[u] and (edge-1)[0] == v :581edge -= 1582583while edge[0] == v and edge < cg.g.neighbors[u+1]:584if edge_label(cg.g,edge) == l:585return True586edge += 1587588return False589590def iterator_in_edges(self, object vertices, bint labels):591"""592Iterate over the incoming edges incident to a sequence of vertices.593594INPUT:595596- ``vertices`` -- a list of vertices597598- ``labels`` -- whether to return labels too599600TEST::601602sage: from sage.graphs.base.static_sparse_backend import StaticSparseBackend603sage: g = StaticSparseBackend(graphs.PetersenGraph())604sage: list(g.iterator_in_edges([0],False))605[(0, 1), (0, 4), (0, 5)]606sage: list(g.iterator_in_edges([0],True))607[(0, 1, None), (0, 4, None), (0, 5, None)]608609::610611sage: DiGraph(digraphs.Path(5),immutable=False).incoming_edges([2])612[(1, 2, None)]613sage: DiGraph(digraphs.Path(5),immutable=True).incoming_edges([2])614[(1, 2, None)]615"""616cdef StaticSparseCGraph cg = self._cg617if not cg.directed:618for x in self.iterator_out_edges(vertices, labels):619yield x620return621622try:623vertices = [self._vertex_to_int[x] for x in vertices]624except KeyError:625raise LookupError("One of the vertices does not belong to the graph")626627cdef int i,j628for i in vertices:629vi = self._vertex_to_labels[i]630for j in range(out_degree(cg.g_rev,i)):631if labels:632yield (self._vertex_to_labels[cg.g_rev.neighbors[i][j]],633vi,634edge_label(cg.g_rev,cg.g_rev.neighbors[i]+j))635else:636yield self._vertex_to_labels[cg.g_rev.neighbors[i][j]], vi637638def iterator_out_edges(self, object vertices, bint labels):639"""640Iterate over the outbound edges incident to a sequence of vertices.641642INPUT:643644- ``vertices`` -- a list of vertices645646- ``labels`` -- whether to return labels too647648TEST::649650sage: from sage.graphs.base.static_sparse_backend import StaticSparseBackend651sage: g = StaticSparseBackend(graphs.PetersenGraph())652sage: list(g.iterator_out_edges([0], False))653[(0, 1), (0, 4), (0, 5)]654sage: list(g.iterator_out_edges([0],True))655[(0, 1, None), (0, 4, None), (0, 5, None)]656657"""658try:659vertices = [self._vertex_to_int[x] for x in vertices]660except KeyError:661raise LookupError("One of the vertices does not belong to the graph")662663cdef StaticSparseCGraph cg = self._cg664cdef int i,j665for i in vertices:666vi = self._vertex_to_labels[i]667for j in range(out_degree(cg.g,i)):668if labels:669yield (vi,670self._vertex_to_labels[cg.g.neighbors[i][j]],671edge_label(cg.g,cg.g.neighbors[i]+j))672else:673yield vi,self._vertex_to_labels[cg.g.neighbors[i][j]]674675def iterator_verts(self, vertices):676r"""677Returns an iterator over the vertices678679INPUT:680681- ``vertices`` -- a list of objects. The method will only return the682elements of the graph which are contained in ``vertices``. It's not683very efficient. If ``vertices`` is equal to ``None``, all the vertices684are returned.685686TEST::687688sage: from sage.graphs.base.static_sparse_backend import StaticSparseBackend689sage: g = StaticSparseBackend(graphs.PetersenGraph())690sage: list(g.iterator_verts(None))691[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]692sage: list(g.iterator_verts([1,"Hey","I am a french fry"]))693[1]694"""695if vertices is None:696return iter(self._vertex_to_labels)697else:698return (x for x in self._vertex_to_labels if x in vertices)699700def num_verts(self):701r"""702Returns the number of vertices703704TEST::705706sage: from sage.graphs.base.static_sparse_backend import StaticSparseBackend707sage: g = StaticSparseBackend(graphs.PetersenGraph())708sage: g.num_verts()70910710"""711return self._order712713def allows_loops(self, value=None):714r"""715Returns whether the graph allows loops716717INPUT:718719- ``value`` -- only useful for compatibility with other graph backends,720where this method can be used to define this boolean. This method721raises an exception if ``value`` is not equal to ``None``.722723TEST::724725sage: from sage.graphs.base.static_sparse_backend import StaticSparseBackend726sage: g = StaticSparseBackend(graphs.PetersenGraph())727sage: g.allows_loops()728False729sage: g = StaticSparseBackend(graphs.PetersenGraph(), loops=True)730sage: g.allows_loops()731True732"""733if value is None:734return self._loops735else:736raise ValueError("The graph is immutable. You cannot change it in any way !")737738def multiple_edges(self, value=None):739r"""740Returns whether the graph allows multiple edges741742INPUT:743744- ``value`` -- only useful for compatibility with other graph backends,745where this method can be used to define this boolean. This method746raises an exception if ``value`` is not equal to ``None``.747748TEST::749750sage: from sage.graphs.base.static_sparse_backend import StaticSparseBackend751sage: g = StaticSparseBackend(graphs.PetersenGraph())752sage: g.multiple_edges()753False754sage: g = StaticSparseBackend(graphs.PetersenGraph(), multiedges=True)755sage: g.multiple_edges()756True757"""758if value is None:759return self._multiedges760else:761raise ValueError("The graph is immutable. You cannot change it in any way !")762763def num_edges(self,directed):764r"""765Returns the number of edges766767INPUT:768769- ``directed`` (boolean) -- whether to consider the graph as directed or770not.771772TEST::773774sage: from sage.graphs.base.static_sparse_backend import StaticSparseBackend775sage: g = StaticSparseBackend(graphs.PetersenGraph())776sage: g.num_edges(False)77715778779Testing the exception::780781sage: g = StaticSparseBackend(digraphs.Circuit(4))782sage: g.num_edges(False)783Traceback (most recent call last):784...785NotImplementedError: Sorry, I have no idea what is expected in this situation. I don't think that it is well-defined either, especially for multigraphs.786787:trac:`15491`::788789sage: g=digraphs.RandomDirectedGNP(10,.3)790sage: gi=DiGraph(g,data_structure="static_sparse")791sage: gi.size() == len(gi.edges())792True793"""794cdef StaticSparseCGraph cg = <StaticSparseCGraph> self._cg795796if directed:797if cg.directed:798# Returns the real number of directed arcs799return int(cg.g.m)800else:801# Returns twice the number of edges, minus the number of802# loops. This is actually equal to the index of803# cg.g.neighbors[cg.g.n] in the array `cg.g.edges`804return int(cg.g.neighbors[cg.g.n]-cg.g.edges)805else:806if cg.directed:807raise NotImplementedError("Sorry, I have no idea what is expected "808"in this situation. I don't think "809"that it is well-defined either, "810"especially for multigraphs.")811else:812# Returns the number of edges813return int(cg.g.m)814815def iterator_edges(self, vertices, bint labels):816r"""817Returns an iterator over the graph's edges.818819INPUT:820821- ``vertices`` -- only returns the edges incident to at least one vertex822of ``vertices``.823824- ``labels`` -- whether to return edge labels too825826TEST::827828sage: from sage.graphs.base.static_sparse_backend import StaticSparseBackend829sage: g = StaticSparseBackend(graphs.PetersenGraph())830sage: list(g.iterator_edges(g.iterator_verts(None), False))831[(0, 1), (0, 4), (0, 5), (1, 2), (1, 6), (2, 3), (2, 7),832(3, 4), (3, 8), (4, 9), (5, 7), (5, 8), (6, 8), (6, 9), (7, 9)]833834:trac:`15665`::835836sage: Graph(immutable=True).edges()837[]838"""839cdef FrozenBitset b_vertices840841if not vertices:842return843844if self._directed:845raise RuntimeError("This is not meant for directed graphs.")846847try:848vertices = [self._vertex_to_int[x] for x in vertices]849b_vertices = FrozenBitset(vertices)850except KeyError:851raise LookupError("One of the vertices does not belong to the graph")852853cdef StaticSparseCGraph cg = self._cg854cdef int i,j,tmp855856for i in vertices:857vi = self._vertex_to_labels[i]858for tmp in range(out_degree(cg.g,i)):859j = cg.g.neighbors[i][tmp]860if j < i and j in b_vertices:861continue862if labels:863yield (vi,864self._vertex_to_labels[j],865edge_label(cg.g,cg.g.neighbors[i]+tmp))866else:867yield vi,self._vertex_to_labels[j]868869def degree(self, v, directed):870r"""871Returns the degree of a vertex872873INPUT:874875- ``v`` -- a vertex876877- ``directed`` -- boolean; whether to take into account the878orientation of this graph in counting the degree of ``v``.879880EXAMPLE::881882sage: g = Graph(graphs.PetersenGraph(), data_structure="static_sparse")883sage: g.degree(0)8843885"""886try:887v = self._vertex_to_int[v]888except KeyError:889raise LookupError("The vertex does not belong to the graph")890891cdef StaticSparseCGraph cg = self._cg892893if directed:894if cg.directed:895return cg.in_degree(v) + cg.out_degree(v)896else:897return 2*cg.out_degree(v)898else:899if cg.directed:900raise NotImplementedError("Sorry, I have no idea what is expected "901"in this situation. I don't think "902"that it is well-defined either, "903"especially for multigraphs.")904else:905return cg.out_degree(v)906907def in_degree(self, v):908r"""909Returns the in-degree of a vertex910911INPUT:912913- ``v`` -- a vertex914915EXAMPLE::916917sage: g = DiGraph(graphs.PetersenGraph(), data_structure="static_sparse")918sage: g.in_degree(0)9193920"""921try:922v = self._vertex_to_int[v]923except KeyError:924raise LookupError("The vertex does not belong to the graph")925926cdef StaticSparseCGraph cg = self._cg927928if cg.directed:929return cg.in_degree(v)930else:931return cg.out_degree(v)932933def out_degree(self, v):934r"""935Returns the out-degree of a vertex936937INPUT:938939- ``v`` -- a vertex940941EXAMPLE::942943sage: g = DiGraph(graphs.PetersenGraph(), data_structure="static_sparse")944sage: g.out_degree(0)9453946"""947try:948v = self._vertex_to_int[v]949except KeyError:950raise LookupError("The vertex does not belong to the graph")951952cdef StaticSparseCGraph cg = self._cg953954return cg.out_degree(v)955956def iterator_nbrs(self, v):957r"""958Returns the neighbors of a vertex959960INPUT:961962- ``v`` -- a vertex963964EXAMPLE::965966sage: g = Graph(graphs.PetersenGraph(), data_structure="static_sparse")967sage: g.neighbors(0)968[1, 4, 5]969"""970try:971v = self._vertex_to_int[v]972except KeyError:973raise LookupError("The vertex does not belong to the graph")974975cdef StaticSparseCGraph cg = self._cg976cdef int i977978for i in range(out_degree(cg.g,v)):979yield self._vertex_to_labels[cg.g.neighbors[v][i]]980981def iterator_out_nbrs(self, v):982r"""983Returns the out-neighbors of a vertex984985INPUT:986987- ``v`` -- a vertex988989EXAMPLE::990991sage: g = DiGraph(graphs.PetersenGraph(), data_structure="static_sparse")992sage: g.neighbors_out(0)993[1, 4, 5]994"""995try:996v = self._vertex_to_int[v]997except KeyError:998raise LookupError("The vertex does not belong to the graph")9991000cdef StaticSparseCGraph cg = self._cg1001cdef int i10021003for i in range(out_degree(cg.g,v)):1004yield self._vertex_to_labels[cg.g.neighbors[v][i]]10051006def iterator_in_nbrs(self, v):1007r"""1008Returns the out-neighbors of a vertex10091010INPUT:10111012- ``v`` -- a vertex10131014EXAMPLE::10151016sage: g = DiGraph(graphs.PetersenGraph(), data_structure="static_sparse")1017sage: g.neighbors_in(0)1018[1, 4, 5]1019"""1020try:1021v = self._vertex_to_int[v]1022except KeyError:1023raise LookupError("The vertex does not belong to the graph")10241025cdef StaticSparseCGraph cg = self._cg1026cdef short_digraph g10271028if cg.directed:1029for i in range(out_degree(cg.g_rev,v)):1030yield self._vertex_to_labels[cg.g_rev.neighbors[v][i]]1031else:1032for i in range(out_degree(cg.g,v)):1033yield self._vertex_to_labels[cg.g.neighbors[v][i]]10341035def _run_it_on_static_instead(f):1036r"""1037A decorator function to force the (Di)Graph functions to compute from a1038static sparse graph310391040This decorator can be used on methods from (Di)Graph. When it is applied,1041the method that was meant to compute something on a graph first converts1042this graph to a static sparse graph, then does what it had to do on this new1043graph. Of course, it makes no sense to decorate Graph.add_vertex with it as1044such a method will never work on an immutable graph. But it can help find1045new bugs, from time to time.10461047EXAMPLE::10481049sage: from sage.graphs.base.static_sparse_backend import _run_it_on_static_instead1050sage: @_run_it_on_static_instead1051....: def new_graph_method(g):1052....: print "My backend is of type", g._backend1053sage: Graph.new_graph_method = new_graph_method1054sage: g = Graph(5)1055sage: print "My backend is of type", g._backend1056My backend is of type <class 'sage.graphs.base.sparse_graph.SparseGraphBackend'>1057sage: g.new_graph_method()1058My backend is of type <class 'sage.graphs.base.static_sparse_backend.StaticSparseBackend'>1059"""1060def same_function_on_static_version(*kwd,**kwds):1061if not isinstance(kwd[0]._backend,StaticSparseBackend):1062gcopy = kwd[0].copy(data_structure="static_sparse")1063return getattr(gcopy,f.__name__)(*kwd[1:],**kwds)1064else:1065return f(*kwd,**kwds)10661067return same_function_on_static_version106810691070