Exemplo n.º 1
0
    def createInstance(self):

        model = Model()
        modelVars = {}

        # x variables are assigned.
        for i in range(self.m):
            for j in range(self.n):
                Vname = 'x_{}_{}'.format(i, j)
                modelVars[Vname] = model.addVar(vtype=GRB.BINARY, name=Vname)

        # Term 2: Objective Function
        obj = quicksum(self.v[j] * modelVars['x_{}_{}'.format(i, j)]
                       for i in range(self.m) for j in range(self.n))

        # Term 3: Each job is assigned to at most 1 machine.
        model.addConstrs(
            quicksum(modelVars['x_{}_{}'.format(i, j)]
                     for i in range(self.m)) <= 1 for j in range(self.n))

        # Term 4: Allows the assignment of at most R resources to a machine.
        model.addConstrs(
            quicksum(modelVars['x_{}_{}'.format(i, j)] * self.r[j]
                     for j in self.J[h]) <= self.R for i in range(self.m)
            for h in range(1, self.k + 1))

        # Term 5: Implicit in varaible definition.

        # Objective Function is set.
        model._vars = modelVars
        model.setObjective(obj, GRB.MAXIMIZE)

        #print(model.getAttr('ModelSense') == GRB.MINIMIZE)

        return model
Exemplo n.º 2
0
def _mmp_solve(w1_ij, x_ij_keys, n, w2_ij=None):
    """A helper function that solves a weighted maximum matching problem.
    """

    m = Model("MBSA")

    if __debug__:
        log(DEBUG, "")
        log(
            DEBUG, "Solving a weighted maximum matching problem with " +
            "%d savings weights." % len(w1_ij))

    # build model
    x_ij = m.addVars(x_ij_keys, obj=w1_ij, vtype=GRB.BINARY, name='x')
    _mmp_add_cnts_sum(m, x_ij, x_ij_keys, w1_ij, n)
    m._vars = x_ij
    m.modelSense = GRB.MAXIMIZE
    m.update()

    # disable output
    m.setParam('OutputFlag', 0)
    m.setParam('TimeLimit', MAX_MIP_SOLVER_RUNTIME)
    m.setParam('Threads', MIP_SOLVER_THREADS)
    #m.write("out.lp")
    m.optimize()

    # restore SIGINT callback handler which is changed by gurobipy
    signal(SIGINT, default_int_handler)

    if __debug__:
        log(DEBUG - 1, "Gurobi runtime = %.2f" % m.Runtime)

    if m.Status == GRB.OPTIMAL:
        if w2_ij == None:
            max_wt, max_merge = max(
                (w1_ij[k], x_ij_keys[k]) for k, v in enumerate(m.X) if v)
        else:
            max_wt, _, max_merge = max((w1_ij[k], w2_ij[k], x_ij_keys[k])
                                       for k, v in enumerate(m.X) if v)

        return max_wt, max_merge[0], max_merge[1]
    elif m.Status == GRB.TIME_LIMIT:
        raise GurobiError(
            10023, "Gurobi timeout reached when attempting to solve GAP")
    elif m.Status == GRB.INTERRUPTED:
        raise KeyboardInterrupt()
    return None
Exemplo n.º 3
0
def gurobi_tsp(distance_matrix):
    """
    Solves tsp problem.
    :param distance_matrix: symmetric matrix of distances, where the i,j element is the distance between object i and j
    :return: matrix containing {0, 1}, 1 for each transition that is included in the tsp solution
    """
    n = len(distance_matrix)
    m = Model()
    m.setParam("OutputFlag", False)
    m.setParam("Threads", 1)

    # Create variables
    vars = {}
    for i in range(n):
        for j in range(i + 1):
            vars[i, j] = m.addVar(obj=0.0 if i == j else distance_matrix[i][j],
                                  vtype=GRB.BINARY,
                                  name="e" + str(i) + "_" + str(j))
            vars[j, i] = vars[i, j]
        m.update()

    # Add degree-2 constraint, and forbid loops
    for i in range(n):
        m.addConstr(quicksum(vars[i, j] for j in range(n)) == 2)
        vars[i, i].ub = 0
    m.update()

    # Optimize model
    m._vars = vars
    m.params.LazyConstraints = 1

    def subtour_fn(model, where):
        return subtourelim(n, model, where)

    m.optimize(subtour_fn)
    solution = m.getAttr("x", vars)
    selected = [(i, j) for i in range(n) for j in range(n)
                if solution[i, j] > 0.5]
    result = np.zeros_like(distance_matrix)
    for (i, j) in selected:
        result[i][j] = 1

    return result
Exemplo n.º 4
0
def tsp_gurobi(edges):
    """
    Modeled using GUROBI python example.
    """
    from gurobipy import Model, GRB, quicksum

    edges = populate_edge_weights(edges)
    incoming, outgoing, nodes = node_to_edge(edges)
    idx = dict((n, i) for i, n in enumerate(nodes))
    nedges = len(edges)
    n = len(nodes)

    m = Model()

    step = lambda x: "u_{0}".format(x)
    # Create variables
    vars = {}
    for i, (a, b, w) in enumerate(edges):
        vars[i] = m.addVar(obj=w, vtype=GRB.BINARY, name=str(i))
    for u in nodes[1:]:
        u = step(u)
        vars[u] = m.addVar(obj=0, vtype=GRB.INTEGER, name=u)
    m.update()

    # Bounds for step variables
    for u in nodes[1:]:
        u = step(u)
        vars[u].lb = 1
        vars[u].ub = n - 1

    # Add degree constraint
    for v in nodes:
        incoming_edges = incoming[v]
        outgoing_edges = outgoing[v]
        m.addConstr(quicksum(vars[x] for x in incoming_edges) == 1)
        m.addConstr(quicksum(vars[x] for x in outgoing_edges) == 1)

    # Subtour elimination
    edge_store = dict(((idx[a], idx[b]), i) for i, (a, b, w) in enumerate(edges))

    # Given a list of edges, finds the shortest subtour
    def subtour(s_edges):
        visited = [False] * n
        cycles = []
        lengths = []
        selected = [[] for i in range(n)]
        for x, y in s_edges:
            selected[x].append(y)
        while True:
            current = visited.index(False)
            thiscycle = [current]
            while True:
                visited[current] = True
                neighbors = [x for x in selected[current] if not visited[x]]
                if len(neighbors) == 0:
                    break
                current = neighbors[0]
                thiscycle.append(current)
            cycles.append(thiscycle)
            lengths.append(len(thiscycle))
            if sum(lengths) == n:
                break
        return cycles[lengths.index(min(lengths))]

    def subtourelim(model, where):
        if where != GRB.callback.MIPSOL:
            return
        selected = []
        # make a list of edges selected in the solution
        sol = model.cbGetSolution([model._vars[i] for i in range(nedges)])
        selected = [edges[i] for i, x in enumerate(sol) if x > .5]
        selected = [(idx[a], idx[b]) for a, b, w in selected]
        # find the shortest cycle in the selected edge list
        tour = subtour(selected)
        if len(tour) == n:
            return
        # add a subtour elimination constraint
        c = tour
        incident = [edge_store[a, b] for a, b in pairwise(c + [c[0]])]
        model.cbLazy(quicksum(model._vars[x] for x in incident) <= len(tour) - 1)

    m.update()

    m._vars = vars
    m.params.LazyConstraints = 1
    m.optimize(subtourelim)

    selected = [v.varName for v in m.getVars() if v.x > .5]
    selected = [int(x) for x in selected if x[:2] != "u_"]
    results = sorted(x for i, x in enumerate(edges) if i in selected) \
                    if selected else None
    return results
