Path: blob/master/src/sage/graphs/graph_decompositions/vertex_separation.pyx
8815 views
r"""1Vertex separation23This module implements several algorithms to compute the vertex separation of a4digraph and the corresponding ordering of the vertices. It also implements tests5functions for evaluation the width of a linear ordering.67Given an ordering8`v_1,\cdots, v_n` of the vertices of `V(G)`, its *cost* is defined as:910.. MATH::1112c(v_1, ..., v_n) = \max_{1\leq i \leq n} c'(\{v_1, ..., v_i\})1314Where1516.. MATH::1718c'(S) = |N^+_G(S)\backslash S|1920The *vertex separation* of a digraph `G` is equal to the minimum cost of an21ordering of its vertices.2223**Vertex separation and pathwidth**2425The vertex separation is defined on a digraph, but one can obtain from a graph26`G` a digraph `D` with the same vertex set, and in which each edge `uv` of `G`27is replaced by two edges `uv` and `vu` in `D`. The vertex separation of `D` is28equal to the pathwidth of `G`, and the corresponding ordering of the vertices of29`D`, also called a *layout*, encodes an optimal path-decomposition of `G`.30This is a result of Kinnersley [Kin92]_ and Bodlaender [Bod98]_.313233**This module contains the following methods**3435.. csv-table::36:class: contentstable37:widths: 30, 7038:delim: |3940:meth:`path_decomposition` | Returns the pathwidth of the given graph and the ordering of the vertices resulting in a corresponding path decomposition41:meth:`vertex_separation` | Returns an optimal ordering of the vertices and its cost for vertex-separation42:meth:`vertex_separation_MILP` | Computes the vertex separation of `G` and the optimal ordering of its vertices using an MILP formulation43:meth:`lower_bound` | Returns a lower bound on the vertex separation of `G`44:meth:`is_valid_ordering` | Test if the linear vertex ordering `L` is valid for (di)graph `G`45:meth:`width_of_path_decomposition` | Returns the width of the path decomposition induced by the linear ordering `L` of the vertices of `G`464748Exponential algorithm for vertex separation49-------------------------------------------5051In order to find an optimal ordering of the vertices for the vertex separation,52this algorithm tries to save time by computing the function `c'(S)` **at most53once** once for each of the sets `S\subseteq V(G)`. These values are stored in54an array of size `2^n` where reading the value of `c'(S)` or updating it can be55done in constant (and small) time.5657Assuming that we can compute the cost of a set `S` and remember it, finding an58optimal ordering is an easy task. Indeed, we can think of the sequence `v_1,59..., v_n` of vertices as a sequence of *sets* `\{v_1\}, \{v_1,v_2\}, ...,60\{v_1,...,v_n\}`, whose cost is precisely `\max c'(\{v_1\}), c'(\{v_1,v_2\}),61... , c'(\{v_1,...,v_n\})`. Hence, when considering the digraph on the `2^n`62sets `S\subseteq V(G)` where there is an arc from `S` to `S'` if `S'=S\cap63\{v\}` for some `v` (that is, if the sets `S` and `S'` can be consecutive in a64sequence), an ordering of the vertices of `G` corresponds to a *path* from65`\emptyset` to `\{v_1,...,v_n\}`. In this setting, checking whether there exists66a ordering of cost less than `k` can be achieved by checking whether there67exists a directed path `\emptyset` to `\{v_1,...,v_n\}` using only sets of cost68less than `k`. This is just a depth-first-search, for each `k`.6970**Lazy evaluation of** `c'`7172In the previous algorithm, most of the time is actually spent on the computation73of `c'(S)` for each set `S\subseteq V(G)` -- i.e. `2^n` computations of74neighborhoods. This can be seen as a huge waste of time when noticing that it is75useless to know that the value `c'(S)` for a set `S` is less than `k` if all the76paths leading to `S` have a cost greater than `k`. For this reason, the value of77`c'(S)` is computed lazily during the depth-first search. Explanation :7879When the depth-first search discovers a set of size less than `k`, the costs of80its out-neighbors (the potential sets that could follow it in the optimal81ordering) are evaluated. When an out-neighbor is found that has a cost smaller82than `k`, the depth-first search continues with this set, which is explored with83the hope that it could lead to a path toward `\{v_1,...,v_n\}`. On the other84hand, if an out-neighbour has a cost larger than `k` it is useless to attempt to85build a cheap sequence going though this set, and the exploration stops86there. This way, a large number of sets will never be evaluated and *a lot* of87computational time is saved this way.8889Besides, some improvement is also made by "improving" the values found by90`c'`. Indeed, `c'(S)` is a lower bound on the cost of a sequence containing the91set `S`, but if all out-neighbors of `S` have a cost of `c'(S) + 5` then one92knows that having `S` in a sequence means a total cost of at least `c'(S) +935`. For this reason, for each set `S` we store the value of `c'(S)`, and replace94it by `\max (c'(S), \min_{\text{next}})` (where `\min_{\text{next}}` is the95minimum of the costs of the out-neighbors of `S`) once the costs of these96out-neighbors have been evaluated by the algrithm.9798.. NOTE::99100Because of its current implementation, this algorithm only works on graphs101on less than 32 vertices. This can be changed to 64 if necessary, but 32102vertices already require 4GB of memory. Running it on 64 bits is not103expected to be doable by the computers of the next decade `:-D`104105**Lower bound on the vertex separation**106107One can obtain a lower bound on the vertex separation of a graph in exponential108time but *small* memory by computing once the cost of each set `S`. Indeed, the109cost of a sequence `v_1, ..., v_n` corresponding to sets `\{v_1\}, \{v_1,v_2\},110..., \{v_1,...,v_n\}` is111112.. MATH::113114\max c'(\{v_1\}),c'(\{v_1,v_2\}),...,c'(\{v_1,...,v_n\})\geq\max c'_1,...,c'_n115116where `c_i` is the minimum cost of a set `S` on `i` vertices. Evaluating the117`c_i` can take time (and in particular more than the previous exact algorithm),118but it does not need much memory to run.119120121MILP formulation for the vertex separation122------------------------------------------123124We describe below a mixed integer linear program (MILP) for determining an125optimal layout for the vertex separation of `G`, which is an improved version of126the formulation proposed in [SP10]_. It aims at building a sequence `S_t` of127sets such that an ordering `v_1, ..., v_n` of the vertices correspond to128`S_0=\{v_1\}, S_2=\{v_1,v_2\}, ..., S_{n-1}=\{v_1,...,v_n\}`.129130**Variables:**131132133- `y_v^t` -- Variable set to 1 if `v\in S_t`, and 0 otherwise. The order of134`v` in the layout is the smallest `t` such that `y_v^t==1`.135136- `u_v^t` -- Variable set to 1 if `v\not \in S_t` and `v` has an in-neighbor in137`S_t`. It is set to 0 otherwise.138139- `x_v^t` -- Variable set to 1 if either `v\in S_t` or if `v` has an in-neighbor140in `S_t`. It is set to 0 otherwise.141142- `z` -- Objective value to minimize. It is equal to the maximum over all step143`t` of the number of vertices such that `u_v^t==1`.144145**MILP formulation:**146147.. MATH::148:nowrap:149150\begin{alignat}{2}151\intertext{Minimize:}152&z&\\153\intertext{Such that:}154x_v^t &\leq x_v^{t+1}& \forall v\in V,\ 0\leq t\leq n-2\\155y_v^t &\leq y_v^{t+1}& \forall v\in V,\ 0\leq t\leq n-2\\156y_v^t &\leq x_w^t& \forall v\in V,\ \forall w\in N^+(v),\ 0\leq t\leq n-1\\157\sum_{v \in V} y_v^{t} &= t+1& 0\leq t\leq n-1\\158x_v^t-y_v^t&\leq u_v^t & \forall v \in V,\ 0\leq t\leq n-1\\159\sum_{v \in V} u_v^t &\leq z& 0\leq t\leq n-1\\1600 \leq x_v^t &\leq 1& \forall v\in V,\ 0\leq t\leq n-1\\1610 \leq u_v^t &\leq 1& \forall v\in V,\ 0\leq t\leq n-1\\162y_v^t &\in \{0,1\}& \forall v\in V,\ 0\leq t\leq n-1\\1630 \leq z &\leq n&164\end{alignat}165166The vertex separation of `G` is given by the value of `z`, and the order of167vertex `v` in the optimal layout is given by the smallest `t` for which168`y_v^t==1`.169170REFERENCES171----------172173.. [Bod98] *A partial k-arboretum of graphs with bounded treewidth*, Hans174L. Bodlaender, Theoretical Computer Science 209(1-2):1-45, 1998.175176.. [Kin92] *The vertex separation number of a graph equals its path-width*,177Nancy G. Kinnersley, Information Processing Letters 42(6):345-350, 1992.178179.. [SP10] *Lightpath Reconfiguration in WDM networks*, Fernando Solano and180Michal Pioro, IEEE/OSA Journal of Optical Communication and Networking1812(12):1010-1021, 2010.182183184AUTHORS185-------186187- Nathann Cohen (2011-10): Initial version and exact exponential algorithm188189- David Coudert (2012-04): MILP formulation and tests functions190191192193METHODS194-------195"""196197include 'sage/ext/stdsage.pxi'198include 'sage/ext/cdefs.pxi'199include 'sage/ext/interrupt.pxi'200include 'fast_digraph.pyx'201from libc.stdint cimport uint8_t, int8_t202203#*****************************************************************************204# Copyright (C) 2011 Nathann Cohen <[email protected]>205#206# Distributed under the terms of the GNU General Public License (GPL)207# http://www.gnu.org/licenses/208#*****************************************************************************209210###############211# Lower Bound #212###############213214def lower_bound(G):215r"""216Returns a lower bound on the vertex separation of `G`217218INPUT:219220- ``G`` -- a digraph221222OUTPUT:223224A lower bound on the vertex separation of `D` (see the module's225documentation).226227.. NOTE::228229This method runs in exponential time but has no memory constraint.230231232EXAMPLE:233234On a circuit::235236sage: from sage.graphs.graph_decompositions.vertex_separation import lower_bound237sage: g = digraphs.Circuit(6)238sage: lower_bound(g)2391240241TEST:242243Given anything else than a DiGraph::244245sage: from sage.graphs.graph_decompositions.vertex_separation import lower_bound246sage: g = graphs.CycleGraph(5)247sage: lower_bound(g)248Traceback (most recent call last):249...250ValueError: The parameter must be a DiGraph.251"""252from sage.graphs.digraph import DiGraph253if not isinstance(G, DiGraph):254raise ValueError("The parameter must be a DiGraph.")255256if G.order() >= 32:257raise ValueError("The graph can have at most 31 vertices.")258259cdef FastDigraph FD = FastDigraph(G)260cdef int * g = FD.graph261cdef int n = FD.n262263# minimums[i] is means to store the value of c'_{i+1}264minimums = <uint8_t *> sage_malloc(sizeof(uint8_t)* n)265cdef unsigned int i266267# They are initialized to n268for 0<= i< n:269minimums[i] = n270271cdef uint8_t tmp, tmp_count272273# We go through all sets274for 1<= i< <unsigned int> (1<<n):275tmp_count = <uint8_t> popcount32(i)276tmp = <uint8_t> compute_out_neighborhood_cardinality(FD, i)277278# And update the costs279minimums[tmp_count-1] = minimum(minimums[tmp_count-1], tmp)280281# We compute the maximum of all those values282for 1<= i< n:283minimums[0] = maximum(minimums[0], minimums[i])284285cdef int min = minimums[0]286287sage_free(minimums)288289return min290291################################292# Exact exponential algorithms #293################################294295def path_decomposition(G, algorithm = "exponential", verbose = False):296r"""297Returns the pathwidth of the given graph and the ordering of the vertices298resulting in a corresponding path decomposition.299300INPUT:301302- ``G`` -- a digraph303304- ``algorithm`` -- (default: ``"exponential"``) Specify the algorithm to use305among306307- ``exponential`` -- Use an exponential time and space algorithm. This308algorithm only works of graphs on less than 32 vertices.309310- ``MILP`` -- Use a mixed integer linear programming formulation. This311algorithm has no size restriction but could take a very long time.312313- ``verbose`` (boolean) -- whether to display information on the314computations.315316OUTPUT:317318A pair ``(cost, ordering)`` representing the optimal ordering of the319vertices and its cost.320321.. NOTE::322323Because of its current implementation, this exponential algorithm only324works on graphs on less than 32 vertices. This can be changed to 54 if325necessary, but 32 vertices already require 4GB of memory.326327EXAMPLE:328329The vertex separation of a cycle is equal to 2::330331sage: from sage.graphs.graph_decompositions.vertex_separation import path_decomposition332sage: g = graphs.CycleGraph(6)333sage: pw, L = path_decomposition(g); pw3342335sage: pwm, Lm = path_decomposition(g, algorithm = "MILP"); pwm3362337338TEST:339340Given anything else than a Graph::341342sage: from sage.graphs.graph_decompositions.vertex_separation import path_decomposition343sage: g = digraphs.Circuit(6)344sage: path_decomposition(g)345Traceback (most recent call last):346...347ValueError: The parameter must be a Graph.348"""349from sage.graphs.graph import Graph350if not isinstance(G, Graph):351raise ValueError("The parameter must be a Graph.")352353from sage.graphs.digraph import DiGraph354if algorithm == "exponential":355return vertex_separation(DiGraph(G), verbose = verbose)356else:357return vertex_separation_MILP(DiGraph(G), verbosity = (1 if verbose else 0))358359360def vertex_separation(G, verbose = False):361r"""362Returns an optimal ordering of the vertices and its cost for363vertex-separation.364365INPUT:366367- ``G`` -- a digraph368369- ``verbose`` (boolean) -- whether to display information on the370computations.371372OUTPUT:373374A pair ``(cost, ordering)`` representing the optimal ordering of the375vertices and its cost.376377.. NOTE::378379Because of its current implementation, this algorithm only works on380graphs on less than 32 vertices. This can be changed to 54 if necessary,381but 32 vertices already require 4GB of memory.382383EXAMPLE:384385The vertex separation of a circuit is equal to 1::386387sage: from sage.graphs.graph_decompositions.vertex_separation import vertex_separation388sage: g = digraphs.Circuit(6)389sage: vertex_separation(g)390(1, [0, 1, 2, 3, 4, 5])391392TEST:393394Given anything else than a DiGraph::395396sage: from sage.graphs.graph_decompositions.vertex_separation import lower_bound397sage: g = graphs.CycleGraph(5)398sage: lower_bound(g)399Traceback (most recent call last):400...401ValueError: The parameter must be a DiGraph.402403Graphs with non-integer vertices::404405sage: from sage.graphs.graph_decompositions.vertex_separation import vertex_separation406sage: D=digraphs.DeBruijn(2,3)407sage: vertex_separation(D)408(2, ['000', '001', '100', '010', '101', '011', '110', '111'])409"""410from sage.graphs.digraph import DiGraph411if not isinstance(G, DiGraph):412raise ValueError("The parameter must be a DiGraph.")413414if G.order() >= 32:415raise ValueError("The graph should have at most 31 vertices !")416417cdef FastDigraph g = FastDigraph(G)418419if verbose:420print "Memory allocation"421g.print_adjacency_matrix()422423sig_on()424425cdef unsigned int mem = 1 << g.n426cdef int8_t * neighborhoods = <int8_t *> sage_malloc(mem)427428if neighborhoods == NULL:429sig_off()430raise MemoryError("Error allocating memory. I just tried to allocate "+str(mem>>10)+"MB, could that be too much ?")431432memset(neighborhoods, <int8_t> -1, mem)433434cdef int i,j , k435for 0 <= k <g.n:436if verbose:437print "Looking for a strategy of cost", str(k)438439if exists(g, neighborhoods, 0, k) <= k:440break441442if verbose:443print "... Found !"444print "Now computing the ordering"445446cdef list order = find_order(g, neighborhoods, k)447448# Relabelling the vertices449cdef list vertices = G.vertices()450for i, j in enumerate(order):451order[i] = vertices[j]452453sage_free(neighborhoods)454sig_off()455456return k, order457458##############################################################################459# Actual algorithm, breadh-first search and updates of the costs of the sets #460##############################################################################461462# Check whether an ordering with the given cost exists, and updates data in the463# neighborhoods array at the same time. See the module's documentation464465cdef inline int exists(FastDigraph g, int8_t * neighborhoods, int current, int cost):466467# If this is true, it means the set has not been evaluated yet468if neighborhoods[current] < 0:469neighborhoods[current] = compute_out_neighborhood_cardinality(g, current)470471# If the cost of this set is too high, there is no point in going further.472# Same thing if the current set is the whole vertex set.473if neighborhoods[current] > cost or (current == (1<<g.n)-1):474return neighborhoods[current]475476# Minimum of the costs of the outneighbors477cdef int mini = g.n478479cdef int i480cdef int next_set481482483for 0<= i<g.n:484if (current >> i)&1:485continue486487# For each of the out-neighbors next_set of current488489next_set = current | 1<<i490491# Check whether there exists a cheap path toward {1..n}, and updated the492# cost.493mini = minimum(mini, exists(g, neighborhoods, next_set, cost))494495# We have found a path !496if mini <= cost:497return mini498499# Updating the cost of the current set with the minimum of the cost of its500# outneighbors.501neighborhoods[current] = mini502503return neighborhoods[current]504505# Returns the ordering once we are sure it exists506cdef list find_order(FastDigraph g, int8_t * neighborhoods, int cost):507cdef list ordering = []508cdef int current = 0509cdef int n = g.n510cdef int i511512while n:513# We look for n vertices514515for 0<= i<g.n:516if (current >> i)&1:517continue518519# Find the next set with small cost (we know it exists)520next_set = current | 1<<i521if neighborhoods[next_set] <= cost:522ordering.append(i)523current = next_set524break525526# One less to find527n -= 1528529return ordering530531# Min/Max functions532533cdef inline int minimum(int a, int b):534if a<b:535return a536else:537return b538539cdef inline int maximum(int a, int b):540if a>b:541return a542else:543return b544545546#################################################################547# Function for testing the validity of a linear vertex ordering #548#################################################################549550def is_valid_ordering(G, L):551r"""552Test if the linear vertex ordering `L` is valid for (di)graph `G`.553554A linear ordering `L` of the vertices of a (di)graph `G` is valid if all555vertices of `G` are in `L`, and if `L` contains no other vertex and no556duplicated vertices.557558INPUT:559560- ``G`` -- a Graph or a DiGraph.561562- ``L`` -- an ordered list of the vertices of ``G``.563564565OUTPUT:566567Returns ``True`` if `L` is a valid vertex ordering for `G`, and ``False``568oterwise.569570571EXAMPLE:572573Path decomposition of a cycle::574575sage: from sage.graphs.graph_decompositions import vertex_separation576sage: G = graphs.CycleGraph(6)577sage: L = [u for u in G.vertices()]578sage: vertex_separation.is_valid_ordering(G, L)579True580sage: vertex_separation.is_valid_ordering(G, [1,2])581False582583TEST:584585Giving anything else than a Graph or a DiGraph::586587sage: from sage.graphs.graph_decompositions import vertex_separation588sage: vertex_separation.is_valid_ordering(2, [])589Traceback (most recent call last):590...591ValueError: The input parameter must be a Graph or a DiGraph.592593Giving anything else than a list::594595sage: from sage.graphs.graph_decompositions import vertex_separation596sage: G = graphs.CycleGraph(6)597sage: vertex_separation.is_valid_ordering(G, {})598Traceback (most recent call last):599...600ValueError: The second parameter must be of type 'list'.601"""602from sage.graphs.graph import Graph603from sage.graphs.digraph import DiGraph604if not isinstance(G, Graph) and not isinstance(G, DiGraph):605raise ValueError("The input parameter must be a Graph or a DiGraph.")606if not isinstance(L, list):607raise ValueError("The second parameter must be of type 'list'.")608609return set(L) == set(G.vertices())610611612####################################################################613# Measurement functions of the widths of some graph decompositions #614####################################################################615616def width_of_path_decomposition(G, L):617r"""618Returns the width of the path decomposition induced by the linear ordering619`L` of the vertices of `G`.620621If `G` is an instance of :mod:`Graph <sage.graphs.graph>`, this function622returns the width `pw_L(G)` of the path decomposition induced by the linear623ordering `L` of the vertices of `G`. If `G` is a :mod:`DiGraph624<sage.graphs.digraph>`, it returns instead the width `vs_L(G)` of the625directed path decomposition induced by the linear ordering `L` of the626vertices of `G`, where627628.. MATH::629630vs_L(G) & = \max_{0\leq i< |V|-1} | N^+(L[:i])\setminus L[:i] |\\631pw_L(G) & = \max_{0\leq i< |V|-1} | N(L[:i])\setminus L[:i] |\\632633INPUT:634635- ``G`` -- a Graph or a DiGraph636637- ``L`` -- a linear ordering of the vertices of ``G``638639EXAMPLES:640641Path decomposition of a cycle::642643sage: from sage.graphs.graph_decompositions import vertex_separation644sage: G = graphs.CycleGraph(6)645sage: L = [u for u in G.vertices()]646sage: vertex_separation.width_of_path_decomposition(G, L)6472648649Directed path decomposition of a circuit::650651sage: from sage.graphs.graph_decompositions import vertex_separation652sage: G = digraphs.Circuit(6)653sage: L = [u for u in G.vertices()]654sage: vertex_separation.width_of_path_decomposition(G, L)6551656657TESTS:658659Path decomposition of a BalancedTree::660661sage: from sage.graphs.graph_decompositions import vertex_separation662sage: G = graphs.BalancedTree(3,2)663sage: pw, L = vertex_separation.path_decomposition(G)664sage: pw == vertex_separation.width_of_path_decomposition(G, L)665True666sage: L.reverse()667sage: pw == vertex_separation.width_of_path_decomposition(G, L)668False669670Directed path decomposition of a circuit::671672sage: from sage.graphs.graph_decompositions import vertex_separation673sage: G = digraphs.Circuit(8)674sage: vs, L = vertex_separation.vertex_separation(G)675sage: vs == vertex_separation.width_of_path_decomposition(G, L)676True677sage: L = [0,4,6,3,1,5,2,7]678sage: vs == vertex_separation.width_of_path_decomposition(G, L)679False680681Giving a wrong linear ordering::682683sage: from sage.graphs.graph_decompositions import vertex_separation684sage: G = Graph()685sage: vertex_separation.width_of_path_decomposition(G, ['a','b'])686Traceback (most recent call last):687...688ValueError: The input linear vertex ordering L is not valid for G.689"""690if not is_valid_ordering(G, L):691raise ValueError("The input linear vertex ordering L is not valid for G.")692693vsL = 0694S = set()695neighbors_of_S_in_V_minus_S = set()696697for u in L:698699# We remove u from the neighbors of S700neighbors_of_S_in_V_minus_S.discard(u)701702# We add vertex u to the set S703S.add(u)704705if G._directed:706Nu = G.neighbors_out(u)707else:708Nu = G.neighbors(u)709710# We add the (out-)neighbors of u to the neighbors of S711for v in Nu:712if (not v in S):713neighbors_of_S_in_V_minus_S.add(v)714715# We update the cost of the vertex separation716vsL = max( vsL, len(neighbors_of_S_in_V_minus_S) )717718return vsL719720721##########################################722# MILP formulation for vertex separation #723##########################################724725def vertex_separation_MILP(G, integrality = False, solver = None, verbosity = 0):726r"""727Computes the vertex separation of `G` and the optimal ordering of its728vertices using an MILP formulation.729730This function uses a mixed integer linear program (MILP) for determining an731optimal layout for the vertex separation of `G`. This MILP is an improved732version of the formulation proposed in [SP10]_. See the :mod:`module's733documentation <sage.graphs.graph_decompositions.vertex_separation>` for more734details on this MILP formulation.735736INPUTS:737738- ``G`` -- a DiGraph739740- ``integrality`` -- (default: ``False``) Specify if variables `x_v^t` and741`u_v^t` must be integral or if they can be relaxed. This has no impact on742the validity of the solution, but it is sometimes faster to solve the743problem using binary variables only.744745- ``solver`` -- (default: ``None``) Specify a Linear Program (LP) solver to746be used. If set to ``None``, the default one is used. For more information747on LP solvers and which default solver is used, see the method748:meth:`solve<sage.numerical.mip.MixedIntegerLinearProgram.solve>` of the749class750:class:`MixedIntegerLinearProgram<sage.numerical.mip.MixedIntegerLinearProgram>`.751752- ``verbose`` -- integer (default: ``0``). Sets the level of verbosity. Set753to 0 by default, which means quiet.754755OUTPUT:756757A pair ``(cost, ordering)`` representing the optimal ordering of the758vertices and its cost.759760EXAMPLE:761762Vertex separation of a De Bruijn digraph::763764sage: from sage.graphs.graph_decompositions import vertex_separation765sage: G = digraphs.DeBruijn(2,3)766sage: vs, L = vertex_separation.vertex_separation_MILP(G); vs7672768sage: vs == vertex_separation.width_of_path_decomposition(G, L)769True770sage: vse, Le = vertex_separation.vertex_separation(G); vse7712772773The vertex separation of a circuit is 1::774775sage: from sage.graphs.graph_decompositions import vertex_separation776sage: G = digraphs.Circuit(6)777sage: vs, L = vertex_separation.vertex_separation_MILP(G); vs7781779780TESTS:781782Comparison with exponential algorithm::783784sage: from sage.graphs.graph_decompositions import vertex_separation785sage: for i in range(10):786... G = digraphs.RandomDirectedGNP(10, 0.2)787... ve, le = vertex_separation.vertex_separation(G)788... vm, lm = vertex_separation.vertex_separation_MILP(G)789... if ve != vm:790... print "The solution is not optimal!"791792Comparison with Different values of the integrality parameter::793794sage: from sage.graphs.graph_decompositions import vertex_separation795sage: for i in range(10): # long time (11s on sage.math, 2012)796... G = digraphs.RandomDirectedGNP(10, 0.2)797... va, la = vertex_separation.vertex_separation_MILP(G, integrality = False)798... vb, lb = vertex_separation.vertex_separation_MILP(G, integrality = True)799... if va != vb:800... print "The integrality parameter change the result!"801802Giving anything else than a DiGraph::803804sage: from sage.graphs.graph_decompositions import vertex_separation805sage: vertex_separation.vertex_separation_MILP([])806Traceback (most recent call last):807...808ValueError: The first input parameter must be a DiGraph.809"""810from sage.graphs.digraph import DiGraph811if not isinstance(G, DiGraph):812raise ValueError("The first input parameter must be a DiGraph.")813814from sage.numerical.mip import MixedIntegerLinearProgram, MIPSolverException815p = MixedIntegerLinearProgram( maximization = False, solver = solver )816817# Declaration of variables.818x = p.new_variable( binary = integrality, dim = 2 )819u = p.new_variable( binary = integrality, dim = 2 )820y = p.new_variable( binary = True, dim = 2 )821z = p.new_variable( integer = True, dim = 1 )822823N = G.num_verts()824V = G.vertices()825826# (2) x[v][t] <= x[v][t+1] for all v in V, and for t:=0..N-2827# (3) y[v][t] <= y[v][t+1] for all v in V, and for t:=0..N-2828for v in V:829for t in xrange(N-1):830p.add_constraint( x[v][t] - x[v][t+1] <= 0 )831p.add_constraint( y[v][t] - y[v][t+1] <= 0 )832833# (4) y[v][t] <= x[w][t] for all v in V, for all w in N^+(v), and for all t:=0..N-1834for v in V:835for w in G.neighbors_out(v):836for t in xrange(N):837p.add_constraint( y[v][t] - x[w][t] <= 0 )838839# (5) sum_{v in V} y[v][t] == t+1 for t:=0..N-1840for t in xrange(N):841p.add_constraint( p.sum([ y[v][t] for v in V ]) == t+1 )842843# (6) u[v][t] >= x[v][t]-y[v][t] for all v in V, and for all t:=0..N-1844for v in V:845for t in xrange(N):846p.add_constraint( x[v][t] - y[v][t] - u[v][t] <= 0 )847848# (7) z >= sum_{v in V} u[v][t] for all t:=0..N-1849for t in xrange(N):850p.add_constraint( p.sum([ u[v][t] for v in V ]) - z['z'] <= 0 )851852# (8)(9) 0 <= x[v][t] and u[v][t] <= 1853if not integrality:854for v in V:855for t in xrange(N):856p.add_constraint( 0 <= x[v][t] <= 1 )857p.add_constraint( 0 <= u[v][t] <= 1 )858859# (10) y[v][t] in {0,1}860p.set_binary( y )861862# (11) 0 <= z <= |V|863p.add_constraint( z['z'] <= N )864865# (1) Minimize z866p.set_objective( z['z'] )867868try:869obj = p.solve( log=verbosity )870except MIPSolverException:871if integrality:872raise ValueError("Unbounded or unexpected error")873else:874raise ValueError("Unbounded or unexpected error. Try with 'integrality = True'.")875876taby = p.get_values( y )877tabz = p.get_values( z )878# since exactly one vertex is processed per step, we can reconstruct the sequence879seq = []880for t in xrange(N):881for v in V:882if (taby[v][t] > 0) and (not v in seq):883seq.append(v)884break885vs = int(round( tabz['z'] ))886887return vs, seq888889890