3

I'm trying to implement a Gurobi model with multiple objective functions (specifically 2) that solves lexicographically (in a hierarchy) but I'm running into an issue where when optimizing the second objective function it degrades the solution to the first one, which should not happen with hierarchical optimizations. It is degrading the first solution up by 1, to decrease the second by 5, could this be an error in how I setup my model hierarchically? This is the code where I set up my model:

    m = Model('lexMin Model')
    m.ModelSense = GRB.MINIMIZE
    variable = m.addVars(k.numVars, vtype=GRB.BINARY, name='variable') 
    m.setObjectiveN(LinExpr(quicksum([variable[j]*k.obj[0][j] for j in range(k.numVars)])),0)
    m.setObjectiveN(LinExpr(quicksum([variable[j]*k.obj[1][j] for j in range(k.numVars)])),1)

    for i in range(0,k.numConst): 
        m.addConstr(quicksum([k.const[i,j]*variable[j] for j in range(k.numVars)] <= k.constRHS[i]))

        m.addConstr(quicksum([variable[j]*k.obj[0][j] for j in range(k.numVars)]) >= r2[0][0])
        m.addConstr(quicksum([variable[j]*k.obj[0][j] for j in range(k.numVars)]) <= r2[1][0])
        m.addConstr(quicksum([variable[j]*k.obj[1][j] for j in range(k.numVars)]) >= r2[1][1])
        m.addConstr(quicksum([variable[j]*k.obj[1][j] for j in range(k.numVars)]) <= r2[0][1])

    m.Params.ObjNumber = 0
    m.ObjNPriority = 1
    m.update()
    m.optimize()

I've double checked and the priority of the second function is 0, the value for the objective functions are nowhere near where they'd be if I prioritized the wrong function. When optimizing the first function it finds the right value, even, but when it moves on to the second value it chooses values that degrade the first value.

The Gurobi output looks like this:

Optimize a model with 6 rows, 375 columns and 2250 nonzeros
Model fingerprint: 0xac5de9aa
Variable types: 0 continuous, 375 integer (375 binary)
Coefficient statistics:
  Matrix range     [1e+01, 1e+02]
  Objective range  [1e+01, 1e+02]
  Bounds range     [1e+00, 1e+00]
  RHS range        [1e+04, 1e+04]

---------------------------------------------------------------------------
Multi-objectives: starting optimization with 2 objectives ... 
---------------------------------------------------------------------------

Multi-objectives: applying initial presolve ...
---------------------------------------------------------------------------

Presolve time: 0.00s
Presolved: 6 rows and 375 columns
---------------------------------------------------------------------------

Multi-objectives: optimize objective 1 () ...
---------------------------------------------------------------------------

Presolve time: 0.00s
Presolved: 6 rows, 375 columns, 2250 nonzeros
Variable types: 0 continuous, 375 integer (375 binary)

Root relaxation: objective -1.461947e+04, 10 iterations, 0.00 seconds

    Nodes    |    Current Node    |     Objective Bounds      |     Work
 Expl Unexpl |  Obj  Depth IntInf | Incumbent    BestBd   Gap | It/Node Time

     0     0 -14619.473    0    3          - -14619.473      -     -    0s
H    0     0                    -14569.00000 -14619.473  0.35%     -    0s
H    0     0                    -14603.00000 -14619.473  0.11%     -    0s
H    0     0                    -14608.00000 -14619.473  0.08%     -    0s
H    0     0                    -14611.00000 -14618.032  0.05%     -    0s
     0     0 -14617.995    0    5 -14611.000 -14617.995  0.05%     -    0s
     0     0 -14617.995    0    3 -14611.000 -14617.995  0.05%     -    0s
