• Gurobi Staff

Can you please provide a minimum reproducible example? See Tutorial: Preparing a Minimal Reproducible Example

What do you mean by constraint cannot be added? Do you see any error messages?

Hi,

To describe my issue, I add a set of continuous variables to the TSP problem in the Gurobi example for callback. The detailed model can be seen:

The detailed code can be seen:

import mathimport randomfrom itertools import combinationsimport gurobipy as gpfrom gurobipy import GRBrandom.seed(1)# Callback - use lazy constraints to eliminate sub-toursdef subtourelim(model, where):    if where == GRB.Callback.MIPSOL:        vals = model.cbGetSolution(model._vars)        print(vals)        eta = model.cbGetSolution(model._eta)        print('------')        print(eta)        # find the shortest cycle in the selected edge list        tour = subtour(vals)        if len(tour) < n:            print(" --- exists subtour --- ")            # add subtour elimination constr. for every pair of cities in tour            model.cbLazy(gp.quicksum(model._vars[i, j]                                     for i, j in combinations(tour, 2))                         <= len(tour)-1)        for i in range(n):            if eta[i] > 1:                model.cbLazy(model._eta[i] <= 1)                print(model._eta[i] <= 1)def subtour(vals):    # make a list of edges selected in the solution    edges = gp.tuplelist((i, j) for i, j in vals.keys()                         if vals[i, j] > 0.5)    unvisited = list(range(n))    cycle = range(n+1)  # initial length has 1 more city    while unvisited:  # true if list is non-empty        thiscycle = []        neighbors = unvisited        while neighbors:            current = neighbors[0]            thiscycle.append(current)            unvisited.remove(current)            neighbors = [j for i, j in edges.select(current, '*')                         if j in unvisited]        if len(cycle) > len(thiscycle):            cycle = thiscycle    return cyclen = int(6)nodes = [i for i in range(n)]# Create n random pointsrandom.seed(1)points = [(random.randint(0, 100), random.randint(0, 100)) for i in range(n)]# Dictionary of Euclidean distance between each pair of pointsdist = {(i, j):        math.sqrt(sum((points[i][k]-points[j][k])**2 for k in range(2)))        for i in range(n) for j in range(i)}m = gp.Model()# Create variablesvars = m.addVars(dist.keys(), vtype=GRB.BINARY, name='e')for i, j in vars.keys():    vars[j, i] = vars[i, j]  # edge in opposite direction    var3 = m.addVars(nodes, vtype=GRB.CONTINUOUS, name='eta')obj = var3.sum()m.setObjective(obj, gp.GRB.MAXIMIZE)m.addConstrs(vars.sum(i, '*') == 2 for i in range(n))m._vars = varsm._eta = var3m.Params.LazyConstraints = 1m.optimize(subtourelim)vals = m.getAttr('X', vars)tour = subtour(vals)assert len(tour) == nprint('')print('Optimal tour: %s' % str(tour))print('Optimal cost: %g' % m.ObjVal)print('')

Obviously, the optimal value of each eta should be 1. But I get 'model is unbounded'. The lazy constraints about eta can be printed. So I guess the lazy constraints are not added. But I don't know why. Please suggest.

• Gurobi Staff

You defined an unbounded model that is detected to be unbounded in the root relaxation.
During presolve a solution is checked in your callback for feasibility. That's why you see the output of lazy constraints. So, the found solution is refused but the unboundedness is detected anyway.
You need to make the model bounded. If you set the upper bound of the eta-variable to a large value, you will see that your lazy constraints work and force eta to be less than or equal to 1.

But this is a very strange way of defining the model. If you know bounds for the variables, define them right away and not by using lazy constraints.