Exemplo n.º 5
0
def _solve_gap(N, D_s, d, C, K, L=None, L_ctr_multipiler=1.0):
    """A helper function that Solves VRP as a Generalized Assignment Problem
    to assign customers to vehicles with a objective function that the delivery
    cost as described in (Fisher & Jaikumar 1981).
    
    D_s is the distance matrix complemented with distances to K seed points.
    That is:
          [D_0]
    D_s = [S  ], where D_0 is the first row of the full distance matrix and
                         S is the distances from seed points to node points
    d is the list of customer demands with d[0]=0 being the depot node
    C is the capacity of the K identical trucks
    
    also, additional (and optional) constraints can be given:
    
    L is the maximum tour cost/duration/length
    L_ctr_multipiler allows iteratively adjusting the max route cost
     approximation constraint in order to avoid producing assignments that are
     ruled infeasible by the feasibility checker. 
    --
    Fisher, M. L. and Jaikumar, R. (1981), A generalized assignment 
    heuristic for vehicle routing. Networks, 11: 109-124.
    """

    ## build the cost approximation matrix "insertion_cost"

    # it is ~ a insertion cost matrix, where each coefficient is the cost
    #  of inserting customer i to the route consisting visit to seed k.
    #
    # we assume that distances are symmetric, but if asymmetric
    #  distances are to be used, take min
    # d_{ik} = min(c_{0i}+c_{i{i_k}}+c_{i{i_k}},
    #              c_{0{i_k}}+c_[{i_k}i}+c_{i0})
    #              -(c_{0{i_k}}+c_{{i_k}0})

    m = Model("GAPCVRP")

    # the order of the keys is important when we interpret the results
    Y_ik_keys = [(i, k) for k in range(K) for i in range(1, N)]

    # delivery cost approximation coefficients for the objective function
    insertion_cost = {(i,k): D_s[0,i]+D_s[k,i]-D_s[k,0] \
                              for i,k in Y_ik_keys}

    # variables and the objective
    Y_ik = m.addVars(Y_ik_keys, obj=insertion_cost, vtype=GRB.BINARY, name='y')

    ## constraints

    # c1, the capacity constraint and optional tour cost constraint cl
    approx_route_cost_constraints = []
    if C: c1_coeffs = d[1:]
    for k in range(K):
        ck_vars = [Y_ik[i, k] for i in range(1, N)]
        if C:
            c1_lhs = LinExpr(c1_coeffs, ck_vars)
            #c1_lhs = Y_ik.prod(c1_coeffs, '*', k)
            m.addConstr(c1_lhs <= C, "c1_k%d" % k)

        # ct = optional tour cost constraints
        #  it is a bit hidden, but the additional side constraint can be found
        #  from Fisher & Jaikumar (1981) p121, 2. paragraph.
        # However, for whatever reason, this does not seem to produce the
        #  same results as reported in their paper as the constraint easily
        #  starts to make the problem infeasible and the exact mechanism to
        #  recover that is not specified in the paper.
        if L:
            ct_coeffs = [
                insertion_cost[(i, k)] * L_ctr_multipiler for i in range(1, N)
            ]
            ct_lhs = LinExpr(ct_coeffs, ck_vars)
            #ct_lhs = Y_ik.prod(ct_coeffs, '*', k)
            constr_l = m.addConstr(ct_lhs <= L, "cl_k%d" % k)
            approx_route_cost_constraints.append(constr_l)

    # c2, the assignment constraints
    for i in range(1, N):
        # c2_1..N every node assigned only to 1 route
        m.addConstr(Y_ik.sum(i, '*') == 1, "c1_i%d" % i)

    ## update the model and solve
    m._vars = Y_ik
    m.modelSense = GRB.MINIMIZE
    m.update()
    #m.write("gapvrp_model.lp")
    # disable output
    m.setParam('OutputFlag', 0)
    m.setParam('Threads', MIP_SOLVER_THREADS)
    # REMOVEME
    m.setParam('MIPFocus', 3)
    m.setParam('TimeLimit', MAX_MIP_SOLVER_RUNTIME)

    m.optimize()

    # restore SIGINT callback handler which is changed by gurobipy
    signal(SIGINT, default_int_handler)

    if __debug__:
        log(DEBUG - 1, "Gurobi runtime = %.2f" % m.Runtime)

    if m.Status == GRB.OPTIMAL:
        return _decision_variables_to_assignments(m, Y_ik, N, K)
    elif m.Status == GRB.INFEASIBLE and L:
        # relax the model and allow violating minimal number of the approximate
        #  route length constraints
        pens = [1.0] * len(approx_route_cost_constraints)
        m.feasRelax(1, True, None, None, None, approx_route_cost_constraints,
                    pens)
        # TODO: not sure if feasRelax can change Status, test it someday
        if m.Status == GRB.INTERRUPTED:
            raise KeyboardInterrupt()  # pass it on
        m.optimize()

        # restore SIGINT callback handler which is changed by gurobipy
        signal(SIGINT, default_int_handler)

        status = m.Status
        if __debug__:
            log(DEBUG - 1, "Relaxed problem Gurobi runtime = %.2f" % m.Runtime)
        if status == GRB.OPTIMAL:
            return _decision_variables_to_assignments(m, Y_ik, N, K)
        elif status == GRB.TIME_LIMIT:
            raise GurobiError(
                10023,
                "Gurobi timeout reached when attempting to solve relaxed SCPCVRP"
            )
        elif m.Status == GRB.INTERRUPTED:
            raise KeyboardInterrupt()  # pass it on
        return None
    elif m.Status == GRB.TIME_LIMIT:
        raise GurobiError(
            10023, "Gurobi timeout reached when attempting to solve GAP")
    elif m.Status == GRB.INTERRUPTED:
        raise KeyboardInterrupt()  # pass it on
    return None