H    0     0                    -14613.00000 -14617.995  0.03%     -    0s
     0     0 -14617.995    0    5 -14613.000 -14617.995  0.03%     -    0s
     0     0 -14617.995    0    5 -14613.000 -14617.995  0.03%     -    0s
     0     0 -14617.995    0    7 -14613.000 -14617.995  0.03%     -    0s
     0     0 -14617.995    0    3 -14613.000 -14617.995  0.03%     -    0s
     0     0 -14617.995    0    4 -14613.000 -14617.995  0.03%     -    0s
     0     0 -14617.995    0    6 -14613.000 -14617.995  0.03%     -    0s
     0     0 -14617.995    0    6 -14613.000 -14617.995  0.03%     -    0s
     0     0 -14617.995    0    6 -14613.000 -14617.995  0.03%     -    0s
     0     0 -14617.720    0    7 -14613.000 -14617.720  0.03%     -    0s
     0     0 -14617.716    0    8 -14613.000 -14617.716  0.03%     -    0s
     0     0 -14617.697    0    8 -14613.000 -14617.697  0.03%     -    0s
     0     0 -14617.661    0    9 -14613.000 -14617.661  0.03%     -    0s
     0     2 -14617.661    0    9 -14613.000 -14617.661  0.03%     -    0s
*  823     0              16    -14614.00000 -14616.351  0.02%   2.8    0s

Cutting planes:
  Gomory: 6
  Cover: 12
  MIR: 4
  StrongCG: 2
  Inf proof: 6
  Zero half: 1

Explored 1242 nodes (3924 simplex iterations) in 0.29 seconds
Thread count was 8 (of 8 available processors)

Solution count 6: -14614 -14613 -14611 ... -14569
No other solutions better than -14614

Optimal solution found (tolerance 1.00e-04)
Best objective -1.461400000000e+04, best bound -1.461400000000e+04, gap 0.0000%
---------------------------------------------------------------------------

Multi-objectives: optimize objective 2 () ...
---------------------------------------------------------------------------


Loaded user MIP start with objective -12798

Presolve removed 1 rows and 0 columns
Presolve time: 0.01s
Presolved: 6 rows, 375 columns, 2250 nonzeros
Variable types: 0 continuous, 375 integer (375 binary)

Root relaxation: objective -1.282967e+04, 28 iterations, 0.00 seconds

    Nodes    |    Current Node    |     Objective Bounds      |     Work
 Expl Unexpl |  Obj  Depth IntInf | Incumbent    BestBd   Gap | It/Node Time

     0     0 -12829.673    0    3 -12798.000 -12829.673  0.25%     -    0s
     0     0 -12829.378    0    4 -12798.000 -12829.378  0.25%     -    0s
     0     0 -12829.378    0    3 -12798.000 -12829.378  0.25%     -    0s
     0     0 -12828.688    0    4 -12798.000 -12828.688  0.24%     -    0s
H    0     0                    -12803.00000 -12828.688  0.20%     -    0s
     0     0 -12825.806    0    5 -12803.000 -12825.806  0.18%     -    0s
     0     0 -12825.193    0    5 -12803.000 -12825.193  0.17%     -    0s
     0     0 -12823.156    0    6 -12803.000 -12823.156  0.16%     -    0s
     0     0 -12822.694    0    7 -12803.000 -12822.694  0.15%     -    0s
     0     0 -12822.679    0    7 -12803.000 -12822.679  0.15%     -    0s
     0     2 -12822.679    0    7 -12803.000 -12822.679  0.15%     -    0s

Cutting planes:
  Cover: 16
  MIR: 6
  StrongCG: 3
  Inf proof: 4
  RLT: 1

Explored 725 nodes (1629 simplex iterations) in 0.47 seconds
Thread count was 8 (of 8 available processors)

Solution count 2: -12803 -12798 
No other solutions better than -12803

Optimal solution found (tolerance 1.00e-04)
Best objective -1.280300000000e+04, best bound -1.280300000000e+04, gap 0.0000%

So it finds the values (-14613,-12803) instead of (-14614,-12798)

mattmilten
  • 6,242
  • 3
  • 35
  • 65

1 Answers1

3

The default MIPGap is 1e-4. The first objective is degrading by less than that. (1/14614 =~ 0.7 e-4). If you lower the MIPGap, your issue should go away. In your code add

m.setObjective('MipGap', 1e-6)

before the optimize.

One way to reason about this behavior is that since you had a MIPGap of 1e-4, you would have accepted the a solution with value -14113, even if you didn't have a second objective.

David Nehme
  • 21,379
  • 8
  • 78
  • 117