coin-or / Clp

COIN-OR Linear Programming Solver
Other
392 stars 82 forks source link

Misreporting of primal infeasbile as of release 1.17.8 #264

Open jwnimmer-tri opened 1 year ago

jwnimmer-tri commented 1 year ago

In Clp 1.17.7, the following program correctly reports a model.status() of 1 - primal infeasible.

In Clp 1.17.8, the status is mis-reported as 4 - stopped due to errors.

NAME          ClpDefau
ROWS
 N  OBJROW
 L  R0000000
 E  R0000001
COLUMNS
    C0000000  OBJROW     -1.           R0000000  1.          
    C0000000  R0000001  2.          
    C0000001  OBJROW     -1.           R0000000  2.          
    C0000001  R0000001  1.          
RHS
    RHS       R0000000  3.             R0000001  4.          
BOUNDS
 LO BOUND     C0000001  2.          
ENDATA

I tested this using the reproduction script (attached in my next post) on Ubuntu 22.04 with GCC 11.3.0 and all of coinbrew's default options.

Bisecting, the failure started happening as of commit 85648fa3894dd6b0bfadce5a698fa6aec8a23b7c (and notably the subsequent commit 4152b2e404a411634398366054e50be0a9ddbb11 did not fix it), and is still failing on the 1.17.8 release tag. FYI @jjhforrest.

\CC @hongkai-dai

jwnimmer-tri commented 1 year ago

Reproduction script:

#!/bin/bash

set -euo pipefail

cat > example.cpp <<EOF
#include "ClpSimplex.hpp"
#include <iostream>

int main(int argc, const char* argv[]) {
  ClpSimplex model;
  model.readMps("infeasible.mps", true);
  model.primal();
  std::cout << "status = " << model.status() << "\n";
  if (model.status() == 1) {
    return 0;  // success
  }
  return 1;  // failure
}
EOF

cat > infeasible.mps <<EOF
NAME          ClpDefau
ROWS
 N  OBJROW
 L  R0000000
 E  R0000001
COLUMNS
    C0000000  OBJROW     -1.           R0000000  1.          
    C0000000  R0000001  2.          
    C0000001  OBJROW     -1.           R0000000  2.          
    C0000001  R0000001  1.          
RHS
    RHS       R0000000  3.             R0000001  4.          
BOUNDS
 LO BOUND     C0000001  2.          
ENDATA
EOF

mkdir good bad
(cd good && wget https://raw.githubusercontent.com/coin-or/coinbrew/master/coinbrew)
chmod u+x good/coinbrew
cp good/coinbrew bad/

(cd good && ./coinbrew fetch Clp@1.17.7)
(cd good && ./coinbrew build --tests=none Clp)
(cd good && g++ -omain ../example.cpp $(env PKG_CONFIG_PATH=dist/lib/pkgconfig pkg-config --cflags --libs clp))
LD_LIBRARY_PATH=good/dist/lib good/main

(cd bad && ./coinbrew fetch Clp@1.17.8)
(cd bad && ./coinbrew build --tests=none Clp)
(cd bad && g++ -omain ../example.cpp $(env PKG_CONFIG_PATH=dist/lib/pkgconfig pkg-config --cflags --libs clp))
LD_LIBRARY_PATH=bad/dist/lib bad/main

Output from 1.17.7:

Coin0001I At line 1 NAME          ClpDefau
Coin0001I At line 2 ROWS
Coin0001I At line 6 COLUMNS
Coin0001I At line 11 RHS
Coin0001I At line 13 BOUNDS
Coin0001I At line 15 ENDATA
Coin0002I Problem ClpDefau has 2 rows, 2 columns and 4 elements
Clp0027I Model was imported from infeasible.mps in 0.000235 seconds
Clp0006I 0  Obj -2 Primal inf 2.9999998 (2) Dual inf 1e+10 (1)
Clp0006I 1  Obj -3 Primal inf 1.9999999 (1)
Clp0006I 1  Obj -3 Primal inf 1.9999999 (1) Dual inf 0.4999999 (1)
Clp0006I 2  Obj -2.3333333 Primal inf 1.3333332 (1)
Clp0001I Primal infeasible - objective value -2.3333333
status = 1

Output from 1.17.8:

Coin0001I At line 1 NAME          ClpDefau
Coin0001I At line 2 ROWS
Coin0001I At line 6 COLUMNS
Coin0001I At line 11 RHS
Coin0001I At line 13 BOUNDS
Coin0001I At line 15 ENDATA
Coin0002I Problem ClpDefau has 2 rows, 2 columns and 4 elements
Clp0027I Model was imported from infeasible.mps in 0.000194 seconds
Clp0006I 0  Obj -2 Primal inf 2.9999998 (2) Dual inf 1e+10 (1)
Clp0006I 1  Obj -3 Primal inf 1.9999999 (1)
Clp0006I 1  Obj -3 Primal inf 1.9999999 (1) Dual inf 0.4999999 (1)
status = 4
hongkai-dai commented 1 year ago

BTW, the linear program we solve is very simple

  max x0 + x1
  s.t x0 + 2 * x1 <= 3;
      2 * x0 + x1 == 4
      x0 >= 0, x1 >= 2

which is obviously infeasible given x0 >= 0, x1 >= 2 and x0 + 2 * x1 <= 3.

jjhforrest commented 1 year ago

Seems to be a series of tiny things going wrong. The code starts off with primal, but then switches to dual and then thinks infeasible after one iteration. The code tested number of iterations since last re-factorization. If >1 would be OK and ==0 also. It decided to switch back to primal, but code gets confused and thinks user wants to stop. I don't think the fix I will put in can do any harm.