Exemplo n.º 6
0
# vars = tupledict()
# for i,j in dist.keys():
#   vars[i,j] = m.addVar(obj=dist[i,j], vtype=GRB.BINARY,
#                        name='e[%d,%d]'%(i,j))

# Add degree-2 constraint

m.addConstrs(vars.sum(i, '*') == 2 for i in range(n))

# Using Python looping constructs, the preceding would be...
#
# for i in range(n):
#   m.addConstr(sum(vars[i,j] for j in range(n)) == 2)

# Optimize model

m._vars = vars
m.Params.lazyConstraints = 1
m.optimize(subtourelim)

vals = m.getAttr('x', vars)
selected = tuplelist((i, j) for i, j in vals.keys() if vals[i, j] > 0.5)

tour = subtour(selected)
assert len(tour) == n

print('')
print('Optimal tour: %s' % str(tour))
print('Optimal cost: %g' % m.objVal)
print('')
Exemplo n.º 7
0
def make_model(strong_inequalities=False,
               relax=False,
               callback=False,
               hascapacity=1):
    # Relabel data
    commodities = data.commodities
    arcs = data.arcs
    capacity = data.capacity
    variable_cost = data.variable_cost
    fixed_cost = data.fixed_cost
    nodes = data.nodes
    demand = data.demand
    periods = data.periods

    # Create optimization model
    env = Env(logfilename="")
    m = Model('multi-period-netflow', env)

    # Create variables
    flow, arc_open = {}, {}
    for t in periods:
        for i, j in arcs:
            arc_open[i, j, t] = m.addVar(vtype=GRB.BINARY,
                                         lb=0.0,
                                         ub=1.0,
                                         obj=fixed_cost[(i, j), t],
                                         name='open_{0:d}_{1:d}_{2:d}'.format(
                                             i, j, t))
            for h in commodities:
                origin, destination = [
                    key_val[1] for key_val in demand.keys() if key_val[0] == h
                ][0]
                upper = capacity[i,
                                 j] if has_capacity else demand[(h,
                                                                 (origin,
                                                                  destination),
                                                                 t)]
                flow[h, i, j,
                     t] = m.addVar(obj=variable_cost[i, j],
                                   name='flow_{0:d}_{1:d}_{2:d}_{3:d}'.format(
                                       h, i, j, t))
    m.update()

    # Arc capacity constraints and unique arc setup constraints
    constrs = []
    for (i, j) in arcs:
        m.addConstr(
            quicksum(arc_open[i, j, l]
                     for l in range(1,
                                    len(data.periods) + 1)) <= 1,
            'unique_setup{0:d}_{1:d}'.format(i, j))
        for t in periods:
            if not hascapacity:
                capacity[i, j] = sum(demand[i] for i in demand.keys()
                                     if i[2] == t)
            m.addConstr(
                quicksum(flow[h, i, j, t] for h in commodities) <=
                capacity[i, j] * quicksum(arc_open[i, j, s]
                                          for s in xrange(1, t + 1)),
                'cap_{0:d}_{1:d}_{2:d}'.format(i, j, t))
            if not callback and strong_inequalities:
                for (commodity, (origin, destination), period) in demand:
                    if period == t:
                        constrs.append(
                            m.addConstr(
                                flow[commodity, i, j, t] <=
                                demand[commodity,
                                       (origin, destination), period] *
                                quicksum(arc_open[i, j, l]
                                         for l in range(1, t + 1)),
                                name='strong_com{0:d}_{1:d}-{2:d}_per{3:d}'.
                                format(commodity, i, j, t)))

    # Flow conservation constraints
    for (commodity, (origin, destination), period) in demand:
        for j in nodes:
            if j == origin:
                node_demand = demand[commodity, (origin, destination), period]
            elif j == destination:
                node_demand = -demand[commodity, (origin, destination), period]
            else:
                node_demand = 0
            h = commodity
            m.addConstr(
                -quicksum(flow[h, i, j, period]
                          for i, j in arcs.select('*', j)) +
                quicksum(flow[h, j, k, period]
                         for j, k in arcs.select(j, '*')) == node_demand,
                'node_{0:d}_{1:d}_{2:d}'.format(h, j, period))

    m.update()

    # Compute optimal solution
    m.setParam("TimeLimit", 7200)
    # m.params.NodeLimit = 1
    # m.params.cuts = 0
    # m.setParam("Threads", 2)
    m.setAttr('Lazy', constrs, [3] * len(constrs))
    # m.write("eyes.lp")
    #
    try:
        if strong_inequalities:
            if not relax:
                # m.setParam("NodeLimit", 1000000)
                # m.params.Cuts = 0
                if callback:
                    print 'callback in action! :)'
                    m.params.preCrush = 1
                    m.update()
                    m._vars = m.getVars()
                    m.optimize(strong_inequalities_callback)
                else:
                    m.optimize(time_callback)
            else:
                m = m.relax()
                m.optimize(time_callback)

        else:
            m.optimize(time_callback)
        if PRINT_VARS:
            for var in m.getVars():
                if str(var.VarName[0]) == 'f' and var.X > 0.0001:
                    name = var.VarName.split('_')
                    print 'arc: \t {} \t commodity: {} \t period: {} \t value: \t {}'.format(
                        (int(name[2]), int(name[3])), int(name[1]),
                        int(name[4]), var.x)
            # Grab the positive flows and see how many variables open during the first period
            positive_flows = [
                var for var in m.getVars()
                if var.VarName[0] == 'o' and var.X > 0.5
            ]
            first_period_arcs = sum([
                var.X for var in positive_flows
                if int(var.VarName.split('_')[3]) == 1
            ])
            print '% of arcs that open in first period: {}%'.format(
                100 * first_period_arcs / len(positive_flows))
            print '% of arcs that are utilized: {}%'.format(
                (100. * len(positive_flows)) / len(data.arcs))
            objective = m.getObjective().getValue()
            fixed_cost_percentage = sum([
                fixed_cost[(i, j), t] * arc_open[i, j, t].X
                for i, j in data.arcs for t in data.periods
            ]) / objective
            print 'Fixed cost percentage: {}%'.format(fixed_cost_percentage *
                                                      100.)
            for var in m.getVars():
                if str(var.VarName[0]) == 'o' and var.X > 0.0001:
                    name = var.VarName.split('_')
                    print 'Arc: \t {} \t Period: {} \t Value: \t {}'.format(
                        (int(name[1]), int(name[2])), int(name[3]), var.X)
            # m.write('trial2.lp')
    except:
        if m.status == GRB.status.INFEASIBLE and DEBUG:
            print 'Infeasible model. Computing IIS..'
            m.computeIIS()
            m.write('trial.ilp')
