Repository: scipy/scipy

Issue:
`scipy.optimize.linprog` `interior-point` presolve bug with trivial bounds
`linprog` with option `interior-point` can report incorrect solution when bounds trivially constrain problem to be infeasible. When solution is finished in presolve, it should be checking for infeasibility/unboundedness but I guess this one slipped through the cracks.

### Reproducing code example:
```
from scipy.optimize import linprog
c = [-4, 1];
A_ub = [[7, -2], [0, 1], [2, -2]]
b_ub = [14, 3, 3]
bounds= [(2, 2), (0,0)]
sol = linprog(c, A_ub = A_ub, b_ub = b_ub, bounds = bounds, method = "interior-point")
print sol
```

### Output:
```
     con: array([], dtype=float64)
     fun: -8.0
 message: 'The solution was determined in presolve as there areno non-trivial constraints.'
     nit: 0
   slack: array([ 0.,  3., -1.])
  status: 0
 success: True
       x: array([ 2.,  0.])
```
But this is incorrect; it violates the third constraint.

### Scipy/Numpy/Python version information:
```
('1.0.0', '1.13.1', sys.version_info(major=2, minor=7, micro=13, releaselevel='final', serial=0))
```

`scipy.optimize.linprog` `interior-point` presolve bug with trivial bounds
`linprog` with option `interior-point` can report incorrect solution when bounds trivially constrain problem to be infeasible. When solution is finished in presolve, it should be checking for infeasibility/unboundedness but I guess this one slipped through the cracks.

### Reproducing code example:
```
from scipy.optimize import linprog
c = [-4, 1];
A_ub = [[7, -2], [0, 1], [2, -2]]
b_ub = [14, 3, 3]
bounds= [(2, 2), (0,0)]
sol = linprog(c, A_ub = A_ub, b_ub = b_ub, bounds = bounds, method = "interior-point")
print sol
```

### Output:
```
     con: array([], dtype=float64)
     fun: -8.0
 message: 'The solution was determined in presolve as there areno non-trivial constraints.'
     nit: 0
   slack: array([ 0.,  3., -1.])
  status: 0
 success: True
       x: array([ 2.,  0.])
```
But this is incorrect; it violates the third constraint.

### Scipy/Numpy/Python version information:
```
('1.0.0', '1.13.1', sys.version_info(major=2, minor=7, micro=13, releaselevel='final', serial=0))
```



Discussion:

Resolving patch:
diff --git a/scipy/optimize/_linprog_ip.py b/scipy/optimize/_linprog_ip.py
--- a/scipy/optimize/_linprog_ip.py
+++ b/scipy/optimize/_linprog_ip.py
@@ -543,6 +543,21 @@ def where(A):
     # identical bounds indicate that variable can be removed
     i_f = np.abs(lb - ub) < tol   # indices of "fixed" variables
     i_nf = np.logical_not(i_f)  # indices of "not fixed" variables
+
+    # test_bounds_equal_but_infeasible
+    if np.all(i_f):  # if bounds define solution, check for consistency
+        residual = b_eq - A_eq.dot(lb)
+        slack = b_ub - A_ub.dot(lb)
+        if ((A_ub.size > 0 and np.any(slack < 0)) or
+                (A_eq.size > 0 and not np.allclose(residual, 0))):
+            status = 2
+            message = ("The problem is (trivially) infeasible because the "
+                       "bounds fix all variables to values inconsistent with "
+                       "the constraints")
+            complete = True
+            return (c, c0, A_ub, b_ub, A_eq, b_eq, bounds,
+                    x, undo, complete, status, message)
+
     if np.any(i_f):
         c0 += c[i_f].dot(lb[i_f])
         b_eq = b_eq - A_eq[:, i_f].dot(lb[i_f])
@@ -562,7 +577,7 @@ def where(A):
         # test_empty_constraint_1
         if c.size == 0:
             status = 0
-            message = ("The solution was determined in presolve as there are"
+            message = ("The solution was determined in presolve as there are "
                        "no non-trivial constraints.")
         elif (np.any(np.logical_and(c < 0, ub == np.inf)) or
                 np.any(np.logical_and(c > 0, lb == -np.inf))):
@@ -570,8 +585,8 @@ def where(A):
             status = 3
             message = ("If feasible, the problem is (trivially) unbounded "
                        "because there are no constraints and at least one "
-                       " element of c is negative. If you wish to check "
-                       " whether the problem is infeasible, turn presolve "
+                       "element of c is negative. If you wish to check "
+                       "whether the problem is infeasible, turn presolve "
                        "off.")
         else:  # test_empty_constraint_2
             status = 0


Test patch:
diff --git a/scipy/optimize/tests/test_linprog.py b/scipy/optimize/tests/test_linprog.py
--- a/scipy/optimize/tests/test_linprog.py
+++ b/scipy/optimize/tests/test_linprog.py
@@ -661,6 +661,24 @@ def cb(xk, **kwargs):
 class BaseTestLinprogIP(LinprogCommonTests):
     method = "interior-point"
 
+    def test_bounds_equal_but_infeasible(self):
+        c = [-4, 1]
+        A_ub = [[7, -2], [0, 1], [2, -2]]
+        b_ub = [14, 0, 3]
+        bounds = [(2, 2), (0, None)]
+        res = linprog(c=c, A_ub=A_ub, b_ub=b_ub, bounds=bounds,
+                      method=self.method)
+        _assert_infeasible(res)
+
+    def test_bounds_equal_but_infeasible2(self):
+        c = [-4, 1]
+        A_eq = [[7, -2], [0, 1], [2, -2]]
+        b_eq = [14, 0, 3]
+        bounds = [(2, 2), (0, None)]
+        res = linprog(c=c, A_eq=A_eq, b_eq=b_eq, bounds=bounds,
+                      method=self.method)
+        _assert_infeasible(res)
+
     def test_magic_square_bug_7044(self):
         # test linprog with a problem with a rank-deficient A_eq matrix
         A, b, c, N = magic_square(3)