Ticket #14806: sparselabel.patch

File sparselabel.patch, 77.9 KB (added by ncohen, 6 years ago)
  • doc/en/reference/graphs/index.rst

    # HG changeset patch
    # User Nathann Cohen <nathann.cohen@gmail.com>
    # Date 1371420794 -7200
    #      Mon Jun 17 00:13:14 2013 +0200
    # Node ID 4e7bb8136b0301b2c98e72c799ad488897ee8f86
    # Parent  a865b75c34853aa0f5842a4d4c12051ce1e6726a
    Immutable graph backend
    
    diff --git a/doc/en/reference/graphs/index.rst b/doc/en/reference/graphs/index.rst
    a b  
    3838   sage/graphs/base/sparse_graph
    3939   sage/graphs/base/dense_graph
    4040   sage/graphs/base/static_sparse_graph
     41   sage/graphs/base/static_sparse_backend
    4142   sage/graphs/base/graph_backends
    4243
    4344Hypergraphs
  • module_list.py

    diff --git a/module_list.py b/module_list.py
    a b  
    389389    Extension('sage.graphs.base.static_sparse_graph',
    390390              sources = ['sage/graphs/base/static_sparse_graph.pyx']),
    391391
     392    Extension('sage.graphs.base.static_sparse_backend',
     393              sources = ['sage/graphs/base/static_sparse_backend.pyx']),
     394
    392395    Extension('sage.graphs.modular_decomposition.modular_decomposition',
    393396              sources = ['sage/graphs/modular_decomposition/modular_decomposition.pyx',
    394397                         'sage/graphs/modular_decomposition/src/dm.c'],
  • sage/graphs/base/graph_backends.py

    diff --git a/sage/graphs/base/graph_backends.py b/sage/graphs/base/graph_backends.py
    a b  
    33
    44"""
    55
    6 
    7 
    86#*******************************************************************************
    97#        Copyright (C) 2008 Robert L. Miller <rlmillster@gmail.com>
    108#
     
    14451443        self._nxg = relabel_nodes(self._nxg,perm)
    14461444        self._nxg.name = name
    14471445
    1448 #         if directed:
    1449 #             oldsucc = self._nxg.succ
    1450 #             oldpred = self._nxg.pred
    1451 #             newsucc = {}
    1452 #             newpred = {}
    1453 #             for v in oldsucc.iterkeys():
    1454 #                 oldtempsucc = oldsucc[v]
    1455 #                 newtempsucc = {}
    1456 #                 for w in oldtempsucc.iterkeys():
    1457 #                     newtempsucc[perm[w]] = oldtempsucc[w]
    1458 #                 newsucc[perm[v]] = newtempsucc
    1459 #             for v in oldpred.iterkeys():
    1460 #                 oldtemppred = oldpred[v]
    1461 #                 newtemppred = {}
    1462 #                 for w in oldtemppred.iterkeys():
    1463 #                     newtemppred[perm[w]] = oldtemppred[w]
    1464 #                 newpred[perm[v]] = newtemppred
    1465 #             self._nxg.adj = newsucc
    1466 #             self._nxg.succ = self._nxg.adj
    1467 #             self._nxg.pred = newpred
    1468 #         else:
    1469 #             oldd = self._nxg.adj
    1470 #             newd = {}
    1471 #             for v in oldd.iterkeys():
    1472 #                 oldtempd = oldd[v]
    1473 #                 newtempd = {}
    1474 #                 for w in oldtempd.iterkeys():
    1475 #                     newtempd[perm[w]] = oldtempd[w]
    1476 #                 newd[perm[v]] = newtempd
    1477 #             self._nxg.adj = newd
    1478 
    14791446    def set_edge_label(self, u, v, l, directed):
    14801447        """
    14811448        Label the edge (u,v) by l.
  • sage/graphs/base/sparse_graph.pyx

    diff --git a/sage/graphs/base/sparse_graph.pyx b/sage/graphs/base/sparse_graph.pyx
    a b  
    17151715            sage: G.edges()
    17161716            [(0, 1, 2)]
    17171717
    1718         Do we remove loops correctly? (Trac 12135)::
     1718        Do we remove loops correctly? (:trac:`12135`)::
    17191719
    17201720            sage: g=Graph({0:[0,0,0]}, implementation='c_graph', sparse=True)
    17211721            sage: g.edges(labels=False)
     
    17331733        if l is None:
    17341734            if self._cg.has_arc_label(u_int, v_int, 0):
    17351735                l_int = 0
    1736             else: 
     1736            else:
    17371737                l_int = self._cg.arc_label(u_int, v_int)
    17381738        else:
    17391739            for l_int in self.edge_labels:
     
    18391839            [(1, 2, 3)]
    18401840
    18411841        """
     1842        # Improvement possible in the code below !
     1843        #
     1844        # It is through this function that Sage answers to G.edges(). That's so
     1845        # unefficient that it hurts to see it. Basically, to answer G.edges(),
     1846        # Sage first builds the list L of all vertices, then and returns all the
     1847        # edges which have at least one endpoint in L. That is, absolutely *ALL*
     1848        # the edges, but it checks this condition on the endpoints for each of
     1849        # them. It tests containment in a LIST, not even a set. That should
     1850        # REALLY be updated.
    18421851        cdef object v, l, L
    18431852        vertices = [get_vertex(v, self.vertex_ints, self.vertex_labels,
    18441853                    self._cg) for v in vertices if self.has_vertex(v)]
  • new file sage/graphs/base/static_sparse_backend.pxd

    diff --git a/sage/graphs/base/static_sparse_backend.pxd b/sage/graphs/base/static_sparse_backend.pxd
    new file mode 100644
    - +  
     1from c_graph cimport CGraph
     2from static_sparse_graph cimport short_digraph, ushort
     3from c_graph import CGraphBackend
     4
     5include 'sage/ext/stdsage.pxi'
     6
     7cdef class StaticSparseCGraph(CGraph):
     8    cdef short_digraph g
     9    cdef short_digraph g_rev
     10    cdef bint directed
     11
     12    cpdef bint has_vertex(self, int n)
     13    cdef int add_vertex_unsafe(self, int k)
     14    cdef int del_vertex_unsafe(self, int v)
     15    cpdef list verts(self)
     16    cdef int has_arc_unsafe(self, int u, int v)
     17    cpdef bint has_arc(self, int u, int v)
     18    cdef int out_neighbors_unsafe(self, int u, int *neighbors, int size) except? -2
     19    cpdef list out_neighbors(self, int u)
     20    cpdef int out_degree(self, int u)
     21    cdef int in_neighbors_unsafe(self, int u, int *neighbors, int size) except? -2
     22    cpdef list in_neighbors(self, int u)
     23    cpdef int in_degree(self, int u)
  • new file sage/graphs/base/static_sparse_backend.pyx

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

    diff --git a/sage/graphs/base/static_sparse_graph.pxd b/sage/graphs/base/static_sparse_graph.pxd
    a b  
    1 ctypedef unsigned short ushort 
     1ctypedef unsigned short ushort
    22
    33include "sage/misc/bitset_pxd.pxi"
    44
     5cdef extern from "stdlib.h":
     6    ctypedef void const_void "const void"
     7    void qsort(void *base, int nmemb, int size,
     8            int(*compar)(const_void *, const_void *)) nogil
     9
     10    void *bsearch(const_void *key, const_void *base, size_t nmemb,
     11                  size_t size, int(*compar)(const_void *, const_void *)) nogil
     12
    513ctypedef struct short_digraph_s:
    6    ushort n
    714   ushort * edges
    815   ushort ** neighbors
     16   PyObject * edge_labels
     17   unsigned int m
     18   ushort n
    919
    1020ctypedef short_digraph_s short_digraph[1]
    1121
    12 cdef int init_short_digraph(short_digraph g, G) except -1
     22cdef int init_short_digraph(short_digraph g, G, edge_labelled = ?) except -1
    1323cdef void free_short_digraph(short_digraph g)
     24cdef int init_reverse(short_digraph dst, short_digraph src) except -1
     25cdef int out_degree(short_digraph g, int u)
     26cdef inline ushort * has_edge(short_digraph g, ushort u, ushort v)
     27cdef inline object edge_label(short_digraph g, ushort * edge)
  • sage/graphs/base/static_sparse_graph.pyx

    diff --git a/sage/graphs/base/static_sparse_graph.pyx b/sage/graphs/base/static_sparse_graph.pyx
    a b  
    2727.. image:: ../../../media/structure.png
    2828
    2929The data structure is actually pretty simple and compact. ``short_digraph`` has
    30 three fields
     30five fields
    3131
    32     * ``n`` (``int``) -- the number of vertices in the graph.
     32    * ``n`` (``unsigned short``) -- the number of vertices in the graph.
     33
     34    * ``m`` (``unsigned int``) -- the number of edges in the graph.
    3335
    3436    * ``edges`` (``unsigned short *``) -- array whose length is the number of
    3537      edges of the graph.
     
    4244      so that it remains easy to enumerate the neighbors of vertex `n-1` : the
    4345      last of them is the element addressed by ``neighbors[n]-1``.
    4446
     47    * ``edge_labels`` -- this cython list associates a label to each edge of the
     48      graph. If a given edge is represented by ``edges[i]``, this its associated
     49      label can be found at ``edge_labels[i]``. This object is usually NULL,
     50      unless the call to ``init_short_digraph`` explicitly requires the labels
     51      to be stored in the data structure.
    4552
    4653In the example given above, vertex 0 has 2,3,5,7,8 and 9 as out-neighbors, but
    4754not 4, which is an out-neighbour of vertex 1. Vertex `n-1` has 2, 5, 8 and 9 as
     
    5461This is *the one thing* to have in mind when working with this data structure::
    5562
    5663    cdef list_edges(short_digraph g):
    57         cdef ushort * v
    58         cdef ushort * end
    59         cdef int i
    60 
     64        cdef int i, j
    6165        for i in range(g.n):
    62             v = g.neighbors[i]
    63             end = g.neighbors[i+1]
    64 
    65             while v < end:
    66                 print "There is an edge from "+str(i)+" to "+str(v[0])
    67                 v += 1
     66            for j in range(g.neighbors[i+1]-g.neighbors[i]):
     67                print "There is an edge from",str(i),"to",g.neighbors[i][j]
    6868
    6969**Advantages**
    7070
    71 Three great points :
     71Two great points :
    7272
    73     * The neighbors of a vertex are contiguous in memory.
    74     * Going from one to the other amounts to increasing a pointer.
     73    * The neighbors of a vertex are C types, and are contiguous in memory.
    7574    * Storing such graphs is incredibly cheaper than storing Python structures.
    7675
    7776Well, I think it would be hard to have anything more efficient than that to
     
    8382    * When creating a ``fast_digraph`` from a ``Graph`` or ``DiGraph`` named
    8483      ``G``, the `i^{\text{th}}` vertex corresponds to ``G.vertices()[i]``
    8584
    86     * The data structure does not support edge labels
    87 
    8885    * In its current implementation (with ``unsigned short`` variables), the
    8986      data structure can handle graphs with at most 65535 vertices. If
    9087      necessary, changing it to ``int`` is totally straightforward.
     
    9390      expected. There is a very useful ``bitset_list`` function for this kind of
    9491      problems :-)
    9592
     93    * When the edges are labelled, most of the space taken by this graph is
     94      taken by edge labels. If no edge is labelled then this space is not
     95      allocated, but if *any* edge has a label then a (possibly empty) label is
     96      stored for each edge, which can represent a lot of memory.
     97
     98    * The data structure stores the number of edges, even though it appears that
     99      this number can be reconstructed with
     100      ``g.neighbors[n]-g.neighbors[0]``. The trick is that not all elements of
     101      the ``g.edges`` array are necessarily used : when an undirected graph
     102      contains loops, only one entry of the array of size `2m` is used to store
     103      it, instead of the expected two. Storing the number of edges is the only
     104      way to avoid an uselessly costly computation to obtain the number of edges
     105      of an undirected, looped, AND labelled graph (think of several loops on
     106      the same vertex with different labels).
     107
    96108    * The codes of this module are well documented, and many answers can be
    97109      found directly in the code.
    98110
    99 Todo list
    100 
    101     * Adjacency test. The data structure can support it efficiently through
    102       dichotomy. It would require to sort the list of edges as it is not done at
    103       the moment. Some calls to the C function ``qsort`` would be sufficient.
    104 
    105111Cython functions
    106112----------------
    107113
    108 ``init_short_digraph(short_digraph g, G)``
     114.. csv-table::
     115    :class: contentstable
     116    :widths: 30, 70
     117    :delim: |
    109118
    110     This method initializes ``short_digraph g`` so that it represents ``G``. If
    111     ``G`` is a ``Graph`` objet (and not a ``DiGraph``), an edge between two
    112     vertices `u` and `v` is replaced by two arcs in both directions.
     119    ``init_short_digraph(short_digraph g, G)`` | Initializes ``short_digraph g`` from a Sage (Di)Graph.
     120    ``int n_edges(short_digraph g)`` | Returns the number of edges in ``g``
     121    ``int out_degree(short_digraph g, int i)`` | Returns the out-degree of vertex `i` in ``g``
     122    ``has_edge(short_digraph g, ushort u, ushort v)`` | Tests the existence of an edge.
     123    ``edge_label(short_digraph g, ushort * edge)`` | Returns the label associated with a given edge
     124    ``init_empty_copy(short_digraph dst, short_digraph src)`` | Allocates ``dst`` so that it can contain as many vertices and edges as ``src``.
     125    ``init_reverse(short_digraph dst, short_digraph src)`` | Initializes ``dst`` to a copy of ``src`` with all edges in the opposite direction.
     126    ``free_short_digraph(short_digraph g)`` | Free the ressources used by ``g``
    113127
    114 ``int n_edges(short_digraph g)``
    115 
    116     Returns the number of edges in ``g``
    117 
    118 ``int out_degree(short_digraph g, int i)``
    119 
    120     Returns the out-degree of vertex `i` in ``g``
    121 
    122 ``init_empty_copy(short_digraph dst, short_digraph src)``
    123 
    124     Allocates memory for ``dst`` so that it can contain as many vertices and
    125     edges as ``src``. Its content is purely random, though.
    126 
    127 ``init_reverse(short_digraph dst, short_digraph src)``
    128 
    129     Initializes ``dst`` so that it represents a copy of ``src`` in which all
    130     edges go in the opposite direction.
     128**Connectivity**
    131129
    132130``can_be_reached_from(short_digraph g, int src, bitset_t reached)``
    133131
     
    142140    component containing ``v`` in ``g``. The variable ``g_reversed`` is assumed
    143141    to represent the reverse of ``g``.
    144142
    145 ``free_short_digraph(short_digraph g)``
    146 
    147     Free the ressources used by ``g``
    148143
    149144What is this module used for ?
    150145------------------------------
     
    158153Cython routines this module implements (as they can not directly call methods
    159154with C arguments).
    160155"""
    161 
    162156include "sage/misc/bitset.pxi"
     157cimport cpython
    163158
    164159##############################################################################
    165160#       Copyright (C) 2010 Nathann Cohen <nathann.cohen@gmail.com>
     
    170165
    171166from sage.graphs.base.c_graph cimport CGraph
    172167
    173 cdef int init_short_digraph(short_digraph g, G) except -1:
     168cdef int init_short_digraph(short_digraph g, G, edge_labelled = False) except -1:
     169    r"""
     170    Initializes ``short_digraph g`` from a Sage (Di)Graph.
    174171
     172    If ``G`` is a ``Graph`` objet (and not a ``DiGraph``), an edge between two
     173    vertices `u` and `v` is replaced by two arcs in both directions.
     174    """
    175175    # g.n is unsigned short, so -1 is actually the maximum value possible.
    176176    g.n = -1
     177    g.edge_labels = NULL
    177178
    178179    if G.order() > g.n:
    179180        raise ValueError("This structure can handle at most "+str(<int> g.n)+" vertices !")
     
    181182        g.n = G.order()
    182183
    183184    cdef int isdigraph
    184    
     185
    185186    from sage.graphs.all import Graph, DiGraph
    186187
    187188    if isinstance(G, DiGraph):
     
    193194
    194195    cdef list vertices = G.vertices()
    195196    cdef dict v_to_id = {}
    196     cdef int i,j
     197    cdef int i,j,v_id
     198    cdef list neighbor_label
     199    cdef list edge_labels
    197200
    198     cdef int n_edges = G.size() if isdigraph else 2*G.size()
     201    g.m = G.size()
     202    cdef int n_edges = g.m if isdigraph else 2*g.m
    199203
    200204    for i, v in enumerate(vertices):
    201205        v_to_id[v] = i
     
    208212    if g.neighbors == NULL:
    209213        raise ValueError("Problem while allocating memory (neighbors)")
    210214
    211 
    212215    # Initializing the value of neighbors
    213216    g.neighbors[0] = g.edges
     217    cdef CGraph cg = <CGraph> G._backend
    214218
    215     cdef CGraph cg = <CGraph> G._backend
    216     for i in range(1,(<int>g.n)+1):
    217         g.neighbors[i] = g.neighbors[i-1] + <int> (cg.out_degree(vertices[i-1]) if isdigraph else G.degree(vertices[i-1]))
     219    if not G.has_loops():
     220        # Normal case
     221        for i in range(1,(<int>g.n)+1):
     222            g.neighbors[i] = g.neighbors[i-1] + <int> (cg.out_degree(vertices[i-1]) if isdigraph else G.degree(vertices[i-1]))
     223    else:
     224        # In the presence of loops. For a funny reason, if a vertex v has a loop
     225        # attached to it and no other incident edge, Sage declares that it has
     226        # degree 2. This way, the sum of the degrees of the vertices is twice
     227        # the number of edges, but then the degree of a vertex is not the number
     228        # of its neighbors anymore. One should never try to think. It never ends
     229        # well.
     230        for i in range(1,(<int>g.n)+1):
     231            g.neighbors[i] = g.neighbors[i-1] + <int> len(G.edges_incident(vertices[i-1]))
    218232
    219     for u,v in G.edge_iterator(labels = False):
    220         i = v_to_id[u]
    221         j = v_to_id[v]
     233    if not edge_labelled:
     234        for u,v in G.edge_iterator(labels = False):
     235            i = v_to_id[u]
     236            j = v_to_id[v]
    222237
    223         g.neighbors[i][0] = j
    224         g.neighbors[i] += 1
     238            g.neighbors[i][0] = j
     239            g.neighbors[i] += 1
    225240
    226         if not isdigraph:
    227             g.neighbors[j][0] = i
    228             g.neighbors[j] += 1
     241            if not isdigraph and i!=j:
     242                g.neighbors[j][0] = i
     243                g.neighbors[j] += 1
    229244
    230     # Reinitializing the value of neighbors
    231     for g.n> i >0:
    232         g.neighbors[i] = g.neighbors[i-1]
     245        # Reinitializing the value of neighbors
     246        for g.n> i >0:
     247            g.neighbors[i] = g.neighbors[i-1]
    233248
    234     g.neighbors[0] = g.edges
     249        g.neighbors[0] = g.edges
     250
     251        # Sorting the neighbors
     252        for i in range(g.n):
     253            qsort(g.neighbors[i],g.neighbors[i+1]-g.neighbors[i],sizeof(ushort),compare_ushort_p)
     254
     255    else:
     256        edge_labels = [None]*n_edges
     257        for v in G:
     258            neighbor_label = [(v_to_id[uu],l) if uu != v else (v_to_id[u],l)
     259                              for u,uu,l in G.edges_incident(v)]
     260            neighbor_label.sort()
     261            v_id = v_to_id[v]
     262
     263            for i,(j,label) in enumerate(neighbor_label):
     264                g.neighbors[v_id][i] = j
     265                edge_labels[(g.neighbors[v_id]+i)-g.edges] = label
     266
     267        g.edge_labels = <void *> edge_labels
     268        cpython.Py_XINCREF(g.edge_labels)
    235269
    236270cdef inline int n_edges(short_digraph g):
    237271    # The number of edges is nothing but a difference of pointers
     
    243277
    244278cdef int init_empty_copy(short_digraph dst, short_digraph src) except -1:
    245279    dst.n = src.n
     280    dst.m = src.m
     281    dst.edge_labels = NULL
     282    cdef list edge_labels
    246283
    247284    dst.edges = <ushort *> sage_malloc(n_edges(src)*sizeof(ushort))
    248285    if dst.edges == NULL:
     
    252289    if dst.neighbors == NULL:
    253290        raise ValueError("Problem while allocating memory (neighbors)")
    254291
     292    if src.edge_labels != NULL:
     293        edge_labels = [None]*n_edges(src)
     294        dst.edge_labels = <void *> edge_labels
     295        cpython.Py_XINCREF(dst.edge_labels)
     296
    255297cdef int init_reverse(short_digraph dst, short_digraph src) except -1:
     298    cdef int i,j,v
    256299    # Allocates memory for dst
    257300    init_empty_copy(dst, src)
    258301
    259302    # Avoiding a later segfault
    260303    if dst.n == 0:
    261304        return 0
    262    
     305
    263306    #### 1/3
    264307    #
    265308    # In a first pass, we count the in-degrees of each vertex and store it in a
     
    273316    # Counting the degrees
    274317    memset(in_degree, 0, src.n*sizeof(int))
    275318
    276     cdef ushort * v = src.edges
    277     cdef ushort * end = src.neighbors[src.n]
    278     while v < end:
    279         in_degree[v[0]] += 1
    280         v += 1
     319    for i in range(n_edges(src)):
     320        in_degree[src.edges[i]] += 1
    281321
    282322    # Updating dst.neighbors
    283     cdef int i
    284323    dst.neighbors[0] = dst.edges
    285324    for i in range(1, src.n+1):
    286325        dst.neighbors[i] = dst.neighbors[i-1] + in_degree[i-1]
     
    292331    # so, we will change the value of dst.neighbors, but that is not so bad as
    293332    # we can fix it afterwards.
    294333    for i in range(0, src.n):
    295         v = src.neighbors[i]
    296         end = src.neighbors[i+1]
     334        for j in range(out_degree(src,i)):
     335            v = src.neighbors[i][j]
     336            dst.neighbors[v][0] = i
    297337
    298         while v < end:
    299             dst.neighbors[v[0]][0] = i
    300             dst.neighbors[v[0]] += 1
    301             v += 1
     338            if dst.edge_labels != NULL:
     339                (<list> dst.edge_labels)[dst.neighbors[v]-dst.edges] = edge_label(src,src.neighbors[i]+j)
     340
     341            dst.neighbors[v] += 1
    302342
    303343    #### 3/3
    304344    #
     
    308348        dst.neighbors[i] = dst.neighbors[i-1]
    309349    dst.neighbors[0] = dst.edges
    310350
     351    return 0
     352
     353cdef int compare_ushort_p(const_void *a, const_void *b):
     354    return (<ushort *> a)[0] - (<ushort *> b)[0]
     355
     356cdef inline ushort * has_edge(short_digraph g, ushort u, ushort v):
     357    r"""
     358    Tests the existence of an edge.
     359
     360    Assumes that the neighbors of each vertex are sorted.
     361    """
     362    return <ushort *> bsearch(&v, g.neighbors[u], g.neighbors[u+1]-g.neighbors[u], sizeof(ushort), compare_ushort_p)
     363
     364cdef inline object edge_label(short_digraph g, ushort * edge):
     365    r"""
     366    Returns the label associated with a given edge
     367    """
     368    if g.edge_labels == NULL:
     369        return None
     370    else:
     371        return (<list> g.edge_labels)[edge-g.edges]
     372
    311373cdef int can_be_reached_from(short_digraph g, int src, bitset_t reached) except -1:
    312374    if g.n == 0:
    313375        return 0
     
    330392    cdef ushort * v
    331393    cdef ushort * end
    332394
    333     # Plain old DFS ... 
     395    # Plain old DFS ...
    334396    #
    335397    #If there is something left on the stack, we remove it consider each of its
    336398    # neighbors. If we find any which has not been reached yet, we set its
     
    357419
    358420    # Computing the set of vertices that can be reached from v in g
    359421    can_be_reached_from(g, v, scc)
    360 
    361422    # Computing the set of vertices that can be reached from v in g *reversed*
    362423    cdef bitset_t scc_reversed
    363424    bitset_init(scc_reversed, g.n)
    364425    can_be_reached_from(g_reversed, v, scc_reversed)
    365 
    366426    # The scc containing v is the intersection of both sets
    367427    bitset_intersection(scc, scc, scc_reversed)
    368428
    369429cdef void free_short_digraph(short_digraph g):
    370 
    371430    if g.edges != NULL:
    372431        sage_free(g.edges)
    373432
    374433    if g.neighbors != NULL:
    375434        sage_free(g.neighbors)
    376435
     436    if g.edge_labels != NULL:
     437        cpython.Py_XDECREF(g.edge_labels)
     438
    377439def strongly_connected_components(G):
    378440    r"""
    379441    Returns the strongly connected components of the given DiGraph.
     
    415477    cdef short_digraph g, gr
    416478
    417479    init_short_digraph(g, G)
    418 
    419480    init_reverse(gr, g)
    420481
    421482    cdef bitset_t seen
    422483    bitset_init(seen, g.n)
    423484    bitset_set_first_n(seen, 0)
    424485
    425 
    426486    cdef bitset_t scc
    427487    bitset_init(scc, g.n)
    428488    bitset_set_first_n(scc, 0)
     
    439499    free_short_digraph(g)
    440500    free_short_digraph(gr)
    441501    return answer
    442 
  • sage/graphs/digraph.py

    diff --git a/sage/graphs/digraph.py b/sage/graphs/digraph.py
    a b  
    9292
    9393from sage.rings.integer import Integer
    9494from sage.misc.superseded import deprecated_function_alias
     95from sage.misc.superseded import deprecation
    9596import sage.graphs.generic_graph_pyx as generic_graph_pyx
    9697from sage.graphs.generic_graph import GenericGraph
    9798from sage.graphs.dot2tex_utils import have_dot2tex
     
    221222          Given this format, weighted is ignored (assumed True).
    222223
    223224       -  ``NX`` - data must be a NetworkX DiGraph.
    224            
    225            .. NOTE:: 
     225
     226           .. NOTE::
    226227
    227228               As Sage's default edge labels is ``None`` while NetworkX uses
    228229               ``{}``, the ``{}`` labels of a NetworkX digraph are automatically
     
    237238    -  ``implementation`` - what to use as a backend for
    238239       the graph. Currently, the options are either 'networkx' or
    239240       'c_graph'
    240    
    241     -  ``sparse`` - only for implementation == 'c_graph'.
    242        Whether to use sparse or dense graphs as backend. Note that
    243        currently dense graphs do not have edge labels, nor can they be
    244        multigraphs
    245    
     241
     242    - ``sparse`` (boolean) -- ``sparse=True`` is an alias for
     243      ``data_structure="sparse"``, and ``sparse=False`` is an alias for
     244      ``data_structure="dense"``.
     245
     246    -  ``data_structure`` -- one of the following
     247
     248       * ``"dense"`` -- selects the :mod:`~sage.graphs.base.dense_graph`
     249         backend.
     250
     251       * ``"sparse"`` -- selects the :mod:`~sage.graphs.base.sparse_graph`
     252         backend.
     253
     254       * ``"static_sparse"`` -- selects the
     255         :mod:`~sage.graphs.base.static_sparse_backend` (this backend is faster
     256         than the sparse backend and smaller in memory, but it is immutable).
     257
     258       *Only available when* ``implementation == 'c_graph'``
     259
    246260    -  ``vertex_labels`` - only for implementation == 'c_graph'.
    247261       Whether to allow any object as a vertex (slower), or
    248262       only the integers 0, ..., n-1, where n is the number of vertices.
    249    
     263
    250264    -  ``convert_empty_dict_labels_to_None`` - this arguments sets
    251265       the default edge labels used by NetworkX (empty dictionaries)
    252266       to be replaced by None, the default Sage edge label. It is
    253        set to ``True`` iff a NetworkX graph is on the input. 
    254    
     267       set to ``True`` iff a NetworkX graph is on the input.
     268
    255269    EXAMPLES:
    256270
    257271    #. A dictionary of dictionaries::
    258                        
     272
    259273            sage: g = DiGraph({0:{1:'x',2:'z',3:'a'}, 2:{5:'out'}}); g
    260274            Digraph on 5 vertices
    261275   
     
    402416
    403417    def __init__(self, data=None, pos=None, loops=None, format=None,
    404418                 boundary=None, weighted=None, implementation='c_graph',
    405                  sparse=True, vertex_labels=True, name=None,
    406                  multiedges=None, convert_empty_dict_labels_to_None=None):
     419                 data_structure="sparse", vertex_labels=True, name=None,
     420                 multiedges=None, convert_empty_dict_labels_to_None=None,
     421                 sparse=True):
    407422        """
    408423        TESTS::
    409424       
     
    474489        GenericGraph.__init__(self)
    475490        from sage.structure.element import is_Matrix
    476491        from sage.misc.misc import uniq
     492
     493        if sparse == False:
     494            if data_structure != "sparse":
     495                raise ValueError("The 'sparse' argument is an alias for "
     496                                 "'data_structure'. Please do not define both.")
     497            data_structure = "dense"
     498
    477499        if format is None and isinstance(data, str):
    478500            format = 'dig6'
    479501            if data[:8] == ">>dig6<<":
     
    798820                    self.add_vertices(range(num_verts))
    799821        elif implementation == 'c_graph':
    800822            if multiedges or weighted:
    801                 if not sparse:
     823                if data_structure == "dense":
    802824                    raise RuntimeError("Multiedge and weighted c_graphs must be sparse.")
     825
     826            # If the data structure is static_sparse, we first build a graph
     827            # using the sparse data structure, then reencode the resulting graph
     828            # as a static sparse graph.
    803829            from sage.graphs.base.sparse_graph import SparseGraphBackend
    804830            from sage.graphs.base.dense_graph import DenseGraphBackend
    805             CGB = SparseGraphBackend if sparse else DenseGraphBackend
     831            if data_structure in ["sparse", "static_sparse"]:
     832                CGB = SparseGraphBackend
     833            elif data_structure == "dense":
     834                 CGB = DenseGraphBackend
     835            else:
     836                raise ValueError("data_structure must be equal to 'sparse', "
     837                                 "'static_sparse' or 'dense'")
    806838            if format == 'DiGraph':
    807839                self._backend = CGB(0, directed=True)
    808840                self.add_vertices(verts)
     
    881913        if format != 'DiGraph' or name is not None:
    882914            self.name(name)
    883915
     916        if data_structure == "static_sparse":
     917            from sage.graphs.base.static_sparse_backend import StaticSparseBackend
     918            ib = StaticSparseBackend(self, loops = loops, multiedges = multiedges)
     919            self._backend = ib
     920
    884921    ### Formats
    885 
    886922    def dig6_string(self):
    887923        """
    888924        Returns the dig6 representation of the digraph as an ASCII string.
     
    10071043        from copy import copy
    10081044        return copy(self)
    10091045
    1010     def to_undirected(self, implementation='c_graph', sparse=None):
     1046    def to_undirected(self, implementation='c_graph', data_structure=None,
     1047                      sparse=None):
    10111048        """
    10121049        Returns an undirected version of the graph. Every directed edge
    10131050        becomes an edge.
    1014        
     1051
     1052        INPUT:
     1053
     1054         - ``implementation`` - string (default: 'networkx') the
     1055           implementation goes here.  Current options are only
     1056           'networkx' or 'c_graph'.
     1057
     1058         - ``data_structure`` -- one of ``"sparse"``, ``"static_sparse"``, or
     1059           ``"dense"``. See the documentation of :class:`Graph` or
     1060           :class:`DiGraph`.
     1061
     1062         - ``sparse`` (boolean) -- ``sparse=True`` is an alias for
     1063           ``data_structure="sparse"``, and ``sparse=False`` is an alias for
     1064           ``data_structure="dense"``.
     1065
    10151066        EXAMPLES::
    1016        
     1067
    10171068            sage: D = DiGraph({0:[1,2],1:[0]})
    10181069            sage: G = D.to_undirected()
    10191070            sage: D.edges(labels=False)
     
    10211072            sage: G.edges(labels=False)
    10221073            [(0, 1), (0, 2)]
    10231074        """
    1024         if sparse is None:
     1075        if sparse != None:
     1076            deprecation(14806,"The 'sparse' keyword has been deprecated, and "
     1077                        "is now replaced by 'data_structure' which has a different "
     1078                        "meaning. Please consult the documentation.")
     1079            data_structure = "sparse" if sparse else "dense"
     1080
     1081        if data_structure is None:
    10251082            from sage.graphs.base.dense_graph import DenseGraphBackend
    1026             sparse = (not isinstance(self._backend, DenseGraphBackend))
     1083            from sage.graphs.base.sparse_graph import SparseGraphBackend
     1084            if isinstance(self._backend, DenseGraphBackend):
     1085                data_structure = "dense"
     1086            elif isinstance(self._backend, SparseGraphBackend):
     1087                data_structure = "sparse"
     1088            else:
     1089                data_structure = "static_sparse"
    10271090        from sage.graphs.all import Graph
    10281091        G = Graph(name=self.name(), pos=self._pos, boundary=self._boundary,
    10291092                  multiedges=self.allows_multiple_edges(), loops=self.allows_loops(),
    1030                   implementation=implementation, sparse=sparse)
     1093                  implementation=implementation, data_structure=data_structure)
    10311094        G.name(self.name())
    10321095        G.add_vertices(self.vertex_iterator())
    10331096        G.add_edges(self.edge_iterator())
     
    30153078            import networkx
    30163079            return networkx.strongly_connected_components(self.networkx_graph(copy=False))
    30173080
    3018 
    30193081    def strongly_connected_component_containing_vertex(self, v):
    30203082        """
    30213083        Returns the strongly connected component containing a given vertex
     
    30613123        """
    30623124        return map(self.subgraph, self.strongly_connected_components())
    30633125
    3064 
    30653126    def strongly_connected_components_digraph(self, keep_labels = False):
    30663127        r"""
    30673128        Returns the digraph of the strongly connected components
  • sage/graphs/generic_graph.py

    diff --git a/sage/graphs/generic_graph.py b/sage/graphs/generic_graph.py
    a b  
    310310from sage.rings.rational import Rational
    311311from generic_graph_pyx import GenericGraph_pyx, spring_layout_fast
    312312from sage.graphs.dot2tex_utils import assert_have_dot2tex
     313from sage.misc.superseded import deprecation
    313314
    314315class GenericGraph(GenericGraph_pyx):
    315316    """
     
    394395            sage: H.add_edge(0,1)
    395396            sage: G == H
    396397            False
    397            
     398
    398399        Note that graphs must be considered weighted, or Sage will not pay
    399400        attention to edge label data in equality testing::
    400            
     401
    401402            sage: foo = Graph(sparse=True)
    402403            sage: foo.add_edges([(0, 1, 1), (0, 2, 2)])
    403404            sage: bar = Graph(sparse=True)
    404             sage: bar.add_edges([(0, 1, 2), (0, 2, 1)]) 
     405            sage: bar.add_edges([(0, 1, 2), (0, 2, 1)])
    405406            sage: foo == bar
    406407            True
    407408            sage: foo.weighted(True)
     
    418419        from sage.graphs.all import Graph
    419420        g1_is_graph = isinstance(self, Graph) # otherwise, DiGraph
    420421        g2_is_graph = isinstance(other, Graph) # otherwise, DiGraph
    421         if g1_is_graph != g2_is_graph:
     422
     423        if (g1_is_graph != g2_is_graph):
    422424            return False
    423425        if self.allows_multiple_edges() != other.allows_multiple_edges():
    424426            return False
    425427        if self.allows_loops() != other.allows_loops():
    426428            return False
    427         if self.vertices() != other.vertices():
     429        if self.order() != other.order():
     430            return False
     431        if self.size() != other.size():
     432            return False
     433        if any(x not in other for x in self):
    428434            return False
    429435        if self._weighted != other._weighted:
    430436            return False
     437
    431438        verts = self.vertices()
    432439        # Finally, we are prepared to check edges:
    433440        if not self.allows_multiple_edges():
     
    687694
    688695    ### Formats
    689696
    690     def __copy__(self, implementation='c_graph', sparse=None):
     697    def __copy__(self, implementation='c_graph', data_structure=None,
     698                 sparse=None):
    691699        """
    692700        Creates a copy of the graph.
    693701
     
    697705           implementation goes here.  Current options are only
    698706           'networkx' or 'c_graph'.
    699707
    700          - ``sparse`` - boolean (default: None) whether the
    701            graph given is sparse or not.
     708         - ``sparse`` (boolean) -- ``sparse=True`` is an alias for
     709           ``data_structure="sparse"``, and ``sparse=False`` is an alias for
     710           ``data_structure="dense"``.
     711
     712         - ``data_structure`` -- one of ``"sparse"``, ``"static_sparse"``, or
     713           ``"dense"``. See the documentation of :class:`Graph` or
     714           :class:`DiGraph`.
    702715
    703716        OUTPUT:
    704717
     
    707720        .. warning::
    708721
    709722           Please use this method only if you need to copy but change the
    710            underlying implementation.  Otherwise simply do ``copy(g)`` 
     723           underlying implementation.  Otherwise simply do ``copy(g)``
    711724           instead of doing ``g.copy()``.
    712725
    713726        EXAMPLES::
    714        
     727
    715728            sage: g=Graph({0:[0,1,1,2]},loops=True,multiedges=True,sparse=True)
    716729            sage: g==copy(g)
    717730            True
     
    760773            sage: h._boundary is g._boundary
    761774            False
    762775        """
    763         if sparse is None:
     776        if sparse != None:
     777            if data_structure != None:
     778                raise ValueError("The 'sparse' argument is an alias for "
     779                                 "'data_structure'. Please do not define both.")
     780            data_structure = "sparse" if sparse else "dense"
     781
     782        if data_structure is None:
    764783            from sage.graphs.base.dense_graph import DenseGraphBackend
    765             sparse = (not isinstance(self._backend, DenseGraphBackend))
     784            from sage.graphs.base.sparse_graph import SparseGraphBackend
     785            if isinstance(self._backend, DenseGraphBackend):
     786                data_structure = "dense"
     787            elif isinstance(self._backend, SparseGraphBackend):
     788                data_structure = "sparse"
     789            else:
     790                data_structure = "static_sparse"
    766791        from copy import copy
    767         G = self.__class__(self, name=self.name(), pos=copy(self._pos), boundary=copy(self._boundary), implementation=implementation, sparse=sparse)
     792        G = self.__class__(self, name=self.name(), pos=copy(self._pos), boundary=copy(self._boundary), implementation=implementation, data_structure=data_structure)
    768793
    769794        attributes_to_copy = ('_assoc', '_embedding')
    770795        for attr in attributes_to_copy:
     
    17111736            self.remove_loops()
    17121737        self._backend.loops(new)
    17131738   
    1714     def loops(self, new=None, labels=True):
     1739    def loops(self, labels=True):
    17151740        """
    17161741        Returns any loops in the (di)graph.
    17171742       
     
    17641789            []
    17651790
    17661791        """
    1767         from sage.misc.superseded import deprecation
    1768         if new is not None:
    1769             deprecation(7634, "The function loops is replaced by allow_loops and allows_loops.")
    17701792        loops = []
    17711793        for v in self:
    17721794            loops += self.edge_boundary([v], [v], labels)
     
    19581980
    19591981        self._backend.multiple_edges(new)
    19601982   
    1961     def multiple_edges(self, new=None, to_undirected=False, labels=True):
     1983    def multiple_edges(self, to_undirected=False, labels=True):
    19621984        """
    19631985        Returns any multiple edges in the (di)graph.
    19641986
     
    20112033            [(1, 2, 'h'), (2, 1, 'g')]
    20122034        """
    20132035        from sage.misc.superseded import deprecation
    2014         if new is not None:
    2015             deprecation(7634, "The function multiple_edges is replaced by allow_multiple_edges and allows_multiple_edges.")
    20162036        multi_edges = []
    20172037        if self._directed and not to_undirected:
    20182038            for v in self:
  • sage/graphs/graph.py

    diff --git a/sage/graphs/graph.py b/sage/graphs/graph.py
    a b  
    505505
    506506from sage.rings.integer import Integer
    507507from sage.misc.superseded import deprecated_function_alias
     508from sage.misc.superseded import deprecation
    508509import sage.graphs.generic_graph_pyx as generic_graph_pyx
    509510from sage.graphs.generic_graph import GenericGraph
    510511from sage.graphs.digraph import DiGraph
     
    644645          labelled p if and only if E is congruent to F mod p
    645646
    646647       -  ``NX`` - data must be a NetworkX Graph.
    647            
    648            .. NOTE:: 
     648
     649           .. NOTE::
    649650
    650651               As Sage's default edge labels is ``None`` while NetworkX uses
    651652               ``{}``, the ``{}`` labels of a NetworkX graph are automatically
     
    653654               behaviour can be overruled by setting the keyword
    654655               ``convert_empty_dict_labels_to_None`` to ``False`` (it is
    655656               ``True`` by default).
    656    
     657
    657658    -  ``boundary`` - a list of boundary vertices, if
    658659       empty, graph is considered as a 'graph without boundary'
    659    
     660
    660661    -  ``implementation`` - what to use as a backend for
    661662       the graph. Currently, the options are either 'networkx' or
    662663       'c_graph'
    663    
    664     -  ``sparse`` - only for implementation == 'c_graph'.
    665        Whether to use sparse or dense graphs as backend. Note that
    666        currently dense graphs do not have edge labels, nor can they be
    667        multigraphs
    668    
     664
     665    - ``sparse`` (boolean) -- ``sparse=True`` is an alias for
     666      ``data_structure="sparse"``, and ``sparse=False`` is an alias for
     667      ``data_structure="dense"``.
     668
     669    -  ``data_structure`` -- one of the following
     670
     671       * ``"dense"`` -- selects the :mod:`~sage.graphs.base.dense_graph`
     672         backend.
     673
     674       * ``"sparse"`` -- selects the :mod:`~sage.graphs.base.sparse_graph`
     675         backend.
     676
     677       * ``"static_sparse"`` -- selects the
     678         :mod:`~sage.graphs.base.static_sparse_backend` (this backend is faster
     679         than the sparse backend and smaller in memory, but it is immutable).
     680
     681       *Only available when* ``implementation == 'c_graph'``
     682
    669683    -  ``vertex_labels`` - only for implementation == 'c_graph'.
    670684       Whether to allow any object as a vertex (slower), or
    671685       only the integers 0, ..., n-1, where n is the number of vertices.
    672    
     686
    673687    -  ``convert_empty_dict_labels_to_None`` - this arguments sets
    674688       the default edge labels used by NetworkX (empty dictionaries)
    675689       to be replaced by None, the default Sage edge label. It is
    676690       set to ``True`` iff a NetworkX graph is on the input.
    677    
    678    
    679     EXAMPLES:
     691
     692    EXAMPLES:
    680693
    681694    We illustrate the first seven input formats (the other two
    682695    involve packages that are currently not standard in Sage):
     
    923936          False
    924937    """
    925938    _directed = False
    926    
     939
    927940    def __init__(self, data=None, pos=None, loops=None, format=None,
    928941                 boundary=None, weighted=None, implementation='c_graph',
    929                  sparse=True, vertex_labels=True, name=None,
    930                  multiedges=None, convert_empty_dict_labels_to_None=None):
     942                 data_structure="sparse", vertex_labels=True, name=None,
     943                 multiedges=None, convert_empty_dict_labels_to_None=None,
     944                 sparse = True):
    931945        """
    932946        TESTS::
    933        
     947
    934948            sage: G = Graph()
    935949            sage: loads(dumps(G)) == G
    936950            True
     
    957971            sage: g.get_pos() == h.get_pos()
    958972            True
    959973
    960         Loops are not counted as multiedges (see trac 11693) and edges
    961         are not counted twice ::
     974        Loops are not counted as multiedges (see :trac:`11693`) and edges are
     975        not counted twice ::
    962976
    963977            sage: Graph([[1,1]],multiedges=False).num_edges()
    964978            1
     
    10261040        msg = ''
    10271041        from sage.structure.element import is_Matrix
    10281042        from sage.misc.misc import uniq
     1043
     1044        if sparse == False:
     1045            if data_structure != "sparse":
     1046                raise ValueError("The 'sparse' argument is an alias for "
     1047                                 "'data_structure'. Please do not define both.")
     1048            data_structure = "dense"
     1049
    10291050        if format is None and isinstance(data, str):
    10301051            if data[:10] == ">>graph6<<":
    10311052                data = data[10:]
     
    14261447                    self.add_vertices(range(num_verts))
    14271448        elif implementation == 'c_graph':
    14281449            if multiedges or weighted:
    1429                 if not sparse:
     1450                if data_structure == "dense":
    14301451                    raise RuntimeError("Multiedge and weighted c_graphs must be sparse.")
     1452
     1453            # If the data structure is static_sparse, we first build a graph
     1454            # using the sparse data structure, then reencode the resulting graph
     1455            # as a static sparse graph.
    14311456            from sage.graphs.base.sparse_graph import SparseGraphBackend
    14321457            from sage.graphs.base.dense_graph import DenseGraphBackend
    1433             CGB = SparseGraphBackend if sparse else DenseGraphBackend
     1458            if data_structure in ["sparse", "static_sparse"]:
     1459                CGB = SparseGraphBackend
     1460            elif data_structure == "dense":
     1461                 CGB = DenseGraphBackend
     1462            else:
     1463                raise ValueError("data_structure must be equal to 'sparse', "
     1464                                 "'static_sparse' or 'dense'")
    14341465            if format == 'Graph':
    14351466                self._backend = CGB(0, directed=False)
    14361467                self.add_vertices(verts)
     
    15511582        if format != 'Graph' or name is not None:
    15521583            self.name(name)
    15531584
     1585        if data_structure == "static_sparse":
     1586            from sage.graphs.base.static_sparse_backend import StaticSparseBackend
     1587            ib = StaticSparseBackend(self, loops = loops, multiedges = multiedges)
     1588            self._backend = ib
     1589
    15541590    ### Formats
    1555 
    15561591    def graph6_string(self):
    15571592        """
    15581593        Returns the graph6 representation of the graph as an ASCII string.
     
    15931628
    15941629        ::
    15951630
    1596             sage: G = Graph(loops=True, multiedges=True,sparse=True)
    1597             sage: Graph(':?',sparse=True) == G
     1631            sage: G = Graph(loops=True, multiedges=True,data_structure="sparse")
     1632            sage: Graph(':?',data_structure="sparse") == G
    15981633            True
    15991634        """
    16001635        n = self.order()
     
    16781713        return False
    16791714
    16801715    ### Properties
    1681 
    16821716    def is_tree(self, certificate = False):
    16831717        """
    16841718        Tests if the graph is a tree
     
    22682302        else:
    22692303            raise ValueError("Algorithm '%s' not yet implemented. Please contribute." %(algorithm))
    22702304
    2271 
    22722305    def is_split(self):
    22732306        r"""
    22742307        Returns ``True`` if the graph is a Split graph, ``False`` otherwise.
     
    23422375
    23432376        return left == right
    23442377
    2345 
    23462378    def is_perfect(self, certificate = False):
    23472379        r"""
    23482380        Tests whether the graph is perfect.
     
    44304462   
    44314463    ### Constructors
    44324464
    4433     def to_directed(self, implementation='c_graph', sparse=None):
     4465    def to_directed(self, implementation='c_graph', data_structure=None,
     4466                    sparse=None):
    44344467        """
    44354468        Returns a directed version of the graph. A single edge becomes two
    44364469        edges, one in each direction.
    4437        
     4470
     4471        INPUT:
     4472
     4473         - ``implementation`` - string (default: 'networkx') the
     4474           implementation goes here.  Current options are only
     4475           'networkx' or 'c_graph'.
     4476
     4477         - ``data_structure`` -- one of ``"sparse"``, ``"static_sparse"``, or
     4478           ``"dense"``. See the documentation of :class:`Graph` or
     4479           :class:`DiGraph`.
     4480
     4481         - ``sparse`` (boolean) -- ``sparse=True`` is an alias for
     4482           ``data_structure="sparse"``, and ``sparse=False`` is an alias for
     4483           ``data_structure="dense"``.
     4484
    44384485        EXAMPLES::
    4439        
     4486
    44404487            sage: graphs.PetersenGraph().to_directed()
    44414488            Petersen graph: Digraph on 10 vertices
    44424489        """
    4443         if sparse is None:
     4490        if sparse != None:
     4491            if data_structure != None:
     4492                raise ValueError("The 'sparse' argument is an alias for "
     4493                                 "'data_structure'. Please do not define both.")
     4494            data_structure = "sparse" if sparse else "dense"
     4495
     4496        if data_structure is None:
    44444497            from sage.graphs.base.dense_graph import DenseGraphBackend
    4445             sparse = (not isinstance(self._backend, DenseGraphBackend))
     4498            from sage.graphs.base.sparse_graph import SparseGraphBackend
     4499            if isinstance(self._backend, DenseGraphBackend):
     4500                data_structure = "dense"
     4501            elif isinstance(self._backend, SparseGraphBackend):
     4502                data_structure = "sparse"
     4503            else:
     4504                data_structure = "static_sparse"
    44464505        from sage.graphs.all import DiGraph
    44474506        D = DiGraph(name=self.name(), pos=self._pos, boundary=self._boundary,
    44484507                    multiedges=self.allows_multiple_edges(),
    4449                     implementation=implementation, sparse=sparse)
     4508                    implementation=implementation, data_structure=data_structure)
    44504509        D.name(self.name())
    44514510        D.add_vertices(self.vertex_iterator())
    44524511        for u,v,l in self.edge_iterator():
  • sage/graphs/graph_generators.py

    diff --git a/sage/graphs/graph_generators.py b/sage/graphs/graph_generators.py
    a b  
    388388      ``'c_graph'``.
    389389
    390390    - ``copy`` (boolean) -- If set to ``True`` (default)
    391       this method makes copies of the graphs before returning 
     391      this method makes copies of the graphs before returning
    392392      them. If set to ``False`` the method returns the graph it
    393393      is working on. The second alternative is faster, but modifying
    394394      any of the graph instances returned by the method may break
    395       the function's behaviour, as it is using these graphs to 
     395      the function's behaviour, as it is using these graphs to
    396396      compute the next ones : only use ``copy_graph = False`` when
    397397      you stick to *reading* the graphs returned.
    398398
     
    646646            extra_property = lambda x: x.size() == size
    647647        else:
    648648            extra_property = lambda x: True
     649
    649650        if augment == 'vertices':
    650651            if vertices is None:
    651652                raise NotImplementedError
     
    14271428    """
    14281429    from sage.groups.perm_gps.partn_ref.refinement_graphs import search_tree
    14291430
    1430    
    14311431    if not property(g):
    14321432        return
    14331433    yield g
     
    16121612        Digraph on 3 vertices
    16131613    """
    16141614    from sage.groups.perm_gps.partn_ref.refinement_graphs import search_tree
     1615
    16151616    if not property(g):
    16161617        return
    16171618    yield g