Exemplo n.º 8
0
def _solve_set_covering(N,
                        active_ptls,
                        relaxed_ptls,
                        forbidden_combos,
                        allow_infeasible=True,
                        D=None,
                        K=None):
    """A helper function that solves a set covering problem. The inputs are
    a list of node sets and corresponding costs for the set.
    --
    Fisher, M. L. and Jaikumar, R. (1981), A generalized assignment 
    heuristic for vehicle routing. Networks, 11: 109-124.
    """

    m = Model("SCPCVRP")
    nactive = len(active_ptls.routes)
    nrelaxed = len(relaxed_ptls.routes)
    nforbidden = len(forbidden_combos)

    # the order of the keys is important when we interpret the results
    X_j_keys = range(nactive + nrelaxed)
    # variables and the objective
    X_j_costs = active_ptls.costs + relaxed_ptls.costs
    X_j_node_sets = active_ptls.nodes + relaxed_ptls.nodes
    #update forbidden indices to match the current active petal set
    X_j_forbidden_combos = []
    for fc in forbidden_combos:
        if all(i < nactive for i in fc):
            X_j_forbidden_combos.append(
                [i if i >= 0 else -i - 1 + nactive for i in fc])

    #print("REMOVEME: Solving with K=%d, %d node sets, and %d solutions forbidden" % (K, nactive+nrelaxed,nforbidden))

    if __debug__:
        log(
            DEBUG, "Solving over-constrained VRP as a set covering problem " +
            "with %d petals, where %d of the possible configurations are forbidden."
            % (nactive + nrelaxed, nforbidden))
        if nforbidden > 0:
            log(DEBUG - 2, " and with following solutions forbidden:")
            log(DEBUG - 3, "(petal indices = %s)" % str(X_j_forbidden_combos))
            for fc in X_j_forbidden_combos:
                fc_sol = [0]
                for i in fc:
                    if i < nactive:
                        fc_sol.extend(active_ptls.routes[i][1:])
                    else:
                        fc_sol.extend(relaxed_ptls.routes[i - nactive][1:])
                fc_sol = without_empty_routes(fc_sol)
                log(DEBUG - 2, "%s (%.2f)" % (fc_sol, objf(fc_sol, D)))

    X_j = m.addVars(X_j_keys, obj=X_j_costs, vtype=GRB.BINARY, name='x')

    ## constraints
    c1_constrs, c2_constrs, c3_constrs = _add_set_covering_constraints(
        m, N, K, X_j, X_j_keys, X_j_node_sets, X_j_forbidden_combos)

    ## update the model and solve
    m._vars = X_j
    m.modelSense = GRB.MINIMIZE
    m.update()
    # disable output
    m.setParam('OutputFlag', 0)
    m.setParam('TimeLimit', MAX_MIP_SOLVER_RUNTIME)
    m.setParam('Threads', MIP_SOLVER_THREADS)
    #m.write("petalout.lp")
    m.optimize()

    # restore SIGINT callback handler which is changed by gurobipy
    signal(SIGINT, default_int_handler)

    if __debug__:
        log(DEBUG - 2, "Gurobi runtime = %.2f" % m.Runtime)
        if m.Status == GRB.OPTIMAL:
            log(DEBUG - 3,
                "Gurobi objective = %.2f" % m.getObjective().getValue())

    if m.Status == GRB.OPTIMAL:
        return _decision_variables_to_petals(m.X, active_ptls,
                                             relaxed_ptls), True
    elif m.Status == GRB.TIME_LIMIT:
        raise GurobiError(
            10023, "Gurobi timeout reached when attempting to solve SCPCVRP")
    elif m.Status == GRB.INTERRUPTED:
        raise KeyboardInterrupt()
    # Sometimes the solution is infeasible, try to relax it a little.
    elif m.Status == GRB.INFEASIBLE and allow_infeasible:
        return _relax_customer_constraints_with_feasRelax(
            m, c1_constrs, active_ptls, relaxed_ptls)
    return None, False
