python-mip icon indicating copy to clipboard operation
python-mip copied to clipboard

Model.write() outputs incorrect objective row in MPS format

Open ryanjoneil opened this issue 3 years ago • 3 comments

Describe the bug

When I formulate a model in python-mip to maximize an objective function and write it to a .mps file, the objective coefficients have the wrong sign.

To Reproduce

Construct the 0-1 knapsack model from the examples and write it to a .mps file.

from mip import Model, xsum, maximize, BINARY

p = [10, 13, 18, 31, 7, 15]
w = [11, 15, 20, 35, 10, 33]
c, I = 47, range(len(w))

m = Model("knapsack")

x = [m.add_var(var_type=BINARY) for i in I]

m.objective = maximize(xsum(p[i] * x[i] for i in I))

m += xsum(w[i] * x[i] for i in I) <= c

m.write('knapsack.mps')

Now call cbc or highs on the resulting file.

$ cbc knapsack.mps

The optimal objective is (incorrectly) 0.

Welcome to the CBC MILP Solver 
Version: 2.10.8 
Build Date: May  7 2022 

command line - cbc knapsack.mps (default strategy 1)
At line 1 NAME          knapsack  FREE
At line 2 ROWS
At line 5 COLUMNS
At line 12 RHS
At line 14 BOUNDS
At line 21 ENDATA
Problem knapsack has 1 rows, 6 columns and 6 elements
Coin0008I knapsack read with 0 errors
Continuous objective value is 0 - 0.00 seconds
Cgl0004I processed model has 0 rows, 0 columns (0 integer (0 of which binary)) and 0 elements
Cbc3007W No integer variables - nothing to do
Cuts at root node changed objective from 0 to -1.79769e+308
Probing was tried 0 times and created 0 cuts of which 0 were active after adding rounds of cuts (0.000 seconds)
Gomory was tried 0 times and created 0 cuts of which 0 were active after adding rounds of cuts (0.000 seconds)
Knapsack was tried 0 times and created 0 cuts of which 0 were active after adding rounds of cuts (0.000 seconds)
Clique was tried 0 times and created 0 cuts of which 0 were active after adding rounds of cuts (0.000 seconds)
MixedIntegerRounding2 was tried 0 times and created 0 cuts of which 0 were active after adding rounds of cuts (0.000 seconds)
FlowCover was tried 0 times and created 0 cuts of which 0 were active after adding rounds of cuts (0.000 seconds)
TwoMirCuts was tried 0 times and created 0 cuts of which 0 were active after adding rounds of cuts (0.000 seconds)
ZeroHalf was tried 0 times and created 0 cuts of which 0 were active after adding rounds of cuts (0.000 seconds)

Result - Optimal solution found

Objective value:                0.00000000
Enumerated nodes:               0
Total iterations:               0
Time (CPU seconds):             0.00
Time (Wallclock seconds):       0.00

Total time (CPU seconds):       0.00   (Wallclock seconds):       0.00

Expected behavior

This happens because the MPS file is minimizing the objective. The coefficients of the output file should be - in this case.

NAME          knapsack  FREE
ROWS
 N  OBJROW
 L  constr(0)
COLUMNS
    var(0) OBJROW 10.          constr(0) 11.         
    var(1) OBJROW 13.          constr(0) 15.         
    var(2) OBJROW 18.          constr(0) 20.         
    var(3) OBJROW 31.          constr(0) 35.         
    var(4) OBJROW 7.           constr(0) 10.         
    var(5) OBJROW 15.          constr(0) 33.         
RHS
    RHS constr(0) 47.         
BOUNDS
 BV BOUND var(0) 1.          
 BV BOUND var(1) 1.          
 BV BOUND var(2) 1.          
 BV BOUND var(3) 1.          
 BV BOUND var(4) 1.          
 BV BOUND var(5) 1.          
ENDATA

If I change their signs:

NAME          knapsack  FREE
ROWS
 N  OBJROW
 L  constr(0)
COLUMNS
    var(0) OBJROW -10.          constr(0) 11.         
    var(1) OBJROW -13.          constr(0) 15.         
    var(2) OBJROW -18.          constr(0) 20.         
    var(3) OBJROW -31.          constr(0) 35.         
    var(4) OBJROW -7.           constr(0) 10.         
    var(5) OBJROW -15.          constr(0) 33.         
RHS
    RHS constr(0) 47.         
BOUNDS
 BV BOUND var(0) 1.          
 BV BOUND var(1) 1.          
 BV BOUND var(2) 1.          
 BV BOUND var(3) 1.          
 BV BOUND var(4) 1.          
 BV BOUND var(5) 1.          
ENDATA

Then the solution is correct.

Result - Optimal solution found

Objective value:                -41.00000000
Enumerated nodes:               0
Total iterations:               0
Time (CPU seconds):             0.01
Time (Wallclock seconds):       0.01

Total time (CPU seconds):       0.01   (Wallclock seconds):       0.01

Desktop (please complete the following information):

  • Operating System, version: 11.6.7
  • Python version: 3.7.8
  • Python-MIP version (we recommend you to test with the latest version): 1.13.0

ryanjoneil avatar Aug 02 '22 18:08 ryanjoneil

Noting that this is an issue with Cbc. I posed the question on #164 as to whether it makes sense to defer writing model files using it, since it seems to have issues generating correct output.

ryanjoneil avatar Aug 03 '22 15:08 ryanjoneil

Confirmed: If I change m = Model(sense=MAXIMIZE) to m = Model(sense=MINIMIZE) and switch the sign of every variable in the objective function in my Python code, the the result of m.write('model.mps') is equivalent to m.write('model.lp') before the changes.

ryanjoneil avatar Aug 03 '22 18:08 ryanjoneil

Maybe related to https://github.com/coin-or/Cbc/issues/519

sebheger avatar Aug 05 '22 20:08 sebheger