Repository: scipy/scipy

Issue:
scipy.linalg.cho_factor trouble with empty nparray
In #7123 (Interior Point Method for `linprog`) TravisCI jobs X.3-X.6 with verbose output, the following occurred:
```
test_linprog.TestLinprogIP.test_unbounded_below_no_presolve ... 

Primal Feasibility  Dual Feasibility    Duality Gap         Step                Path Parameter      Objective          
0.0                 0.0                 1.0                 -                   1.0                 0.0                 
 ** On entry to DPOTRF parameter number  4 had an illegal value

travis_time:end:0866793a:start=1500403878367062329,finish=1500404444732519125,duration=566365456796
[0K
[32;1mThe command "python -u $OPTIMIZE runtests.py -g -m $TESTMODE $COVERAGE $USE_WHEEL_BUILD |& tee runtests.log" exited with 0.[0m
travis_time:start:005bf86c
[0K$ tools/validate_runtests_log.py $TESTMODE < runtests.log
*** Test runner validation errored: did the run really finish?

travis_time:end:005bf86c:start=1500404444739032504,finish=1500404444793625359,duration=54592855
[0K
[31;1mThe command "tools/validate_runtests_log.py $TESTMODE < runtests.log" exited with 255.[0m
travis_time:start:04b5985c
[0K$ if [ "${REFGUIDE_CHECK}" == "1" ]; then python runtests.py -g --refguide-check; fi

travis_time:end:04b5985c:start=1500404444800632628,finish=1500404444806269233,duration=5636605
[0K
[32;1mThe command "if [ "${REFGUIDE_CHECK}" == "1" ]; then python runtests.py -g --refguide-check; fi" exited with 0.[0m
travis_fold:start:cache.2
```

As @ilayn expected, this was traced to a call to `cho_factor` acting on an empty `ndarray` with shape `(0,0)`. 


### Reproducing code example:
```
import numpy as np
from scipy.linalg import cho_factor
cho_factor(np.empty((0,0)))
```

### Error message:
This does not simply cause the test to fail. It halts the testing altogether.

On my machines, however, the call above produces a `ValueError`:
```
Traceback (most recent call last):

  File "<ipython-input-14-e4b1c49a19b3>", line 1, in <module>
    cho_factor(np.array([]).reshape(0,0))

  File "/Users/matthaberland/Desktop/ScipyDev/scipy/scipy/linalg/decomp_cholesky.py", line 132, in cho_factor
    check_finite=check_finite)

  File "/Users/matthaberland/Desktop/ScipyDev/scipy/scipy/linalg/decomp_cholesky.py", line 33, in _cholesky
    % -info)

ValueError: illegal value in 4-th argument of internal potrf
```
The unit test doesn't even _fail_ locally because the offending line is within a `try` block, and is handled appropriately. This problem was only identified in TravisCI testing.

### Scipy/Numpy/Python version information:
```
1.0.0.dev0+c29306e 1.12.0 sys.version_info(major=3, minor=6, micro=0, releaselevel='final', serial=0)
```
Issue discovered in #7123 unit tests; more information may be available there.




Discussion:
Guess that needs an explicit check within ``cho_factor``, and then directly raise a ``LinAlgError`` (or return empty arrays of the same shape?).
I think there are two issues: one is what @rgommers mentioned that zero sized matrix shouldn't have survived up to the lapack call and the second is why ?POTRF is dying instead of gracefully returning back with -4 error code. 


![image](https://user-images.githubusercontent.com/1303842/28362380-81fb231a-6c7c-11e7-8603-8a7fc6b401f3.png)

but on Windows command prompt

![image](https://user-images.githubusercontent.com/1303842/28362479-d5a10d9a-6c7c-11e7-9b41-613a8ea731f4.png)

I believe there is something fishy about the wrapper too.



Resolving patch:
diff --git a/scipy/linalg/decomp_cholesky.py b/scipy/linalg/decomp_cholesky.py
--- a/scipy/linalg/decomp_cholesky.py
+++ b/scipy/linalg/decomp_cholesky.py
@@ -2,35 +2,45 @@
 
 from __future__ import division, print_function, absolute_import
 
-from numpy import asarray_chkfinite, asarray
+from numpy import asarray_chkfinite, asarray, atleast_2d
 
 # Local imports
 from .misc import LinAlgError, _datacopied
 from .lapack import get_lapack_funcs
 
 __all__ = ['cholesky', 'cho_factor', 'cho_solve', 'cholesky_banded',
-            'cho_solve_banded']
+           'cho_solve_banded']
 
 
 def _cholesky(a, lower=False, overwrite_a=False, clean=True,
-                check_finite=True):
+              check_finite=True):
     """Common code for cholesky() and cho_factor()."""
 