Exemplo n.º 9
0
def solve_tsp_gurobi(D, selected_idxs):
    #print
    #print "============== GUROBI ============="

    n = len(selected_idxs)
    if selected_idxs[0] == selected_idxs[-1]:
        n = n - 1

    # no need to invoke Gurobi for tiny TSP cases
    if n <= 3:
        sol = None
        obj_f = 0.0
        if n > 1:
            sol = list(selected_idxs)
            if sol[0] != sol[-1]:
                sol.append(sol[0])
            obj_f = sum(D[sol[i - 1], sol[i]] for i in range(1, len(sol)))
        return sol, obj_f

    m = Model("TSP")
    m.params.OutputFlag = 0

    # Create variables
    edgevars = {}

    for i in range(n):
        for j in range(i + 1):
            from_node = selected_idxs[i]
            to_node = selected_idxs[j]
            edgevars[i, j] = m.addVar(obj=D[from_node, to_node],
                                      vtype=GRB.BINARY,
                                      name='e' + str(i) + '_' + str(j))
            edgevars[j, i] = edgevars[i, j]
        m.update()

    # Add degree-2 constraint, and forbid loops
    for i in range(n):
        m.addConstr(quicksum(edgevars[i, j] for j in range(n)) == 2)
        edgevars[i, i].ub = 0
    m.update()

    # Optimize model
    m._vars = edgevars
    m.params.LazyConstraints = 1
    m.setParam('TimeLimit', MAX_MIP_SOLVER_RUNTIME)
    m.setParam('Threads', MIP_SOLVER_THREADS)

    m.optimize(_subtourelim)

    # restore SIGINT callback handler which is changed by gurobipy
    signal(SIGINT, default_int_handler)

    status = m.Status
    if status == GRB.TIME_LIMIT:
        raise GurobiError(
            10023, "Gurobi timeout reached when attempting to solve TSP")
    elif m.Status == GRB.INTERRUPTED:
        raise KeyboardInterrupt()

    solution = m.getAttr('x', edgevars)
    selected = [(i, j) for i in range(n) for j in range(n)
                if solution[i, j] > 0.5]
    cycles = _subtour(selected, n)
    assert len(cycles) == n

    # make the route always start from the 1st index
    sol = [selected_idxs[i] for i in cycles] + [selected_idxs[0]]
    obj_f = m.objVal
    return sol, obj_f
