scipy: A numerical bug in scipy.optimize.linprog

Hi. I appreciate for contributors to scipy and extended communities. I learned the history of scipy on wikipedia and confirmed why this library is one of the highest quality OSS. But unfortunately, I found apparently a bug in scipy.optimize.linprog. As I run the following test.py, the LP solver incorrectly reports that LP is infeasible.

Reproducing code example:

#test.py
import codecs
from scipy.optimize import linprog
import numpy as np

c=[0,0,0,0,0,0,0,0,0]
ref = np.array([0.917113,0.270876,-0.395955,-0.196867,0.404834,1])
matA = np.array([
[0.656013,-0.365063,-0.983576,1.11294,0.579682,1],
[0.799693,0.50039,-1.32221,0.327986,0.694136,1],
[1.54842,-0.321544,-1.81128,0.949596,0.634807,1],
[1.12977,-0.271752,-1.74754,1.24775,0.641763,1],
[1.05679,0.506714,-0.583986,-0.627243,0.647723,1],
[1,0,0,0,0,1],
[0,1,0,0,0,1],
[0,0,0,1,0,1],
[0,0,0,0,1,1]
])
matA_in=matA.T.tolist()
vecb_in=ref.tolist()
xrange = [
[0,1],[0,1],[0,1],[0,1],[0,1],[0,1],[0,1],[0,1],[0,1]
]
res = linprog(c, A_eq=matA_in, b_eq=vecb_in,bounds=xrange, method='interior-point')
logfile=codecs.open('pyout.log','w','utf-8')
print(res, file=logfile)
if res.status == 2:
    print("0UNSAT",file=codecs.open('check_result.log','w','utf-8'))
else:
    print("1SAT",file=codecs.open('check_result.log','w','utf-8'))

# But the answer is SAT and
ans_x=np.array([0.04934082, 0, 0, 0.00702774, 0.57388938, 0.2703235, 0, 0.09941856, 0])
# and an evidence of correctness is that 
residual = np.dot(matA_in, ans_x)-ref
print("residual = ",file=logfile)
print(residual,file=logfile)
# we get residual = [-9.9293934e-07 -5.2687482e-07, -6.6600600e-08, -3.0158354e-07, -5.2039340e-07,  0.0000000e+00] 
#test.py end 

Error message:

scipy.optimize.linprog reports the following UNSAT decision.

con: array([-1.69741633, -0.25037501,  1.94817659, -1.35441056, -1.22095061,
       -2.57498303])

scipy.optimize.linprog reports the following UNSAT decision.      fun: 0.0
 message: 'The algorithm terminated successfully and determined that the problem is infeasible.'
     nit: 5
   slack: array([], dtype=float64)
  status: 2
 success: False
       x: array([0.70514251, 0.10725385, 0.53002431, 0.11401373, 0.59845995,
       0.4842205 , 0.62316526, 0.0673947 , 0.34530822]) 

Scipy/Numpy/Python version information:

Here is a version info. Perhaps little bit old?

>> import sys, scipy, numpy; print(scipy.__version__, numpy.__version__, sys.version_info)
1.2.1 1.17.4 sys.version_info(major=3, minor=7, micro=7, releaselevel='final', serial=0)

Can you reproduce this? I don’t have much time to look into the implementation of this library, but I can generate “MANY” similar class of incorrect reports having the same property orz.

A special property of ref and matA is that all row vectors satisfies the following constraint; [1,1,1,1,1,-1].ref=0 [1,1,1,1,1,-1].matA[i]=0, for any i, 0<=i<9

Here ref and matA[i] are on the same hyper-plane which share the same normal vector [1,1,1,1,1,-1]. So my initial guess is that the bug might result from inadequately solving a matrix equation. If a constraint matrix matA is rank-deficient. A matrix inversion could break a solution to Given_matrix*vecX=Given_vecB. In Eigen3 which I use, there are three QR decompositions with such rank-deficiency in mind. eigen.tuxfamily.org/dox/classEigen_1_1FullPivHouseholderQR.html eigen.tuxfamily.org/dox/classEigen_1_1ColPivHouseholderQR.html eigen.tuxfamily.org/dox/classEigen_1_1HouseholderQR.html

IPOPT also fails to figure out a SATsifiable assignment and instead converge incorrectly in a similar setting. But it happens less frequently than scipy.optimize.linprog showed. Another guess is that it counld result from a difficulty of solving an LP using the current floating point arithmetic IEEE754-FP that causes a rounding-error. A similar technical issue might be in scipy.optimize.linprog/interior-point. But not for sure.

Best regards, Masataka Nishi

About this issue

  • Original URL
  • State: closed
  • Created 4 years ago
  • Comments: 26 (14 by maintainers)

Most upvoted comments

Thanks. Sorry the existing linprog didn’t work out for you. I know you switched to C++, but in case you prefer Python, HiGHS will be available through the linprog interface in the next release of SciPy.

I switched to HiGHS-1.0 (C++ API) and it does not fail in the cases that I reported above. Now the matA grows up >5 rows*800 columns. Excellent! (though nobody know if I could report a bug next month…) I appreciate for @mdhaber, @pv @jajhall and @mckib2 for these fast, sincere and interactive debugging.

It also might be worthwhile for you to install a development version of the code so you can modify it.

You would also be able to use HiGHS through it without changing your code. We added HiGHS simplex and IPM as linprog methods but those aren’t in a release yet.