Ticket #7563: trac_7563.patch

File trac_7563.patch, 41.3 KB (added by ncohen, 11 years ago)
  • doc/en/reference/graphs.rst

    # HG changeset patch
    # User Nathann Cohen <nathann.cohen@gmail.com>
    # Date 1276375484 -7200
    # Node ID 0117059ea902dd0c13072866775bb439e112895c
    # Parent  9c2c2ec3f1fb7d18e921163ddb1d8bce97983a39
    trac 7563 -- PQ-Trees and Graph.is_interval
    
    diff -r 9c2c2ec3f1fb -r 0117059ea902 doc/en/reference/graphs.rst
    a b  
    1919   sage/graphs/base/c_graph
    2020   sage/graphs/base/sparse_graph
    2121   sage/graphs/base/dense_graph
     22
     23   sage/graphs/pq_trees.py
     24 No newline at end of file
  • sage/graphs/generic_graph.py

    diff -r 9c2c2ec3f1fb -r 0117059ea902 sage/graphs/generic_graph.py
    a b  
    71407140            g.delete_vertex(v)
    71417141        return True
    71427142
     7143    def is_interval(self, certificate = False):
     7144        r"""
     7145        Check whether self is an interval graph
     7146
     7147        INPUT:
     7148
     7149        - ``certificate`` (boolean) -- The function returns ``True``
     7150          or ``False`` according to the graph, when ``certificate =
     7151          False`` (default). When ``certificate = True`` and the graph
     7152          is an interval graph, a dictionary whose keys are the
     7153          vertices and values are pairs of integers are returned
     7154          instead of ``True``. They correspond to an embedding of the
     7155          interval graph, each vertex being represented by an interval
     7156          going from the first of the two values to the second.
     7157
     7158        ALGORITHM:
     7159
     7160        Through the use of PQ-Trees
     7161
     7162        AUTHOR :
     7163
     7164        Nathann Cohen (implementation)
     7165
     7166        EXAMPLES:
     7167
     7168        A Petersen Graph is not chordal, nor car it be an interval
     7169        graph ::
     7170
     7171            sage: g = graphs.PetersenGraph()
     7172            sage: g.is_interval()
     7173            False
     7174
     7175        Though we can build intervals from the corresponding random
     7176        generator::
     7177
     7178            sage: g = graphs.RandomInterval(20)
     7179            sage: g.is_interval()
     7180            True
     7181
     7182        This method can also return, given an interval graph, a
     7183        possible embedding (we can actually compute all of them
     7184        through the PQ-Tree structures)::
     7185
     7186            sage: g = Graph(':S__@_@A_@AB_@AC_@ACD_@ACDE_ACDEF_ACDEFG_ACDEGH_ACDEGHI_ACDEGHIJ_ACDEGIJK_ACDEGIJKL_ACDEGIJKLMaCEGIJKNaCEGIJKNaCGIJKNPaCIP')
     7187            sage: d = g.is_interval(certificate = True)
     7188            sage: print d                                    # not tested
     7189            {0: (0, 20), 1: (1, 9), 2: (2, 36), 3: (3, 5), 4: (4, 38), 5: (6, 21), 6: (7, 27), 7: (8, 12), 8: (10, 29), 9: (11, 16), 10: (13, 39), 11: (14, 31), 12: (15, 32), 13: (17, 23), 14: (18, 22), 15: (19, 33), 16: (24, 25), 17: (26, 35), 18: (28, 30), 19: (34, 37)}
     7190
     7191        From this embedding, we can clearly build an interval graph
     7192        isomorphic to the previous one::
     7193
     7194            sage: g2 = graphs.IntervalGraph(d.values())
     7195            sage: g2.is_isomorphic(g)
     7196            True
     7197
     7198        .. SEEALSO::
     7199
     7200        - :mod:`Interval Graph Recognition <sage.graphs.pq_trees>`.
     7201
     7202        - :meth:`PQ <sage.graphs.pq_trees.PQ>`
     7203          -- Implementation of PQ-Trees.
     7204
     7205        """
     7206
     7207        # An interval graph first is a chordal graph. Without this,
     7208        # there is no telling how we should find its maximal cliques,
     7209        # by the way :-)
     7210
     7211        if not self.is_chordal():
     7212            return False
     7213
     7214        from sage.sets.set import Set
     7215        from sage.combinat.subset import Subsets
     7216
     7217        # First, we need to gather the list of maximal cliques, which
     7218        # is easy as the graph is chordal
     7219
     7220        cliques = []
     7221        clique_subsets = []
     7222
     7223        # As we will be deleting vertices ...
     7224        g = self.copy()
     7225       
     7226        for cc in self.connected_components_subgraphs():
     7227
     7228            # We pick a perfect elimination order for every connected
     7229            # component. We will then iteratively take the last vertex
     7230            # in the order (a simplicial vertex) and consider the
     7231            # clique it forms with its neighbors. If we do not have an
     7232            # inclusion-wise larger clique in our list, we add it !
     7233
     7234            peo = cc.lex_BFS()
     7235
     7236
     7237
     7238            while peo:
     7239                v = peo.pop()
     7240                clique = Set( [v] + cc.neighbors(v))
     7241                cc.delete_vertex(v)
     7242
     7243                if not any([clique in cs for cs in clique_subsets]):
     7244                    cliques.append(clique)
     7245                    clique_subsets.append(Subsets(clique))
     7246
     7247
     7248        from sage.graphs.pq_trees import reorder_sets
     7249
     7250        try:
     7251            ordered_sets = reorder_sets(cliques)
     7252            if not certificate:
     7253                return True
     7254
     7255        except ValueError:
     7256            return False
     7257
     7258        # We are now listing the maximal cliques in the given order,
     7259        # and keeping track of the vertices appearing/disappearing
     7260
     7261        current = set([])
     7262        beg = {}
     7263        end = {}
     7264
     7265        i = 0
     7266
     7267        ordered_sets.append([])
     7268        for S in map(set,ordered_sets):
     7269            for v in current-S:
     7270                end[v] = i
     7271                i = i + 1
     7272
     7273            for v in S-current:
     7274                beg[v] = i
     7275                i = i + 1
     7276
     7277            current = S
     7278
     7279
     7280        return dict([(v, (beg[v], end[v])) for v in self])
     7281       
     7282
    71437283    def is_clique(self, vertices=None, directed_clique=False):
    71447284        """
    71457285        Returns True if the set ``vertices`` is a clique, False
  • new file sage/graphs/pq_trees.py

    diff -r 9c2c2ec3f1fb -r 0117059ea902 sage/graphs/pq_trees.py
    - +  
     1r"""
     2PQ-Trees
     3
     4This module implements PQ-Trees and methods to help recognise Interval Graphs.
     5
     6Class and Methods
     7-----------------
     8
     9"""
     10
     11# Constants, to make the code more readable
     12
     13FULL           = 2
     14PARTIAL        = 1
     15EMPTY          = 0
     16ALIGNED        = True
     17UNALIGNED      = False
     18
     19##########################################################################
     20# Some Lambda Functions                                                  #
     21#                                                                        #
     22# As the elements of a PQ-Tree can be either P-Trees, Q-Trees, or the    #
     23# sets themselves (the leaves), the following lambda function are        #
     24# meant to be applied both on PQ-Trees and Sets, and mimic for the       #
     25# latter the behaviour we expect from the corresponding methods          #
     26# defined in class PQ                                                    #
     27##########################################################################
     28
     29set_contiguous = lambda tree, x : (
     30    tree.set_contiguous(x) if isinstance(tree, PQ) else
     31    ((FULL, ALIGNED) if x in tree
     32     else (EMPTY, ALIGNED)))
     33
     34new_P = lambda liste : P(liste) if len(liste) > 1 else liste[0]
     35new_Q = lambda liste : Q(liste) if len(liste) > 1 else liste[0]
     36
     37flatten = lambda x : x.flatten() if isinstance(x, PQ) else x
     38
     39impossible_msg = "Impossible"
     40
     41def reorder_sets(sets):
     42    r"""
     43    Reorders a collection of sets such that each element appears on an
     44    interval.
     45
     46    Given a collection of sets `C = S_1,...,S_k` on a ground set `X`,
     47    this function attempts to reorder them in such a way that `\forall
     48    x \in X` and `i<j` with `x\in S_i, S_j`, then `x\in S_l` for every
     49    `i<l<j` if it exists.
     50
     51    INPUT:
     52
     53    - ``sets`` - a list of instances of ``list, Set`` or ``set``
     54
     55    ALGORITHM:
     56
     57    PQ-Trees
     58
     59    EXAMPLE:
     60
     61    There is only one way (up to reversal) to represent contiguously
     62    the sequence ofsets `\{i-1, i, i+1\}`::
     63
     64        sage: from sage.graphs.pq_trees import reorder_sets
     65        sage: seq = [Set([i-1,i,i+1]) for i in range(1,15)]
     66
     67    We apply a random permutation::
     68
     69        sage: p = Permutations(len(seq)).random_element()
     70        sage: seq = [ seq[p(i+1)-1] for i in range(len(seq)) ]
     71        sage: ordered = reorder_sets(seq)
     72        sage: if not 0 in ordered[0]:
     73        ...      ordered = ordered.reverse()
     74        sage: print ordered
     75        [{0, 1, 2}, {1, 2, 3}, {2, 3, 4}, {3, 4, 5}, {4, 5, 6}, {5, 6, 7}, {8, 6, 7}, {8, 9, 7}, {8, 9, 10}, {9, 10, 11}, {10, 11, 12}, {11, 12, 13}, {12, 13, 14}, {13, 14, 15}]
     76    """
     77
     78    if len(sets) == 1:
     79        return sets
     80
     81    s = set([])
     82
     83    for ss in sets:
     84        for i in ss:
     85            s.add(i)
     86
     87    tree = P(sets)
     88
     89
     90    for i in s:
     91        tree.set_contiguous(i)
     92        tree = flatten(tree)
     93
     94    return tree.ordering()
     95
     96class PQ:
     97    r"""
     98    This class implements the PQ-Tree, used for the recognition of
     99    Interval Graphs, or equivalently for matrices having the so-caled
     100    "consecutive ones property".
     101
     102    Briefly, we are given a collection `C=S_1, ..., S_n` of sets on a
     103    common ground set `X`, and we would like to reorder the elements
     104    of `C` in such a way that for every element `x\in X` such that
     105    `x\in S_i` and `x\in S_j`, `i<j`, we have `x\in S_l` for all
     106    `i<l<j`. This property could also be rephrased as : the sets
     107    containing `x` are an interval.
     108
     109    To achieve it, we will actually compute ALL the orderings
     110    satisfying such constraints using the structure of PQ-Tree, by
     111    adding the constraints one at a time.
     112
     113        * At first, there is no constraint : all the permutations are
     114          allowed. We will then build a tree composed of one node
     115          linked to all the sets in our collection (his children). As
     116          we want to remember that all the permutations of his
     117          children are allowed, we will label it with "P", making it a
     118          P-Tree.
     119
     120        * We are now picking an element `x \in X`, and we want to
     121          ensure that all the elements `C_x` containing it are
     122          contiguous. We can remove them from their tree `T_1`, create
     123          a second tree `T_2` whose only children are the `C_x`, and
     124          attach this `T_2` to `T_1`. We also make this new tree a
     125          `P-Tree`, as all the elements of `C_x` can be permuted as
     126          long as they stay close to each other. Obviously, the whole
     127          tree `T_2` can be prmuter with the other children of `T_1`
     128          in any way -- it does not impair the fact that the sequence
     129          of the children will ensure the sets containing `x` are
     130          contiguous.
     131
     132        * We would like to repeat the same procedure for `x' \in X`,
     133          but we are now encountering a problem : there may be sets
     134          containing both `x'` and `x`, along with others containing
     135          only `x` or only `x'`. We can permute the sets containing
     136          only `x'` together, or the sets containing both `x` and `x'`
     137          together, but we may NOT permute all the sets containing
     138          `x'` together as this may break the relationship between the
     139          sets containing `x`. We need `Q`-Trees. A `Q`-Tree is a tree
     140          whose children are ordered, even though their order could be
     141          reversed (if the children of a `Q`-Tree are `c_1c_2
     142          ... c_k`, we can change it to `c_k ... c_2c_1`). We can now
     143          express all the orderings satisfying our two constraints the
     144          following way :
     145
     146            * We create a tree `T_1` gathering all the elements not
     147              containing `x` nor `x'`, and make it a `P-Tree`
     148
     149            * We create 3 `P`-Trees `T_{x, x'}, T_x, T_{x'}`, which
     150              respectively have for children the elements or our
     151              collection containing
     152
     153                * both `x` and `x'`
     154                * only `x`
     155                * only `x'`
     156
     157            * To ensure our constraints on both elements, we create a
     158              `Q`-tree `T_2` whose children are in order `T_x, T_{x,
     159              x'}, T_{x'}`
     160
     161            * We now make this `Q`-Tree `T_2` a children of the
     162              `P`-Tree `T_1`
     163
     164    Using these two types of tree, and exploring the different cases
     165    of intersection, it is possible to represent all the possible
     166    permutations of our sets satisfying or constraints, or to prove
     167    that no such ordering exists. This is the whole purpose of this
     168    class and this algorithm, and is explained with more details in many
     169    places, for example in the following document from Hajiaghayi [Haj]_.
     170
     171    REFERENCES:
     172
     173    .. [Haj] M. Hajiaghayi
     174      http://www-math.mit.edu/~hajiagha/pp11.ps
     175
     176    AUTHOR : Nathann Cohen
     177    """
     178
     179    def __init__(self, seq):
     180        r"""
     181        Construction of a PQ-Tree
     182
     183        EXAMPLE::
     184
     185            sage: from sage.graphs.pq_trees import P, Q
     186            sage: p = Q([[1,2], [2,3], P([[2,4], [2,8], [2,9]])])
     187        """
     188        from sage.sets.set import Set
     189
     190        self._children = []
     191        for e in seq:
     192            if isinstance(e, list):
     193                e = Set(e)
     194
     195            if not e in self._children:
     196                self._children.append(e)
     197
     198    def reverse(self):
     199        r"""
     200        Recursively reverses ``self`` and its children
     201
     202        EXAMPLE::
     203
     204            sage: from sage.graphs.pq_trees import P, Q
     205            sage: p = Q([[1,2], [2,3], P([[2,4], [2,8], [2,9]])])
     206            sage: p.ordering()
     207            [{1, 2}, {2, 3}, {2, 4}, {8, 2}, {9, 2}]
     208            sage: p.reverse()
     209            sage: p.ordering()
     210            [{9, 2}, {8, 2}, {2, 4}, {2, 3}, {1, 2}]
     211        """
     212        for i in self._children:
     213            if isinstance(i, PQ):
     214                i.reverse()
     215
     216        self._children.reverse()
     217
     218    def __contains__(self, v):
     219        r"""
     220        Tests whether there exists an element of ``self`` containing
     221        an element ``v``
     222
     223        INPUT:
     224
     225        - ``v`` -- an element of the ground set
     226
     227        EXAMPLE::
     228
     229            sage: from sage.graphs.pq_trees import P, Q
     230            sage: p = Q([[1,2], [2,3], P([[2,4], [2,8], [2,9]])])
     231            sage: 5 in p
     232            False
     233            sage: 9 in p
     234            True
     235
     236        """
     237        for i in self:
     238            if v in i:
     239                return True
     240        False
     241
     242    def split(self, v):
     243        r"""
     244        Returns the subsequences of children containing and not
     245        containing ``v``
     246
     247        INPUT:
     248
     249        - ``v`` -- an element of the ground set
     250
     251        OUTPUT:
     252
     253        Two lists, the first containing the children of ``self``
     254        containing ``v``, and the other containing the other children.
     255
     256        .. NOTE::
     257
     258           This command is meant to be used on a partial tree, once it
     259           has be "set continuous" on an element ``v`` and aligned it
     260           to the right. Hence, none of the list should be empty (an
     261           exception is raised if that happens, as it would reveal a
     262           bug in the algorithm) and the sum ``contains +
     263           does_not_contain`` should be equal to the sequence of
     264           children of ``self``.
     265
     266        EXAMPLE::
     267
     268            sage: from sage.graphs.pq_trees import P, Q
     269            sage: p = Q([[1,2], [2,3], P([[2,4], [2,8], [2,9]])])
     270            sage: p.reverse()
     271            sage: contains, does_not_contain = p.split(1)
     272            sage: contains
     273            [{1, 2}]
     274            sage: does_not_contain
     275            [('P', [{9, 2}, {8, 2}, {2, 4}]), {2, 3}]
     276            sage: does_not_contain + contains == p._children
     277            True
     278
     279        """
     280        contains = []
     281        does_not_contain = []
     282
     283        for i in self:
     284            if v in i:
     285                contains.append(i)
     286            else:
     287                does_not_contain.append(i)
     288
     289        if not contains or not does_not_contain:
     290            raise ValueError("None of the sets should be empty !")
     291
     292        return contains, does_not_contain
     293
     294    def __iter__(self):
     295        r"""
     296        Iterates over the children of ``self``.
     297
     298        EXAMPLE::
     299
     300            sage: from sage.graphs.pq_trees import P, Q
     301            sage: p = Q([[1,2], [2,3], P([[2,4], [2,8], [2,9]])])
     302            sage: for i in p:
     303            ...      print i
     304            {1, 2}
     305            {2, 3}
     306            ('P', [{2, 4}, {8, 2}, {9, 2}])
     307        """
     308
     309        for i in self._children:
     310            yield i
     311
     312    def cardinality(self):
     313        r"""
     314        Returns the number of children of ``self``
     315
     316        EXAMPLE::
     317
     318            sage: from sage.graphs.pq_trees import P, Q
     319            sage: p = Q([[1,2], [2,3], P([[2,4], [2,8], [2,9]])])
     320            sage: p.cardinality()
     321            3
     322        """
     323        return len(self._children)
     324
     325    def ordering(self):
     326        r"""
     327        Returns the current ordering given by listing the leaves from
     328        left to right.
     329
     330        EXAMPLE:
     331
     332            sage: from sage.graphs.pq_trees import P, Q
     333            sage: p = Q([[1,2], [2,3], P([[2,4], [2,8], [2,9]])])
     334            sage: p.ordering()                               
     335            [{1, 2}, {2, 3}, {2, 4}, {8, 2}, {9, 2}]
     336        """
     337        value = []
     338        for i in self:
     339            if isinstance(i, PQ):
     340                value.extend(i.ordering())
     341            else:
     342                value.append(i)
     343
     344        return value
     345
     346    def __repr__(self):
     347        r"""
     348        Succintly represents ``self``.
     349
     350        EXAMPLE::
     351
     352            sage: from sage.graphs.pq_trees import P, Q
     353            sage: p = Q([[1,2], [2,3], P([[2,4], [2,8], [2,9]])])
     354            sage: print p
     355            ('Q', [{1, 2}, {2, 3}, ('P', [{2, 4}, {8, 2}, {9, 2}])])
     356        """
     357        return str((("P" if self.is_P() else "Q"),self._children))
     358
     359    def simplify(self, v, left = False, right = False):
     360        r"""
     361        Returns a simplified copy of self according to the element ``v``
     362
     363        If ``self`` is a partial P-tree for ``v``, we would like to
     364        restrict the permutations of its children to permutations
     365        keeping the children containing ``v`` contiguous. This
     366        function also "locks" all the elements not containing ``v``
     367        inside a `P`-tree, which is useful when one want to keep the
     368        elements containing ``v`` on one side (which is the case when
     369        this method is called).
     370
     371        INPUT:
     372
     373        - ``left, right`` (booleans) -- whether ``v`` is aligned to the
     374          right or to the left
     375
     376        - ``v```-- an element of the ground set
     377
     378        OUTPUT:
     379
     380        If ``self`` is a `Q`-Tree, the sequence of its children is
     381        returned. If ``self`` is a `P`-tree, 2 `P`-tree are returned,
     382        namely the two `P`-tree defined above and restricting the
     383        permutations, in the order implied by ``left, right`` (if
     384        ``right =True``, the second `P`-tree will be the one gathering
     385        the elements containing ``v``, if ``left=True``, the
     386        opposite).
     387
     388        .. NOTE::
     389
     390           This method is assumes that ``self`` is partial for ``v``,
     391           and aligned to the side indicated by ``left, right``.
     392
     393        EXAMPLES:
     394
     395        A `P`-Tree ::
     396
     397            sage: from sage.graphs.pq_trees import P, Q
     398            sage: p = P([[2,4], [1,2], [0,8], [0,5]])
     399            sage: p.simplify(0, right = True)
     400            [('P', [{2, 4}, {1, 2}]), ('P', [{0, 8}, {0, 5}])]
     401
     402        A `Q`-Tree ::
     403
     404            sage: q = Q([[2,4], [1,2], [0,8], [0,5]])
     405            sage: q.simplify(0, right = True)
     406            [{2, 4}, {1, 2}, {0, 8}, {0, 5}]
     407        """
     408        if sum([left, right]) !=1:
     409            raise ValueError("Exactly one of left or right must be specified")
     410
     411        if self.is_Q():
     412            return self._children
     413        else:
     414
     415            contains, does_not_contain = self.split(v)
     416           
     417            A = new_P(does_not_contain)
     418            B = new_P(contains)
     419
     420            if right:
     421                return [A, B]
     422            else:
     423                return [B, A]
     424
     425    def flatten(self):
     426        r"""
     427        Returns a flattened copy of ``self``
     428
     429        If self has only one child, we may as well consider its
     430        child's children, as ``self`` encodes no information. This
     431        method recursively "flattens" trees having only on PQ-tree
     432        child, and returns it.
     433
     434        EXAMPLE::
     435
     436            sage: from sage.graphs.pq_trees import P, Q
     437            sage: p = Q([P([[2,4], [2,8], [2,9]])])
     438            sage: p.flatten()
     439            ('P', [{2, 4}, {8, 2}, {9, 2}])
     440        """
     441        if self.cardinality() == 1:
     442            return flatten(self._children[0])
     443        else:
     444            self._children = [flatten(x) for x in self._children]
     445            return self
     446
     447
     448    def is_P(self):
     449        r"""
     450        Tests whether ``self`` is a `P`-Tree
     451
     452        EXAMPLE::
     453
     454            sage: from sage.graphs.pq_trees import P, Q
     455            sage: P([[0,1],[2,3]]).is_P()
     456            True
     457            sage: Q([[0,1],[2,3]]).is_P()
     458            False
     459        """
     460        return isinstance(self,P)
     461
     462    def is_Q(self):
     463        r"""
     464        Tests whether ``self`` is a `Q`-Tree
     465
     466        EXAMPLE::
     467
     468            sage: from sage.graphs.pq_trees import P, Q
     469            sage: Q([[0,1],[2,3]]).is_Q()
     470            True
     471            sage: P([[0,1],[2,3]]).is_Q()
     472            False
     473        """
     474        return isinstance(self,Q)
     475
     476class P(PQ):
     477    r"""
     478    A P-Tree is a PQ-Tree whose children are
     479    not ordered (they can be permuted in any way)
     480    """
     481    def set_contiguous(self, v):
     482        r"""
     483        Updates ``self`` so that its sets containing ``v`` are
     484        contiguous for any admissible permutation of its subtrees.
     485
     486        This function also ensures, whenever possible,
     487        that all the sets containing ``v`` are located on an interval
     488        on the right side of the ordering.
     489
     490        INPUT:
     491
     492        - ``v`` -- an element of the ground set
     493
     494        OUTPUT:
     495
     496        According to the cases :
     497
     498            * ``(EMPTY, ALIGNED)`` if no set of the tree contains
     499              an occurrence of ``v``
     500
     501            * ``(FULL, ALIGNED)`` if all the sets of the tree contain
     502              ``v``
     503
     504            * ``(PARTIAL, ALIGNED)`` if some (but not all) of the sets
     505              contain ``v``, all of which are aligned
     506              to the right of the ordering at the end when the function ends
     507
     508            * ``(PARTIAL, UNALIGNED)`` if some (but not all) of the
     509              sets contain ``v``, though it is impossible to align them
     510              all to the right
     511
     512        In any case, the sets containing ``v`` are contiguous when this
     513        function ends. If there is no possibility of doing so, the function
     514        raises a ``ValueError`` exception.
     515
     516        EXAMPLE:
     517
     518        Ensuring the sets containing ``0`` are continuous::
     519
     520            sage: from sage.graphs.pq_trees import P, Q
     521            sage: p = P([[0,3], [1,2], [2,3], [2,4], [4,0],[2,8], [2,9]])
     522            sage: p.set_contiguous(0)
     523            (1, True)
     524            sage: print p
     525            ('P', [{1, 2}, {2, 3}, {2, 4}, {8, 2}, {9, 2}, ('P', [{0, 3}, {0, 4}])])
     526
     527        Impossible situation::
     528
     529            sage: p = P([[0,1], [1,2], [2,3], [3,0]])
     530            sage: p.set_contiguous(0)
     531            (1, True)
     532            sage: p.set_contiguous(1)
     533            (1, True)
     534            sage: p.set_contiguous(2)
     535            (1, True)
     536            sage: p.set_contiguous(3)
     537            Traceback (most recent call last):
     538            ...
     539            ValueError: Impossible
     540
     541        """
     542
     543        ###############################################################
     544        # Defining Variables :                                        #
     545        #                                                             #
     546        # Collecting the information of which children are FULL of v, #
     547        # which ones are EMPTY, PARTIAL_ALIGNED and PARTIAL_UNALIGNED #
     548        #                                                             #
     549        # Defining variables for their cardinals, just to make the    #
     550        # code slightly more readable :-)                             #
     551        ###############################################################
     552
     553        seq = [set_contiguous(x, v) for x in self]
     554        self.flatten()
     555        seq = [set_contiguous(x, v) for x in self]
     556
     557        f_seq = dict(zip(self, seq))
     558
     559        set_FULL                = []
     560        set_EMPTY               = []
     561        set_PARTIAL_ALIGNED     = []
     562        set_PARTIAL_UNALIGNED   = []
     563
     564        sorting = {
     565            (FULL, ALIGNED)     : set_FULL,
     566            (EMPTY, ALIGNED)    : set_EMPTY,
     567            (PARTIAL, ALIGNED)  : set_PARTIAL_ALIGNED,
     568            (PARTIAL, UNALIGNED) : set_PARTIAL_UNALIGNED
     569            }
     570
     571        for i in self:
     572            sorting[f_seq[i]].append(i)
     573
     574        n_FULL                  = len(set_FULL)
     575        n_EMPTY                 = len(set_EMPTY)
     576        n_PARTIAL_ALIGNED       = len(set_PARTIAL_ALIGNED)
     577        n_PARTIAL_UNALIGNED     = len(set_PARTIAL_UNALIGNED)
     578
     579        counts = dict(map(lambda (x,y) : (x,len(y)), sorting.iteritems()))
     580
     581        # Excludes the situation where there is no solution.
     582        # read next comment for more explanations
     583
     584        if (n_PARTIAL_ALIGNED + n_PARTIAL_UNALIGNED > 2 or
     585            (n_PARTIAL_UNALIGNED >= 1 and n_EMPTY != self.cardinality() -1)):
     586
     587            raise ValueError(impossible_msg)
     588
     589        # From now on, there are at most two pq-trees which are partially filled
     590        # If there is one which is not aligned to the right, all the others are empty
     591
     592        #########################################################
     593        # 1/2                                                   #
     594        #                                                       #
     595        # Several easy cases where we can decide without paying #
     596        # attention                                             #
     597        #########################################################
     598
     599        # All the children are FULL
     600        elif n_FULL == self.cardinality():
     601            return FULL, True
     602
     603        # All the children are empty
     604        elif n_EMPTY == self.cardinality():
     605            return EMPTY, True
     606
     607        # There is a PARTIAL UNALIGNED element (and all the others are
     608        # empty as we checked before
     609
     610        elif n_PARTIAL_UNALIGNED == 1:
     611            return (PARTIAL, UNALIGNED)
     612
     613        # If there is just one partial element and all the others are
     614        # empty, we just reorder the set to put it at the right end
     615
     616        elif (n_PARTIAL_ALIGNED == 1 and
     617              n_EMPTY == self.cardinality()-1):
     618
     619            self._children = set_EMPTY + set_PARTIAL_ALIGNED
     620            return (PARTIAL, ALIGNED)
     621
     622       
     623        ################################################################
     624        # 2/2                                                          #
     625        #                                                              #
     626        # From now on, there are at most two partial pq-trees and all  #
     627        # of them have v aligned to their right                        #
     628        #                                                              #
     629        # We now want to order them in such a way that all the         #
     630        # elements containing v are located on the right               #
     631        ################################################################
     632
     633        else:
     634
     635            self._children = []
     636
     637            # We first move the empty elements to the left, if any
     638
     639            if n_EMPTY > 0:
     640                self._children.extend(set_EMPTY)
     641
     642            # If there is one partial element we but have to add it to
     643            # the sequence, then add all the full elements
     644
     645            # We must also make sure these elements will not be
     646            # reordered in such a way that the elements containing v
     647            # are not contiguous
     648   
     649            # ==> We create a Q-tree
     650
     651            if n_PARTIAL_ALIGNED < 2:
     652
     653                new = []
     654
     655
     656                # add the partial element, if any
     657                if n_PARTIAL_ALIGNED == 1:
     658
     659                    subtree = set_PARTIAL_ALIGNED[0]
     660                    new.extend(subtree.simplify(v, right = ALIGNED))
     661
     662
     663                # Then the full elements, if any, in a P-tree (we can
     664                # permute any two of them while keeping all the
     665                # elements containing v on an interval
     666
     667                if n_FULL > 0:
     668
     669                    new.append(new_P(set_FULL))
     670
     671                # We lock all of them in a Q-tree
     672
     673                self._children.append(new_Q(new))
     674               
     675                return PARTIAL, True
     676
     677            # If there are 2 partial elements, we take care of both
     678            # ends. We also know it will not be possible to align the
     679            # interval of sets containing v to the right
     680
     681            else:
     682
     683                new = []
     684
     685                # The second partal element is aligned to the right
     686                # while, as we want to put it at the end of the
     687                # interval, it should be aligned to the left
     688                set_PARTIAL_ALIGNED[1].reverse()
     689
     690                # 1/3
     691                # Left partial subtree
     692                subtree = set_PARTIAL_ALIGNED[0]
     693                new.extend(subtree.simplify(v, right = ALIGNED))
     694
     695                # 2/3
     696                # Center (Full elements, in a P-tree, as they can be
     697                # permuted)
     698
     699                if n_FULL > 0:
     700                    new.append(new_P(set_FULL))
     701
     702                # 3/3
     703                # Right partial subtree
     704                subtree = set_PARTIAL_ALIGNED[1]
     705                new.extend(subtree.simplify(v, left= ALIGNED))
     706
     707                # We add all of it, locked in a Q-Tree
     708                self._children.append(new_Q(new))
     709
     710                return PARTIAL, False
     711
     712class Q(PQ):
     713    r"""
     714    A Q-Tree is a PQ-Tree whose children are
     715    ordered up to reversal
     716    """
     717
     718    def set_contiguous(self, v):
     719        r"""
     720        Updates ``self`` so that its sets containing ``v`` are
     721        contiguous for any admissible permutation of its subtrees.
     722
     723        This function also ensures, whenever possible,
     724        that all the sets containing ``v`` are located on an interval
     725        on the right side of the ordering.
     726
     727        INPUT:
     728
     729        - ``v`` -- an element of the ground set
     730
     731        OUTPUT:
     732
     733        According to the cases :
     734
     735            * ``(EMPTY, ALIGNED)`` if no set of the tree contains
     736              an occurrence of ``v``
     737
     738            * ``(FULL, ALIGNED)`` if all the sets of the tree contain
     739              ``v``
     740
     741            * ``(PARTIAL, ALIGNED)`` if some (but not all) of the sets
     742              contain ``v``, all of which are aligned
     743              to the right of the ordering at the end when the function ends
     744
     745            * ``(PARTIAL, UNALIGNED)`` if some (but not all) of the
     746              sets contain ``v``, though it is impossible to align them
     747              all to the right
     748
     749        In any case, the sets containing ``v`` are contiguous when this
     750        function ends. If there is no possibility of doing so, the function
     751        raises a ``ValueError`` exception.
     752
     753        EXAMPLE:
     754
     755        Ensuring the sets containing ``0`` are continuous::
     756
     757            sage: from sage.graphs.pq_trees import P, Q
     758            sage: q = Q([[2,3], Q([[3,0],[3,1]]), Q([[4,0],[4,5]])])
     759            sage: q.set_contiguous(0)
     760            (1, False)
     761            sage: print q
     762            ('Q', [{2, 3}, {1, 3}, {0, 3}, {0, 4}, {4, 5}])
     763
     764        Impossible situation::
     765
     766            sage: p = Q([[0,1], [1,2], [2,0]])
     767            sage: p.set_contiguous(0)
     768            Traceback (most recent call last):
     769            ...
     770            ValueError: Impossible
     771
     772
     773        """
     774
     775
     776        #################################################################
     777        # Guidelines :                                                  #
     778        #                                                               #
     779        # As the tree is a Q-Tree, we can but reverse the order in      #
     780        # which the elements appear. It means that we can but check     #
     781        # the elements containing v are already contiguous (even        #
     782        # though we have to take special care of partial elements --    #
     783        # the endpoints of the interval), and answer accordingly        #
     784        # (partial, full, empty, aligned..). We also want to align the  #
     785        # elements containing v to the right if possible.               #
     786        ################################################################
     787
     788
     789        ###############################################################
     790        # Defining Variables :                                        #
     791        #                                                             #
     792        # Collecting the information of which children are FULL of v, #
     793        # which ones are EMPTY, PARTIAL_ALIGNED and PARTIAL_UNALIGNED #
     794        #                                                             #
     795        # Defining variables for their cardinals, just to make the    #
     796        # code slightly more readable :-)                             #
     797        ###############################################################
     798
     799        seq = [set_contiguous(x, v) for x in self]
     800        self.flatten()
     801        seq = [set_contiguous(x, v) for x in self]
     802
     803        f_seq = dict(zip(self, seq))
     804
     805        set_FULL                = []
     806        set_EMPTY               = []
     807        set_PARTIAL_ALIGNED     = []
     808        set_PARTIAL_UNALIGNED   = []
     809
     810        sorting = {
     811            (FULL, ALIGNED)     : set_FULL,
     812            (EMPTY, ALIGNED)    : set_EMPTY,
     813            (PARTIAL, ALIGNED)  : set_PARTIAL_ALIGNED,
     814            (PARTIAL, UNALIGNED) : set_PARTIAL_UNALIGNED
     815            }
     816
     817        for i in self:
     818            sorting[f_seq[i]].append(i)
     819
     820        n_FULL                  = len(set_FULL)
     821        n_EMPTY                 = len(set_EMPTY)
     822        n_PARTIAL_ALIGNED       = len(set_PARTIAL_ALIGNED)
     823        n_PARTIAL_UNALIGNED     = len(set_PARTIAL_UNALIGNED)
     824
     825        counts = dict(map(lambda (x,y) : (x,len(y)), sorting.iteritems()))
     826
     827        ###################################################################
     828        #                                                                 #
     829        # Picking the good ordering for the children :                    #
     830        #                                                                 #
     831        #                                                                 #
     832        # There is a possibility of aligning to the right iif             #
     833        # the vector can assume the form (as a regular expression) :      #
     834        #                                                                 #
     835        # (EMPTY *) PARTIAL (FULL *) Of course, each of these three       #
     836        # members could be empty                                          #
     837        #                                                                 #
     838        # Hence, in the following case we reverse the vector :            #
     839        #                                                                 #
     840        # * if the last element is empty (as we checked the whole         #
     841        #   vector is not empty                                           #
     842        #                                                                 #
     843        # * if the last element is partial, aligned, and all the          #
     844        #   others are full                                               #
     845        ###################################################################
     846       
     847        if (f_seq[self._children[-1]] == (EMPTY, ALIGNED) or
     848            (f_seq[self._children[-1]] == (PARTIAL, ALIGNED) and n_FULL == self.cardinality() - 1)):
     849
     850            # We reverse the order of the elements in the SET only. Which means that they are still aligned to the right !
     851            self._children.reverse()
     852
     853
     854        #########################################################
     855        # 1/2                                                   #
     856        #                                                       #
     857        # Several easy cases where we can decide without paying #
     858        # attention                                             #
     859        #########################################################
     860
     861
     862        # Excludes the situation where there is no solution.
     863        # read next comment for more explanations
     864
     865        if (n_PARTIAL_ALIGNED + n_PARTIAL_UNALIGNED > 2 or
     866            (n_PARTIAL_UNALIGNED >= 1 and n_EMPTY != self.cardinality() -1)):
     867
     868            raise ValueError(impossible_msg)
     869
     870        # From now on, there are at most two pq-trees which are partially filled
     871        # If there is one which is not aligned to the right, all the others are empty
     872
     873        # First trivial case, no checking neded
     874        elif n_FULL == self.cardinality():
     875            return FULL, True
     876
     877        # Second trivial case, no checking needed
     878        elif n_EMPTY == self.cardinality():
     879            return EMPTY, True
     880
     881        # Third trivial case, no checking needed
     882        elif n_PARTIAL_UNALIGNED == 1:
     883            return (PARTIAL, UNALIGNED)
     884
     885        # If there is just one partial element
     886        # and all the others are empty, we just reorder
     887        # the set to put it at the right end
     888
     889        elif (n_PARTIAL_ALIGNED == 1 and
     890              n_EMPTY == self.cardinality()-1):
     891
     892            if set_PARTIAL_ALIGNED[0] == self._children[-1]:
     893                return (PARTIAL, ALIGNED)
     894           
     895            else:
     896                return (PARTIAL, UNALIGNED)
     897
     898
     899        ##############################################################
     900        # 2/2                                                        #
     901        #                                                            #
     902        # We iteratively consider all the children, and check        #
     903        # that the elements containing v are indeed                  #
     904        # locate on an interval.                                     #
     905        #                                                            #
     906        # We are also interested in knowing whether this interval is #
     907        # aligned to the right                                       #
     908        #                                                            #
     909        # Because of the previous tests, we can assume there are at  #
     910        # most two partial pq-trees and all of them are aligned to   #
     911        # their right                                                #
     912        ##############################################################
     913
     914        else:
     915
     916            new_children = []
     917
     918            # Two variables to remember where we are
     919            # according to the interval
     920
     921            seen_nonempty = False
     922            seen_right_end = False
     923
     924
     925            for i in self:
     926               
     927                type, aligned = f_seq[i]               
     928
     929                # We met an empty element
     930                if type == EMPTY:
     931
     932                    # 2 possibilities :
     933                    #
     934                    #  * we have NOT met a non-empty element before
     935                    #    and it just means we are looking at the
     936                    #    leading empty elements
     937                    #
     938                    #  * we have met a non-empty element before and it
     939                    #    means we will never met another non-empty
     940                    #    element again => we have seen the right end
     941                    #    of the interval
     942
     943                    new_children.append(i)
     944                       
     945                    if seen_nonempty:
     946                        seen_right_end = True
     947
     948                # We met a non-empty element
     949                else:
     950                    if seen_right_end:
     951                        raise ValueError(impossible_msg)
     952
     953
     954                    if type == PARTIAL:
     955
     956                        # if we see an ALIGNED partial tree after
     957                        # having seen a nonempty element then the
     958                        # partial tree must be aligned to the left and
     959                        # so we have seen the right end
     960
     961                        if seen_nonempty and aligned:
     962                            i.reverse()
     963                            seen_right_end = True
     964
     965                            # right partial subtree
     966                            subtree = i
     967                            new_children.extend(subtree.simplify(v, left = True))
     968
     969                        # If we see an UNALIGNED partial element after
     970                        # having met a nonempty element, there is no
     971                        # solution to the alignment problem
     972                           
     973                        elif seen_nonempty and not aligned:
     974                            raise ValueError(impossible_msg)
     975
     976                        # If we see an unaligned element but no non-empty
     977                        # element since the beginning, we are witnessing both the
     978                        # left and right end
     979
     980                        elif not seen_nonempty and not aligned:
     981                            raise ValueError("Bon, ben ca arrive O_o")
     982                            seen_right_end = True
     983
     984                        elif not seen_nonempty and aligned:
     985
     986                            # left partial subtree
     987                            subtree = i
     988
     989                            new_children.extend(subtree.simplify(v, right = True))
     990                       
     991
     992                    else:
     993                        new_children.append(i)                       
     994
     995                    seen_nonempty = True
     996
     997            # Setting the updated sequence of children
     998            self._children = new_children
     999
     1000           
     1001            # Whether we achieved an alignment to the right is the
     1002            # complement of whether we have seen the right end
     1003
     1004            return (PARTIAL, not seen_right_end)
     1005