Ticket #11961: trac_11961.patch

File trac_11961.patch, 8.0 KB (added by ncohen, 8 years ago)
  • sage/graphs/generic_graph.py

    # HG changeset patch
    # User Nathann Cohen <nathann.cohen@gmail.com>
    # Date 1319883176 -7200
    # Node ID 5c858dfb7d028e27bce55a644c285055962d0b80
    # Parent  10c003377e10d83cbef987843b417273f08e9908
    trac 11961 -- Bug in Graph.is_chordal -- two algorithms
    
    diff --git a/sage/graphs/generic_graph.py b/sage/graphs/generic_graph.py
    a b  
    95549554                vertices.append(v)
    95559555        return self.subgraph(vertices=vertices, inplace=inplace)
    95569556
    9557     def is_chordal(self, certificate = False):
     9557    def is_chordal(self, certificate = False, algorithm = "B"):
    95589558        r"""
    95599559        Tests whether the given graph is chordal.
    95609560   
     
    95949594                * ``(False, Hole)`` when the graph is not chordal, where
    95959595                  ``Hole`` (a ``Graph`` object) is an induced subgraph of
    95969596                  ``self`` isomorphic to a hole.
     9597
     9598        - ``algorithm`` -- Two algorithms are available for this method (see
     9599          next section), which can be selected by setting ``algorithm = "A"`` or
     9600          ``algorithm = "B"`` (default). While they will agree on whether the
     9601          given graph is chordal, they can not be expected to return the same
     9602          certificates.
    95979603   
    95989604        ALGORITHM:
    95999605   
     
    96049610   
    96059611        This problem can be solved in `O(m)` [Rose75]_ ( where `m` is the number
    96069612        of edges in the graph ) but this implementation is not linear because of
    9607         the complexity of Lex BFS. Improving Lex BFS to linear complexity would
    9608         make this algorithm linear.
    9609    
    9610         The complexity of this algorithm is equal to the complexity of the
    9611         implementation of Lex BFS.
    9612    
     9613        the complexity of Lex BFS.
     9614
     9615        .. NOTE::
     9616
     9617            Because of a past bug (#11735, #11961), the first implementation
     9618            (algorithm A) of this method sometimes returned as certificates
     9619            subgraphs which were **not** holes. Since then, this bug has been
     9620            fixed and the values are now double-checked before being returned,
     9621            so that the algorithm only returns correct values or raises an
     9622            exception. In the case where an exception is raised, the user is
     9623            advised to switch to the other algorithm. And to **please** report
     9624            the bug :-)
     9625
    96139626        EXAMPLES:
    96149627   
    96159628        The lexicographic product of a Path and a Complete Graph
     
    97029715            else:
    97039716                return all( gg.is_chordal() for gg in self.connected_components_subgraphs() )
    97049717
    9705         peo,t_peo = self.lex_BFS(tree=True)
    9706 
     9718        hole = None
    97079719        g = self.copy()
    9708         peo.reverse()
    9709    
    9710         # Iteratively removing vertices and checking everything is fine.
    9711         for v in peo:
    9712 
    9713             if t_peo.out_degree(v)>0:
    9714 
     9720
     9721        # Algorithms
     9722        #
     9723        # They find the perfect elimination ordering or produce a hole
     9724
     9725        if algorithm == "A":
     9726
     9727            peo,t_peo = self.lex_BFS(tree=True)
     9728            peo.reverse()
     9729       
     9730            # Iteratively removing vertices and checking everything is fine.
     9731            for v in peo:
     9732   
     9733                if t_peo.out_degree(v) == 0:
     9734                    g.delete_vertex(v)
     9735                    continue
     9736   
    97159737                x = t_peo.neighbor_out_iterator(v).next()
    97169738                S = self.neighbors(x)+[x]
    9717 
     9739   
    97189740                if not frozenset(g.neighbors(v)).issubset(S):
    9719 
     9741   
    97209742                    # Do we need to return a hole ?
    97219743                    if certificate:
    9722 
     9744   
    97239745                        # In this case, let us take two nonadjacent neighbors of v
    97249746                        # In this case, let us take two nonadjacent neighbors of
    97259747                        # v. In order to do so, we pick a vertex y which is a
    97269748                        # neighbor of v but is not adjacent to x, which we know
    97279749                        # exists by the test written two lines above.
    9728 
     9750   
    97299751                        for y in g.neighbors(v):
    97309752                            if y not in S:
    97319753                                break
    9732 
     9754   
    97339755                        g.delete_vertices([vv for vv in g.neighbors(v) if vv != y and vv != x])
    97349756                        g.delete_vertex(v)
    9735 
     9757   
    97369758                        # Our hole is v + (a shortest path between x and y not
    97379759                        # containing v or any of its neighbors).
    9738 
     9760   
    97399761                        hole = self.subgraph([v] + g.shortest_path(x,y))
    9740 
    9741                         # There was a bug there once, so it's better to check the
    9742                         # answer is valid, especally when it is so cheap ;-)
    9743 
    9744                         if hole.order() <= 3 or not hole.is_regular(k=2):
    9745                             raise Exception("The graph is not chordal, and something went wrong in the computation of the certificate. Please report this bug, providing the graph if possible !")
    9746 
    9747                         return (False, hole)
     9762   
     9763                        # End of the algorithm
     9764                        break
    97489765                    else:
    97499766                        return False
    9750 
    9751             g.delete_vertex(v)
    9752 
     9767   
     9768                g.delete_vertex(v)
     9769
     9770        elif algorithm == "B":
     9771
     9772            peo,t_peo = self.lex_BFS(reverse=True, tree=True)
     9773         
     9774            # Remembering the (closed) neighborhoods of each vertex
     9775            neighbors_subsets = dict([(v,self.neighbors(v)+[v]) for v in g])
     9776            pos_in_peo = dict(zip(peo, range(self.order())))
     9777
     9778            # Iteratively removing vertices and checking everything is fine.
     9779            for v in reversed(peo):
     9780         
     9781                if (t_peo.out_degree(v)>0 and
     9782                    not frozenset([v1 for v1 in g.neighbors(v) if pos_in_peo[v1] > pos_in_peo[v]]).issubset(
     9783                        neighbors_subsets[t_peo.neighbor_out_iterator(v).next()])):
     9784         
     9785                    # Do we need to return a hole ?
     9786                    if certificate:
     9787         
     9788                        # In this case, let us take two nonadjacent neighbors of
     9789                        # v. In order to do so, we pick a vertex y which is a
     9790                        # neighbor of v but is not adjacent to x, which we know
     9791                        # exists by the test written two lines above.
     9792                        max_tup = (-1, 0)
     9793                        nb1 = [u for u in g.neighbors(v) if pos_in_peo[u] > pos_in_peo[v]]
     9794                        for xi in nb1:
     9795                            for yi in nb1:
     9796                                if not yi in neighbors_subsets[xi]:
     9797                                    new_tup = (pos_in_peo[xi], pos_in_peo[yi])
     9798                                    if max_tup < new_tup:
     9799                                        max_tup = new_tup
     9800                                        x, y = xi, yi
     9801         
     9802                        # Our hole is v + (a shortest path between x and y not
     9803                        # containing v or any of its neighbors).
     9804
     9805                        #g.delete_vertices([vv for vv in g.vertices() if pos_in_peo[vv] < pos_in_peo[v]])
     9806
     9807                        g.delete_vertices([vv for vv in g.neighbors(v) if vv != y and vv != x])
     9808                        g.delete_vertex(v)
     9809         
     9810                        hole = self.subgraph([v] + g.shortest_path(x,y))
     9811         
     9812                        # End of the algorithm
     9813                        break
     9814                    else:
     9815                        return False
     9816
     9817       
     9818        # Returning values
     9819        # ----------------
     9820
     9821        # 1- The graph is not chordal
     9822
     9823        if not hole is None:
     9824            # There was a bug there once, so it's better to check the
     9825            # answer is valid, especally when it is so cheap ;-)
     9826   
     9827            if hole.order() <= 3 or not hole.is_regular(k=2):
     9828                raise Exception("The graph is not chordal, and something went wrong in the computation of the certificate. Please report this bug, providing the graph if possible !")
     9829   
     9830            return (False, hole)
     9831
     9832
     9833        # 2- The graph is chordal
    97539834        if certificate:
    97549835            return True, peo
    97559836