Ticket #12716: trac_12716_linear_ordering.patch

File trac_12716_linear_ordering.patch, 35.7 KB (added by dcoudert, 8 years ago)
  • doc/en/reference/graphs.rst

    # HG changeset patch
    # User dcoudert <david.coudert@inria.fr>
    # Date 1332092768 -3600
    # Node ID 46f64db1dee735774b0c1a40399cf43ff66a13e1
    # Parent  3daf3f6b8ad89033a3d19dfe1de3790bedf5042f
    trac #12716 - Implements the class LinearOrdering and corresponding methods
    
    diff --git a/doc/en/reference/graphs.rst b/doc/en/reference/graphs.rst
    a b  
    5151   sage/graphs/pq_trees
    5252   sage/graphs/matchpoly
    5353   sage/graphs/graph_decompositions/vertex_separation
     54   sage/graphs/graph_decompositions/linear_ordering
    5455   sage/graphs/graph_decompositions/rankwidth
    5556   sage/graphs/modular_decomposition/modular_decomposition
    5657   sage/graphs/convexity_properties
  • new file sage/graphs/graph_decompositions/linear_ordering.py

    diff --git a/sage/graphs/graph_decompositions/linear_ordering.py b/sage/graphs/graph_decompositions/linear_ordering.py
    new file mode 100644
    - +  
     1r"""
     2Linear orderings of graphs and digraphs
     3
     4This module implements several algorithms for computing and evaluating linear
     5vertex ordering of graphs and digraphs. The <width> of a linear ordering is one
     6of the following values.
     7
     8            +--------------+----------------------------+
     9            | short name   |  long name                 |
     10            +==============+============================+
     11            | vs           | vertex_separation          |
     12            +--------------+----------------------------+
     13            | pw           | pathwidth                  |
     14            +--------------+----------------------------+
     15            | tw           | treewidth                  |
     16            +--------------+----------------------------+
     17            | bw           | bandwidth                  |
     18            +--------------+----------------------------+
     19            | pn           | process_number             |
     20            +--------------+----------------------------+
     21            | cw           | cutwidth                   |
     22            +--------------+----------------------------+
     23            | mcw          | modified_cutwidth          |
     24            +--------------+----------------------------+
     25            | ola          | optimal_linear_arrangement |
     26            +--------------+----------------------------+
     27            | sc           | sum_cut                    |
     28            +--------------+----------------------------+
     29            | mfi          | minimun_fill_in            |
     30            +--------------+----------------------------+
     31
     32Some of these widths are defined for both graphs and digraphs, and others only
     33for graphs or for digraphs.
     34
     35We follow the formulations proposed in [Bod98]_ and [BFK+11]_ for the evaluation
     36of the widths induced by a linear vertex ordering `L=[v_1, ..., v_n]` of the
     37vertices of a (di)graph `G=(V,E)`.  Thus, we consider that we have a function
     38`f` associating an integer to each 3-tuple, consisting of a (di)graph `G=(V,E)`,
     39a vertex set `S\subseteq V`, and a vertex `v\in V`. Let `L[:i]=[v_1, ..., v_i]`
     40be a prefix of the ordering and so a subset of `V`. We consider the problems
     41that can be formulated as the computation of:
     42
     43.. MATH::
     44
     45    \text{cost}(G, L) = \max_{0\leq i<|V(G)|} f(G, L[:i]\setminus\{v\}, v)
     46
     47or
     48
     49.. MATH::
     50
     51    \text{cost}(G, L) = \sum_{0\leq i<|V(G)|} f(G, L[:i]\setminus\{v\}, v)
     52
     53
     54
     55Given an ordering `v_1, ..., v_n` of the vertices of `V(G)`, its *cost* is
     56defined as:
     57
     58
     59**Vertex separation**
     60
     61The vertex separation of a linear ordering `L` of the vertices of a digraph `D`
     62is measured as the maximum number of out-neighbors with position `k>i` in the
     63ordering of the vertices with position `j<=i` in the ordering. That is,
     64
     65.. MATH::
     66   vs_L(D) =  \max_{1\leq i\leq |V|-1} |\{N^+(L[:i])\setminus L[:i]\}|
     67
     68
     69**Path decomposition**
     70
     71The cost of a path decomposition induced by a linear ordering `L` of the
     72vertices of a graph `G` is measured as the maximum number of neighbors with
     73position `k>i` in the ordering of the vertices with position `j<=i` in the
     74ordering. That is,
     75
     76.. MATH::
     77   pw_L(G) =  \max_{1\leq i\leq |V|-1} |\{N(L[:i])\setminus L[:i]\}|
     78
     79
     80
     81**Tree decomposition**
     82
     83The value ``tw_L(G)`` of the tree decomposition induced by the ordering `L` of
     84the vertices of `G`.
     85
     86The cost of a tree decomposition is mesured by the maximum number of vertices
     87with position `j > i` in the vertex ordering that can be reached from a vertex
     88`v` at position `i` through a path with internal vertices at positions `<=
     89i`. Let `Z(i)` be the set of vertices that can be reached from the vertex at
     90position `i`. We have
     91
     92.. MATH::
     93
     94   tw_L(G) =  \max_{1\leq i\leq |V|-1} | Z( i ) |
     95
     96
     97
     98**Bandwidth**
     99
     100The bandwidth minimization problem is to find a linear vertex ordering that
     101minimizes the maximum dilation among all the edges, where the dilatation of an
     102edge is the distance between its endpoints in the vertex ordering. We can extend
     103the notion to digraphs where the dilatation of an arc `(u,v)` is the distance
     104between its endpoints if `v` has a higher index that `u` in the ordering, and 0
     105otherwise.
     106
     107So, the bandwidth `bw_L(G)` of a graph `G=(V,E)`, or the bandwidth `bw_L(D)` for
     108a digraph `D=(V,A)`, induced by a given vertex ordering `L` are measured as
     109follows,
     110
     111.. MATH::
     112
     113   bw_L(G) =  \max_{\{u,v\}\in E} | L[u] - L[v] |
     114
     115   bw_L(D) =  \max_{\{u,v\}\in A} max( 0, L[v] - L[u] )
     116
     117
     118
     119**Process number**
     120
     121The process number of a linear ordering `L` of the vertices of a digraph `D` is
     122measured as the maximum number of out-neighbors with position `k>=i` (`i`
     123included) in the ordering of the vertices with position `j<=i` (`i` included) in
     124the ordering. Thus, loops on vertices of the digraph are also taken into
     125account. This notion can also be formulated for undirected graphs (see [CoSe11]_
     126for more details on this notion). We have,
     127
     128.. MATH::
     129
     130   pn_L(D) =  \max_{1\leq i\leq |V|} |\{N^+(L[:i])\setminus L[:i-1]\}|
     131
     132   pn_L(G) =  \max_{1\leq i\leq |V|} |\{N(L[:i])\setminus L[:i-1]\}|
     133
     134
     135
     136**Cutwidth and modified cutwidth**
     137
     138The cutwidth minimization problem is to find an ordering that minimizes the
     139maximum number of edges between vertices with index `j<=i` in the ordering and
     140vertices with index `k>i` in the ordering. When considering digraphs, only arcs
     141from `j<=i` to `k>i` are considered.  For the modified cutwidth, only edges (or
     142arcs) from vertices with indexes `j<i` to vertices with indexes `k>i` are
     143considered. So we count the number of edges (or arcs) passing over the vertex
     144with index `i`.
     145
     146So, the cutwidth `cw_L(G)` of a graph `G=(V,E)`, or the cutwidth `cw_L(D)` for a
     147digraph `D=(V,A)`, or the modified cutwidth `mcw_L(G)` of a graph `G=(V,E)`, or
     148the modified cutwidth `mcw_L(D)` for a digraph `D=(V,A)`, induced by a given
     149vertex ordering `L` are measured as follows,
     150
     151.. MATH::
     152
     153   cw_L(D) =  \max_{1\leq i\leq |V|-1}|\{(u,v)\in A,\ L[u]\leq i<L[v]\}|
     154
     155   mcw_L(D) = \max_{1\leq i\leq |V|-1}|\{(u,v)\in A,\ L[u]< i<L[v]\}|
     156
     157
     158**Optimal linear arrangement**
     159
     160The optimal linear arrangement minimization problem is to find an ordering of
     161the vertices that minimizes the sum over all intervals `[i,i+1]` of the number
     162of edges between vertices with index `j<=i` in the ordering and vertices with
     163index `k>i` in the ordering. When considering digraphs, only arcs from `j<=i` to
     164`k>i` are considered. So, the cost of a linear arrangement `ola_L(G)` of a graph
     165`G=(V,E)`, or `ola_L(D)` for a digraph `D=(V,A)`, induced by a given vertex
     166ordering `L` are measured as follows,
     167
     168.. MATH::
     169
     170   ola_L(G) = \sum_{1\leq i\leq |V|-1} |\{(u,v)\in E,\ L[u]\leq i<L[v]\}|
     171
     172   ola_L(D) = \sum_{1\leq i\leq |V|-1} |\{(u,v)\in A,\ L[u]\leq i<L[v]\}|
     173
     174However, realizing that in the sum edge `(u,v)` counts for the distance between
     175`u` and `v` in the ordering, we obtain a better formulation. Indeed, the optimal
     176linear arrangement minimization problem is to find an ordering that minimizes
     177the sum of the dilation of the edges, where the dilatation of an edge is the
     178distance between its endpoints in the vertex ordering. We can extend the notion
     179to digraphs where the dilatation of an arc `(u,v)` is the distance between its
     180endpoints if `v` has a higher index that `u` in the ordering, and 0 otherwise.
     181So, the cost of a linear arrangement `ola_L(G)` of a graph `G=(V,E)`, or
     182`ola_L(D)` for a digraph `D=(V,A)`, induced by a given vertex ordering `L` are
     183measured as follows,
     184
     185.. MATH::
     186
     187   ola_L(G) =  \sum_{\{u,v\}\in E} | L[u] - L[v] |
     188
     189   ola_L(D) =  \sum_{\{u,v\}\in A} max( 0, L[v] - L[u] )
     190
     191
     192**Sum Cut**
     193
     194The sum cut problem as a linear vertex ordering problem is to minimize the sum
     195of the vertex cuts induced by the ordering. A cut is the size of the
     196neighborhood in `L[i+1:]` of the vertices in `L[:i]`. This notion naturally
     197extend to digraphs. So the sum of the cost of the cuts induced by an ordering is
     198measured as,
     199
     200.. MATH::
     201   sc_L(D) =  \sum_{1\leq i\leq |V|-1} |\{N^+(L[:i])\setminus L[:i]\}|
     202
     203   sc_L(G) =  \sum_{1\leq i\leq |V|-1} |\{N(L[:i])\setminus L[:i]\}|
     204
     205
     206
     207**Minimum fill-in**
     208
     209The cost of a fill-in is mesured by the sum of the number of vertices with
     210position `j > i` in the vertex ordering that can be reached from a vertex `v` at
     211position `i` through a path with internal vertices at positions `<= i` (see tree
     212decompositions). Let `Z(i)` be the set of vertices that can be reached from the
     213vertex at position `i`. We have
     214
     215.. MATH::
     216
     217   mfi_L(G) =  \sum_{1\leq i\leq |V|-1} | Z( i ) |
     218
     219
     220
     221REFERENCES:
     222
     223.. [BFK+06] *On exact algorithms for treewidth*, Hans L. Bodlaender, Fedor
     224  V. Fomin, Arie M.C.A. Koster, Dieter Kratsch, and Dimitrios M. Thilikos,
     225  Proceedings 14th Annual European Symposium on Algorithms (ESA), volume 4168 of
     226  Lecture Notes in Computer Science, pages 672-683. Springer, 2006.
     227
     228.. [BFK+11] *A note on exact algorithms for vertex ordering problems on graphs*,
     229  Hans L. Bodlaender, Fedor V. Fomin, Arie M.C.A. Koster, Dieter Kratsch, and
     230  Dimitrios M. Thilikos, Theory of Computing Systems, 2011 to appear.
     231  http://dx.doi.org/10.1007/s00224-011-9312-0.
     232
     233.. [Bod98] *A partial k-arboretum of graphs with bounded treewidth*, Hans
     234  L. Bodlaender, Theoretical Computer Science, Volume 209, Issues 1-2, Pages
     235  1-45, 6 December 1998
     236
     237.. [CoSe11] *Characterization of graphs and digraphs with small process number*,
     238  D. Coudert and J-S. Sereni, Discrete Applied Mathematics (DAM),
     239  159(11):1094-1109, July 2011.
     240
     241.. [Kin92] *The vertex separation number of a graph equals its path-width*,
     242  Nancy G. Kinnersley, Information Processing Letters, Volume 42, Issue 6, Pages
     243  345-350, 24 July 1992.
     244
     245
     246
     247AUTHORS:
     248
     249- David Coudert (2012-03-20): initial version
     250
     251
     252
     253.. TODO::
     254
     255    * implement methods for computing linear orderings
     256
     257
     258
     259Methods
     260-------
     261"""
     262
     263
     264#*****************************************************************************
     265#          Copyright (C) 2012 David Coudert <david.coudert@inria.fr>
     266#
     267# Distributed  under  the  terms  of  the  GNU  General  Public  License (GPL)
     268#                         http://www.gnu.org/licenses/
     269#*****************************************************************************
     270
     271
     272class LinearOrdering():
     273    r"""
     274    Class gathering methods for manipulating linear orderings of (di)graphs.
     275
     276    This module implements several algorithms for computing and evaluating
     277    linear vertex ordering of graphs and digraphs. The <width> of a linear
     278    ordering is one of the following values.
     279
     280    """
     281
     282    def _repr_(self):
     283        r"""
     284        Returns a string representation of the class LinearOrdering.
     285        """
     286        return "Linear Ordering Class"
     287
     288    def _latex_(self):
     289        r"""
     290        Returns the LaTeX representation of the class LinearOrdering.
     291        """
     292        return 'Linear Ordering Class'
     293
     294
     295    ##########################################################
     296    # Function for testing the validity of a vertex ordering #
     297    ##########################################################
     298
     299    def is_valid(self, G, L):
     300        r"""
     301        Test if a given linear ordering `L` is valid for a given (di)graph `G`.
     302
     303        Returns True if `L` is a valid vertex ordering for `G`, that is if all
     304        vertices of `G` are in `L`, and `L` contains no other vertex and no
     305        duplicated vertices.
     306
     307        INPUT:
     308
     309        - ``G`` -- a graph or a digraph
     310
     311        - ``L`` -- an ordering of the vertices of ``G``
     312
     313
     314        OUTPUT:
     315
     316        Returns True if `L` is a valid vertex ordering for `G`, and False
     317        oterwise.
     318
     319
     320        EXAMPLE:
     321
     322        Path decomposition of a cycle::
     323
     324            sage: from sage.graphs.graph_decompositions.linear_ordering import LinearOrdering
     325            sage: LO = LinearOrdering()
     326            sage: G = graphs.CycleGraph(6)
     327            sage: L = [u for u in G.vertices()]
     328            sage: LO.is_valid(G, L)
     329            True
     330            sage: LO.is_valid(G, [1,2])
     331            False
     332
     333        TEST:
     334
     335        Giving anything else than a Graph or a DiGraph::
     336
     337            sage: from sage.graphs.graph_decompositions.linear_ordering import LinearOrdering
     338            sage: LO = LinearOrdering()
     339            sage: LO.is_valid(2, [])
     340            Traceback (most recent call last):
     341            ...
     342            ValueError: The input parameter must be a Graph or a DiGraph.
     343        """
     344        from sage.graphs.graph import Graph
     345        from sage.graphs.digraph import DiGraph
     346        if not isinstance(G, Graph) and not isinstance(G, DiGraph):
     347            raise ValueError("The input parameter must be a Graph or a DiGraph.")
     348
     349        A = set( G.vertices() )
     350        B = set( L )
     351        return len( A.symmetric_difference(B) ) == 0
     352
     353
     354    ############################################################################
     355    # Front end function for the evaluation of linear vertex orderings with    #
     356    # different widths                                                         #
     357    ############################################################################
     358
     359    def width_of(self, G, L, width = 'vertex_separation'):
     360        r"""
     361        Returns the <width> of the linear ordering `L` for `G`.
     362
     363        This function returns the <width> of the vertex ordering L, where
     364        width is one of the following values
     365
     366            +--------------+----------------------------+
     367            | short name   | long name                  |
     368            +==============+============================+
     369            | vs           | vertex_separation          |
     370            +--------------+----------------------------+
     371            | pw           | pathwidth                  |
     372            +--------------+----------------------------+
     373            | tw           | treewidth                  |
     374            +--------------+----------------------------+
     375            | bw           | bandwidth                  |
     376            +--------------+----------------------------+
     377            | pn           | process_number             |
     378            +--------------+----------------------------+
     379            | cw           | cutwidth                   |
     380            +--------------+----------------------------+
     381            | mcw          | modified_cutwidth          |
     382            +--------------+----------------------------+
     383            | ola          | optimal_linear_arrangement |
     384            +--------------+----------------------------+
     385            | sc           | sum_cut                    |
     386            +--------------+----------------------------+
     387            | mfi          | minimun_fill_in            |
     388            +--------------+----------------------------+
     389
     390        See the module's documentation for more details on these widths.
     391
     392        INPUT:
     393
     394        - ``G`` -- a graph or a digraph (possibly with multi-edges)
     395
     396        - ``L`` -- an ordering of the vertices of ``G``
     397
     398        - ``width`` -- is the graph invariant to consider (vertex_separation by
     399          default)
     400
     401
     402        OUTPUT:
     403
     404        The value of the measured graph invariants for this vertex ordering.
     405
     406
     407        NOTES:
     408
     409            All width are defined for undirected graphs.
     410
     411            For directed graphs, only vertex_separation, bandwidth,
     412            process_number, cutwidth, modified_cutwidth,
     413            optimal_linear_arrangement, and sum_cut are defined.  Measurements
     414            are performed using arcs from u=L[i] to v=L[j], with i<=j.
     415
     416        EXAMPLE:
     417
     418        Path decomposition of a cycle::
     419
     420            sage: from sage.graphs.graph_decompositions.linear_ordering import LinearOrdering
     421            sage: LO = LinearOrdering()
     422            sage: G = graphs.CycleGraph(6)
     423            sage: LO.width_of(G, [0, 1, 2, 3, 4, 5], 'pw')
     424            2
     425            sage: LO.width_of(G, [0, 2, 4, 1, 5, 3], 'pw')
     426            3
     427
     428        TEST:
     429
     430        Giving anything else than a Graph or a DiGraph::
     431
     432            sage: from sage.graphs.graph_decompositions.linear_ordering import LinearOrdering
     433            sage: LO = LinearOrdering()
     434            sage: LO.width_of(2, [], 'pw')
     435            Traceback (most recent call last):
     436            ...
     437            ValueError: The input parameter must be a Graph or a DiGraph.
     438
     439        Giving a vertex ordering on a different set of vertices::
     440
     441            sage: from sage.graphs.graph_decompositions.linear_ordering import LinearOrdering
     442            sage: LO = LinearOrdering()
     443            sage: G = graphs.CycleGraph(6)
     444            sage: LO.width_of(G, [1, 2, 3], 'pw')
     445            Traceback (most recent call last):
     446            ...
     447            ValueError: The input parameter is not a valid vertex ordering.
     448
     449        Giving a not implemented width parameter::
     450
     451            sage: from sage.graphs.graph_decompositions.linear_ordering import LinearOrdering
     452            sage: LO = LinearOrdering()
     453            sage: G = graphs.CycleGraph(6)
     454            sage: L = [u for u in G.vertices()]
     455            sage: LO.width_of(G, L, 'frite')
     456            Traceback (most recent call last):
     457            ...
     458            ValueError: The desired width evaluation function has not been implemented so far.  Feel free to add it.
     459        """
     460        from sage.graphs.graph import Graph
     461        from sage.graphs.digraph import DiGraph
     462        if not isinstance(G, Graph) and not isinstance(G, DiGraph):
     463            raise ValueError("The input parameter must be a Graph or a DiGraph.")
     464
     465        if not self.is_valid(G, L):
     466            raise ValueError("The input parameter is not a valid vertex ordering.")
     467
     468        if width in ['pathwidth','pw']:
     469            return self.width_of_path_decomposition(G, L)
     470        elif width in ['vertex_separation','vs']:
     471            return self.width_of_vertex_separation(G, L)
     472        elif width in ['process_number','pn']:
     473            return self.width_of_process_number(G, L)
     474        elif width in ['treewidth','tw']:
     475            return self.width_of_tree_decomposition(G, L)
     476        elif width in ['cutwidth','cw']:
     477            return self.width_of_cutwidth(G, L)
     478        elif width in ['modified_cutwidth','mcw']:
     479            return self.width_of_modified_cutwidth(G, L)
     480        elif width in ['bandwidth','bw']:
     481            return self.width_of_bandwidth(G, L)
     482        elif width in ['sum_cut','sc']:
     483            return self.width_of_sum_cut(G, L)
     484        elif width in ['minimum_fill_in','mfi']:
     485            return self.width_of_fill_in(G, L)
     486        elif width in ['optimal_linear_arrangement','ola']:
     487            return self.width_of_linear_arrangement(G, L)
     488        else:
     489            raise ValueError("The desired width evaluation function has not been implemented so far.  Feel free to add it.")
     490
     491
     492
     493
     494    ###############################################
     495    # Methods for pathwidth and vertex separation #
     496    ###############################################
     497
     498    def width_of_path_decomposition(self, G, L):
     499        r"""
     500        Returns the value `pw_L(G)` of the path decomposition induced by the
     501        vertex ordering `L` for `G`, where
     502
     503        ..MATH::
     504
     505            pw_L(G) =  \max_{0\leq i< |V|-1} | N(L[:i])\setminus L[:i] |
     506
     507        INPUT:
     508
     509        - ``G`` -- a Graph
     510
     511        - ``L`` -- an ordering of the vertices of ``G``
     512
     513
     514        OUTPUT:
     515
     516        The value ``pw_L(G)`` of the path decomposition induced by the ordering
     517        `L` of the vertices of `G`.
     518
     519        EXAMPLE:
     520
     521        Path decomposition of a cycle::
     522
     523            sage: from sage.graphs.graph_decompositions.linear_ordering import LinearOrdering
     524            sage: LO = LinearOrdering()
     525            sage: G = graphs.CycleGraph(6)
     526            sage: LO.width_of_path_decomposition(G, [0, 1, 2, 3, 4, 5])
     527            2
     528            sage: LO.width_of_path_decomposition(G, [0, 2, 4, 1, 5, 3])
     529            3
     530
     531        TEST:
     532
     533        Giving anything else than a Graph::
     534
     535            sage: from sage.graphs.graph_decompositions.linear_ordering import LinearOrdering
     536            sage: LO = LinearOrdering()
     537            sage: G = digraphs.Circuit(4)
     538            sage: LO.width_of_path_decomposition(G, [])
     539            Traceback (most recent call last):
     540            ...
     541            ValueError: The input parameter must be a Graph.
     542
     543        Giving a vertex ordering on a different set of vertices::
     544
     545            sage: from sage.graphs.graph_decompositions.linear_ordering import LinearOrdering
     546            sage: LO = LinearOrdering()
     547            sage: G = graphs.CycleGraph(6)
     548            sage: LO.width_of_path_decomposition(G, [1, 2, 3])
     549            Traceback (most recent call last):
     550            ...
     551            ValueError: The input parameter is not a valid vertex ordering.
     552        """
     553        # Path decompositions are defined only for graphs
     554        from sage.graphs.graph import Graph
     555        if not isinstance(G, Graph):
     556            raise ValueError("The input parameter must be a Graph.")
     557
     558        if not self.is_valid(G, L):
     559            raise ValueError("The input parameter is not a valid vertex ordering.")
     560
     561        pwL = 0
     562        S = []
     563        neighbors_of_S_in_V_minus_S = []
     564
     565        for u in L:
     566
     567            # We remove u from the list of neighbors of S
     568            if u in neighbors_of_S_in_V_minus_S:
     569                neighbors_of_S_in_V_minus_S.remove(u)
     570
     571            # We add vertex u to the set S
     572            S += [u]
     573
     574            # We add the neighbors of u to the list of neighbors of S
     575            for v in G.neighbors(u):
     576                if (not v in S) and (not v in neighbors_of_S_in_V_minus_S):
     577                    neighbors_of_S_in_V_minus_S += [v]
     578
     579            # We update the cost of the path decomposition
     580            pwL = max( pwL, len(neighbors_of_S_in_V_minus_S) )
     581
     582        return pwL
     583
     584
     585    def width_of_vertex_separation(self, G, L):
     586        r"""
     587        Returns the value `vs_L(G)` of the vertex separation induced by the
     588        vertex ordering `L` for `G`, where
     589
     590        ..MATH::
     591
     592            vs_L(G) =  \max_{0\leq i< |V|-1} | N^+(L[:i])\setminus L[:i] |
     593
     594        INPUT:
     595
     596        - ``G`` -- a graph or a digraph
     597
     598        - ``L`` -- an ordering of the vertices of ``G``
     599
     600
     601        OUTPUT:
     602
     603        The value ``vs_L(G)`` of the vertex separation induced by the ordering
     604        `L` of the vertices of `G`.
     605
     606        NOTES:
     607
     608            The vertex separation is defined for both graphs and digraphs. The
     609            vertex separation of a graph is also the cost of the path
     610            decomposition induced by the vertex ordering (see the module's
     611            documentation).
     612
     613        EXAMPLE:
     614
     615        Vertex separation of a cycle::
     616
     617            sage: from sage.graphs.graph_decompositions.linear_ordering import LinearOrdering
     618            sage: LO = LinearOrdering()
     619            sage: G = graphs.CycleGraph(6)
     620            sage: L = [u for u in G.vertices()]
     621            sage: LO.width_of_vertex_separation(G, L)
     622            2
     623
     624        Vertex separation of a cicuit::
     625
     626            sage: from sage.graphs.graph_decompositions.linear_ordering import LinearOrdering
     627            sage: LO = LinearOrdering()
     628            sage: G = digraphs.Circuit(6)
     629            sage: L = [u for u in G.vertices()]
     630            sage: LO.width_of_vertex_separation(G, L)
     631            1
     632
     633
     634        TEST:
     635
     636        Giving anything else than a Graph or a DiGraph::
     637
     638            sage: from sage.graphs.graph_decompositions.linear_ordering import LinearOrdering
     639            sage: LO = LinearOrdering()
     640            sage: LO.width_of_vertex_separation(2, [])
     641            Traceback (most recent call last):
     642            ...
     643            ValueError: The input parameter must be a Graph or a DiGraph.
     644
     645        Giving a vertex ordering on a different set of vertices::
     646
     647            sage: from sage.graphs.graph_decompositions.linear_ordering import LinearOrdering
     648            sage: LO = LinearOrdering()
     649            sage: G = digraphs.Circuit(6)
     650            sage: LO.width_of_vertex_separation(G, [1, 2, 3])
     651            Traceback (most recent call last):
     652            ...
     653            ValueError: The input parameter is not a valid vertex ordering.
     654        """
     655        # If G is a graph, we instead evaluate the path decomposition
     656        from sage.graphs.graph import Graph
     657        if isinstance(G, Graph):
     658            return self.width_of_path_decomposition(G, L)
     659
     660        if not self.is_valid(G, L):
     661            raise ValueError("The input parameter is not a valid vertex ordering.")
     662
     663        vsL = 0
     664        S = []
     665        neighbors_of_S_in_V_minus_S = []
     666
     667        for u in L:
     668
     669            # We remove u from the list of neighbors of S
     670            if u in neighbors_of_S_in_V_minus_S:
     671                neighbors_of_S_in_V_minus_S.remove(u)
     672
     673            # We add vertex u to the set S
     674            S += [u]
     675
     676            # We add the out-neighbors of u to the list of neighbors of S
     677            for v in G.neighbors_out(u):
     678                if (not v in S) and (not v in neighbors_of_S_in_V_minus_S):
     679                    neighbors_of_S_in_V_minus_S += [v]
     680
     681            # We update the cost of the vertex separation
     682            vsL = max( vsL, len(neighbors_of_S_in_V_minus_S) )
     683
     684        return vsL
     685
     686
     687    def __vertex_separation_MILP__(self, D, integrality = False, verbosity = 0):
     688        r"""
     689        Implements an MILP for the vertex separation
     690
     691        INPUTS:
     692
     693        - ``D`` -- a digraph
     694
     695        OUTPUT:
     696
     697        A pair ``(cost, ordering)`` representing the optimal ordering of the
     698        vertices and its cost.
     699
     700
     701        VARIABLES:
     702
     703        x[v,t] = 1 if and only if the working lightpath of connection v is torn down at epoch t
     704                    So if an agent is put on vertex v at step t
     705
     706        y[v,t] = 1 if and only if the new lightpath of connection v is set up at epoch t
     707                So if v is processed at step t
     708
     709        u[v,t] = 1 if and only if the connection corresponding to v is disrupted at epoch t
     710                So if there is an agent on v at step t
     711
     712        z          Objective = max number of concurrently disrupted connections
     713
     714        CONSTANT:
     715        T         maximum number of epoch/steps. T <= 2.N
     716
     717        TERMINOLOGY:
     718        An epoch is the period of time between the establishment of 2 new lightpaths
     719        So between the processing of 2 nodes
     720        => 1 epoch per connection => #epochs = T = N
     721
     722        CONSTRAINTS:
     723            (1) Minimize z
     724            (2) x[v][t] <= x[v][t+1] for all v in V, and for t:=0..T-2
     725            (3) y[v][t] <= y[v][t+1]        for all v in V, and for t:=0..T-2
     726            (miss)  y[v][t] <= x[v][t]     for all v in V, and for all t:=0..T-1
     727                to ensure that once a vertex is processed, it has already been covered....
     728            (4) y[v][t] <= x[w][t]  for all v in V, for all w in N^+(v), and for all t:=0..T-1
     729            (5) sum_{v in V} y[v][0] <= 1
     730            (6) sum_{v in V} y[v][t+1] <= sum_{v in V} y[v][t] + 1  for t:=0..T-2
     731            (7) sum_{v in V} y[v][T-1] = |V|
     732            (8) u[v][t] >= x[v][t]-y[v][t]    for all v in V, and for all t:=0..T-1
     733            (9) z >= sum_{v in V} u[v][t]   for all t:=0..T-1
     734            (10) 0 <= x[v][t] and u[v][t] <= 1
     735                 Meaning both are between 0 and 1
     736            (11) y[v][t] in {0,1}
     737            (12) 0 <= z <= |V|
     738        """
     739        from sage.numerical.mip import MixedIntegerLinearProgram, Sum, MIPSolverException
     740        p = MixedIntegerLinearProgram( maximization = False )
     741
     742        if integrality:
     743            x = p.new_variable( integer = True, dim = 2 )
     744            u = p.new_variable( integer = True, dim = 2 )
     745        else:
     746            x = p.new_variable( dim = 2 )
     747            u = p.new_variable( dim = 2 )
     748            y = p.new_variable( integer = True, dim = 2 )
     749        z = p.new_variable( integer = True, dim = 1 )
     750
     751        N = D.num_verts()
     752        T=N
     753
     754        #  (2) x[v][t] <= x[v][t+1] for all v in V, and for t:=0..T-2
     755        #  (3) y[v][t] <= y[v][t+1]        for all v in V, and for t:=0..T-2
     756        for v in D.vertices():
     757            for t in xrange(T-1):
     758                p.add_constraint( x[v][t] - x[v][t+1], max = 0 )
     759                p.add_constraint( y[v][t] - y[v][t+1], max = 0 )
     760
     761        # (miss)  y[v][t] <= x[v][t]     for all v in V, and for all t:=0..T-1
     762        # for v in D.vertices():
     763        #     for t in range(T):
     764        #        p.add_constraint(y[v][t]-x[v][t],max=0)
     765        #
     766        #  (4) y[v][t] <= x[w][t]  for all v in V, for all w in N^+(v), and for all t:=0..T-1
     767        for v in D.vertices():
     768            for w in D.neighbors_out(v):
     769                for t in xrange(T):
     770                    p.add_constraint( y[v][t] - x[w][t], max = 0 )
     771
     772        #  (5) sum_{v in V} y[v][0] <= 1
     773        p.add_constraint( Sum( y[v][0] for v in D.vertices() ), max = 1 )
     774
     775        #  (6) sum_{v in V} y[v][t+1] <= sum_{v in V} y[v][t] + 1  for t:=0..T-2
     776        for t in xrange(T-1):
     777            p.add_constraint( Sum( y[v][t+1] - y[v][t] for v in D.vertices() ), max = 1 )
     778
     779        #  (7) sum_{v in V} y[v][T-1] = |V|
     780        p.add_constraint( Sum( y[v][T-1] for v in D.vertices() ), min = N )
     781        p.add_constraint( Sum( y[v][T-1] for v in D.vertices() ), max = N )
     782
     783        #  (8) u[v][t] >= x[v][t]-y[v][t]    for all v in V, and for all t:=0..T-1
     784        for v in D.vertices():
     785            for t in xrange(T):
     786                p.add_constraint( x[v][t] - y[v][t] - u[v][t], max = 0 )
     787
     788        #  (9) z >= sum_{v in V} u[v][t]   for all t:=0..T-1
     789        for t in xrange(T):
     790            p.add_constraint( Sum( u[v][t] for v in D.vertices() ) - z['z'], max = 0 )
     791
     792        # (10) 0 <= x[v][t] and u[v][t] <= 1
     793        # (11) y[v][t] in {0,1}
     794        for v in D.vertices():
     795            for t in xrange(T):
     796                p.add_constraint( x[v][t], min = 0 )
     797                p.add_constraint( x[v][t], max = 1 )
     798                p.add_constraint( u[v][t], min = 0 )
     799                p.add_constraint( u[v][t], max = 1 )
     800                p.set_binary( y[v][t] )
     801        # (12) 0 <= z <= |V|
     802        p.add_constraint( z['z'], min = 0 )
     803        p.add_constraint( z['z'], max = N )
     804
     805        #  (1) Minimize z
     806        p.set_objective( z['z'] )
     807
     808        try:
     809            obj = p.solve( log=verbosity )
     810
     811            taby = p.get_values( y )
     812            tabz = p.get_values( z )
     813            # since exactly one vertex is processed per epoch, we can reconstruct the sequence
     814            seq = []
     815            for t in xrange(T):
     816                for v in D.vertices():
     817                    if (taby[v][t] > 0) and (not v in seq):
     818                        seq += [v]
     819            vs = int(round( tabz['z'] ));
     820
     821        except MIPSolverException:
     822            print "VSC NOT working with fractional stuff\n",D.edges(labels=False)
     823            print " => try with integral version"
     824            (vs,seq)=self.__vertex_separation_MILP__(D, True, verbosity)
     825            print vs,seq
     826            raise ValueError("Unbounded or unexpected error")
     827
     828        del p
     829        return vs,seq;
     830
     831
     832
     833
     834
     835    def vertex_separation(self, G, method = 'exp'):
     836        r"""
     837        Returns an optimal ordering of the vertices and its cost for
     838        vertex-separation.
     839
     840        INPUT:
     841
     842        - ``G`` -- a digraph
     843
     844        - ``method`` (string) -- the method to use
     845
     846            - 'exp' (default) -- Uses an algorithm with time and space
     847              complexity in ``2^n``. Because of its current implementation, this
     848              algorithm only works on graphs on less than 32 vertices. This can
     849              be changed to 54 if necessary, but 32 vertices already require 4GB
     850              of memory.
     851
     852            - 'MILP' -- Uses an MILP implementation.
     853
     854        OUTPUT:
     855
     856        A pair ``(cost, ordering)`` representing the optimal ordering of the
     857        vertices and its cost.
     858
     859        EXAMPLE:
     860
     861        The vertex separation of a circuit is equal to 1::
     862
     863            sage: from sage.graphs.graph_decompositions.linear_ordering import LinearOrdering
     864            sage: LO = LinearOrdering()
     865            sage: g = digraphs.Circuit(6)
     866            sage: LO.vertex_separation(g)
     867            (1, [0, 1, 2, 3, 4, 5])
     868
     869        TEST:
     870
     871        Given anything else than a DiGraph::
     872
     873            sage: from sage.graphs.graph_decompositions.linear_ordering import LinearOrdering
     874            sage: LO = LinearOrdering()
     875            sage: g = graphs.CycleGraph(6)
     876            sage: LO.vertex_separation(g)
     877            Traceback (most recent call last):
     878            ...
     879            ValueError: The input parameter must be a DiGraph.
     880        """
     881        from sage.graphs.graph import DiGraph
     882        if not isinstance(G, DiGraph):
     883            raise ValueError("The input parameter must be a DiGraph.")
     884
     885        if method == 'MILP':
     886            return self.__vertex_separation_MILP__(G)
     887        else:
     888            # default method
     889            from sage.graphs.graph_decompositions import vertex_separation
     890            return vertex_separation.vertex_separation(G)
     891
     892    def path_decomposition(self, G, method = 'exp'):
     893        r"""
     894        Returns the pathwidth of the given graph and the ordering of the
     895        vertices resulting in a corresponding path decomposition.
     896
     897        INPUT:
     898
     899        - ``G`` -- a graph
     900
     901        - ``method`` (string) -- the method to use
     902
     903            - 'exp' (default) -- Uses an algorithm with time and space
     904              complexity in ``2^n``. Because of its current implementation, this
     905              algorithm only works on graphs on less than 32 vertices. This can
     906              be changed to 54 if necessary, but 32 vertices already require 4GB
     907              of memory.
     908
     909            - 'MILP' -- Uses an MILP implementation.
     910
     911        OUTPUT:
     912
     913        A pair ``(cost, ordering)`` representing the optimal ordering of the
     914        vertices and its cost.
     915
     916        EXAMPLE:
     917
     918        The pathwidth of a circuit is equal to 2::
     919
     920            sage: from sage.graphs.graph_decompositions.linear_ordering import LinearOrdering
     921            sage: LO = LinearOrdering()
     922            sage: g = graphs.CycleGraph(6)
     923            sage: LO.path_decomposition(g)
     924            (2, [0, 1, 2, 3, 4, 5])
     925
     926        TEST:
     927
     928        Given anything else than a Graph::
     929
     930            sage: from sage.graphs.graph_decompositions.linear_ordering import LinearOrdering
     931            sage: LO = LinearOrdering()
     932            sage: g = digraphs.Circuit(6)
     933            sage: LO.path_decomposition(g)
     934            Traceback (most recent call last):
     935            ...
     936            ValueError: The input parameter must be a Graph.
     937        """
     938        from sage.graphs.graph import Graph
     939        if not isinstance(G, Graph):
     940            raise ValueError("The input parameter must be a Graph.")
     941
     942        if method == 'MILP':
     943            from sage.graphs.graph import DiGraph
     944            D = DiGraph( G.edges(labels = None) + [(v,u) for u,v in G.edges(labels = None)] )
     945            return self.__vertex_separation_MILP__(D)
     946        else:
     947            # default method
     948            from sage.graphs.graph_decompositions import vertex_separation
     949            return vertex_separation.path_decomposition(G)
     950
     951