-    if check_finite:
-        a1 = asarray_chkfinite(a)
-    else:
-        a1 = asarray(a)
-    if len(a1.shape) != 2 or a1.shape[0] != a1.shape[1]:
-        raise ValueError('expected square matrix')
+    a1 = asarray_chkfinite(a) if check_finite else asarray(a)
+    a1 = atleast_2d(a1)
+
+    # Dimension check
+    if a1.ndim != 2:
+        raise ValueError('Input array needs to be 2 dimensional but received '
+                         'a {}d-array.'.format(a1.ndim))
+    # Squareness check
+    if a1.shape[0] != a1.shape[1]:
+        raise ValueError('Input array is expected to be square but has '
+                         'the shape: {}.'.format(a1.shape))
+
+    # Quick return for square empty array
+    if a1.size == 0:
+        return a1.copy(), lower
 
     overwrite_a = overwrite_a or _datacopied(a1, a)
     potrf, = get_lapack_funcs(('potrf',), (a1,))
     c, info = potrf(a1, lower=lower, overwrite_a=overwrite_a, clean=clean)
     if info > 0:
-        raise LinAlgError("%d-th leading minor not positive definite" % info)
+        raise LinAlgError("%d-th leading minor of the array is not positive "
+                          "definite" % info)
     if info < 0:
-        raise ValueError('illegal value in %d-th argument of internal potrf'
-                                                                    % -info)
+        raise ValueError('LAPACK reported an illegal value in {}-th argument'
+                         'on entry to "POTRF".'.format(-info))
     return c, lower
 
 
@@ -78,7 +88,7 @@ def cholesky(a, lower=False, overwrite_a=False, check_finite=True):
 
     """
     c, lower = _cholesky(a, lower=lower, overwrite_a=overwrite_a, clean=True,
-                            check_finite=check_finite)
+                         check_finite=check_finite)
     return c
 
 
@@ -129,7 +139,7 @@ def cho_factor(a, lower=False, overwrite_a=False, check_finite=True):
 
     """
     c, lower = _cholesky(a, lower=lower, overwrite_a=overwrite_a, clean=False,
-                            check_finite=check_finite)
+                         check_finite=check_finite)
     return c, lower
 
 
@@ -177,7 +187,7 @@ def cho_solve(c_and_lower, b, overwrite_b=False, check_finite=True):
     x, info = potrs(c, b1, lower=lower, overwrite_b=overwrite_b)
     if info != 0:
         raise ValueError('illegal value in %d-th argument of internal potrs'
-                                                                    % -info)
+                         % -info)
     return x
 
 
@@ -233,7 +243,7 @@ def cholesky_banded(ab, overwrite_ab=False, lower=False, check_finite=True):
         raise LinAlgError("%d-th leading minor not positive definite" % info)
     if info < 0:
         raise ValueError('illegal value in %d-th argument of internal pbtrf'
-                                                                    % -info)
+                         % -info)
     return c
 
 
@@ -287,5 +297,5 @@ def cho_solve_banded(cb_and_lower, b, overwrite_b=False, check_finite=True):
         raise LinAlgError("%d-th leading minor not positive definite" % info)
     if info < 0:
         raise ValueError('illegal value in %d-th argument of internal pbtrs'
-                                                                    % -info)
+                         % -info)
     return x


Test patch:
diff --git a/scipy/linalg/tests/test_decomp_cholesky.py b/scipy/linalg/tests/test_decomp_cholesky.py
--- a/scipy/linalg/tests/test_decomp_cholesky.py
+++ b/scipy/linalg/tests/test_decomp_cholesky.py
@@ -1,8 +1,9 @@
 from __future__ import division, print_function, absolute_import
 