Exemplo n.º 10
0
    def __init__(self,
                 n_vertices,
                 edges,
                 constraints,
                 k,
                 minsize,
                 verbosity=0,
                 symmetry_breaking=True,
                 overlap=False,
                 single_cut=False,
                 timeout=None):
        self.check_graph(n_vertices, edges)
        self.n_vertices = n_vertices
        self.k = k
        self.verbosity = verbosity
        self.timeout = timeout

        model = Model('graph_clustering')

        mvars = []
        for i in range(k):
            cvars = []
            for j in range(n_vertices):
                v = model.addVar(lb=0.0, ub=1.0, vtype=GRB.BINARY)
                cvars.append(v)
            mvars.append(cvars)
        model.update()

        ineq_sense = GRB.GREATER_EQUAL if overlap else GRB.EQUAL
        # constraint: each vertex in exactly/at least one cluster
        for v in range(n_vertices):
            model.addConstr(quicksum([mvars[i][v] for i in range(k)]),
                            ineq_sense, 1)

        # symmetry-breaking constraints
        if symmetry_breaking:
            model.addConstr(mvars[0][0], GRB.EQUAL, 1)
            for i in range(2, k):
                model.addConstr(
                    quicksum([mvars[i - 1][j] for j in range(n_vertices)]) <=
                    quicksum([mvars[i][j] for j in range(n_vertices)]))

        # size constraint
        for i in range(k):
            model.addConstr(
                quicksum([mvars[i][v] for v in range(n_vertices)]) >= minsize)

        obj_expr = LinExpr()
        # indicators for violation of cl constraints
        for (u, v, w) in constraints:
            for i in range(k):
                y = model.addVar(lb=0.0, ub=1.0, vtype=GRB.BINARY)
                model.update()
                model.addConstr(y >= mvars[i][u] + mvars[i][v] - 1)
                obj_expr.add(y, w)

        model.setObjective(obj_expr, GRB.MINIMIZE)
        model.params.OutputFlag = self.verbosity
        model.Params.PreCrush = 1
        model.Params.LazyConstraints = 1

        model._cutfinder = Cut_Finder(n_vertices, edges)
        model._vars = mvars
        model._k = k
        model._relobj = None
        model._impcounter = 0
        model._single_cut = single_cut

        # runtime information
        model._root_cuttime = 0
        model._tree_cuttime = 0

        self.model = model