Ticket #13917: trac_13917.patch

File trac_13917.patch, 17.7 KB (added by ncohen, 8 years ago)
  • doc/en/reference/graphs/index.rst

    # HG changeset patch
    # User Nathann Cohen <nathann.cohen@gmail.com>
    # Date 1357313418 -3600
    # Node ID c1208a86125e08b8f6e3e7a0d9f23e4329818118
    # Parent  f8d13a70c806e4233a4d7927f1857a8cbf90af5f
    IndependentSets class
    
    diff --git a/doc/en/reference/graphs/index.rst b/doc/en/reference/graphs/index.rst
    a b  
    4848
    4949   sage/graphs/graph_coloring
    5050   sage/graphs/cliquer
     51   sage/graphs/independent_sets
    5152   sage/graphs/comparability
    5253   sage/graphs/line_graph
    5354   sage/graphs/spanning_tree
  • module_list.py

    diff --git a/module_list.py b/module_list.py
    a b  
    354354              sources = ['sage/graphs/cliquer.pyx'],
    355355              libraries = ['cliquer']),
    356356
     357    Extension('sage.graphs.independent_sets',
     358              sources = ['sage/graphs/independent_sets.pyx']),
    357359
    358360    Extension('sage.graphs.graph_decompositions.vertex_separation',
    359361              sources = ['sage/graphs/graph_decompositions/vertex_separation.pyx']),
  • sage/graphs/graph.py

    diff --git a/sage/graphs/graph.py b/sage/graphs/graph.py
    a b  
    47054705
    47064706
    47074707    ### Cliques
    4708    
     4708
    47094709    def cliques_maximal(self):
    47104710        """
    47114711        Returns the list of all maximal cliques, with each clique represented
    47124712        by a list of vertices. A clique is an induced complete subgraph, and a
    47134713        maximal clique is one not contained in a larger one.
    4714        
     4714
    47154715        .. NOTE::
    4716        
     4716
    47174717            Currently only implemented for undirected graphs. Use to_undirected
    47184718            to convert a digraph to an undirected graph.
    4719        
     4719
    47204720        ALGORITHM:
    4721        
    4722         This function is based on NetworkX's implementation of the Bron and
    4723         Kerbosch Algorithm [BroKer1973]_.
    4724            
     4721
     4722        - For graphs on `>64` vertices this function uses NetworkX's
     4723          implementation of the Bron and Kerbosch Algorithm [BroKer1973]_.
     4724
     4725        - For small graphs, this function uses
     4726          :mod:`sage.graphs.independent_sets`.
     4727
    47254728        REFERENCE:
    47264729
    47274730        .. [BroKer1973] Coen Bron and Joep Kerbosch. (1973). Algorithm 457:
    47284731          Finding All Cliques of an Undirected Graph. Commun. ACM. v
    47294732          16. n 9.  pages 575-577. ACM Press. [Online] Available:
    47304733          http://www.ram.org/computing/rambin/rambin.html
    4731        
     4734
    47324735        EXAMPLES::
    4733        
     4736
    47344737            sage: graphs.ChvatalGraph().cliques_maximal()
    4735             [[0, 1], [0, 4], [0, 6], [0, 9], [2, 1], [2, 3], [2, 6], [2, 8], [3, 4], [3, 7], [3, 9], [5, 1], [5, 4], [5, 10], [5, 11], [7, 1], [7, 8], [7, 11], [8, 4], [8, 10], [10, 6], [10, 9], [11, 6], [11, 9]]
     4738            [[0, 1], [0, 4], [0, 6], [0, 9], [1, 2], [1, 5], [1, 7], [2, 3],
     4739             [2, 6], [2, 8], [3, 4], [3, 7], [3, 9], [4, 5], [4, 8], [5, 10],
     4740             [5, 11], [6, 10], [6, 11], [7, 8], [7, 11], [8, 10], [9, 10], [9, 11]]
    47364741            sage: G = Graph({0:[1,2,3], 1:[2], 3:[0,1]})
    47374742            sage: G.show(figsize=[2,2])
    47384743            sage: G.cliques_maximal()
    47394744            [[0, 1, 2], [0, 1, 3]]
    47404745            sage: C=graphs.PetersenGraph()
    47414746            sage: C.cliques_maximal()
    4742             [[0, 1], [0, 4], [0, 5], [2, 1], [2, 3], [2, 7], [3, 4], [3, 8], [6, 1], [6, 8], [6, 9], [7, 5], [7, 9], [8, 5], [9, 4]]
     4747            [[0, 1], [0, 4], [0, 5], [1, 2], [1, 6], [2, 3], [2, 7], [3, 4],
     4748             [3, 8], [4, 9], [5, 7], [5, 8], [6, 8], [6, 9], [7, 9]]
    47434749            sage: C = Graph('DJ{')
    47444750            sage: C.cliques_maximal()
    4745             [[4, 0], [4, 1, 2, 3]]
     4751            [[0, 4], [1, 2, 3, 4]]
    47464752
    47474753        """
    4748         import networkx
    4749         return sorted(networkx.find_cliques(self.networkx_graph(copy=False)))
     4754        if self.order() > 0 and self.order() <= 64:
     4755            from sage.graphs.independent_sets import IndependentSets
     4756            return sorted(IndependentSets(self, maximal = True, complement = True))
     4757        else:
     4758            import networkx
     4759            return sorted(networkx.find_cliques(self.networkx_graph(copy=False)))
    47504760
    47514761    cliques = deprecated_function_alias(5739, cliques_maximal)
    47524762
     
    49084918            {0: 1, 1: 1, 2: 1, 3: 1, 4: 2}
    49094919            sage: E = C.cliques_maximal()
    49104920            sage: E
    4911             [[4, 0], [4, 1, 2, 3]]
     4921            [[0, 4], [1, 2, 3, 4]]
    49124922            sage: C.cliques_number_of(cliques=E)
    49134923            {0: 1, 1: 1, 2: 1, 3: 1, 4: 2}
    49144924            sage: F = graphs.Grid2dGraph(2,3)
     
    53715381            {0: 2, 1: 4, 2: 4, 3: 4, 4: 4}
    53725382            sage: E = C.cliques_maximal()
    53735383            sage: E
    5374             [[4, 0], [4, 1, 2, 3]]
     5384            [[0, 4], [1, 2, 3, 4]]
    53755385            sage: C.cliques_vertex_clique_number(cliques=E,algorithm="networkx")
    53765386            {0: 2, 1: 4, 2: 4, 3: 4, 4: 4}
    53775387            sage: F = graphs.Grid2dGraph(2,3)
     
    54345444            {0: [[4, 0]], 1: [[4, 1, 2, 3]], 2: [[4, 1, 2, 3]], 3: [[4, 1, 2, 3]], 4: [[4, 0], [4, 1, 2, 3]]}
    54355445            sage: E = C.cliques_maximal()
    54365446            sage: E
    5437             [[4, 0], [4, 1, 2, 3]]
     5447            [[0, 4], [1, 2, 3, 4]]
    54385448            sage: C.cliques_containing_vertex(cliques=E)
    5439             {0: [[4, 0]], 1: [[4, 1, 2, 3]], 2: [[4, 1, 2, 3]], 3: [[4, 1, 2, 3]], 4: [[4, 0], [4, 1, 2, 3]]}
     5449            {0: [[0, 4]], 1: [[1, 2, 3, 4]], 2: [[1, 2, 3, 4]], 3: [[1, 2, 3, 4]], 4: [[0, 4], [1, 2, 3, 4]]}
    54405450            sage: F = graphs.Grid2dGraph(2,3)
    54415451            sage: X = F.cliques_containing_vertex()
    54425452            sage: for v in sorted(X.iterkeys()):
     
    54625472        """
    54635473        Returns the clique complex of self. This is the largest simplicial complex on
    54645474        the vertices of self whose 1-skeleton is self.
    5465        
     5475
    54665476        This is only makes sense for undirected simple graphs.
    54675477
    54685478        EXAMPLES::
     
    54705480            sage: g = Graph({0:[1,2],1:[2],4:[]})
    54715481            sage: g.clique_complex()
    54725482            Simplicial complex with vertex set (0, 1, 2, 4) and facets {(4,), (0, 1, 2)}
    5473            
     5483
    54745484            sage: h = Graph({0:[1,2,3,4],1:[2,3,4],2:[3]})
    54755485            sage: x = h.clique_complex()
    54765486            sage: x
  • new file sage/graphs/independent_sets.pxd

    diff --git a/sage/graphs/independent_sets.pxd b/sage/graphs/independent_sets.pxd
    new file mode 100644
    - +  
     1from libc.stdint cimport uint32_t, uint64_t
     2
     3cdef class IndependentSets:
     4    cdef uint64_t count
     5    cdef uint64_t[64] g
     6    cdef list vertices
     7    cdef dict vertex_to_int
     8    cdef int n
     9    cdef int i
     10    cdef int count_only
     11    cdef uint64_t one
     12    cdef uint64_t current_set
     13    cdef int maximal
  • new file sage/graphs/independent_sets.pyx

    diff --git a/sage/graphs/independent_sets.pyx b/sage/graphs/independent_sets.pyx
    new file mode 100644
    - +  
     1r"""
     2Independent sets
     3
     4This module implements the :class:`IndependentSets` class which can be used to :
     5
     6- List the independent sets of a graph
     7- Count them (which is obviously faster)
     8- Test whether a set of vertices is an independent set
     9
     10It can also be restricted to focus on (inclusionwise) maximal independent
     11sets. See the documentation of :class:`IndependentSets` for actual examples.
     12
     13Classes and methods
     14-------------------
     15
     16"""
     17
     18cdef inline int isin(uint64_t s,int i):
     19    return (s>>i)&1
     20
     21cdef inline int ismaximal(uint64_t[64] g, int n, uint64_t s):
     22    cdef int i
     23    for i in range(n):
     24        if (not isin(s,i)) and ((g[i]&s) == 0):
     25            return False
     26
     27    return True
     28
     29cdef class IndependentSets:
     30    r"""
     31    An instance of this class represents the set of independent sets of a graph.
     32
     33    INPUT:
     34
     35    - ``G`` -- a graph
     36
     37    - ``maximal`` (boolean) -- whether to only consider (inclusionwise) maximal
     38      independent sets. Set to ``False`` by default.
     39
     40    - ``complement`` (boolean) -- whether to consider the graph's complement
     41      (i.e. cliques instead of independent sets). Set to ``False`` by default.
     42
     43    .. WARNING::
     44
     45        - This implementation can only handle graphs with `\leq 64` vertices.
     46
     47    EXAMPLES:
     48
     49    Listing all independent sets of the Claw graph::
     50
     51        sage: from sage.graphs.independent_sets import IndependentSets
     52        sage: g = graphs.ClawGraph()
     53        sage: list(IndependentSets(g))
     54        [[0], [1], [1, 2], [1, 2, 3], [1, 3], [2], [2, 3], [3], []]
     55
     56    Count them::
     57
     58        sage: IndependentSets(g).cardinality()
     59        9
     60
     61    List only the maximal independent sets::
     62
     63        sage: list(IndependentSets(g, maximal = True))
     64        [[0], [1, 2, 3]]
     65
     66    And count them::
     67
     68        sage: IndependentSets(g, maximal = True).cardinality()
     69        2
     70
     71    """
     72    def __init__(self, G, maximal = False, complement = False):
     73        r"""
     74        Constructor for this class
     75
     76        TESTS::
     77
     78            sage: from sage.graphs.independent_sets import IndependentSets
     79            sage: IndependentSets(graphs.PetersenGraph())
     80            <sage.graphs.independent_sets.IndependentSets object...
     81
     82        Compute the number of matchings, and check with Sage's implementation::
     83
     84            sage: from sage.graphs.independent_sets import IndependentSets
     85            sage: from sage.graphs.matchpoly import matching_polynomial
     86            sage: def check_matching(G):
     87            ...       number_of_matchings = sum(map(abs,matching_polynomial(G).coeffs()))
     88            ...       if number_of_matchings != IndependentSets(G.line_graph()).cardinality():
     89            ...           print "Ooooch !"
     90            sage: for i in range(30):
     91            ...       check_matching(graphs.RandomGNP(11,.3))
     92
     93        Compare the result with the output of subgraph_search::
     94
     95            sage: from sage.sets.set import Set
     96            sage: def check_matching(G):
     97            ...       IS = set(map(Set,list(IndependentSets(G))))
     98            ...       if not all(G.subgraph(l).is_independent_set() for l in IS):
     99            ...          print "Gloops"
     100            ...       omega = max(map(len,IS))
     101            ...       IS2 = [Set([x]) for x in range(G.order())] + [Set([])]
     102            ...       for n in range(2,omega+1):
     103            ...           IS2.extend(map(Set,list(G.subgraph_search_iterator(G.__class__(n), induced = True))))
     104            ...       if len(IS) != len(set(IS2)):
     105            ...          print "Oops"
     106            ...          print len(IS), len(set(IS2))
     107            sage: for i in range(5):
     108            ...       check_matching(graphs.RandomGNP(11,.3))
     109        """
     110        cdef int i
     111        if G.order() > 64 or G.order() == 0:
     112            raise ValueError("This class can only handle non-empty graphs of size at most 64")
     113
     114        # Map from Vertex to Integer, and from Integer to Vertex
     115        self.vertex_to_int = {v:id for id,v in enumerate(G.vertices())}
     116        self.vertices = G.vertices()
     117        self.n = G.order()
     118        self.maximal = maximal
     119
     120        # 1<<32 is not a good idea, as it is equal to 1. one << 32 is not, though.
     121        self.one = 1
     122
     123        # A local copy of the graph on 64-bits integers
     124        for i in range(self.n):
     125            self.g[i] = 0
     126        for u,v in G.edges(labels = False):
     127            u = self.vertex_to_int[u]
     128            v = self.vertex_to_int[v]
     129            self.g[u] |= (self.one<<v)
     130            self.g[v] |= (self.one<<u)
     131
     132        # Complementing the graph is easy with this data structure.
     133        if complement:
     134            for i in range(self.n):
     135                self.g[i] |= (self.one<<i)
     136                self.g[i] = ~self.g[i]
     137
     138        self.count_only = False
     139        self._reset()
     140
     141    def _reset(self):
     142        r"""
     143        Set the variables used by the iterator to 0
     144
     145        TESTS::
     146
     147            sage: from sage.graphs.independent_sets import IndependentSets
     148            sage: IndependentSets(graphs.PetersenGraph() ) # indirect doctest
     149            <sage.graphs.independent_sets.IndependentSets object ...
     150
     151        """
     152        self.i = 0
     153        self.current_set = 1
     154        self.count = 0
     155
     156    def __iter__(self):
     157        r"""
     158        Returns self, as it has a __next__ method.
     159
     160        TESTS::
     161
     162            sage: from sage.graphs.independent_sets import IndependentSets
     163            sage: list(IndependentSets(graphs.PetersenGraph()))
     164            [[0], [0, 2], [0, 2, 6], [0, 2, 8], [0, 2, 8, 9], [0, 2, 9],
     165             [0, 3], [0, 3, 6], [0, 3, 6, 7], [0, 3, 7], [0, 3, 9],
     166             [0, 6], [0, 6, 7], [0, 7], [0, 7, 8], [0, 8], [0, 8, 9],
     167             [0, 9], [1], [1, 3], [1, 3, 5], [1, 3, 5, 9], [1, 3, 7],
     168             [1, 3, 9], [1, 4], [1, 4, 5], [1, 4, 7], [1, 4, 7, 8],
     169             [1, 4, 8], [1, 5], [1, 5, 9], [1, 7], [1, 7, 8], [1, 8],
     170             [1, 8, 9], [1, 9], [2], [2, 4], [2, 4, 5], [2, 4, 5, 6],
     171             [2, 4, 6], [2, 4, 8], [2, 5], [2, 5, 6], [2, 5, 9],
     172             [2, 6], [2, 8], [2, 8, 9], [2, 9], [3], [3, 5],
     173             [3, 5, 6], [3, 5, 9], [3, 6], [3, 6, 7], [3, 7],
     174             [3, 9], [4], [4, 5], [4, 5, 6], [4, 6], [4, 6, 7], [4, 7],
     175             [4, 7, 8], [4, 8], [5], [5, 6], [5, 9], [6], [6, 7], [7],
     176             [7, 8], [8], [8, 9], [9], []]
     177        """
     178        return self
     179
     180    def __next__(self):
     181        r"""
     182        Computes the next independent set
     183
     184        TESTS::
     185
     186            sage: from sage.graphs.independent_sets import IndependentSets
     187            sage: list(IndependentSets(graphs.PetersenGraph()))
     188            [[0], ...
     189
     190        """
     191        cdef list ans
     192        cdef int j
     193        cdef uint64_t tmp
     194
     195        if self.i == -1:
     196            raise StopIteration
     197
     198        # At every moment of the algorithm current_set represents an independent
     199        # set, except for the ith bit. All bits >i are zero.
     200
     201        while True:
     202
     203            # If i is in current_set
     204            if isin(self.current_set,self.i):
     205
     206                # We have found an independent set !
     207                if(self.g[self.i]&self.current_set) == 0:
     208
     209                    # Saving that set
     210                    tmp = self.current_set
     211
     212                    # Preparing for the next set, except if we set the last bit.
     213                    if self.i < self.n-1:
     214
     215                        # Adding (i+1)th bit
     216                        self.current_set |= self.one<<(self.i+1)
     217                        self.i += 1
     218                    else:
     219                        self.current_set &= ~(self.one<<(self.i))
     220
     221                    # Returning the result if necessary ...
     222
     223                    if self.maximal and not ismaximal(self.g,self.n, tmp):
     224                        continue
     225
     226                    self.count += 1
     227                    if not self.count_only:
     228                        return [self.vertices[j] for j in range(self.i+1) if isin(tmp,j)]
     229
     230                else:
     231                    # Removing the ith bit
     232                    self.current_set &= ~(self.one<<self.i)
     233
     234                    # Preparing for the next set !
     235                    if self.i < self.n-1:
     236                        self.current_set |= self.one<<(self.i+1)
     237                        self.i += 1
     238
     239            # Not already included in the set
     240            else:
     241                if self.i == 0:
     242                    if not self.maximal:
     243                        self.count+=1
     244                        self.i = -1
     245                        return []
     246                    else:
     247                        raise StopIteration
     248
     249                # Going backward, we explored all we could there !
     250                if isin(self.current_set,self.i-1):
     251                    self.current_set &= ~(self.one<<(self.i-1))
     252                    self.current_set |= self.one<<(self.i)
     253                else:
     254                    self.i -= 1
     255
     256    def cardinality(self):
     257        r"""
     258        Computes and returns the number of independent sets
     259
     260        TESTS::
     261
     262            sage: from sage.graphs.independent_sets import IndependentSets
     263            sage: IndependentSets(graphs.PetersenGraph()).cardinality()
     264            76
     265
     266        Only maximal ones::
     267
     268            sage: from sage.graphs.independent_sets import IndependentSets
     269            sage: IndependentSets(graphs.PetersenGraph(), maximal = True).cardinality()
     270            15
     271        """
     272        self.count_only = 1
     273
     274        for i in self:
     275            pass
     276
     277        from sage.rings.integer import Integer
     278        count = Integer(self.count)
     279        self._reset()
     280        return count
     281
     282    def __contains__(self, S):
     283        r"""
     284        Checks whether the set is an independent set (possibly maximal)
     285
     286        - ``S`` -- a set of vertices to be tested.
     287
     288        EXAMPLE::
     289
     290        All independent sets of PetersenGraph are ... independent sets ::
     291
     292            sage: from sage.graphs.independent_sets import IndependentSets
     293            sage: IS = IndependentSets(graphs.PetersenGraph())
     294            sage: all( s in IS for s in IS)
     295            True
     296
     297        Same with maximal independent sets::
     298
     299            sage: IS = IndependentSets(graphs.PetersenGraph(), maximal = True)
     300            sage: all( s in IS for s in IS)
     301            True
     302        """
     303        # Set of vertices as a uint64_t
     304        cdef uint64_t s = 0
     305
     306        cdef int i
     307        for I in S:
     308            try:
     309                i = self.vertex_to_int[I]
     310            except KeyError:
     311                raise ValueError("An element of the set being tested does not belong to ")
     312
     313            # Adding the new vertex to s
     314            s |= (self.one<<i)
     315
     316            # Checking that the set s is independent
     317            if (self.g[i]&s) != 0:
     318                return False
     319
     320        if self.maximal and not ismaximal(self.g, self.n,s):
     321            return False
     322
     323        return True
     324
     325cdef printgraph(uint64_t[64] g):
     326    r"""
     327    Prints a uint64_t graph
     328    """
     329    import sys
     330    for i in range(64):
     331        printset(g[i])
     332
     333cdef printset(uint64_t s):
     334    r"""
     335    Prints a uint64_t as a binary string
     336    """
     337    for j in range(64):
     338        print ((s>>(63-j))&1),
     339    print ""