-from numpy.testing import assert_array_almost_equal
+from numpy.testing import assert_array_almost_equal, assert_array_equal, \
+    assert_raises
 
-from numpy import array, transpose, dot, conjugate, zeros_like
+from numpy import array, transpose, dot, conjugate, zeros_like, empty
 from numpy.random import random
 from scipy.linalg import cholesky, cholesky_banded, cho_solve_banded, \
      cho_factor, cho_solve
@@ -13,58 +14,58 @@
 class TestCholesky(object):
 
     def test_simple(self):
-        a = [[8,2,3],[2,9,3],[3,3,6]]
+        a = [[8, 2, 3], [2, 9, 3], [3, 3, 6]]
         c = cholesky(a)
-        assert_array_almost_equal(dot(transpose(c),c),a)
+        assert_array_almost_equal(dot(transpose(c), c), a)
         c = transpose(c)
-        a = dot(c,transpose(c))
-        assert_array_almost_equal(cholesky(a,lower=1),c)
+        a = dot(c, transpose(c))
+        assert_array_almost_equal(cholesky(a, lower=1), c)
 
     def test_check_finite(self):
-        a = [[8,2,3],[2,9,3],[3,3,6]]
+        a = [[8, 2, 3], [2, 9, 3], [3, 3, 6]]
         c = cholesky(a, check_finite=False)
-        assert_array_almost_equal(dot(transpose(c),c),a)
+        assert_array_almost_equal(dot(transpose(c), c), a)
         c = transpose(c)
-        a = dot(c,transpose(c))
-        assert_array_almost_equal(cholesky(a,lower=1, check_finite=False),c)
+        a = dot(c, transpose(c))
+        assert_array_almost_equal(cholesky(a, lower=1, check_finite=False), c)
 
     def test_simple_complex(self):
-        m = array([[3+1j,3+4j,5],[0,2+2j,2+7j],[0,0,7+4j]])
-        a = dot(transpose(conjugate(m)),m)
+        m = array([[3+1j, 3+4j, 5], [0, 2+2j, 2+7j], [0, 0, 7+4j]])
+        a = dot(transpose(conjugate(m)), m)
         c = cholesky(a)
-        a1 = dot(transpose(conjugate(c)),c)
-        assert_array_almost_equal(a,a1)
+        a1 = dot(transpose(conjugate(c)), c)
+        assert_array_almost_equal(a, a1)
         c = transpose(c)
-        a = dot(c,transpose(conjugate(c)))
-        assert_array_almost_equal(cholesky(a,lower=1),c)
+        a = dot(c, transpose(conjugate(c)))
+        assert_array_almost_equal(cholesky(a, lower=1), c)
 
     def test_random(self):
         n = 20
         for k in range(2):
-            m = random([n,n])
+            m = random([n, n])
             for i in range(n):
-                m[i,i] = 20*(.1+m[i,i])
-            a = dot(transpose(m),m)
+                m[i, i] = 20*(.1+m[i, i])
+            a = dot(transpose(m), m)
             c = cholesky(a)
-            a1 = dot(transpose(c),c)
-            assert_array_almost_equal(a,a1)
+            a1 = dot(transpose(c), c)
+            assert_array_almost_equal(a, a1)
             c = transpose(c)
-            a = dot(c,transpose(c))
-            assert_array_almost_equal(cholesky(a,lower=1),c)
+            a = dot(c, transpose(c))
+            assert_array_almost_equal(cholesky(a, lower=1), c)
 
     def test_random_complex(self):
         n = 20
         for k in range(2):
-            m = random([n,n])+1j*random([n,n])
+            m = random([n, n])+1j*random([n, n])
             for i in range(n):
-                m[i,i] = 20*(.1+abs(m[i,i]))
-            a = dot(transpose(conjugate(m)),m)
+                m[i, i] = 20*(.1+abs(m[i, i]))
+            a = dot(transpose(conjugate(m)), m)
             c = cholesky(a)
-            a1 = dot(transpose(conjugate(c)),c)
-            assert_array_almost_equal(a,a1)
+            a1 = dot(transpose(conjugate(c)), c)
+            assert_array_almost_equal(a, a1)
             c = transpose(c)
