# Ticket #12716: trac_12716-review.patch

File trac_12716-review.patch, 9.0 KB (added by ncohen, 8 years ago)
• ## sage/graphs/graph_decompositions/vertex_separation.pyx

# HG changeset patch
# User Nathann Cohen <nathann.cohen@gmail.com>
# Date 1334072918 -7200
# Node ID de6bc52cf4a69cc72a8dd2aa4087d0ffcb07c1bb
# Parent  cb71d94752d432671881ce3bfbcf78f9d8248379
MILP formulation and test functions for vertex separation -- reviewer's patch

diff --git a/sage/graphs/graph_decompositions/vertex_separation.pyx b/sage/graphs/graph_decompositions/vertex_separation.pyx
 a This module implements several algorithms to compute the vertex separation of a digraph and the corresponding ordering of the vertices. It also implements tests functions for evaluation the width of a linear orderings. functions for evaluation the width of a linear ordering. Given an ordering v_1,\cdots, v_n of the vertices of V(G), its *cost* is defined as: c(v_1, ..., v_n) = \max_{1\leq i \leq n} c'(\{v_1, ..., v_i\}) Where Where .. MATH:: Because of its current implementation, this algorithm only works on graphs on less than 32 vertices. This can be changed to 64 if necessary, but 32 vertices already require 4GB of memory. vertices already require 4GB of memory. Running it on 64 bits is not expected to be doable by the computers of the next decade :-D **Lower bound on the vertex separation** MILP formulation for the vertex separation ------------------------------------------ We describe bellow a mixed integer linear program (MILP) for determining an optimal layout for the vertex separation of G. This MILP is an improved version of the formulation proposed in [SP10]_. We describe below a mixed integer linear program (MILP) for determining an optimal layout for the vertex separation of G, which is an improved version of the formulation proposed in [SP10]_. It aims at building a sequence S_t of sets such that an ordering v_1, ..., v_n of the vertices correspond to S_0=\{v_1\}, S_2=\{v_1,v_2\}, ..., S_{n-1}=\{v_1,...,v_n\}. VARIABLES: **Variables:** - x_v^t -- Variable set to 1 if either v has an in-neighbor u such that u\in S_{t'}=\{v_1, v_2,\cdots, v_{t'}\} and v\in N_G^+(S_{t'})\backslash S_{t'} with t'\leq t, or v\in S_t. It is set to 0 otherwise. - y_v^t -- Variable set to 1 if v\in S_t, and 0 otherwise. The order of - y_v^t -- Variable set to 1 if v\in S_t, and 0 otherwise. The order of v in the layout is the smallest t such that y_v^t==1. - u_v^t -- Variable set to 1 if v\in N_G^+(S_t)\backslash S_t has an in-neighbor in S_t, and set to 0 otherwise. - u_v^t -- Variable set to 1 if v\not \in S_t and v has an in-neighbor in S_t. It is set to 0 otherwise. - z -- Objective value to minimize. It is equal to the maximum over all step - x_v^t -- is morally equal to "y_v^t+u_v^t". It is set to 1 if either v\in S_t or if v has an in-neighbor in S_t. It is set to 0 otherwise. - z -- Objective value to minimize. It is equal to the maximum over all step t of the number of vertices such that u_v^t==1. MILP formulation: **MILP formulation:** .. MATH:: :nowrap: vertex v in the optimal layout is given by the smallest t for which y_v^t==1. REFERENCES ---------- EXAMPLE: On a cycle:: On a circuit:: sage: from sage.graphs.graph_decompositions.vertex_separation import lower_bound sage: g = digraphs.Circuit(6) sage_free(neighborhoods) _sig_off _sig_off return k, order OUTPUT: Returns True if L is a valid vertex ordering for G, and False Returns True if L is a valid vertex ordering for G, and False oterwise. if not isinstance(L, list): raise ValueError("The second parameter must be of type 'list'.") return len( set( G.vertices() ).symmetric_difference( set(L) ) ) == 0 return set(L) == set(G.vertices()) #################################################################### Returns the width of the path decomposition induced by the linear ordering L of the vertices of G. If G is an instance of Graph, this function returns the width pw_L(G) of the path decomposition induced by the linear ordering L of the vertices of G. If G is a DiGraph, it returns instead the width vs_L(G) of the If G is an instance of :mod:Graph , this function returns the width pw_L(G) of the path decomposition induced by the linear ordering L of the vertices of G. If G is a :mod:DiGraph , it returns instead the width vs_L(G) of the directed path decomposition induced by the linear ordering L of the vertices of G, where raise ValueError("The input linear vertex ordering L is not valid for G.") vsL = 0 S = [] neighbors_of_S_in_V_minus_S = [] S = set() neighbors_of_S_in_V_minus_S = set() 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 remove u from the neighbors of S neighbors_of_S_in_V_minus_S.discard(u) # We add vertex u to the set S S.append(u) S.add(u) if G._directed: Nu = G.neighbors_out(u) else: Nu = G.neighbors(u) # We add the (out-)neighbors of u to the list of neighbors of S # We add the (out-)neighbors of u to the neighbors of S for v in Nu: if (not v in S) and (not v in neighbors_of_S_in_V_minus_S): neighbors_of_S_in_V_minus_S.append(v) if (not v in S): neighbors_of_S_in_V_minus_S.add(v) # We update the cost of the vertex separation vsL = max( vsL, len(neighbors_of_S_in_V_minus_S) ) This function uses a mixed integer linear program (MILP) for determining an optimal layout for the vertex separation of G. This MILP is an improved version of the formulation proposed in [SP10]_. See the module's documentation for more details on this MILP formulation. version of the formulation proposed in [SP10]_. See the :mod:module's documentation  for more details on this MILP formulation. INPUTS: p = MixedIntegerLinearProgram( maximization = False, solver = solver ) # Declaration of variables. x = p.new_variable( integer = integrality, dim = 2 ) u = p.new_variable( integer = integrality, dim = 2 ) y = p.new_variable( integer = True, dim = 2 ) x = p.new_variable( binary = integrality, dim = 2 ) u = p.new_variable( binary = integrality, dim = 2 ) y = p.new_variable( binary = True, dim = 2 ) z = p.new_variable( integer = True, dim = 1 ) N = G.num_verts() # (10) 0 <= x[v][t] and u[v][t] <= 1 # (11) y[v][t] in {0,1} for v in V: for t in xrange(N): 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] ) if not integrality: for v in V: for t in xrange(N): 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 ) # (11) y[v][t] in {0,1} p.set_binary( y ) # (12) 0 <= z <= |V| p.add_constraint( z['z'], min = 0 ) p.add_constraint( z['z'], max = N ) #  (1) Minimize z try: obj = p.solve( log=verbosity ) taby = p.get_values( y ) tabz = p.get_values( z ) # since exactly one vertex is processed per step, we can reconstruct the sequence seq = [] for t in xrange(N): for v in V: if (taby[v][t] > 0) and (not v in seq): seq.append(v) break vs = int(round( tabz['z'] )) except MIPSolverException: if integrality: raise ValueError("Unbounded or unexpected error") else: raise ValueError("Unbounded or unexpected error. Try with 'integrality = True'.") del p taby = p.get_values( y ) tabz = p.get_values( z ) # since exactly one vertex is processed per step, we can reconstruct the sequence seq = [] for t in xrange(N): for v in V: if (taby[v][t] > 0) and (not v in seq): seq.append(v) break vs = int(round( tabz['z'] )) return vs, seq