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 b  
    33
    44This module implements several algorithms to compute the vertex separation of a
    55digraph and the corresponding ordering of the vertices. It also implements tests
    6 functions for evaluation the width of a linear orderings.
     6functions for evaluation the width of a linear ordering.
    77
    88Given an ordering
    99`v_1,\cdots, v_n` of the vertices of `V(G)`, its *cost* is defined as:
     
    1212
    1313    c(v_1, ..., v_n) = \max_{1\leq i \leq n} c'(\{v_1, ..., v_i\})
    1414
    15 Where 
     15Where
    1616
    1717.. MATH::
    1818
     
    112112
    113113    Because of its current implementation, this algorithm only works on graphs
    114114    on less than 32 vertices. This can be changed to 64 if necessary, but 32
    115     vertices already require 4GB of memory.
     115    vertices already require 4GB of memory. Running it on 64 bits is not
     116    expected to be doable by the computers of the next decade `:-D`
    116117
    117118**Lower bound on the vertex separation**
    118119
     
    133134MILP formulation for the vertex separation
    134135------------------------------------------
    135136
    136 We describe bellow a mixed integer linear program (MILP) for determining an
    137 optimal layout for the vertex separation of `G`. This MILP is an improved
    138 version of the formulation proposed in [SP10]_.
     137We describe below a mixed integer linear program (MILP) for determining an
     138optimal layout for the vertex separation of `G`, which is an improved version of
     139the formulation proposed in [SP10]_. It aims at building a sequence `S_t` of
     140sets such that an ordering `v_1, ..., v_n` of the vertices correspond to
     141`S_0=\{v_1\}, S_2=\{v_1,v_2\}, ..., S_{n-1}=\{v_1,...,v_n\}`.
    139142
    140 VARIABLES:
     143**Variables:**
    141144
    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.
    145145
    146 - ``y_v^t`` -- Variable set to 1 if `v\in S_t`, and 0 otherwise. The order of
     146- `y_v^t` -- Variable set to 1 if `v\in S_t`, and 0 otherwise. The order of
    147147  `v` in the layout is the smallest `t` such that `y_v^t==1`.
    148148
    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.
     149- `u_v^t` -- Variable set to 1 if `v\not \in S_t` and `v` has an in-neighbor in
     150  `S_t`. It is set to 0 otherwise.
    151151
    152 - ``z`` -- Objective value to minimize. It is equal to the maximum over all step
     152- `x_v^t` -- is morally equal to "`y_v^t+u_v^t`". It is set to 1 if either
     153  `v\in S_t` or if `v` has an in-neighbor in `S_t`. It is set to 0 otherwise.
     154
     155- `z` -- Objective value to minimize. It is equal to the maximum over all step
    153156  `t` of the number of vertices such that `u_v^t==1`.
    154157
    155 MILP formulation:
     158**MILP formulation:**
    156159
    157160.. MATH::
    158161    :nowrap:
     
    179182vertex `v` in the optimal layout is given by the smallest `t` for which
    180183`y_v^t==1`.
    181184
    182 
    183185REFERENCES
    184186----------
    185187
     
    244246
    245247    EXAMPLE:
    246248
    247     On a cycle::
     249    On a circuit::
    248250
    249251        sage: from sage.graphs.graph_decompositions.vertex_separation import lower_bound
    250252        sage: g = digraphs.Circuit(6)
     
    464466
    465467    sage_free(neighborhoods)
    466468
    467     _sig_off 
     469    _sig_off
    468470
    469471    return k, order
    470472
     
    577579
    578580    OUTPUT:
    579581
    580     Returns `True` if `L` is a valid vertex ordering for `G`, and False
     582    Returns ``True`` if `L` is a valid vertex ordering for `G`, and ``False``
    581583    oterwise.
    582584
    583585
     
    619621    if not isinstance(L, list):
    620622        raise ValueError("The second parameter must be of type 'list'.")
    621623
    622     return len( set( G.vertices() ).symmetric_difference( set(L) ) ) == 0
     624    return set(L) == set(G.vertices())
    623625
    624626
    625627####################################################################
     
    631633    Returns the width of the path decomposition induced by the linear ordering
    632634    `L` of the vertices of `G`.
    633635
    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
     636    If `G` is an instance of :mod:`Graph <sage.graphs.graph>`, this function
     637    returns the width `pw_L(G)` of the path decomposition induced by the linear
     638    ordering `L` of the vertices of `G`. If `G` is a :mod:`DiGraph
     639    <sage.graphs.digraph>`, it returns instead the width `vs_L(G)` of the
    637640    directed path decomposition induced by the linear ordering `L` of the
    638641    vertices of `G`, where
    639642
     
    703706        raise ValueError("The input linear vertex ordering L is not valid for G.")
    704707
    705708    vsL = 0
    706     S = []
    707     neighbors_of_S_in_V_minus_S = []
     709    S = set()
     710    neighbors_of_S_in_V_minus_S = set()
    708711
    709712    for u in L:
    710713
    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        # We remove u from the neighbors of S
     715        neighbors_of_S_in_V_minus_S.discard(u)
    714716
    715717        # We add vertex u to the set S
    716         S.append(u)
     718        S.add(u)
    717719
    718720        if G._directed:
    719721            Nu = G.neighbors_out(u)
    720722        else:
    721723            Nu = G.neighbors(u)
    722724
    723         # We add the (out-)neighbors of u to the list of neighbors of S
     725        # We add the (out-)neighbors of u to the neighbors of S
    724726        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            if (not v in S):
     728                neighbors_of_S_in_V_minus_S.add(v)
    727729
    728730        # We update the cost of the vertex separation
    729731        vsL = max( vsL, len(neighbors_of_S_in_V_minus_S) )
     
    742744
    743745    This function uses a mixed integer linear program (MILP) for determining an
    744746    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    version of the formulation proposed in [SP10]_. See the :mod:`module's
     748    documentation <sage.graphs.graph_decompositions.vertex_separation>` for more
     749    details on this MILP formulation.
    747750
    748751    INPUTS:
    749752
     
    815818    p = MixedIntegerLinearProgram( maximization = False, solver = solver )
    816819
    817820    # 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    x = p.new_variable( binary = integrality, dim = 2 )
     822    u = p.new_variable( binary = integrality, dim = 2 )
     823    y = p.new_variable( binary = True, dim = 2 )
    821824    z = p.new_variable( integer = True, dim = 1 )
    822825
    823826    N = G.num_verts()
     
    858861
    859862    # (10) 0 <= x[v][t] and u[v][t] <= 1
    860863    # (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] )
     864    if not integrality:
     865        for v in V:
     866            for t in xrange(N):
     867                p.add_constraint( x[v][t], min = 0 )
     868                p.add_constraint( x[v][t], max = 1 )
     869                p.add_constraint( u[v][t], min = 0 )
     870                p.add_constraint( u[v][t], max = 1 )
     871
     872    # (11) y[v][t] in {0,1}
     873    p.set_binary( y )
    868874
    869875    # (12) 0 <= z <= |V|
    870     p.add_constraint( z['z'], min = 0 )
    871876    p.add_constraint( z['z'], max = N )
    872877
    873878    #  (1) Minimize z
     
    875880
    876881    try:
    877882        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 
    890883    except MIPSolverException:
    891884        if integrality:
    892885            raise ValueError("Unbounded or unexpected error")
    893886        else:
    894887            raise ValueError("Unbounded or unexpected error. Try with 'integrality = True'.")
    895888
    896     del p
     889    taby = p.get_values( y )
     890    tabz = p.get_values( z )
     891    # since exactly one vertex is processed per step, we can reconstruct the sequence
     892    seq = []
     893    for t in xrange(N):
     894        for v in V:
     895            if (taby[v][t] > 0) and (not v in seq):
     896                seq.append(v)
     897                break
     898    vs = int(round( tabz['z'] ))
     899
    897900    return vs, seq
    898