Repository: scipy/scipy

Issue:
tgsen uses an output argument for computing a default argument
tgsen uses `lwork=max(2*m*(n-m),1)` as default argument (for the complex case) where `integer intent(out) :: m`. I assume this means that lwork will always be 1, which is usually insufficient. m is the amount of eigenpairs that will be selected (which may be larger than the amount of nonzeros in the select array in the non-complex case).

Note that the same issue exists for the non-complex case, but this is less visible, since the default is set to `lwork=max(MAX(4*n+16,2*m*(n-m)),1)`, so this includes `4*n+16`.

#### Reproducing code example:

```
@pytest.mark.parametrize('dtype', DTYPES)
def test_gges_tgsen(dtype):
    seed(1234)
    atol = np.finfo(dtype).eps*100

    n = 10
    a = generate_random_dtype_array([n, n], dtype=dtype)
    b = generate_random_dtype_array([n, n], dtype=dtype)

    gges, tgsen = get_lapack_funcs(('gges', 'tgsen'), dtype=dtype)

    result = gges(lambda x: None, a, b, overwrite_a=False, overwrite_b=False)
    assert_equal(result[-1], 0)

    s = result[0]
    t = result[1]
    q = result[-4]
    z = result[-3]

    if dtype in COMPLEX_DTYPES:
        assert_allclose(s, np.triu(s), rtol=0, atol=atol)
        assert_allclose(t, np.triu(t), rtol=0, atol=atol)

    assert_allclose(q @ s @ z.conj().T, a, rtol=0, atol=atol)
    assert_allclose(q @ t @ z.conj().T, b, rtol=0, atol=atol)

    result = tgsen(select, s, t, q, z)
    assert_equal(result[-1], 0)

    s = result[0]
    t = result[1]
    q = result[-9]
    z = result[-8]

    if dtype in COMPLEX_DTYPES:
        assert_allclose(s, np.triu(s), rtol=0, atol=atol)
        assert_allclose(t, np.triu(t), rtol=0, atol=atol)

    assert_allclose(q @ s @ z.conj().T, a, rtol=0, atol=atol)
    assert_allclose(q @ t @ z.conj().T, b, rtol=0, atol=atol)
```

#### Error message:

```
 ** On entry to CTGSEN parameter number 21 had an illegal value
 ** On entry to ZTGSEN parameter number 21 had an illegal value
```

#### Scipy/Numpy/Python version information:
Scipy commit 55cf5c301e58f24092639c01e48c2bd139807906
Numpy 1.19.4
Python 3.8.5



Discussion:
Also, it needs to be +1 the actual default value. See also

https://github.com/scipy/scipy/blob/6e15c52a40370d629dfa4951eb58e7f2b267ed61/scipy/linalg/_decomp_qz.py#L372-L374
Well indeed we clearly made a mistake in that wrapper.
Correction: I am surprised that it is actually working. Most settings should have lead to segfaults. I'll fix this 
Well apparently, I've opened a can of worms since when I touched these wrappers, `linalg.qz` and `linalg.ordqz` stopped working. So it will get a bit complicated as a fix. 

However, there is basically no way to get a straightforward formula to get the minimal `lwork` and `liwork` values hence there will be an additional `?tgsen_lwork` functions wrapped to get the right values. Then the regular `?tgsen` call can be requested.

Resolving patch:
diff --git a/scipy/linalg/_decomp_qz.py b/scipy/linalg/_decomp_qz.py
--- a/scipy/linalg/_decomp_qz.py
+++ b/scipy/linalg/_decomp_qz.py
@@ -2,7 +2,6 @@
 
 import numpy as np
 from numpy import asarray_chkfinite
-
 from .misc import LinAlgError, _datacopied, LinAlgWarning
 from .lapack import get_lapack_funcs
 
@@ -147,10 +146,10 @@ def qz(A, B, output='real', lwork=None, sort=None, overwrite_a=False,
     """
     QZ decomposition for generalized eigenvalues of a pair of matrices.
 
-    The QZ, or generalized Schur, decomposition for a pair of N x N
-    nonsymmetric matrices (A,B) is::
+    The QZ, or generalized Schur, decomposition for a pair of n-by-n
+    matrices (A,B) is::
 
-        (A,B) = (Q*AA*Z', Q*BB*Z')
+        (A,B) = (Q @ AA @ Z*, Q @ BB @ Z*)
 
     where AA, BB is in generalized Schur form if BB is upper-triangular
     with non-negative diagonal and AA is upper-triangular, or for real QZ
@@ -266,8 +265,6 @@ def ordqz(A, B, sort='lhp', output='real', overwrite_a=False,
           overwrite_b=False, check_finite=True):
     """QZ decomposition for a pair of matrices with reordering.
 
-    .. versionadded:: 0.17.0
-
     Parameters
     ----------
     A : (N, N) array_like
@@ -334,6 +331,8 @@ def ordqz(A, B, sort='lhp', output='real', overwrite_a=False,
     real; if positive, then the ``j``th and ``(j+1)``st eigenvalues are a
     complex conjugate pair, with ``ALPHAI(j+1)`` negative.
 
+    .. versionadded:: 0.17.0
+
     See also
     --------
     qz
@@ -351,52 +350,43 @@ def ordqz(A, B, sort='lhp', output='real', overwrite_a=False,
     array([ True,  True, False, False], dtype=bool)
 
     """
-    # NOTE: should users be able to set these?
-    lwork = None
-    result, typ = _qz(A, B, output=output, lwork=lwork, sort=None,
-                      overwrite_a=overwrite_a, overwrite_b=overwrite_b,
-                      check_finite=check_finite)
-    AA, BB, Q, Z = result[0], result[1], result[-4], result[-3]
-    if typ not in 'cz':
-        alpha, beta = result[3] + result[4]*1.j, result[5]
+    (AA, BB, _, *ab, Q, Z, _, _), typ = _qz(A, B, output=output, sort=None,
+                                            overwrite_a=overwrite_a,
+                                            overwrite_b=overwrite_b,
+                                            check_finite=check_finite)
+
+    if typ == 's':
+        alpha, beta = ab[0] + ab[1]*np.complex64(1j), ab[2]
+    elif typ == 'd':
+        alpha, beta = ab[0] + ab[1]*1.j, ab[2]
     else:
-        alpha, beta = result[3], result[4]
+        alpha, beta = ab
 
     sfunction = _select_function(sort)
     select = sfunction(alpha, beta)
 
-    tgsen, = get_lapack_funcs(('tgsen',), (AA, BB))
-
-    if lwork is None or lwork == -1:
-        result = tgsen(select, AA, BB, Q, Z, lwork=-1)
-        lwork = result[-3][0].real.astype(np.int_)
-        # looks like wrong value passed to ZTGSYL if not
-        lwork += 1
-
-    liwork = None
-    if liwork is None or liwork == -1:
-        result = tgsen(select, AA, BB, Q, Z, liwork=-1)
-        liwork = result[-2][0]
-
-    result = tgsen(select, AA, BB, Q, Z, lwork=lwork, liwork=liwork)
+    tgsen = get_lapack_funcs('tgsen', (AA, BB))
+    # the real case needs 4n + 16 lwork
+    lwork = 4*AA.shape[0] + 16 if typ in 'sd' else 1
+    AAA, BBB, *ab, QQ, ZZ, _, _, _, _, info = tgsen(select, AA, BB, Q, Z,
+                                                    ijob=0,
+                                                    lwork=lwork, liwork=1)
+
+    # Once more for tgsen output
+    if typ == 's':
+        alpha, beta = ab[0] + ab[1]*np.complex64(1j), ab[2]
+    elif typ == 'd':
+        alpha, beta = ab[0] + ab[1]*1.j, ab[2]
+    else:
+        alpha, beta = ab
 
-    info = result[-1]
     if info < 0:
-        raise ValueError("Illegal value in argument %d of tgsen" % -info)
+        raise ValueError(f"Illegal value in argument {-info} of tgsen")
     elif info == 1:
         raise ValueError("Reordering of (A, B) failed because the transformed"
                          " matrix pair (A, B) would be too far from "
                          "generalized Schur form; the problem is very "
                          "ill-conditioned. (A, B) may have been partially "
-                         "reorded. If requested, 0 is returned in DIF(*), "
-                         "PL, and PR.")
-
-    # for real results has a, b, alphar, alphai, beta, q, z, m, pl, pr, dif,
-    # work, iwork, info
-    if typ in ['f', 'd']:
-        alpha = result[2] + result[3] * 1.j
-        return (result[0], result[1], alpha, result[4], result[5], result[6])
-    # for complex results has a, b, alpha, beta, q, z, m, pl, pr, dif, work,
-    # iwork, info
-    else:
-        return result[0], result[1], result[2], result[3], result[4], result[5]
+                         "reordered.")
+
+    return AAA, BBB, alpha, beta, QQ, ZZ
diff --git a/scipy/linalg/lapack.py b/scipy/linalg/lapack.py
--- a/scipy/linalg/lapack.py
+++ b/scipy/linalg/lapack.py
@@ -694,6 +694,11 @@
    ctgsen
    ztgsen
 
+   stgsen_lwork
+   dtgsen_lwork
+   ctgsen_lwork
+   ztgsen_lwork
+
    stpttf
    dtpttf
    ctpttf


Test patch: