A numerical bug in scipy.optimize.linprog
See original GitHub issueHi. 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
Issue Analytics
- State:
 - Created 3 years ago
 - Comments:26 (11 by maintainers)
 

Top Related StackOverflow Question
Thanks. Sorry the existing
linprogdidn’t work out for you. I know you switched to C++, but in case you prefer Python, HiGHS will be available through thelinproginterface 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.