# HG changeset patch
# User dcoudert
# 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/doc/en/reference/graphs.rst
+++ b/doc/en/reference/graphs.rst
@@ -51,6 +51,7 @@
sage/graphs/pq_trees
sage/graphs/matchpoly
sage/graphs/graph_decompositions/vertex_separation
+ sage/graphs/graph_decompositions/linear_ordering
sage/graphs/graph_decompositions/rankwidth
sage/graphs/modular_decomposition/modular_decomposition
sage/graphs/convexity_properties
diff --git a/sage/graphs/graph_decompositions/linear_ordering.py b/sage/graphs/graph_decompositions/linear_ordering.py
new file mode 100644
--- /dev/null
+++ b/sage/graphs/graph_decompositions/linear_ordering.py
@@ -0,0 +1,951 @@
+r"""
+Linear orderings of graphs and digraphs
+
+This module implements several algorithms for computing and evaluating linear
+vertex ordering of graphs and digraphs. The of a linear ordering is one
+of the following values.
+
+ +--------------+----------------------------+
+ | short name | long name |
+ +==============+============================+
+ | vs | vertex_separation |
+ +--------------+----------------------------+
+ | pw | pathwidth |
+ +--------------+----------------------------+
+ | tw | treewidth |
+ +--------------+----------------------------+
+ | bw | bandwidth |
+ +--------------+----------------------------+
+ | pn | process_number |
+ +--------------+----------------------------+
+ | cw | cutwidth |
+ +--------------+----------------------------+
+ | mcw | modified_cutwidth |
+ +--------------+----------------------------+
+ | ola | optimal_linear_arrangement |
+ +--------------+----------------------------+
+ | sc | sum_cut |
+ +--------------+----------------------------+
+ | mfi | minimun_fill_in |
+ +--------------+----------------------------+
+
+Some of these widths are defined for both graphs and digraphs, and others only
+for graphs or for digraphs.
+
+We follow the formulations proposed in [Bod98]_ and [BFK+11]_ for the evaluation
+of the widths induced by a linear vertex ordering `L=[v_1, ..., v_n]` of the
+vertices of a (di)graph `G=(V,E)`. Thus, we consider that we have a function
+`f` associating an integer to each 3-tuple, consisting of a (di)graph `G=(V,E)`,
+a vertex set `S\subseteq V`, and a vertex `v\in V`. Let `L[:i]=[v_1, ..., v_i]`
+be a prefix of the ordering and so a subset of `V`. We consider the problems
+that can be formulated as the computation of:
+
+.. MATH::
+
+ \text{cost}(G, L) = \max_{0\leq i<|V(G)|} f(G, L[:i]\setminus\{v\}, v)
+
+or
+
+.. MATH::
+
+ \text{cost}(G, L) = \sum_{0\leq i<|V(G)|} f(G, L[:i]\setminus\{v\}, v)
+
+
+
+Given an ordering `v_1, ..., v_n` of the vertices of `V(G)`, its *cost* is
+defined as:
+
+
+**Vertex separation**
+
+The vertex separation of a linear ordering `L` of the vertices of a digraph `D`
+is measured as the maximum number of out-neighbors with position `k>i` in the
+ordering of the vertices with position `j<=i` in the ordering. That is,
+
+.. MATH::
+ vs_L(D) = \max_{1\leq i\leq |V|-1} |\{N^+(L[:i])\setminus L[:i]\}|
+
+
+**Path decomposition**
+
+The cost of a path decomposition induced by a linear ordering `L` of the
+vertices of a graph `G` is measured as the maximum number of neighbors with
+position `k>i` in the ordering of the vertices with position `j<=i` in the
+ordering. That is,
+
+.. MATH::
+ pw_L(G) = \max_{1\leq i\leq |V|-1} |\{N(L[:i])\setminus L[:i]\}|
+
+
+
+**Tree decomposition**
+
+The value ``tw_L(G)`` of the tree decomposition induced by the ordering `L` of
+the vertices of `G`.
+
+The cost of a tree decomposition is mesured by the maximum number of vertices
+with position `j > i` in the vertex ordering that can be reached from a vertex
+`v` at position `i` through a path with internal vertices at positions `<=
+i`. Let `Z(i)` be the set of vertices that can be reached from the vertex at
+position `i`. We have
+
+.. MATH::
+
+ tw_L(G) = \max_{1\leq i\leq |V|-1} | Z( i ) |
+
+
+
+**Bandwidth**
+
+The bandwidth minimization problem is to find a linear vertex ordering that
+minimizes the maximum dilation among all the edges, where the dilatation of an
+edge is the distance between its endpoints in the vertex ordering. We can extend
+the notion to digraphs where the dilatation of an arc `(u,v)` is the distance
+between its endpoints if `v` has a higher index that `u` in the ordering, and 0
+otherwise.
+
+So, the bandwidth `bw_L(G)` of a graph `G=(V,E)`, or the bandwidth `bw_L(D)` for
+a digraph `D=(V,A)`, induced by a given vertex ordering `L` are measured as
+follows,
+
+.. MATH::
+
+ bw_L(G) = \max_{\{u,v\}\in E} | L[u] - L[v] |
+
+ bw_L(D) = \max_{\{u,v\}\in A} max( 0, L[v] - L[u] )
+
+
+
+**Process number**
+
+The process number of a linear ordering `L` of the vertices of a digraph `D` is
+measured as the maximum number of out-neighbors with position `k>=i` (`i`
+included) in the ordering of the vertices with position `j<=i` (`i` included) in
+the ordering. Thus, loops on vertices of the digraph are also taken into
+account. This notion can also be formulated for undirected graphs (see [CoSe11]_
+for more details on this notion). We have,
+
+.. MATH::
+
+ pn_L(D) = \max_{1\leq i\leq |V|} |\{N^+(L[:i])\setminus L[:i-1]\}|
+
+ pn_L(G) = \max_{1\leq i\leq |V|} |\{N(L[:i])\setminus L[:i-1]\}|
+
+
+
+**Cutwidth and modified cutwidth**
+
+The cutwidth minimization problem is to find an ordering that minimizes the
+maximum number of edges between vertices with index `j<=i` in the ordering and
+vertices with index `k>i` in the ordering. When considering digraphs, only arcs
+from `j<=i` to `k>i` are considered. For the modified cutwidth, only edges (or
+arcs) from vertices with indexes `j*i` are
+considered. So we count the number of edges (or arcs) passing over the vertex
+with index `i`.
+
+So, the cutwidth `cw_L(G)` of a graph `G=(V,E)`, or the cutwidth `cw_L(D)` for a
+digraph `D=(V,A)`, or the modified cutwidth `mcw_L(G)` of a graph `G=(V,E)`, or
+the modified cutwidth `mcw_L(D)` for a digraph `D=(V,A)`, induced by a given
+vertex ordering `L` are measured as follows,
+
+.. MATH::
+
+ cw_L(D) = \max_{1\leq i\leq |V|-1}|\{(u,v)\in A,\ L[u]\leq ii` in the ordering. When considering digraphs, only arcs from `j<=i` to
+`k>i` are considered. So, the cost of a linear arrangement `ola_L(G)` of a graph
+`G=(V,E)`, or `ola_L(D)` for a digraph `D=(V,A)`, induced by a given vertex
+ordering `L` are measured as follows,
+
+.. MATH::
+
+ ola_L(G) = \sum_{1\leq i\leq |V|-1} |\{(u,v)\in E,\ L[u]\leq i i` in the vertex ordering that can be reached from a vertex `v` at
+position `i` through a path with internal vertices at positions `<= i` (see tree
+decompositions). Let `Z(i)` be the set of vertices that can be reached from the
+vertex at position `i`. We have
+
+.. MATH::
+
+ mfi_L(G) = \sum_{1\leq i\leq |V|-1} | Z( i ) |
+
+
+
+REFERENCES:
+
+.. [BFK+06] *On exact algorithms for treewidth*, Hans L. Bodlaender, Fedor
+ V. Fomin, Arie M.C.A. Koster, Dieter Kratsch, and Dimitrios M. Thilikos,
+ Proceedings 14th Annual European Symposium on Algorithms (ESA), volume 4168 of
+ Lecture Notes in Computer Science, pages 672-683. Springer, 2006.
+
+.. [BFK+11] *A note on exact algorithms for vertex ordering problems on graphs*,
+ Hans L. Bodlaender, Fedor V. Fomin, Arie M.C.A. Koster, Dieter Kratsch, and
+ Dimitrios M. Thilikos, Theory of Computing Systems, 2011 to appear.
+ http://dx.doi.org/10.1007/s00224-011-9312-0.
+
+.. [Bod98] *A partial k-arboretum of graphs with bounded treewidth*, Hans
+ L. Bodlaender, Theoretical Computer Science, Volume 209, Issues 1-2, Pages
+ 1-45, 6 December 1998
+
+.. [CoSe11] *Characterization of graphs and digraphs with small process number*,
+ D. Coudert and J-S. Sereni, Discrete Applied Mathematics (DAM),
+ 159(11):1094-1109, July 2011.
+
+.. [Kin92] *The vertex separation number of a graph equals its path-width*,
+ Nancy G. Kinnersley, Information Processing Letters, Volume 42, Issue 6, Pages
+ 345-350, 24 July 1992.
+
+
+
+AUTHORS:
+
+- David Coudert (2012-03-20): initial version
+
+
+
+.. TODO::
+
+ * implement methods for computing linear orderings
+
+
+
+Methods
+-------
+"""
+
+
+#*****************************************************************************
+# Copyright (C) 2012 David Coudert
+#
+# Distributed under the terms of the GNU General Public License (GPL)
+# http://www.gnu.org/licenses/
+#*****************************************************************************
+
+
+class LinearOrdering():
+ r"""
+ Class gathering methods for manipulating linear orderings of (di)graphs.
+
+ This module implements several algorithms for computing and evaluating
+ linear vertex ordering of graphs and digraphs. The of a linear
+ ordering is one of the following values.
+
+ """
+
+ def _repr_(self):
+ r"""
+ Returns a string representation of the class LinearOrdering.
+ """
+ return "Linear Ordering Class"
+
+ def _latex_(self):
+ r"""
+ Returns the LaTeX representation of the class LinearOrdering.
+ """
+ return 'Linear Ordering Class'
+
+
+ ##########################################################
+ # Function for testing the validity of a vertex ordering #
+ ##########################################################
+
+ def is_valid(self, G, L):
+ r"""
+ Test if a given linear ordering `L` is valid for a given (di)graph `G`.
+
+ Returns True if `L` is a valid vertex ordering for `G`, that is if all
+ vertices of `G` are in `L`, and `L` contains no other vertex and no
+ duplicated vertices.
+
+ INPUT:
+
+ - ``G`` -- a graph or a digraph
+
+ - ``L`` -- an ordering of the vertices of ``G``
+
+
+ OUTPUT:
+
+ Returns True if `L` is a valid vertex ordering for `G`, and False
+ oterwise.
+
+
+ EXAMPLE:
+
+ Path decomposition of a cycle::
+
+ sage: from sage.graphs.graph_decompositions.linear_ordering import LinearOrdering
+ sage: LO = LinearOrdering()
+ sage: G = graphs.CycleGraph(6)
+ sage: L = [u for u in G.vertices()]
+ sage: LO.is_valid(G, L)
+ True
+ sage: LO.is_valid(G, [1,2])
+ False
+
+ TEST:
+
+ Giving anything else than a Graph or a DiGraph::
+
+ sage: from sage.graphs.graph_decompositions.linear_ordering import LinearOrdering
+ sage: LO = LinearOrdering()
+ sage: LO.is_valid(2, [])
+ Traceback (most recent call last):
+ ...
+ ValueError: The input parameter must be a Graph or a DiGraph.
+ """
+ from sage.graphs.graph import Graph
+ from sage.graphs.digraph import DiGraph
+ if not isinstance(G, Graph) and not isinstance(G, DiGraph):
+ raise ValueError("The input parameter must be a Graph or a DiGraph.")
+
+ A = set( G.vertices() )
+ B = set( L )
+ return len( A.symmetric_difference(B) ) == 0
+
+
+ ############################################################################
+ # Front end function for the evaluation of linear vertex orderings with #
+ # different widths #
+ ############################################################################
+
+ def width_of(self, G, L, width = 'vertex_separation'):
+ r"""
+ Returns the of the linear ordering `L` for `G`.
+
+ This function returns the of the vertex ordering L, where
+ width is one of the following values
+
+ +--------------+----------------------------+
+ | short name | long name |
+ +==============+============================+
+ | vs | vertex_separation |
+ +--------------+----------------------------+
+ | pw | pathwidth |
+ +--------------+----------------------------+
+ | tw | treewidth |
+ +--------------+----------------------------+
+ | bw | bandwidth |
+ +--------------+----------------------------+
+ | pn | process_number |
+ +--------------+----------------------------+
+ | cw | cutwidth |
+ +--------------+----------------------------+
+ | mcw | modified_cutwidth |
+ +--------------+----------------------------+
+ | ola | optimal_linear_arrangement |
+ +--------------+----------------------------+
+ | sc | sum_cut |
+ +--------------+----------------------------+
+ | mfi | minimun_fill_in |
+ +--------------+----------------------------+
+
+ See the module's documentation for more details on these widths.
+
+ INPUT:
+
+ - ``G`` -- a graph or a digraph (possibly with multi-edges)
+
+ - ``L`` -- an ordering of the vertices of ``G``
+
+ - ``width`` -- is the graph invariant to consider (vertex_separation by
+ default)
+
+
+ OUTPUT:
+
+ The value of the measured graph invariants for this vertex ordering.
+
+
+ NOTES:
+
+ All width are defined for undirected graphs.
+
+ For directed graphs, only vertex_separation, bandwidth,
+ process_number, cutwidth, modified_cutwidth,
+ optimal_linear_arrangement, and sum_cut are defined. Measurements
+ are performed using arcs from u=L[i] to v=L[j], with i<=j.
+
+ EXAMPLE:
+
+ Path decomposition of a cycle::
+
+ sage: from sage.graphs.graph_decompositions.linear_ordering import LinearOrdering
+ sage: LO = LinearOrdering()
+ sage: G = graphs.CycleGraph(6)
+ sage: LO.width_of(G, [0, 1, 2, 3, 4, 5], 'pw')
+ 2
+ sage: LO.width_of(G, [0, 2, 4, 1, 5, 3], 'pw')
+ 3
+
+ TEST:
+
+ Giving anything else than a Graph or a DiGraph::
+
+ sage: from sage.graphs.graph_decompositions.linear_ordering import LinearOrdering
+ sage: LO = LinearOrdering()
+ sage: LO.width_of(2, [], 'pw')
+ Traceback (most recent call last):
+ ...
+ ValueError: The input parameter must be a Graph or a DiGraph.
+
+ Giving a vertex ordering on a different set of vertices::
+
+ sage: from sage.graphs.graph_decompositions.linear_ordering import LinearOrdering
+ sage: LO = LinearOrdering()
+ sage: G = graphs.CycleGraph(6)
+ sage: LO.width_of(G, [1, 2, 3], 'pw')
+ Traceback (most recent call last):
+ ...
+ ValueError: The input parameter is not a valid vertex ordering.
+
+ Giving a not implemented width parameter::
+
+ sage: from sage.graphs.graph_decompositions.linear_ordering import LinearOrdering
+ sage: LO = LinearOrdering()
+ sage: G = graphs.CycleGraph(6)
+ sage: L = [u for u in G.vertices()]
+ sage: LO.width_of(G, L, 'frite')
+ Traceback (most recent call last):
+ ...
+ ValueError: The desired width evaluation function has not been implemented so far. Feel free to add it.
+ """
+ from sage.graphs.graph import Graph
+ from sage.graphs.digraph import DiGraph
+ if not isinstance(G, Graph) and not isinstance(G, DiGraph):
+ raise ValueError("The input parameter must be a Graph or a DiGraph.")
+
+ if not self.is_valid(G, L):
+ raise ValueError("The input parameter is not a valid vertex ordering.")
+
+ if width in ['pathwidth','pw']:
+ return self.width_of_path_decomposition(G, L)
+ elif width in ['vertex_separation','vs']:
+ return self.width_of_vertex_separation(G, L)
+ elif width in ['process_number','pn']:
+ return self.width_of_process_number(G, L)
+ elif width in ['treewidth','tw']:
+ return self.width_of_tree_decomposition(G, L)
+ elif width in ['cutwidth','cw']:
+ return self.width_of_cutwidth(G, L)
+ elif width in ['modified_cutwidth','mcw']:
+ return self.width_of_modified_cutwidth(G, L)
+ elif width in ['bandwidth','bw']:
+ return self.width_of_bandwidth(G, L)
+ elif width in ['sum_cut','sc']:
+ return self.width_of_sum_cut(G, L)
+ elif width in ['minimum_fill_in','mfi']:
+ return self.width_of_fill_in(G, L)
+ elif width in ['optimal_linear_arrangement','ola']:
+ return self.width_of_linear_arrangement(G, L)
+ else:
+ raise ValueError("The desired width evaluation function has not been implemented so far. Feel free to add it.")
+
+
+
+
+ ###############################################
+ # Methods for pathwidth and vertex separation #
+ ###############################################
+
+ def width_of_path_decomposition(self, G, L):
+ r"""
+ Returns the value `pw_L(G)` of the path decomposition induced by the
+ vertex ordering `L` for `G`, where
+
+ ..MATH::
+
+ pw_L(G) = \max_{0\leq i< |V|-1} | N(L[:i])\setminus L[:i] |
+
+ INPUT:
+
+ - ``G`` -- a Graph
+
+ - ``L`` -- an ordering of the vertices of ``G``
+
+
+ OUTPUT:
+
+ The value ``pw_L(G)`` of the path decomposition induced by the ordering
+ `L` of the vertices of `G`.
+
+ EXAMPLE:
+
+ Path decomposition of a cycle::
+
+ sage: from sage.graphs.graph_decompositions.linear_ordering import LinearOrdering
+ sage: LO = LinearOrdering()
+ sage: G = graphs.CycleGraph(6)
+ sage: LO.width_of_path_decomposition(G, [0, 1, 2, 3, 4, 5])
+ 2
+ sage: LO.width_of_path_decomposition(G, [0, 2, 4, 1, 5, 3])
+ 3
+
+ TEST:
+
+ Giving anything else than a Graph::
+
+ sage: from sage.graphs.graph_decompositions.linear_ordering import LinearOrdering
+ sage: LO = LinearOrdering()
+ sage: G = digraphs.Circuit(4)
+ sage: LO.width_of_path_decomposition(G, [])
+ Traceback (most recent call last):
+ ...
+ ValueError: The input parameter must be a Graph.
+
+ Giving a vertex ordering on a different set of vertices::
+
+ sage: from sage.graphs.graph_decompositions.linear_ordering import LinearOrdering
+ sage: LO = LinearOrdering()
+ sage: G = graphs.CycleGraph(6)
+ sage: LO.width_of_path_decomposition(G, [1, 2, 3])
+ Traceback (most recent call last):
+ ...
+ ValueError: The input parameter is not a valid vertex ordering.
+ """
+ # Path decompositions are defined only for graphs
+ from sage.graphs.graph import Graph
+ if not isinstance(G, Graph):
+ raise ValueError("The input parameter must be a Graph.")
+
+ if not self.is_valid(G, L):
+ raise ValueError("The input parameter is not a valid vertex ordering.")
+
+ pwL = 0
+ S = []
+ neighbors_of_S_in_V_minus_S = []
+
+ for u in L:
+
+ # We remove u from the list of neighbors of S
+ if u in neighbors_of_S_in_V_minus_S:
+ neighbors_of_S_in_V_minus_S.remove(u)
+
+ # We add vertex u to the set S
+ S += [u]
+
+ # We add the neighbors of u to the list of neighbors of S
+ for v in G.neighbors(u):
+ if (not v in S) and (not v in neighbors_of_S_in_V_minus_S):
+ neighbors_of_S_in_V_minus_S += [v]
+
+ # We update the cost of the path decomposition
+ pwL = max( pwL, len(neighbors_of_S_in_V_minus_S) )
+
+ return pwL
+
+
+ def width_of_vertex_separation(self, G, L):
+ r"""
+ Returns the value `vs_L(G)` of the vertex separation induced by the
+ vertex ordering `L` for `G`, where
+
+ ..MATH::
+
+ vs_L(G) = \max_{0\leq i< |V|-1} | N^+(L[:i])\setminus L[:i] |
+
+ INPUT:
+
+ - ``G`` -- a graph or a digraph
+
+ - ``L`` -- an ordering of the vertices of ``G``
+
+
+ OUTPUT:
+
+ The value ``vs_L(G)`` of the vertex separation induced by the ordering
+ `L` of the vertices of `G`.
+
+ NOTES:
+
+ The vertex separation is defined for both graphs and digraphs. The
+ vertex separation of a graph is also the cost of the path
+ decomposition induced by the vertex ordering (see the module's
+ documentation).
+
+ EXAMPLE:
+
+ Vertex separation of a cycle::
+
+ sage: from sage.graphs.graph_decompositions.linear_ordering import LinearOrdering
+ sage: LO = LinearOrdering()
+ sage: G = graphs.CycleGraph(6)
+ sage: L = [u for u in G.vertices()]
+ sage: LO.width_of_vertex_separation(G, L)
+ 2
+
+ Vertex separation of a cicuit::
+
+ sage: from sage.graphs.graph_decompositions.linear_ordering import LinearOrdering
+ sage: LO = LinearOrdering()
+ sage: G = digraphs.Circuit(6)
+ sage: L = [u for u in G.vertices()]
+ sage: LO.width_of_vertex_separation(G, L)
+ 1
+
+
+ TEST:
+
+ Giving anything else than a Graph or a DiGraph::
+
+ sage: from sage.graphs.graph_decompositions.linear_ordering import LinearOrdering
+ sage: LO = LinearOrdering()
+ sage: LO.width_of_vertex_separation(2, [])
+ Traceback (most recent call last):
+ ...
+ ValueError: The input parameter must be a Graph or a DiGraph.
+
+ Giving a vertex ordering on a different set of vertices::
+
+ sage: from sage.graphs.graph_decompositions.linear_ordering import LinearOrdering
+ sage: LO = LinearOrdering()
+ sage: G = digraphs.Circuit(6)
+ sage: LO.width_of_vertex_separation(G, [1, 2, 3])
+ Traceback (most recent call last):
+ ...
+ ValueError: The input parameter is not a valid vertex ordering.
+ """
+ # If G is a graph, we instead evaluate the path decomposition
+ from sage.graphs.graph import Graph
+ if isinstance(G, Graph):
+ return self.width_of_path_decomposition(G, L)
+
+ if not self.is_valid(G, L):
+ raise ValueError("The input parameter is not a valid vertex ordering.")
+
+ vsL = 0
+ S = []
+ neighbors_of_S_in_V_minus_S = []
+
+ for u in L:
+
+ # We remove u from the list of neighbors of S
+ if u in neighbors_of_S_in_V_minus_S:
+ neighbors_of_S_in_V_minus_S.remove(u)
+
+ # We add vertex u to the set S
+ S += [u]
+
+ # We add the out-neighbors of u to the list of neighbors of S
+ for v in G.neighbors_out(u):
+ if (not v in S) and (not v in neighbors_of_S_in_V_minus_S):
+ neighbors_of_S_in_V_minus_S += [v]
+
+ # We update the cost of the vertex separation
+ vsL = max( vsL, len(neighbors_of_S_in_V_minus_S) )
+
+ return vsL
+
+
+ def __vertex_separation_MILP__(self, D, integrality = False, verbosity = 0):
+ r"""
+ Implements an MILP for the vertex separation
+
+ INPUTS:
+
+ - ``D`` -- a digraph
+
+ OUTPUT:
+
+ A pair ``(cost, ordering)`` representing the optimal ordering of the
+ vertices and its cost.
+
+
+ VARIABLES:
+
+ x[v,t] = 1 if and only if the working lightpath of connection v is torn down at epoch t
+ So if an agent is put on vertex v at step t
+
+ y[v,t] = 1 if and only if the new lightpath of connection v is set up at epoch t
+ So if v is processed at step t
+
+ u[v,t] = 1 if and only if the connection corresponding to v is disrupted at epoch t
+ So if there is an agent on v at step t
+
+ z Objective = max number of concurrently disrupted connections
+
+ CONSTANT:
+ T maximum number of epoch/steps. T <= 2.N
+
+ TERMINOLOGY:
+ An epoch is the period of time between the establishment of 2 new lightpaths
+ So between the processing of 2 nodes
+ => 1 epoch per connection => #epochs = T = N
+
+ CONSTRAINTS:
+ (1) Minimize z
+ (2) x[v][t] <= x[v][t+1] for all v in V, and for t:=0..T-2
+ (3) y[v][t] <= y[v][t+1] for all v in V, and for t:=0..T-2
+ (miss) y[v][t] <= x[v][t] for all v in V, and for all t:=0..T-1
+ to ensure that once a vertex is processed, it has already been covered....
+ (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
+ (5) sum_{v in V} y[v][0] <= 1
+ (6) sum_{v in V} y[v][t+1] <= sum_{v in V} y[v][t] + 1 for t:=0..T-2
+ (7) sum_{v in V} y[v][T-1] = |V|
+ (8) u[v][t] >= x[v][t]-y[v][t] for all v in V, and for all t:=0..T-1
+ (9) z >= sum_{v in V} u[v][t] for all t:=0..T-1
+ (10) 0 <= x[v][t] and u[v][t] <= 1
+ Meaning both are between 0 and 1
+ (11) y[v][t] in {0,1}
+ (12) 0 <= z <= |V|
+ """
+ from sage.numerical.mip import MixedIntegerLinearProgram, Sum, MIPSolverException
+ p = MixedIntegerLinearProgram( maximization = False )
+
+ if integrality:
+ x = p.new_variable( integer = True, dim = 2 )
+ u = p.new_variable( integer = True, dim = 2 )
+ else:
+ x = p.new_variable( dim = 2 )
+ u = p.new_variable( dim = 2 )
+ y = p.new_variable( integer = True, dim = 2 )
+ z = p.new_variable( integer = True, dim = 1 )
+
+ N = D.num_verts()
+ T=N
+
+ # (2) x[v][t] <= x[v][t+1] for all v in V, and for t:=0..T-2
+ # (3) y[v][t] <= y[v][t+1] for all v in V, and for t:=0..T-2
+ for v in D.vertices():
+ for t in xrange(T-1):
+ p.add_constraint( x[v][t] - x[v][t+1], max = 0 )
+ p.add_constraint( y[v][t] - y[v][t+1], max = 0 )
+
+ # (miss) y[v][t] <= x[v][t] for all v in V, and for all t:=0..T-1
+ # for v in D.vertices():
+ # for t in range(T):
+ # p.add_constraint(y[v][t]-x[v][t],max=0)
+ #
+ # (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
+ for v in D.vertices():
+ for w in D.neighbors_out(v):
+ for t in xrange(T):
+ p.add_constraint( y[v][t] - x[w][t], max = 0 )
+
+ # (5) sum_{v in V} y[v][0] <= 1
+ p.add_constraint( Sum( y[v][0] for v in D.vertices() ), max = 1 )
+
+ # (6) sum_{v in V} y[v][t+1] <= sum_{v in V} y[v][t] + 1 for t:=0..T-2
+ for t in xrange(T-1):
+ p.add_constraint( Sum( y[v][t+1] - y[v][t] for v in D.vertices() ), max = 1 )
+
+ # (7) sum_{v in V} y[v][T-1] = |V|
+ p.add_constraint( Sum( y[v][T-1] for v in D.vertices() ), min = N )
+ p.add_constraint( Sum( y[v][T-1] for v in D.vertices() ), max = N )
+
+ # (8) u[v][t] >= x[v][t]-y[v][t] for all v in V, and for all t:=0..T-1
+ for v in D.vertices():
+ for t in xrange(T):
+ p.add_constraint( x[v][t] - y[v][t] - u[v][t], max = 0 )
+
+ # (9) z >= sum_{v in V} u[v][t] for all t:=0..T-1
+ for t in xrange(T):
+ p.add_constraint( Sum( u[v][t] for v in D.vertices() ) - z['z'], max = 0 )
+
+ # (10) 0 <= x[v][t] and u[v][t] <= 1
+ # (11) y[v][t] in {0,1}
+ for v in D.vertices():
+ for t in xrange(T):
+ p.add_constraint( x[v][t], min = 0 )
+ p.add_constraint( x[v][t], max = 1 )
+ p.add_constraint( u[v][t], min = 0 )
+ p.add_constraint( u[v][t], max = 1 )
+ p.set_binary( y[v][t] )
+ # (12) 0 <= z <= |V|
+ p.add_constraint( z['z'], min = 0 )
+ p.add_constraint( z['z'], max = N )
+
+ # (1) Minimize z
+ p.set_objective( z['z'] )
+
+ try:
+ obj = p.solve( log=verbosity )
+
+ taby = p.get_values( y )
+ tabz = p.get_values( z )
+ # since exactly one vertex is processed per epoch, we can reconstruct the sequence
+ seq = []
+ for t in xrange(T):
+ for v in D.vertices():
+ if (taby[v][t] > 0) and (not v in seq):
+ seq += [v]
+ vs = int(round( tabz['z'] ));
+
+ except MIPSolverException:
+ print "VSC NOT working with fractional stuff\n",D.edges(labels=False)
+ print " => try with integral version"
+ (vs,seq)=self.__vertex_separation_MILP__(D, True, verbosity)
+ print vs,seq
+ raise ValueError("Unbounded or unexpected error")
+
+ del p
+ return vs,seq;
+
+
+
+
+
+ def vertex_separation(self, G, method = 'exp'):
+ r"""
+ Returns an optimal ordering of the vertices and its cost for
+ vertex-separation.
+
+ INPUT:
+
+ - ``G`` -- a digraph
+
+ - ``method`` (string) -- the method to use
+
+ - 'exp' (default) -- Uses an algorithm with time and space
+ complexity in ``2^n``. Because of its current implementation, this
+ algorithm only works on graphs on less than 32 vertices. This can
+ be changed to 54 if necessary, but 32 vertices already require 4GB
+ of memory.
+
+ - 'MILP' -- Uses an MILP implementation.
+
+ OUTPUT:
+
+ A pair ``(cost, ordering)`` representing the optimal ordering of the
+ vertices and its cost.
+
+ EXAMPLE:
+
+ The vertex separation of a circuit is equal to 1::
+
+ sage: from sage.graphs.graph_decompositions.linear_ordering import LinearOrdering
+ sage: LO = LinearOrdering()
+ sage: g = digraphs.Circuit(6)
+ sage: LO.vertex_separation(g)
+ (1, [0, 1, 2, 3, 4, 5])
+
+ TEST:
+
+ Given anything else than a DiGraph::
+
+ sage: from sage.graphs.graph_decompositions.linear_ordering import LinearOrdering
+ sage: LO = LinearOrdering()
+ sage: g = graphs.CycleGraph(6)
+ sage: LO.vertex_separation(g)
+ Traceback (most recent call last):
+ ...
+ ValueError: The input parameter must be a DiGraph.
+ """
+ from sage.graphs.graph import DiGraph
+ if not isinstance(G, DiGraph):
+ raise ValueError("The input parameter must be a DiGraph.")
+
+ if method == 'MILP':
+ return self.__vertex_separation_MILP__(G)
+ else:
+ # default method
+ from sage.graphs.graph_decompositions import vertex_separation
+ return vertex_separation.vertex_separation(G)
+
+ def path_decomposition(self, G, method = 'exp'):
+ r"""
+ Returns the pathwidth of the given graph and the ordering of the
+ vertices resulting in a corresponding path decomposition.
+
+ INPUT:
+
+ - ``G`` -- a graph
+
+ - ``method`` (string) -- the method to use
+
+ - 'exp' (default) -- Uses an algorithm with time and space
+ complexity in ``2^n``. Because of its current implementation, this
+ algorithm only works on graphs on less than 32 vertices. This can
+ be changed to 54 if necessary, but 32 vertices already require 4GB
+ of memory.
+
+ - 'MILP' -- Uses an MILP implementation.
+
+ OUTPUT:
+
+ A pair ``(cost, ordering)`` representing the optimal ordering of the
+ vertices and its cost.
+
+ EXAMPLE:
+
+ The pathwidth of a circuit is equal to 2::
+
+ sage: from sage.graphs.graph_decompositions.linear_ordering import LinearOrdering
+ sage: LO = LinearOrdering()
+ sage: g = graphs.CycleGraph(6)
+ sage: LO.path_decomposition(g)
+ (2, [0, 1, 2, 3, 4, 5])
+
+ TEST:
+
+ Given anything else than a Graph::
+
+ sage: from sage.graphs.graph_decompositions.linear_ordering import LinearOrdering
+ sage: LO = LinearOrdering()
+ sage: g = digraphs.Circuit(6)
+ sage: LO.path_decomposition(g)
+ Traceback (most recent call last):
+ ...
+ ValueError: The input parameter must be a Graph.
+ """
+ from sage.graphs.graph import Graph
+ if not isinstance(G, Graph):
+ raise ValueError("The input parameter must be a Graph.")
+
+ if method == 'MILP':
+ from sage.graphs.graph import DiGraph
+ D = DiGraph( G.edges(labels = None) + [(v,u) for u,v in G.edges(labels = None)] )
+ return self.__vertex_separation_MILP__(D)
+ else:
+ # default method
+ from sage.graphs.graph_decompositions import vertex_separation
+ return vertex_separation.path_decomposition(G)
+
+
*