-            a = dot(c,transpose(conjugate(c)))
-            assert_array_almost_equal(cholesky(a,lower=1),c)
+            a = dot(c, transpose(conjugate(c)))
+            assert_array_almost_equal(cholesky(a, lower=1), c)
 
 
 class TestCholeskyBanded(object):
@@ -73,16 +74,16 @@ class TestCholeskyBanded(object):
     def test_check_finite(self):
         # Symmetric positive definite banded matrix `a`
         a = array([[4.0, 1.0, 0.0, 0.0],
-                    [1.0, 4.0, 0.5, 0.0],
-                    [0.0, 0.5, 4.0, 0.2],
-                    [0.0, 0.0, 0.2, 4.0]])
+                   [1.0, 4.0, 0.5, 0.0],
+                   [0.0, 0.5, 4.0, 0.2],
+                   [0.0, 0.0, 0.2, 4.0]])
         # Banded storage form of `a`.
         ab = array([[-1.0, 1.0, 0.5, 0.2],
-                     [4.0, 4.0, 4.0, 4.0]])
+                    [4.0, 4.0, 4.0, 4.0]])
         c = cholesky_banded(ab, lower=False, check_finite=False)
         ufac = zeros_like(a)
-        ufac[list(range(4)),list(range(4))] = c[-1]
-        ufac[(0,1,2),(1,2,3)] = c[0,1:]
+        ufac[list(range(4)), list(range(4))] = c[-1]
+        ufac[(0, 1, 2), (1, 2, 3)] = c[0, 1:]
         assert_array_almost_equal(a, dot(ufac.T, ufac))
 
         b = array([0.0, 0.5, 4.2, 4.2])
@@ -92,16 +93,16 @@ def test_check_finite(self):
     def test_upper_real(self):
         # Symmetric positive definite banded matrix `a`
         a = array([[4.0, 1.0, 0.0, 0.0],
-                    [1.0, 4.0, 0.5, 0.0],
-                    [0.0, 0.5, 4.0, 0.2],
-                    [0.0, 0.0, 0.2, 4.0]])
+                   [1.0, 4.0, 0.5, 0.0],
+                   [0.0, 0.5, 4.0, 0.2],
+                   [0.0, 0.0, 0.2, 4.0]])
         # Banded storage form of `a`.
         ab = array([[-1.0, 1.0, 0.5, 0.2],
-                     [4.0, 4.0, 4.0, 4.0]])
+                    [4.0, 4.0, 4.0, 4.0]])
         c = cholesky_banded(ab, lower=False)
         ufac = zeros_like(a)
-        ufac[list(range(4)),list(range(4))] = c[-1]
-        ufac[(0,1,2),(1,2,3)] = c[0,1:]
+        ufac[list(range(4)), list(range(4))] = c[-1]
+        ufac[(0, 1, 2), (1, 2, 3)] = c[0, 1:]
         assert_array_almost_equal(a, dot(ufac.T, ufac))
 
         b = array([0.0, 0.5, 4.2, 4.2])
@@ -111,16 +112,16 @@ def test_upper_real(self):
     def test_upper_complex(self):
         # Hermitian positive definite banded matrix `a`
         a = array([[4.0, 1.0, 0.0, 0.0],
-                    [1.0, 4.0, 0.5, 0.0],
-                    [0.0, 0.5, 4.0, -0.2j],
-                    [0.0, 0.0, 0.2j, 4.0]])
+                   [1.0, 4.0, 0.5, 0.0],
+                   [0.0, 0.5, 4.0, -0.2j],
+                   [0.0, 0.0, 0.2j, 4.0]])
         # Banded storage form of `a`.
         ab = array([[-1.0, 1.0, 0.5, -0.2j],
-                     [4.0, 4.0, 4.0, 4.0]])
+                    [4.0, 4.0, 4.0, 4.0]])
         c = cholesky_banded(ab, lower=False)
         ufac = zeros_like(a)
-        ufac[list(range(4)),list(range(4))] = c[-1]
-        ufac[(0,1,2),(1,2,3)] = c[0,1:]
+        ufac[list(range(4)), list(range(4))] = c[-1]
+        ufac[(0, 1, 2), (1, 2, 3)] = c[0, 1:]
         assert_array_almost_equal(a, dot(ufac.conj().T, ufac))
 
         b = array([0.0, 0.5, 4.0-0.2j, 0.2j + 4.0])
