Repository: scipy/scipy

Issue:
linprog error: UnboundLocalError: local variable 'nit2' referenced before assignment
Hello!  First time posting an issue, so let me know if you need more/different information.

I am getting the following error when calling `optimize.linprog` in `scipy` v1.2.1.

```---------------------------------------------------------------------------
UnboundLocalError                         Traceback (most recent call last)
<ipython-input-46-27a3a22e62a0> in <module>()
----> 1 results = linprog(c, A_ub, b_ub, A_eq, b_eq)

/usr/local/anaconda/lib/python3.6/site-packages/scipy/optimize/_linprog.py in linprog(c, A_ub, b_ub, A_eq, b_eq, bounds, method, callback, options)
    464         if meth == 'simplex':
    465             x, status, message, iteration = _linprog_simplex(
--> 466                 c, c0=c0, A=A, b=b, callback=callback, _T_o=T_o, **solver_options)
    467         elif meth == 'interior-point':
    468             x, status, message, iteration = _linprog_ip(

/usr/local/anaconda/lib/python3.6/site-packages/scipy/optimize/_linprog_simplex.py in _linprog_simplex(c, c0, A, b, maxiter, disp, callback, tol, bland, _T_o, **unknown_options)
    613     x = solution[:m]
    614 
--> 615     return x, status, messages[status], int(nit2)
    616 

UnboundLocalError: local variable 'nit2' referenced before assignment
```

From looking at https://github.com/scipy/scipy/blob/v1.2.1/scipy/optimize/_linprog_simplex.py, it appears that `nit2` is always returned, but only assigned if `status==0`, where `status` is returned from the call to `_solve_simplex`.

Unfortunately, I am working with somewhat large matrices, so I don't know how to easily provide the data here to reproduce the error.  The structure is as follows.

- The call is `results = linprog(c, A_ub, b_ub, A_eq, b_eq)`
- `c.shape` is `(41,)`
- `A_ub.shape, b_ub.shape` is `(150, 41), (150,)`
- `A_eq.shape, b_eq.shape` is `(1, 41), (1,)`


### Scipy/Numpy/Python version information:
```
import sys, scipy, numpy; print(scipy.__version__, numpy.__version__, sys.version_info)
1.2.1 1.16.1 sys.version_info(major=3, minor=6, micro=7, releaselevel='final', serial=0)
```



Discussion:
@jmpounders Have you tried `results = linprog(c, A_ub, b_ub, A_eq, b_eq, method = 'interior-point')`? It is much better for large systems (well, all but the very smallest systems).
BTW while `simplex` is the default now, there are two big changes that will help when SciPy 1.3 comes out. 

- `interior-point` will replace `simplex` as the default
- `revised simplex` (an implementation of the "revised simplex" method, not a revision of `simplex`) will become available

Note that `interior-point` does not generally provide a basic solution, that is, its solutions are on (or iteratively approaching) an optimal face rather than a vertex. 

If you need a basic solution, `revised simplex` will be the one to use. We'll keep `simplex` around, but `revised simplex` is intended to be the drop-in replacement.

Copy-pasting the matrices here is fine. They won't look good, but it would help for testing.
@mdhaber I had the same error so I thought I'd share my matrices. Not sure if this was true for @jmpounders but mine are both large and sparse. I'm also running v1.2.1. 

```none
lp_sol = linprog(c, A_ub=A_ub, b_ub=b_ub, A_eq=A_eq, b_eq=b_eq, options = {"maxiter":5000, "tol":1.0e-10,"disp": False})
  File "/usr/local/Cellar/python3/3.6.4/Frameworks/Python.framework/Versions/3.6/lib/python3.6/site-packages/scipy/optimize/_linprog.py", line 466, in linprog
    c, c0=c0, A=A, b=b, callback=callback, _T_o=T_o, **solver_options)
  File "/usr/local/Cellar/python3/3.6.4/Frameworks/Python.framework/Versions/3.6/lib/python3.6/site-packages/scipy/optimize/_linprog_simplex.py", line 615, in _linprog_simplex
    return x, status, messages[status], int(nit2)
UnboundLocalError: local variable 'nit2' referenced before assignment
```

I should also note that at least in my specific case using ```interior-point``` caused repeats of these two warnings:

```none
/usr/local/Cellar/python3/3.6.4/Frameworks/Python.framework/Versions/3.6/lib/python3.6/site-packages/scipy/optimize/_linprog_ip.py:273: OptimizeWarning: Solving system with option 'sym_pos':True failed. It is normal for this to happen occasionally, especially as the solution is approached. However, if you see this frequently, consider setting option 'sym_pos' to False.
  OptimizeWarning)
/usr/local/Cellar/python3/3.6.4/Frameworks/Python.framework/Versions/3.6/lib/python3.6/site-packages/scipy/optimize/_linprog_ip.py:285: OptimizeWarning: Solving system with option 'sym_pos':False failed. This may happen occasionally, especially as the solution is approached. However, if you see this frequently, your problem may be numerically challenging. If you cannot improve the formulation, consider setting 'lstsq' to True. Consider also setting `presolve` to True, if it is not already.
  OptimizeWarning)
```

Setting 'lstsq':True and/or 'presolve':True really took a lot of CPU resource and took too long, though its possible this could be avoided with options I'm unaware of. Thanks!

These are actually csvs, which github doesn't like so the extension is .txt
```A_ub = np.loadtxt('A_ub.txt', delimiter=',')```
[A_eq.txt](https://github.com/scipy/scipy/files/3020740/A_eq.txt)
[A_ub.txt](https://github.com/scipy/scipy/files/3020741/A_ub.txt)
[b_eq.txt](https://github.com/scipy/scipy/files/3020742/b_eq.txt)
[b_ub.txt](https://github.com/scipy/scipy/files/3020743/b_ub.txt)
[c.txt](https://github.com/scipy/scipy/files/3020744/c.txt)

**Edit: Fixed this by raising ```tol```, so it seems like this error is thrown when ```T[-1:-1] >= tol``` and ```status != 0```. 

@colsmit Thanks for submitting this problem. Both methods are having a hard time solving the problem (but I'm not getting the same error). I don't have a quick fix for you, unfortunately. This is very unusual. 

When you say that you fixed it by raising `tol`, do you mean that the simplex method solved the problem? Can you copy the solution information?
@mdhaber Sorry for no response with the matrices from my problem.  They are on another system, and I tried, but don't have an easy way of getting them over here.  `linprog` also get's called thousands of times on matrices that are generated on the fly, so it's a bit hard to extract...

I did find that the `interior-point` method worked for my application.

@colsmit My matrices were also (relatively) large and sparse.  I believe the difficulty is when the problem is numerically ill-conditions and perhaps _almost_ infeasible.  
@jmpounders good to hear that interior-point worked for you.
Note that if you want to take advantage of the sparsity, you can convert `A_ub` and `A_eq` to a sparse format (e.g. `scipy.sparse.csc_matrix(A_ub)`) or set `options={'sparse':True}` in `linprog` (interior-point only). If speed is really important, you might consider constructing the matrices in a sparse format to begin with; [`dok_matrix`](https://docs.scipy.org/doc/scipy/reference/generated/scipy.sparse.dok_matrix.html#scipy.sparse.dok_matrix) usually best for that.

Are you able to say what the application is? Always interesting to know what the code gets used for.
@mdhaber sorry for a lack of clarity! Here's code that breaks (`UnboundedLocalError`)
```python
A_ub = np.loadtxt('A_ub.txt', delimiter=',')
A_eq = np.loadtxt('A_eq.txt', delimiter=',')
b_ub = np.loadtxt('b_ub.txt', delimiter=',')
b_eq = np.loadtxt('b_eq.txt', delimiter=',')
c = np.loadtxt('c.txt', delimiter=',')

lp_sol = linprog(c, A_ub=A_ub, b_ub=b_ub, A_eq=A_eq, b_eq=b_eq, method='simplex',
                 options={"maxiter": 5000, "tol": 1.0e-10, "disp": False})
```
Here's code that does not:
```python
A_ub = np.loadtxt('A_ub.txt', delimiter=',')
A_eq = np.loadtxt('A_eq.txt', delimiter=',')
b_ub = np.loadtxt('b_ub.txt', delimiter=',')
b_eq = np.loadtxt('b_eq.txt', delimiter=',')
c = np.loadtxt('c.txt', delimiter=',')

lp_sol = linprog(c, A_ub=A_ub, b_ub=b_ub, A_eq=A_eq, b_eq=b_eq, method='simplex',
                 options={"maxiter": 5000, "tol": 1.0e-6, "disp": False})
```
Doing a tiny bit of debugging in _linprog_simplex.py, ```status == 3``` at line 615 right before the exception, so my issue seems to be that I have an unbounded problem (no pivot found) thats not generating the right error message when no solution is found. As I understand it, raising `tol` just means I avoided the `UnboundedLocalError` by accepting a worse fit.

My implementation is OSS for fitting a new-ish class of flexible probability distributions to data, the linear programming is for edge cases when the least squares solver breaks. I'm translating a preexisting implementation from R to Python. This was first done in R using lp_solve, but since the Python implementation of that library isn't on PyPl it makes sense to use scipy for simplicity. For speed, I think the real answer is just to wait for ```revised simplex```, lp_solve finds a solution to this problem in a fraction of the time and I don't know what else to attribute the speed to. 
@colsmit Thanks. I'm going to try `revised_simplex` on it soon; I'm hoping that will help. 

If it is unbounded, I'm a little less surprised that `interior-point` has trouble. Hopefully I can partially blame the algorithm. Most interior-point methods don't even have the ability to provide certificates of infeasibility/unboundedness. This one does, but I imagine it's not perfect. In any case, I can see the advantage of basis-exchange methods here.
@Kai-Striega Thought I should ping you in case you are interested in patching the original `simplex`
@colsmit I don't think your problem is unbounded. I think the optimal objective values is ~0.0577. The revised simplex method solves it without too much trouble. Interior-point actually gets very close to the optimal objective value within a few iterations, but has trouble converging when it gets close to the solution - just like the warning says.  It terminates successfully if you raise the tolerance (e.g. 
`options={'tol':1e-5, 'display':True}`. Does `interior-point` meet your needs for now? If not, is it easy for you to get a development version of SciPy so you can use `revised simplex`? 
@Kai-Striega actually, let me know if you plan to fix this bug in original simplex. If not, let's label this `wontfix` and close it, since the defect is only in original simplex. 
@mdhaber I'll take a look at it, but I'll be flat out for the next few days so it may take some time. 

Resolving patch:
diff --git a/scipy/optimize/_linprog_simplex.py b/scipy/optimize/_linprog_simplex.py
--- a/scipy/optimize/_linprog_simplex.py
+++ b/scipy/optimize/_linprog_simplex.py
@@ -608,6 +608,7 @@ def _linprog_simplex(c, c0, A, b, maxiter=1000, disp=False, callback=None,
                                   maxiter=maxiter, tol=tol, bland=bland, _T_o=_T_o)
     # if pseudo objective is zero, remove the last row from the tableau and
     # proceed to phase 2
+    nit2 = nit1
     if abs(T[-1, -1]) < tol:
         # Remove the pseudo-objective row from the tableau
         T = T[:-1, :]
@@ -616,7 +617,6 @@ def _linprog_simplex(c, c0, A, b, maxiter=1000, disp=False, callback=None,
     else:
         # Failure to find a feasible starting point
         status = 2
-        nit2 = nit1
         messages[status] = (
             "Phase 1 of the simplex method failed to find a feasible "
             "solution. The pseudo-objective function evaluates to {0:.1e} "


Test patch: