Repository: scipy/scipy

Issue:
Request for Python wrapper for LAPACK's ?pptrf (Cholesky factorization in packed format)
**Is your feature request related to a problem? Please describe.**

scipy.linalg.lapack already has wrappers for ?tpsv (triangular solve in packed format).  ?pptrf (Cholesky factorization in packed format) seems to be a common enough function that can be used to generate the matrix passed to ?tpsv, but it is not wrapped at the Python level.

**Describe the solution you'd like**

Add a wrapper for ?pptrf.

**Describe alternatives you've considered**

Manually converting between unpacked and packed formats, but this is not exactly fast.



Discussion:
@anntzer consider adding items for all wrappers you want?

- [ ] `?pptrf`
- [ ] `?ZZZZ`
etc. in that way one can have more in one.
(Right now that's the only one I need, but I will add more as needed :))

Resolving patch:
diff --git a/scipy/linalg/lapack.py b/scipy/linalg/lapack.py
--- a/scipy/linalg/lapack.py
+++ b/scipy/linalg/lapack.py
@@ -488,6 +488,31 @@
    cpotrs
    zpotrs
 
+   sppcon
+   dppcon
+   cppcon
+   zppcon
+
+   sppsv
+   dppsv
+   cppsv
+   zppsv
+
+   spptrf
+   dpptrf
+   cpptrf
+   zpptrf
+
+   spptri
+   dpptri
+   cpptri
+   zpptri
+
+   spptrs
+   dpptrs
+   cpptrs
+   zpptrs
+
    sptsv
    dptsv
    cptsv
@@ -985,7 +1010,8 @@ def _compute_lwork(routine, *args, **kwargs):
     if len(ret) == 2:
         return _check_work_float(ret[0].real, dtype, int_dtype)
     else:
-        return tuple(_check_work_float(x.real, dtype, int_dtype) for x in ret[:-1])
+        return tuple(_check_work_float(x.real, dtype, int_dtype)
+                     for x in ret[:-1])
 
 
 def _check_work_float(value, dtype, int_dtype):
@@ -1002,10 +1028,12 @@ def _check_work_float(value, dtype, int_dtype):
     value = int(value)
     if int_dtype.itemsize == 4:
         if value < 0 or value > _int32_max:
-            raise ValueError("Too large work array required -- computation cannot "
-                             "be performed with standard 32-bit LAPACK.")
+            raise ValueError("Too large work array required -- computation "
+                             "cannot be performed with standard 32-bit"
+                             " LAPACK.")
     elif int_dtype.itemsize == 8:
         if value < 0 or value > _int64_max:
-            raise ValueError("Too large work array required -- computation cannot "
-                             "be performed with standard 64-bit LAPACK.")
+            raise ValueError("Too large work array required -- computation"
+                             " cannot be performed with standard 64-bit"
+                             " LAPACK.")
     return value


Test patch:
diff --git a/scipy/linalg/tests/test_lapack.py b/scipy/linalg/tests/test_lapack.py
--- a/scipy/linalg/tests/test_lapack.py
+++ b/scipy/linalg/tests/test_lapack.py
@@ -19,9 +19,9 @@
 
 from numpy.random import rand, randint, seed
 
-from scipy.linalg import _flapack as flapack, lapack
-from scipy.linalg import inv, svd, cholesky, solve, ldl, norm, block_diag, qr
-from scipy.linalg import eigh
+from scipy.linalg import (_flapack as flapack, lapack, inv, svd, cholesky,
+                          solve, ldl, norm, block_diag, qr, eigh)
+
 from scipy.linalg.lapack import _compute_lwork
 from scipy.stats import ortho_group, unitary_group
 
@@ -1748,7 +1748,8 @@ def test_syequb():
         assert_equal(np.log2(s).astype(int), desired_log2s)
 
 
-@pytest.mark.skipif(True, reason="Failing on some OpenBLAS version, see gh-12276")
+@pytest.mark.skipif(True,
+                    reason="Failing on some OpenBLAS version, see gh-12276")
 def test_heequb():
     # zheequb has a bug for versions =< LAPACK 3.9.0
     # See Reference-LAPACK gh-61 and gh-408
@@ -2923,3 +2924,51 @@ def test_ptsvx_NAG(d, e, b, x):
     df, ef, x_ptsvx, rcond, ferr, berr, info = ptsvx(d, e, b)
     # determine ptsvx's solution and x are the same.
     assert_array_almost_equal(x, x_ptsvx)
+
+
+@pytest.mark.parametrize('lower', [False, True])
+@pytest.mark.parametrize('dtype', DTYPES)
+def test_pptrs_pptri_pptrf_ppsv_ppcon(dtype, lower):
+    seed(1234)
+    atol = np.finfo(dtype).eps*100
+    # Manual conversion to/from packed format is feasible here.
+    n, nrhs = 10, 4
+    a = generate_random_dtype_array([n, n], dtype=dtype)
+    b = generate_random_dtype_array([n, nrhs], dtype=dtype)
+
+    a = a.conj().T + a + np.eye(n, dtype=dtype) * dtype(5.)
+    if lower:
+        inds = ([x for y in range(n) for x in range(y, n)],
+                [y for y in range(n) for x in range(y, n)])
+    else:
+        inds = ([x for y in range(1, n+1) for x in range(y)],
+                [y-1 for y in range(1, n+1) for x in range(y)])
+    ap = a[inds]
+    ppsv, pptrf, pptrs, pptri, ppcon = get_lapack_funcs(
+        ('ppsv', 'pptrf', 'pptrs', 'pptri', 'ppcon'),
+        dtype=dtype,
+        ilp64="preferred")
+
+    ul, info = pptrf(n, ap, lower=lower)
+    assert_equal(info, 0)
+    aul = cholesky(a, lower=lower)[inds]
+    assert_allclose(ul, aul, rtol=0, atol=atol)
+
+    uli, info = pptri(n, ul, lower=lower)
+    assert_equal(info, 0)
+    auli = inv(a)[inds]
+    assert_allclose(uli, auli, rtol=0, atol=atol)
+
+    x, info = pptrs(n, ul, b, lower=lower)
+    assert_equal(info, 0)
+    bx = solve(a, b)
+    assert_allclose(x, bx, rtol=0, atol=atol)
+
+    xv, info = ppsv(n, ap, b, lower=lower)
+    assert_equal(info, 0)
+    assert_allclose(xv, bx, rtol=0, atol=atol)
+
+    anorm = np.linalg.norm(a, 1)
+    rcond, info = ppcon(n, ap, anorm=anorm, lower=lower)
+    assert_equal(info, 0)
+    assert_(abs(1/rcond - np.linalg.cond(a, p=1))*rcond < 1)