@@ -130,16 +131,16 @@ def test_upper_complex(self):
     def test_lower_real(self):
         # Symmetric positive definite banded matrix `a`
         a = array([[4.0, 1.0, 0.0, 0.0],
-                    [1.0, 4.0, 0.5, 0.0],
-                    [0.0, 0.5, 4.0, 0.2],
-                    [0.0, 0.0, 0.2, 4.0]])
+                   [1.0, 4.0, 0.5, 0.0],
+                   [0.0, 0.5, 4.0, 0.2],
+                   [0.0, 0.0, 0.2, 4.0]])
         # Banded storage form of `a`.
         ab = array([[4.0, 4.0, 4.0, 4.0],
                     [1.0, 0.5, 0.2, -1.0]])
         c = cholesky_banded(ab, lower=True)
         lfac = zeros_like(a)
-        lfac[list(range(4)),list(range(4))] = c[0]
-        lfac[(1,2,3),(0,1,2)] = c[1,:3]
+        lfac[list(range(4)), list(range(4))] = c[0]
+        lfac[(1, 2, 3), (0, 1, 2)] = c[1, :3]
         assert_array_almost_equal(a, dot(lfac, lfac.T))
 
         b = array([0.0, 0.5, 4.2, 4.2])
@@ -149,16 +150,16 @@ def test_lower_real(self):
     def test_lower_complex(self):
         # Hermitian positive definite banded matrix `a`
         a = array([[4.0, 1.0, 0.0, 0.0],
-                    [1.0, 4.0, 0.5, 0.0],
-                    [0.0, 0.5, 4.0, -0.2j],
-                    [0.0, 0.0, 0.2j, 4.0]])
+                   [1.0, 4.0, 0.5, 0.0],
+                   [0.0, 0.5, 4.0, -0.2j],
+                   [0.0, 0.0, 0.2j, 4.0]])
         # Banded storage form of `a`.
         ab = array([[4.0, 4.0, 4.0, 4.0],
                     [1.0, 0.5, 0.2j, -1.0]])
         c = cholesky_banded(ab, lower=True)
         lfac = zeros_like(a)
-        lfac[list(range(4)),list(range(4))] = c[0]
-        lfac[(1,2,3),(0,1,2)] = c[1,:3]
+        lfac[list(range(4)), list(range(4))] = c[0]
+        lfac[(1, 2, 3), (0, 1, 2)] = c[1, :3]
         assert_array_almost_equal(a, dot(lfac, lfac.conj().T))
 
         b = array([0.0, 0.5j, 3.8j, 3.8])
@@ -168,21 +169,36 @@ def test_lower_complex(self):
 
 class TestOverwrite(object):
     def test_cholesky(self):
-        assert_no_overwrite(cholesky, [(3,3)])
+        assert_no_overwrite(cholesky, [(3, 3)])
 
     def test_cho_factor(self):
-        assert_no_overwrite(cho_factor, [(3,3)])
+        assert_no_overwrite(cho_factor, [(3, 3)])
 
     def test_cho_solve(self):
-        x = array([[2,-1,0], [-1,2,-1], [0,-1,2]])
+        x = array([[2, -1, 0], [-1, 2, -1], [0, -1, 2]])
         xcho = cho_factor(x)
         assert_no_overwrite(lambda b: cho_solve(xcho, b), [(3,)])
 
     def test_cholesky_banded(self):
-        assert_no_overwrite(cholesky_banded, [(2,3)])
+        assert_no_overwrite(cholesky_banded, [(2, 3)])
 
     def test_cho_solve_banded(self):
         x = array([[0, -1, -1], [2, 2, 2]])
         xcho = cholesky_banded(x)
         assert_no_overwrite(lambda b: cho_solve_banded((xcho, False), b),
                             [(3,)])
+
+
+class TestEmptyArray(object):
+    def test_cho_factor_empty_square(self):
+        a = empty((0, 0))
+        b = array([])
+        c = array([[]])
+        d = []
+        e = [[]]
+
+        x, _ = cho_factor(a)
+        assert_array_equal(x, a)
+
+        for x in ([b, c, d, e]):
+            assert_raises(ValueError, cho_factor, x)