Ticket #12716: trac_12716_linear_ordering.patch

File trac_12716_linear_ordering.patch, 35.7 KB (added by dcoudert, 14 months 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