Ticket #12716: trac_12716_MILP.patch

File trac_12716_MILP.patch, 21.9 KB (added by dcoudert, 8 years ago)
  • sage/graphs/graph_decompositions/vertex_separation.pyx

    # HG changeset patch
    # User dcoudert <david.coudert@inria.fr>
    # Date 1333889402 -7200
    # Node ID 70b3aa8dd7faf486e7370c2e517e74a9928c8b54
    # Parent  3daf3f6b8ad89033a3d19dfe1de3790bedf5042f
    Trac #12716 -- MILP formulation and test functions for vertex separation
    
    diff --git a/sage/graphs/graph_decompositions/vertex_separation.pyx b/sage/graphs/graph_decompositions/vertex_separation.pyx
    a b  
    11r"""
    22Vertex separation
    33
    4 This module implements several algorithms to compute the vertex
    5 separation of a digraph and the corresponding ordering of the
    6 vertices. Given an ordering `v_1, ..., v_n` of the vertices of `V(G)`,
    7 its *cost* is defined as:
     4This module implements several algorithms to compute the vertex separation of a
     5digraph and the corresponding ordering of the vertices. It also implements tests
     6functions for evaluation the width of a linear orderings.
     7
     8Given an ordering
     9`v_1,\cdots, v_n` of the vertices of `V(G)`, its *cost* is defined as:
    810
    911.. MATH::
    1012
     
    1618
    1719    c'(S) = |N^+_G(S)\backslash S|
    1820
    19 The *vertex separation* of a digraph `G` is equal to the minimum cost
    20 of an ordering of its vertices.
     21The *vertex separation* of a digraph `G` is equal to the minimum cost of an
     22ordering of its vertices.
    2123
    2224**Vertex separation and pathwidth**
    2325
     
    2527`G` a digraph `D` with the same vertex set, and in which each edge `uv` of `G`
    2628is replaced by two edges `uv` and `vu` in `D`. The vertex separation of `D` is
    2729equal to the pathwidth of `G`, and the corresponding ordering of the vertices of
    28 `D` encodes an optimal path-decomposition of `G`.
    29 
     30`D`, also called a *layout*, encodes an optimal path-decomposition of `G`.
    3031This is a result of Kinnersley [Kin92]_ and Bodlaender [Bod98]_.
    3132
    32 **References**
    3333
    34 .. [Kin92] *The vertex separation number of a graph equals its path-width*,
    35   Nancy G. Kinnersley,
    36   Information Processing Letters,
    37   Volume 42, Issue 6, Pages 345-350,
    38   24 July 1992
     34**This module contains the following methods**
    3935
    40 .. [Bod98] *A partial k-arboretum of graphs with bounded treewidth*,
    41   Hans L. Bodlaender,
    42   Theoretical Computer Science,
    43   Volume 209, Issues 1-2, Pages 1-45,
    44   6 December 1998
     36===================================  ===========================================
    4537
    46 **Authors**
     38:meth:`path_decomposition`           Returns the pathwidth of the given graph
     39                                     and the ordering of the vertices resulting
     40                                     in a corresponding path decomposition
    4741
    48     - Nathann Cohen
     42:meth:`vertex_separation`            Returns an optimal ordering of the vertices
     43                                     and its cost for vertex-separation
     44
     45:meth:`vertex_separation_MILP`       Computes the vertex separation of `G` and
     46                                     the optimal ordering of its vertices using
     47                                     an MILP formulation
     48
     49:meth:`lower_bound`                  Returns a lower bound on the vertex
     50                                     separation of `G`
     51
     52:meth:`is_valid_ordering`            Test if the linear vertex ordering `L` is
     53                                     valid for (di)graph `G`
     54
     55:meth:`width_of_path_decomposition`  Returns the width of the path decomposition
     56                                     induced by the linear ordering `L` of the
     57                                     vertices of `G`
     58===================================  ===========================================
     59
    4960
    5061Exponential algorithm for vertex separation
    5162-------------------------------------------
     
    114125
    115126    \max c'(\{v_1\}),c'(\{v_1,v_2\}),...,c'(\{v_1,...,v_n\})\geq\max c'_1,...,c'_n
    116127
    117 where `c_i` is the minimum cost of a set `S` on `i`
    118 vertices. Evaluating the `c_i` can take time (and in particular more
    119 than the previous exact algorithm), but it does not need much memory
    120 to run.
     128where `c_i` is the minimum cost of a set `S` on `i` vertices. Evaluating the
     129`c_i` can take time (and in particular more than the previous exact algorithm),
     130but it does not need much memory to run.
    121131
    122 Methods
     132
     133MILP formulation for the vertex separation
     134------------------------------------------
     135
     136We describe bellow a mixed integer linear program (MILP) for determining an
     137optimal layout for the vertex separation of `G`. This MILP is an improved
     138version of the formulation proposed in [SP10]_.
     139
     140VARIABLES:
     141
     142- ``x_v^t`` -- Variable set to 1 if either `v` has an in-neighbor `u` such that
     143  `u\in S_{t'}=\{v_1, v_2,\cdots, v_{t'}\}` and `v\in N_G^+(S_{t'})\backslash
     144  S_{t'}` with `t'\leq t`, or `v\in S_t`. It is set to 0 otherwise.
     145
     146- ``y_v^t`` -- Variable set to 1 if `v\in S_t`, and 0 otherwise. The order of
     147  `v` in the layout is the smallest `t` such that `y_v^t==1`.
     148
     149- ``u_v^t`` -- Variable set to 1 if `v\in N_G^+(S_t)\backslash S_t` has an
     150  in-neighbor in `S_t`, and set to 0 otherwise.
     151
     152- ``z`` -- Objective value to minimize. It is equal to the maximum over all step
     153  `t` of the number of vertices such that `u_v^t==1`.
     154
     155MILP formulation:
     156
     157.. MATH::
     158    :nowrap:
     159
     160    \begin{alignat}{2}
     161    \intertext{Minimize:}
     162    &z&\\
     163    \intertext{Such that:}
     164    x_v^t &\leq x_v^{t+1}& \forall v\in V,\ 0\leq t\leq n-2\\
     165    y_v^t &\leq y_v^{t+1}& \forall v\in V,\ 0\leq t\leq n-2\\
     166    y_v^t &\leq x_w^t& \forall v\in V,\ \forall w\in N^+(v),\ 0\leq t\leq n-1\\
     167    \sum_{v \in V} y_v^0 &\leq 1&\\
     168    \sum_{v \in V} y_v^{t+1} &\leq 1+\sum_{v \in V} y_v^t& 0\leq t\leq n-2\\
     169    \sum_{v \in V} y_v^{N-1} &= n&\\
     170    x_v^t-y_v^t&\leq u_v^t & \forall v \in V,\ 0\leq t\leq n-1\\
     171    \sum_{v \in V} u_v^t &\leq z& 0\leq t\leq n-1\\
     172    0 \leq x_v^t &\leq 1& \forall v\in V,\ 0\leq t\leq n-1\\
     173    0 \leq u_v^t &\leq 1& \forall v\in V,\ 0\leq t\leq n-1\\
     174    y_v^t &\in \{0,1\}& \forall v\in V,\ 0\leq t\leq n-1\\
     175    0 \leq z &\leq n&
     176    \end{alignat}
     177
     178The vertex separation of `G` is given by the value of `z`, and the order of
     179vertex `v` in the optimal layout is given by the smallest `t` for which
     180`y_v^t==1`.
     181
     182
     183REFERENCES
     184----------
     185
     186.. [Bod98] *A partial k-arboretum of graphs with bounded treewidth*, Hans
     187  L. Bodlaender, Theoretical Computer Science 209(1-2):1-45, 1998.
     188
     189.. [Kin92] *The vertex separation number of a graph equals its path-width*,
     190  Nancy G. Kinnersley, Information Processing Letters 42(6):345-350, 1992.
     191
     192.. [SP10] *Lightpath Reconfiguration in WDM networks*, Fernando Solano and
     193  Michal Pioro, IEEE/OSA Journal of Optical Communication and Networking
     194  2(12):1010-1021, 2010.
     195
     196
     197AUTHORS
     198-------
     199
     200- Nathann Cohen (2011-10): Initial version and exact exponential algorithm
     201
     202- David Coudert (2012-04): MILP formulation and tests functions
     203
     204
     205
     206METHODS
    123207-------
    124208"""
    125209
     
    154238    documentation).
    155239
    156240    .. NOTE::
    157        
     241
    158242        This method runs in exponential time but has no memory constraint.
    159243
    160244
     
    217301
    218302    return min
    219303
    220 ####################
    221 # Exact algorithms #
    222 ####################
     304################################
     305# Exact exponential algorithms #
     306################################
    223307
    224 def path_decomposition(G, verbose = False):
     308def path_decomposition(G, algorithm = "exponential", verbose = False):
    225309    r"""
    226310    Returns the pathwidth of the given graph and the ordering of the vertices
    227311    resulting in a corresponding path decomposition.
     
    230314
    231315    - ``G`` -- a digraph
    232316
     317    - ``algorithm`` -- (default: ``"exponential"``) Specify the algorithm to use
     318      among
     319
     320      - ``exponential`` -- Use an exponential time and space algorithm. This
     321        algorithm only works of graphs on less than 32 vertices.
     322
     323      - ``MILP`` -- Use a mixed integer linear programming formulation. This
     324        algorithm has no size restriction but could take a very long time.
     325
    233326    - ``verbose`` (boolean) -- whether to display information on the
    234327      computations.
    235328
     
    240333
    241334    .. NOTE::
    242335
    243         Because of its current implementation, this algorithm only works on
    244         graphs on less than 32 vertices. This can be changed to 54 if necessary,
    245         but 32 vertices already require 4GB of memory.
     336        Because of its current implementation, this exponential algorithm only
     337        works on graphs on less than 32 vertices. This can be changed to 54 if
     338        necessary, but 32 vertices already require 4GB of memory.
    246339
    247340    EXAMPLE:
    248341
    249     The vertex separation of a circuit is equal to 2::
     342    The vertex separation of a cycle is equal to 2::
    250343
    251344        sage: from sage.graphs.graph_decompositions.vertex_separation import path_decomposition
    252345        sage: g = graphs.CycleGraph(6)
    253         sage: path_decomposition(g)
    254         (2, [0, 1, 2, 3, 4, 5])
     346        sage: pw, L = path_decomposition(g); pw
     347        2
     348        sage: pwm, Lm = path_decomposition(g, algorithm = "MILP"); pwm
     349        2
    255350
    256351    TEST:
    257352
     
    267362    from sage.graphs.graph import Graph
    268363    if not isinstance(G, Graph):
    269364        raise ValueError("The parameter must be a Graph.")
    270    
     365
    271366    from sage.graphs.digraph import DiGraph
    272     return vertex_separation(DiGraph(G), verbose = verbose)
     367    if algorithm == "exponential":
     368        return vertex_separation(DiGraph(G), verbose = verbose)
     369    else:
     370        return vertex_separation_MILP(DiGraph(G), verbosity = (1 if verbose else 0))
     371
    273372
    274373def vertex_separation(G, verbose = False):
    275374    r"""
     
    296395
    297396    EXAMPLE:
    298397
    299     The vertex separation of a circuit is equal to 2::
     398    The vertex separation of a circuit is equal to 1::
    300399
    301400        sage: from sage.graphs.graph_decompositions.vertex_separation import vertex_separation
    302401        sage: g = digraphs.Circuit(6)
    303402        sage: vertex_separation(g)
    304         (1, [0, 1, 2, 3, 4, 5])       
     403        (1, [0, 1, 2, 3, 4, 5])
    305404
    306405    TEST:
    307406
     
    316415
    317416    Graphs with non-integer vertices::
    318417
     418        sage: from sage.graphs.graph_decompositions.vertex_separation import vertex_separation
    319419        sage: D=digraphs.DeBruijn(2,3)
    320420        sage: vertex_separation(D)
    321421        (2, ['000', '001', '100', '010', '101', '011', '110', '111'])
     
    454554        return a
    455555    else:
    456556        return b
     557
     558
     559#################################################################
     560# Function for testing the validity of a linear vertex ordering #
     561#################################################################
     562
     563def is_valid_ordering(G, L):
     564    r"""
     565    Test if the linear vertex ordering `L` is valid for (di)graph `G`.
     566
     567    A linear ordering `L` of the vertices of a (di)graph `G` is valid if all
     568    vertices of `G` are in `L`, and if `L` contains no other vertex and no
     569    duplicated vertices.
     570
     571    INPUT:
     572
     573    - ``G`` -- a Graph or a DiGraph.
     574
     575    - ``L`` -- an ordered list of the vertices of ``G``.
     576
     577
     578    OUTPUT:
     579
     580    Returns `True` if `L` is a valid vertex ordering for `G`, and False
     581    oterwise.
     582
     583
     584    EXAMPLE:
     585
     586    Path decomposition of a cycle::
     587
     588        sage: from sage.graphs.graph_decompositions import vertex_separation
     589        sage: G = graphs.CycleGraph(6)
     590        sage: L = [u for u in G.vertices()]
     591        sage: vertex_separation.is_valid_ordering(G, L)
     592        True
     593        sage: vertex_separation.is_valid_ordering(G, [1,2])
     594        False
     595
     596    TEST:
     597
     598    Giving anything else than a Graph or a DiGraph::
     599
     600        sage: from sage.graphs.graph_decompositions import vertex_separation
     601        sage: vertex_separation.is_valid_ordering(2, [])
     602        Traceback (most recent call last):
     603        ...
     604        ValueError: The input parameter must be a Graph or a DiGraph.
     605
     606    Giving anything else than a list::
     607
     608        sage: from sage.graphs.graph_decompositions import vertex_separation
     609        sage: G = graphs.CycleGraph(6)
     610        sage: vertex_separation.is_valid_ordering(G, {})
     611        Traceback (most recent call last):
     612        ...
     613        ValueError: The second parameter must be of type 'list'.
     614    """
     615    from sage.graphs.graph import Graph
     616    from sage.graphs.digraph import DiGraph
     617    if not isinstance(G, Graph) and not isinstance(G, DiGraph):
     618        raise ValueError("The input parameter must be a Graph or a DiGraph.")
     619    if not isinstance(L, list):
     620        raise ValueError("The second parameter must be of type 'list'.")
     621
     622    return len( set( G.vertices() ).symmetric_difference( set(L) ) ) == 0
     623
     624
     625####################################################################
     626# Measurement functions of the widths of some graph decompositions #
     627####################################################################
     628
     629def width_of_path_decomposition(G, L):
     630    r"""
     631    Returns the width of the path decomposition induced by the linear ordering
     632    `L` of the vertices of `G`.
     633
     634    If `G` is an instance of Graph, this function returns the width `pw_L(G)` of
     635    the path decomposition induced by the linear ordering `L` of the vertices of
     636    `G`. If `G` is a DiGraph, it returns instead the width `vs_L(G)` of the
     637    directed path decomposition induced by the linear ordering `L` of the
     638    vertices of `G`, where
     639
     640    .. MATH::
     641
     642        vs_L(G) & =  \max_{0\leq i< |V|-1} | N^+(L[:i])\setminus L[:i] |\\
     643        pw_L(G) & =  \max_{0\leq i< |V|-1} | N(L[:i])\setminus L[:i] |\\
     644
     645    INPUT:
     646
     647    - ``G`` -- a Graph or a DiGraph
     648
     649    - ``L`` -- a linear ordering of the vertices of ``G``
     650
     651    EXAMPLES:
     652
     653    Path decomposition of a cycle::
     654
     655        sage: from sage.graphs.graph_decompositions import vertex_separation
     656        sage: G = graphs.CycleGraph(6)
     657        sage: L = [u for u in G.vertices()]
     658        sage: vertex_separation.width_of_path_decomposition(G, L)
     659        2
     660
     661    Directed path decomposition of a circuit::
     662
     663        sage: from sage.graphs.graph_decompositions import vertex_separation
     664        sage: G = digraphs.Circuit(6)
     665        sage: L = [u for u in G.vertices()]
     666        sage: vertex_separation.width_of_path_decomposition(G, L)
     667        1
     668
     669    TESTS:
     670
     671    Path decomposition of a BalancedTree::
     672
     673        sage: from sage.graphs.graph_decompositions import vertex_separation
     674        sage: G = graphs.BalancedTree(3,2)
     675        sage: pw, L = vertex_separation.path_decomposition(G)
     676        sage: pw == vertex_separation.width_of_path_decomposition(G, L)
     677        True
     678        sage: L.reverse()
     679        sage: pw == vertex_separation.width_of_path_decomposition(G, L)
     680        False
     681
     682    Directed path decomposition of a circuit::
     683
     684        sage: from sage.graphs.graph_decompositions import vertex_separation
     685        sage: G = digraphs.Circuit(8)
     686        sage: vs, L = vertex_separation.vertex_separation(G)
     687        sage: vs == vertex_separation.width_of_path_decomposition(G, L)
     688        True
     689        sage: L = [0,4,6,3,1,5,2,7]
     690        sage: vs == vertex_separation.width_of_path_decomposition(G, L)
     691        False
     692
     693    Giving a wrong linear ordering::
     694
     695        sage: from sage.graphs.graph_decompositions import vertex_separation
     696        sage: G = Graph()
     697        sage: vertex_separation.width_of_path_decomposition(G, ['a','b'])
     698        Traceback (most recent call last):
     699        ...
     700        ValueError: The input linear vertex ordering L is not valid for G.
     701    """
     702    if not is_valid_ordering(G, L):
     703        raise ValueError("The input linear vertex ordering L is not valid for G.")
     704
     705    vsL = 0
     706    S = []
     707    neighbors_of_S_in_V_minus_S = []
     708
     709    for u in L:
     710
     711        # We remove u from the list of neighbors of S
     712        if u in neighbors_of_S_in_V_minus_S:
     713            neighbors_of_S_in_V_minus_S.remove(u)
     714
     715        # We add vertex u to the set S
     716        S.append(u)
     717
     718        if G._directed:
     719            Nu = G.neighbors_out(u)
     720        else:
     721            Nu = G.neighbors(u)
     722
     723        # We add the (out-)neighbors of u to the list of neighbors of S
     724        for v in Nu:
     725            if (not v in S) and (not v in neighbors_of_S_in_V_minus_S):
     726                neighbors_of_S_in_V_minus_S.append(v)
     727
     728        # We update the cost of the vertex separation
     729        vsL = max( vsL, len(neighbors_of_S_in_V_minus_S) )
     730
     731    return vsL
     732
     733
     734##########################################
     735# MILP formulation for vertex separation #
     736##########################################
     737
     738def vertex_separation_MILP(G, integrality = False, solver = None, verbosity = 0):
     739    r"""
     740    Computes the vertex separation of `G` and the optimal ordering of its
     741    vertices using an MILP formulation.
     742
     743    This function uses a mixed integer linear program (MILP) for determining an
     744    optimal layout for the vertex separation of `G`. This MILP is an improved
     745    version of the formulation proposed in [SP10]_. See the module's
     746    documentation for more details on this MILP formulation.
     747
     748    INPUTS:
     749
     750    - ``G`` -- a DiGraph
     751
     752    - ``integrality`` -- (default: ``False``) Specify if all variables must be
     753      integral of if some variables can be relaxed.
     754
     755    - ``solver`` -- (default: ``None``) Specify a Linear Program (LP) solver to
     756      be used. If set to ``None``, the default one is used. For more information
     757      on LP solvers and which default solver is used, see the method
     758      :meth:`solve<sage.numerical.mip.MixedIntegerLinearProgram.solve>` of the
     759      class
     760      :class:`MixedIntegerLinearProgram<sage.numerical.mip.MixedIntegerLinearProgram>`.
     761
     762    - ``verbose`` -- integer (default: ``0``). Sets the level of verbosity. Set
     763      to 0 by default, which means quiet.
     764
     765    OUTPUT:
     766
     767    A pair ``(cost, ordering)`` representing the optimal ordering of the
     768    vertices and its cost.
     769
     770    EXAMPLE:
     771
     772    Vertex separation of a De Bruijn digraph::
     773
     774        sage: from sage.graphs.graph_decompositions import vertex_separation
     775        sage: G = digraphs.DeBruijn(2,3)
     776        sage: vs, L = vertex_separation.vertex_separation_MILP(G); vs
     777        2
     778        sage: vs == vertex_separation.width_of_path_decomposition(G, L)
     779        True
     780        sage: vse, Le = vertex_separation.vertex_separation(G); vse
     781        2
     782
     783    The vertex separation of a circuit is 1::
     784
     785        sage: from sage.graphs.graph_decompositions import vertex_separation
     786        sage: G = digraphs.Circuit(6)
     787        sage: vs, L = vertex_separation.vertex_separation_MILP(G); vs
     788        1
     789
     790    TESTS:
     791
     792    Comparison with exponential algorithm::
     793
     794        sage: from sage.graphs.graph_decompositions import vertex_separation
     795        sage: for i in range(20):
     796        ...       G = digraphs.RandomDirectedGNP(10,0.2)
     797        ...       ve,le = vertex_separation.vertex_separation(G)
     798        ...       vm,lm = vertex_separation.vertex_separation_MILP(G)
     799        ...       if ve != vm:
     800        ...          print "The solution is not optimal!"
     801
     802    Giving anything else than a DiGraph::
     803
     804        sage: from sage.graphs.graph_decompositions import vertex_separation
     805        sage: vertex_separation.vertex_separation_MILP([])
     806        Traceback (most recent call last):
     807        ...
     808        ValueError: The first input parameter must be a DiGraph.
     809    """
     810    from sage.graphs.digraph import DiGraph
     811    if not isinstance(G, DiGraph):
     812        raise ValueError("The first input parameter must be a DiGraph.")
     813
     814    from sage.numerical.mip import MixedIntegerLinearProgram, Sum, MIPSolverException
     815    p = MixedIntegerLinearProgram( maximization = False, solver = solver )
     816
     817    # Declaration of variables.
     818    x = p.new_variable( integer = integrality, dim = 2 )
     819    u = p.new_variable( integer = integrality, dim = 2 )
     820    y = p.new_variable( integer = True, dim = 2 )
     821    z = p.new_variable( integer = True, dim = 1 )
     822
     823    N = G.num_verts()
     824    V = G.vertices()
     825
     826    #  (2) x[v][t] <= x[v][t+1]   for all v in V, and for t:=0..N-2
     827    #  (3) y[v][t] <= y[v][t+1]   for all v in V, and for t:=0..N-2
     828    for v in V:
     829        for t in xrange(N-1):
     830            p.add_constraint( x[v][t] - x[v][t+1], max = 0 )
     831            p.add_constraint( y[v][t] - y[v][t+1], max = 0 )
     832
     833    #  (4) y[v][t] <= x[w][t]  for all v in V, for all w in N^+(v), and for all t:=0..N-1
     834    for v in V:
     835        for w in G.neighbors_out(v):
     836            for t in xrange(N):
     837                p.add_constraint( y[v][t] - x[w][t], max = 0 )
     838
     839    #  (5) sum_{v in V} y[v][0] <= 1
     840    p.add_constraint( Sum([ y[v][0] for v in V ]), max = 1 )
     841
     842    #  (6) sum_{v in V} y[v][t+1] <= sum_{v in V} y[v][t] + 1  for t:=0..N-2
     843    for t in xrange(N-1):
     844        p.add_constraint( Sum([ y[v][t+1] - y[v][t] for v in V ]), max = 1 )
     845
     846    #  (7) sum_{v in V} y[v][N-1] = N
     847    p.add_constraint( Sum([ y[v][N-1] for v in V ]), min = N )
     848    p.add_constraint( Sum([ y[v][N-1] for v in V ]), max = N )
     849
     850    #  (8) u[v][t] >= x[v][t]-y[v][t]    for all v in V, and for all t:=0..N-1
     851    for v in V:
     852        for t in xrange(N):
     853            p.add_constraint( x[v][t] - y[v][t] - u[v][t], max = 0 )
     854
     855    #  (9) z >= sum_{v in V} u[v][t]   for all t:=0..N-1
     856    for t in xrange(N):
     857        p.add_constraint( Sum([ u[v][t] for v in V ]) - z['z'], max = 0 )
     858
     859    # (10) 0 <= x[v][t] and u[v][t] <= 1
     860    # (11) y[v][t] in {0,1}
     861    for v in V:
     862        for t in xrange(N):
     863            p.add_constraint( x[v][t], min = 0 )
     864            p.add_constraint( x[v][t], max = 1 )
     865            p.add_constraint( u[v][t], min = 0 )
     866            p.add_constraint( u[v][t], max = 1 )
     867            p.set_binary( y[v][t] )
     868
     869    # (12) 0 <= z <= |V|
     870    p.add_constraint( z['z'], min = 0 )
     871    p.add_constraint( z['z'], max = N )
     872
     873    #  (1) Minimize z
     874    p.set_objective( z['z'] )
     875
     876    try:
     877        obj = p.solve( log=verbosity )
     878
     879        taby = p.get_values( y )
     880        tabz = p.get_values( z )
     881        # since exactly one vertex is processed per step, we can reconstruct the sequence
     882        seq = []
     883        for t in xrange(N):
     884            for v in V:
     885                if (taby[v][t] > 0) and (not v in seq):
     886                    seq.append(v)
     887                    break
     888        vs = int(round( tabz['z'] ))
     889
     890    except MIPSolverException:
     891        if integrality:
     892            raise ValueError("Unbounded or unexpected error")
     893        else:
     894            raise ValueError("Unbounded or unexpected error. Try with 'integrality = True'.")
     895
     896    del p
     897    return vs, seq
     898