diff --git a/kwant/linalg/decomp_ev.py b/kwant/linalg/decomp_ev.py index bd92ce9335d2ba31edc4535f0a8a12a5828e2943..8e7b95d68b0eae6701f3fe38a624653a5aa67f68 100644 --- a/kwant/linalg/decomp_ev.py +++ b/kwant/linalg/decomp_ev.py @@ -50,18 +50,5 @@ def gen_eig(a, b, left=False, right=True, overwrite_ab=False): The right eigenvector corresponding to the eigenvalue ``alpha[i]/beta[i]`` is the column ``vr[:,i]``. """ - - ltype, a, b = lapack.prepare_for_lapack(overwrite_ab, a, b) - - if a.ndim != 2 or b.ndim != 2: - raise ValueError("gen_eig requires both a and be to be matrices") - - if a.shape[0] != a.shape[1]: - raise ValueError("gen_eig requires square matrix input") - - if b.shape[0] != a.shape[0] or b.shape[1] != a.shape[1]: - raise ValueError("gen_eig requires a and be to have the same shape") - - ggev = getattr(lapack, ltype + "ggev") - - return ggev(a, b, left, right) + a, b = lapack.prepare_for_lapack(overwrite_ab, a, b) + return lapack.ggev(a, b, left, right) diff --git a/kwant/linalg/decomp_lu.py b/kwant/linalg/decomp_lu.py index 79accc254feaa53206a9ea2f4a9d977a0d904138..639b547bfdb082eb0e45df3180bb31ceeb67937f 100644 --- a/kwant/linalg/decomp_lu.py +++ b/kwant/linalg/decomp_lu.py @@ -45,20 +45,8 @@ def lu_factor(a, overwrite_a=False): singular : boolean Whether the matrix a is singular (up to machine precision) """ - - ltype, a = lapack.prepare_for_lapack(overwrite_a, a) - - if a.ndim != 2: - raise ValueError("lu_factor expects a matrix") - - if ltype == 'd': - return lapack.dgetrf(a) - elif ltype == 'z': - return lapack.zgetrf(a) - elif ltype == 's': - return lapack.sgetrf(a) - else: - return lapack.cgetrf(a) + a = lapack.prepare_for_lapack(overwrite_a, a) + return lapack.getrf(a) def lu_solve(matrix_factorization, b): @@ -83,23 +71,9 @@ def lu_solve(matrix_factorization, b): "a singular matrix. Result of solve step " "are probably unreliable") - ltype, lu, b = lapack.prepare_for_lapack(False, lu, b) + lu, b = lapack.prepare_for_lapack(False, lu, b) ipiv = np.ascontiguousarray(np.asanyarray(ipiv), dtype=lapack.int_dtype) - - if b.ndim > 2: - raise ValueError("lu_solve: b must be a vector or matrix") - - if lu.shape[0] != b.shape[0]: - raise ValueError("lu_solve: incompatible dimensions of b") - - if ltype == 'd': - return lapack.dgetrs(lu, ipiv, b) - elif ltype == 'z': - return lapack.zgetrs(lu, ipiv, b) - elif ltype == 's': - return lapack.sgetrs(lu, ipiv, b) - else: - return lapack.cgetrs(lu, ipiv, b) + return lapack.getrs(lu, ipiv, b) def rcond_from_lu(matrix_factorization, norm_a, norm="1"): @@ -127,17 +101,6 @@ def rcond_from_lu(matrix_factorization, norm_a, norm="1"): norm specified in norm """ (lu, ipiv, singular) = matrix_factorization - if not norm in ("1", "I"): - raise ValueError("norm in rcond_from_lu must be either '1' or 'I'") norm = norm.encode('utf8') # lapack expects bytes - - ltype, lu = lapack.prepare_for_lapack(False, lu) - - if ltype == 'd': - return lapack.dgecon(lu, norm_a, norm) - elif ltype == 'z': - return lapack.zgecon(lu, norm_a, norm) - elif ltype == 's': - return lapack.sgecon(lu, norm_a, norm) - else: - return lapack.cgecon(lu, norm_a, norm) + lu = lapack.prepare_for_lapack(False, lu) + return lapack.gecon(lu, norm_a, norm) diff --git a/kwant/linalg/decomp_schur.py b/kwant/linalg/decomp_schur.py index 4accc17ff0f1643c9532ada85a29dfa08a2b67fa..d65432132efd3084e26a25643dbc36b82f48b39c 100644 --- a/kwant/linalg/decomp_schur.py +++ b/kwant/linalg/decomp_schur.py @@ -62,18 +62,8 @@ def schur(a, calc_q=True, calc_ev=True, overwrite_a=False): LinAlgError If the underlying QR iteration fails to converge. """ - - ltype, a = lapack.prepare_for_lapack(overwrite_a, a) - - if a.ndim != 2: - raise ValueError("Expect matrix as input") - - if a.shape[0] != a.shape[1]: - raise ValueError("Expect square matrix") - - gees = getattr(lapack, ltype + "gees") - - return gees(a, calc_q, calc_ev) + a = lapack.prepare_for_lapack(overwrite_a, a) + return lapack.gees(a, calc_q, calc_ev) def convert_r2c_schur(t, q): @@ -192,9 +182,7 @@ def order_schur(select, t, q, calc_ev=True, overwrite_tq=False): ``calc_ev == True`` """ - ltype, t, q = lapack.prepare_for_lapack(overwrite_tq, t, q) - - trsen = getattr(lapack, ltype + "trsen") + t, q = lapack.prepare_for_lapack(overwrite_tq, t, q) # Figure out if select is a function or array. isfun = isarray = True @@ -223,7 +211,7 @@ def order_schur(select, t, q, calc_ev=True, overwrite_tq=False): t, q = convert_r2c_schur(t, q) return order_schur(select, t, q, calc_ev, True) - return trsen(select, t, q, calc_ev) + return lapack.trsen(select, t, q, calc_ev) def evecs_from_schur(t, q, select=None, left=False, right=True, @@ -267,13 +255,7 @@ def evecs_from_schur(t, q, select=None, left=False, right=True, ``right == True``. """ - ltype, t, q = lapack.prepare_for_lapack(overwrite_tq, t, q) - - if (t.shape[0] != t.shape[1] or q.shape[0] != q.shape[1] - or t.shape[0] != q.shape[0]): - raise ValueError("Invalid Schur decomposition as input") - - trevc = getattr(lapack, ltype + "trevc") + t, q = lapack.prepare_for_lapack(overwrite_tq, t, q) # check if select is a function or an array if select is not None: @@ -300,7 +282,7 @@ def evecs_from_schur(t, q, select=None, left=False, right=True, else: selectarr = None - return trevc(t, q, selectarr, left, right) + return lapack.trevc(t, q, selectarr, left, right) def gen_schur(a, b, calc_q=True, calc_z=True, calc_ev=True, @@ -364,21 +346,8 @@ def gen_schur(a, b, calc_q=True, calc_z=True, calc_ev=True, LinAlError If the underlying QZ iteration fails to converge. """ - - ltype, a, b = lapack.prepare_for_lapack(overwrite_ab, a, b) - - if a.ndim != 2 or b.ndim != 2: - raise ValueError("Expect matrices as input") - - if a.shape[0] != a.shape[1]: - raise ValueError("Expect square matrix a") - - if a.shape[0] != b.shape[0] or a.shape[0] != b.shape[1]: - raise ValueError("Shape of b is incompatible to matrix a") - - gges = getattr(lapack, ltype + "gges") - - return gges(a, b, calc_q, calc_z, calc_ev) + a, b = lapack.prepare_for_lapack(overwrite_ab, a, b) + return lapack.gges(a, b, calc_q, calc_z, calc_ev) def order_gen_schur(select, s, t, q=None, z=None, calc_ev=True, @@ -439,22 +408,8 @@ def order_gen_schur(select, s, t, q=None, z=None, calc_ev=True, LinAlError If the problem is too ill-conditioned. """ - ltype, s, t, q, z = lapack.prepare_for_lapack(overwrite_stqz, s, t, q, z) + s, t, q, z = lapack.prepare_for_lapack(overwrite_stqz, s, t, q, z) - if (s.ndim != 2 or t.ndim != 2 or - (q is not None and q.ndim != 2) or - (z is not None and z.ndim != 2)): - raise ValueError("Expect matrices as input") - - if ((s.shape[0] != s.shape[1] or t.shape[0] != t.shape[1] or - s.shape[0] != t.shape[0]) or - (q is not None and (q.shape[0] != q.shape[1] or - s.shape[0] != q.shape[0])) or - (z is not None and (z.shape[0] != z.shape[1] or - s.shape[0] != z.shape[0]))): - raise ValueError("Invalid Schur decomposition as input") - - tgsen = getattr(lapack, ltype + "tgsen") # Figure out if select is a function or array. isfun = isarray = True @@ -492,7 +447,7 @@ def order_gen_schur(select, s, t, q=None, z=None, calc_ev=True, return order_gen_schur(select, s, t, q, z, calc_ev, True) - return tgsen(select, s, t, q, z, calc_ev) + return lapack.tgsen(select, s, t, q, z, calc_ev) def convert_r2c_gen_schur(s, t, q=None, z=None): @@ -536,7 +491,7 @@ def convert_r2c_gen_schur(s, t, q=None, z=None): If it fails to convert a 2x2 block into complex form (unlikely). """ - ltype, s, t, q, z = lapack.prepare_for_lapack(True, s, t, q, z) + s, t, q, z = lapack.prepare_for_lapack(True, s, t, q, z) # Note: overwrite=True does not mean much here, the arrays are all copied if (s.ndim != 2 or t.ndim != 2 or @@ -656,20 +611,7 @@ def evecs_from_gen_schur(s, t, q=None, z=None, select=None, """ - ltype, s, t, q, z = lapack.prepare_for_lapack(overwrite_qz, s, t, q, z) - - if (s.ndim != 2 or t.ndim != 2 or - (q is not None and q.ndim != 2) or - (z is not None and z.ndim != 2)): - raise ValueError("Expect matrices as input") - - if ((s.shape[0] != s.shape[1] or t.shape[0] != t.shape[1] or - s.shape[0] != t.shape[0]) or - (q is not None and (q.shape[0] != q.shape[1] or - s.shape[0] != q.shape[0])) or - (z is not None and (z.shape[0] != z.shape[1] or - s.shape[0] != z.shape[0]))): - raise ValueError("Invalid Schur decomposition as input") + s, t, q, z = lapack.prepare_for_lapack(overwrite_qz, s, t, q, z) if left and q is None: raise ValueError("Matrix q must be provided for left eigenvectors") @@ -677,8 +619,6 @@ def evecs_from_gen_schur(s, t, q=None, z=None, select=None, if right and z is None: raise ValueError("Matrix z must be provided for right eigenvectors") - tgevc = getattr(lapack, ltype + "tgevc") - # Check if select is a function or an array. if select is not None: isfun = isarray = True @@ -704,4 +644,4 @@ def evecs_from_gen_schur(s, t, q=None, z=None, select=None, else: selectarr = None - return tgevc(s, t, q, z, selectarr, left, right) + return lapack.tgevc(s, t, q, z, selectarr, left, right) diff --git a/kwant/linalg/f_lapack.pxd b/kwant/linalg/f_lapack.pxd deleted file mode 100644 index 1a0304af871c6bb75994eb0668389e0d6ad2b54d..0000000000000000000000000000000000000000 --- a/kwant/linalg/f_lapack.pxd +++ /dev/null @@ -1,217 +0,0 @@ -# Copyright 2011-2013 Kwant authors. -# -# This file is part of Kwant. It is subject to the license terms in the file -# LICENSE.rst found in the top-level directory of this distribution and at -# http://kwant-project.org/license. A list of Kwant authors can be found in -# the file AUTHORS.rst at the top-level directory of this distribution and at -# http://kwant-project.org/authors. - -ctypedef int l_int -ctypedef int l_logical - -cdef extern: - void sgetrf_(l_int *, l_int *, float *, l_int *, l_int *, l_int *) - void dgetrf_(l_int *, l_int *, double *, l_int *, l_int *, l_int *) - void cgetrf_(l_int *, l_int *, float complex *, l_int *, l_int *, - l_int *) - void zgetrf_(l_int *, l_int *, double complex *, l_int *, l_int *, - l_int *) - - void sgetrs_(char *, l_int *, l_int *, float *, l_int *, l_int *, - float *, l_int *, l_int *) - void dgetrs_(char *, l_int *, l_int *, double *, l_int *, l_int *, - double *, l_int *, l_int *) - void cgetrs_(char *, l_int *, l_int *, float complex *, l_int *, - l_int *, float complex *, l_int *, l_int *) - void zgetrs_(char *, l_int *, l_int *, double complex *, l_int *, - l_int *, double complex *, l_int *, l_int *) - - void sgecon_(char *, l_int *, float *, l_int *, float *, float *, - float *, l_int *, l_int *) - void dgecon_(char *, l_int *, double *, l_int *, double *, double *, - double *, l_int *, l_int *) - void cgecon_(char *, l_int *, float complex *, l_int *, float *, - float *, float complex *, float *, l_int *) - void zgecon_(char *, l_int *, double complex *, l_int *, double *, - double *, double complex *, double *, l_int *) - - void sggev_(char *, char *, l_int *, float *, l_int *, float *, l_int *, - float *, float *, float *, float *, l_int *, float *, l_int *, - float *, l_int *, l_int *) - void dggev_(char *, char *, l_int *, double *, l_int *, double *, l_int *, - double *, double *, double *, double *, l_int *, - double *, l_int *, double *, l_int *, l_int *) - void cggev_(char *, char *, l_int *, float complex *, l_int *, - float complex *, l_int *, float complex *, float complex *, - float complex *, l_int *, float complex *, l_int *, - float complex *, l_int *, float *, l_int *) - void zggev_(char *, char *, l_int *, double complex *, l_int *, - double complex *, l_int *, double complex *, - double complex *, double complex *, l_int *, - double complex *, l_int *, double complex *, l_int *, - double *, l_int *) - - void sgees_(char *, char *, l_logical (*)(float *, float *), - l_int *, float *, l_int *, l_int *, - float *, float *, float *, l_int *, - float *, l_int *, l_logical *, l_int *) - void dgees_(char *, char *, l_logical (*)(double *, double *), - l_int *, double *, l_int *, l_int *, - double *, double *, double *, l_int *, - double *, l_int *, l_logical *, l_int *) - void cgees_(char *, char *, - l_logical (*)(float complex *), - l_int *, float complex *, - l_int *, l_int *, float complex *, - float complex *, l_int *, - float complex *, l_int *, float *, - l_logical *, l_int *) - void zgees_(char *, char *, - l_logical (*)(double complex *), - l_int *, double complex *, - l_int *, l_int *, double complex *, - double complex *, l_int *, - double complex *, l_int *, - double *, l_logical *, l_int *) - - void strsen_(char *, char *, l_logical *, l_int *, - float *, l_int *, float *, - l_int *, float *, float *, l_int *, - float *, float *, float *, l_int *, - l_int *, l_int *, l_int *) - void dtrsen_(char *, char *, l_logical *, - l_int *, double *, l_int *, - double *, l_int *, double *, double *, - l_int *, double *, double *, double *, - l_int *, l_int *, l_int *, l_int *) - void ctrsen_(char *, char *, l_logical *, - l_int *, float complex *, - l_int *, float complex *, - l_int *, float complex *, l_int *, - float *, float *, float complex *, - l_int *, l_int *) - void ztrsen_(char *, char *, l_logical *, - l_int *, double complex *, - l_int *, double complex *, - l_int *, double complex *, l_int *, - double *, double *, double complex *, - l_int *, l_int *) - - void strevc_(char *, char *, l_logical *, - l_int *, float *, l_int *, - float *, l_int *, float *, l_int *, - l_int *, l_int *, float *, l_int *) - void dtrevc_(char *, char *, l_logical *, - l_int *, double *, l_int *, - double *, l_int *, double *, - l_int *, l_int *, l_int *, double *, - l_int *) - void ctrevc_(char *, char *, l_logical *, - l_int *, float complex *, - l_int *, float complex *, - l_int *, float complex *, - l_int *, l_int *, l_int *, - float complex *, float *, l_int *) - void ztrevc_(char *, char *, l_logical *, - l_int *, double complex *, - l_int *, double complex *, - l_int *, double complex *, - l_int *, l_int *, l_int *, - double complex *, double *, l_int *) - - void sgges_(char *, char *, char *, - l_logical (*)(float *, float *, float *), - l_int *, float *, l_int *, float *, - l_int *, l_int *, float *, float *, - float *, float *, l_int *, float *, - l_int *, float *, l_int *, l_logical *, - l_int *) - void dgges_(char *, char *, char *, - l_logical (*)(double *, double *, double *), - l_int *, double *, l_int *, double *, - l_int *, l_int *, double *, double *, - double *, double *, l_int *, double *, - l_int *, double *, l_int *, - l_logical *, l_int *) - void cgges_(char *, char *, char *, - l_logical (*)(float complex *, float complex *), - l_int *, float complex *, - l_int *, float complex *, - l_int *, l_int *, float complex *, - float complex *, float complex *, - l_int *, float complex *, - l_int *, float complex *, - l_int *, float *, l_logical *, l_int *) - void zgges_(char *, char *, char *, - l_logical (*)(double complex *, double complex *), - l_int *, double complex *, - l_int *, double complex *, - l_int *, l_int *, double complex *, - double complex *, - double complex *, l_int *, - double complex *, l_int *, - double complex *, l_int *, - double *, l_logical *, l_int *) - - void stgsen_(l_int *, l_logical *, - l_logical *, l_logical *, - l_int *, float *, l_int *, float *, - l_int *, float *, float *, float *, - float *, l_int *, float *, l_int *, - l_int *, float *, float *, float *, float *, - l_int *, l_int *, l_int *, l_int *) - void dtgsen_(l_int *, l_logical *, - l_logical *, l_logical *, - l_int *, double *, l_int *, - double *, l_int *, double *, double *, - double *, double *, l_int *, double *, - l_int *, l_int *, double *, double *, - double *, double *, l_int *, l_int *, - l_int *, l_int *) - void ctgsen_(l_int *, l_logical *, - l_logical *, l_logical *, - l_int *, float complex *, - l_int *, float complex *, - l_int *, float complex *, - float complex *, - float complex *, l_int *, - float complex *, l_int *, l_int *, - float *, float *, float *, - float complex *, l_int *, l_int *, - l_int *, l_int *) - void ztgsen_(l_int *, l_logical *, - l_logical *, l_logical *, - l_int *, double complex *, - l_int *, double complex *, - l_int *, double complex *, - double complex *, - double complex *, l_int *, - double complex *, l_int *, l_int *, - double *, double *, double *, - double complex *, l_int *, l_int *, - l_int *, l_int *) - - void stgevc_(char *, char *, l_logical *, - l_int *, float *, l_int *, - float *, l_int *, float *, - l_int *, float *, l_int *, - l_int *, l_int *, float *, l_int *) - void dtgevc_(char *, char *, l_logical *, - l_int *, double *, l_int *, - double *, l_int *, double *, - l_int *, double *, l_int *, - l_int *, l_int *, double *, l_int *) - void ctgevc_(char *, char *, l_logical *, - l_int *, float complex *, - l_int *, float complex *, - l_int *, float complex *, - l_int *, float complex *, - l_int *, l_int *, l_int *, - float complex *, float *, l_int *) - void ztgevc_(char *, char *, l_logical *, - l_int *, double complex *, - l_int *, double complex *, - l_int *, double complex *, - l_int *, double complex *, - l_int *, l_int *, l_int *, - double complex *, double *, l_int *) diff --git a/kwant/linalg/f_lapack.py b/kwant/linalg/f_lapack.py deleted file mode 100644 index 39b702b5394dafb773744dfa1d0faa05c17a419c..0000000000000000000000000000000000000000 --- a/kwant/linalg/f_lapack.py +++ /dev/null @@ -1,14 +0,0 @@ -# Copyright 2011-2013 Kwant authors. -# -# This file is part of Kwant. It is subject to the license terms in the file -# LICENSE.rst found in the top-level directory of this distribution and at -# http://kwant-project.org/license. A list of Kwant authors can be found in -# the file AUTHORS.rst at the top-level directory of this distribution and at -# http://kwant-project.org/authors. - -__all__ = ['l_int_dtype', 'l_logical_dtype'] - -import numpy as np - -l_int_dtype = np.int32 -l_logical_dtype = np.int32 diff --git a/kwant/linalg/lapack.pyx b/kwant/linalg/lapack.pyx index d6699a0e11ea912506d417d1e1ab59270c3270a3..e173bc0ce28e860f93ee58d36c725d020fd95c72 100644 --- a/kwant/linalg/lapack.pyx +++ b/kwant/linalg/lapack.pyx @@ -1,4 +1,4 @@ -# Copyright 2011-2013 Kwant authors. +# Copyright 2011-2017 Kwant authors. # # This file is part of Kwant. It is subject to the license terms in the file # LICENSE.rst found in the top-level directory of this distribution and at @@ -8,29 +8,53 @@ """Low-level access to LAPACK functions. """ -__all__ = ['sgetrf', 'dgetrf', 'cgetrf', 'zgetrf', - 'sgetrs', 'dgetrs', 'cgetrs', 'zgetrs', - 'sgecon', 'dgecon', 'cgecon', 'zgecon', - 'sggev', 'dggev', 'cggev', 'zggev', - 'sgees', 'dgees', 'cgees', 'zgees', - 'strsen', 'dtrsen', 'ctrsen', 'ztrsen', - 'strevc', 'dtrevc', 'ctrevc', 'ztrevc', - 'sgges', 'dgges', 'cgges', 'zgges', - 'stgsen', 'dtgsen', 'ctgsen', 'ztgsen', - 'stgevc', 'dtgevc', 'ctgevc', 'ztgevc', +__all__ = ['getrf', + 'getrs', + 'gecon', + 'ggev', + 'gees', + 'trsen', + 'trevc', + 'gges', + 'tgsen', + 'tgevc', 'prepare_for_lapack'] import numpy as np cimport numpy as np -# Strangely absolute imports from kwant.linalg.f_lapack don't work here. So -# continue using traditional implicit relative imports. -from . import f_lapack -from . cimport f_lapack -from .f_lapack cimport l_int, l_logical +cimport scipy.linalg.cython_lapack as lapack -int_dtype = f_lapack.l_int_dtype -logical_dtype = f_lapack.l_logical_dtype +ctypedef int l_int +ctypedef bint l_logical + +int_dtype = np.int32 +logical_dtype = np.int32 + +ctypedef float complex float_complex +ctypedef double complex double_complex + +ctypedef fused scalar: + float + double + float complex + double complex + +ctypedef fused single_precision: + float + float complex + +ctypedef fused double_precision: + double + double complex + +ctypedef fused cmplx: + float complex + double complex + +ctypedef fused floating: + float + double # exceptions @@ -51,284 +75,161 @@ def assert_fortran_mat(*mats): raise ValueError("Input matrix must be Fortran contiguous") -# Wrappers for xGETRF -def sgetrf(np.ndarray[np.float32_t, ndim=2] A): - cdef l_int M, N, info - cdef np.ndarray[l_int, ndim=1] ipiv - - assert_fortran_mat(A) - - M = A.shape[0] - N = A.shape[1] - ipiv = np.empty(min(M,N), dtype = f_lapack.l_int_dtype) - - f_lapack.sgetrf_(&M, &N, <float *>A.data, &M, - <l_int *>ipiv.data, &info) - - assert info >= 0, "Argument error in sgetrf" - - return (A, ipiv, info > 0 or M != N) - -def dgetrf(np.ndarray[np.float64_t, ndim=2] A): - cdef l_int M, N, info - cdef np.ndarray[l_int, ndim=1] ipiv - - assert_fortran_mat(A) - - M = A.shape[0] - N = A.shape[1] - ipiv = np.empty(min(M,N), dtype = f_lapack.l_int_dtype) - - f_lapack.dgetrf_(&M, &N, <double *>A.data, &M, - <l_int *>ipiv.data, &info) - - assert info >= 0, "Argument error in dgetrf" - - return (A, ipiv, info > 0 or M != N) - -def cgetrf(np.ndarray[np.complex64_t, ndim=2] A): - cdef l_int M, N, info - cdef np.ndarray[l_int, ndim=1] ipiv - - assert_fortran_mat(A) - - M = A.shape[0] - N = A.shape[1] - ipiv = np.empty(min(M,N), dtype = f_lapack.l_int_dtype) +cdef np.ndarray maybe_complex(scalar selector, + np.ndarray real, np.ndarray imag): + cdef np.ndarray r + r = real + if scalar in floating: + if imag.nonzero()[0].size: + r = real + 1j * imag + return r - f_lapack.cgetrf_(&M, &N, <float complex *>A.data, &M, - <l_int *>ipiv.data, &info) - assert info >= 0, "Argument error in cgetrf" +cdef l_int lwork_from_qwork(scalar qwork): + if scalar in floating: + return <l_int>qwork + else: + return <l_int>qwork.real - return (A, ipiv, info > 0 or M != N) -def zgetrf(np.ndarray[np.complex128_t, ndim=2] A): +def getrf(np.ndarray[scalar, ndim=2] A): cdef l_int M, N, info - cdef np.ndarray[l_int, ndim=1] ipiv + cdef np.ndarray[l_int] ipiv assert_fortran_mat(A) M = A.shape[0] N = A.shape[1] - ipiv = np.empty(min(M,N), dtype = f_lapack.l_int_dtype) - - f_lapack.zgetrf_(&M, &N, <double complex *>A.data, &M, - <l_int *>ipiv.data, &info) - - assert info >= 0, "Argument error in zgetrf" + ipiv = np.empty(min(M,N), dtype = int_dtype) + + if scalar is float: + lapack.sgetrf(&M, &N, <float *>A.data, &M, + <l_int *>ipiv.data, &info) + elif scalar is double: + lapack.dgetrf(&M, &N, <double *>A.data, &M, + <l_int *>ipiv.data, &info) + elif scalar is float_complex: + lapack.cgetrf(&M, &N, <float complex *>A.data, &M, + <l_int *>ipiv.data, &info) + elif scalar is double_complex: + lapack.zgetrf(&M, &N, <double complex *>A.data, &M, + <l_int *>ipiv.data, &info) + + assert info >= 0, "Argument error in getrf" return (A, ipiv, info > 0 or M != N) -# Wrappers for xGETRS -def sgetrs(np.ndarray[np.float32_t, ndim=2] LU, - np.ndarray[l_int, ndim=1] IPIV, B): +def getrs(np.ndarray[scalar, ndim=2] LU, np.ndarray[l_int] IPIV, + np.ndarray B): cdef l_int N, NRHS, info - cdef np.ndarray b assert_fortran_mat(LU) - # again: workaround for 1x1-Fortran bug in NumPy < v2.0 - if (not isinstance(B, np.ndarray) or - (B.ndim == 2 and (B.shape[0] > 1 or B.shape[1] > 1) and - not B.flags["F_CONTIGUOUS"])): - raise ValueError("In dgetrs: B must be a Fortran ordered NumPy array") - - b = B - N = LU.shape[0] - if b.ndim == 1: - NRHS = 1 - elif b.ndim == 2: - NRHS = B.shape[1] - else: - raise ValueError("In sgetrs: B must be a vector or matrix") - - f_lapack.sgetrs_("N", &N, &NRHS, <float *>LU.data, &N, - <l_int *>IPIV.data, <float *>b.data, &N, - &info) - - assert info == 0, "Argument error in sgetrs" - - return b + # Consistency checks for LU and B -def dgetrs(np.ndarray[np.float64_t, ndim=2] LU, - np.ndarray[l_int, ndim=1] IPIV, B): - cdef l_int N, NRHS, info - cdef np.ndarray b - - assert_fortran_mat(LU) + if B.descr.type_num != LU.descr.type_num: + raise TypeError('B must have same dtype as LU') - # again: workaround for 1x1-Fortran bug in NumPy < v2.0 - if (not isinstance(B, np.ndarray) or - (B.ndim == 2 and (B.shape[0] > 1 or B.shape[1] > 1) and + # Workaround for 1x1-Fortran bug in NumPy < v2.0 + if ((B.ndim == 2 and (B.shape[0] > 1 or B.shape[1] > 1) and not B.flags["F_CONTIGUOUS"])): - raise ValueError("In dgetrs: B must be a Fortran ordered NumPy array") - - b = B - N = LU.shape[0] - if b.ndim == 1: - NRHS = 1 - elif b.ndim == 2: - NRHS = b.shape[1] - else: - raise ValueError("In dgetrs: B must be a vector or matrix") - - f_lapack.dgetrs_("N", &N, &NRHS, <double *>LU.data, &N, - <l_int *>IPIV.data, <double *>b.data, &N, - &info) + raise ValueError("B must be Fortran ordered") - assert info == 0, "Argument error in dgetrs" + if B.ndim > 2: + raise ValueError("B must be a vector or matrix") - return b + if LU.shape[0] != B.shape[0]: + raise ValueError('LU and B have incompatible shapes') -def cgetrs(np.ndarray[np.complex64_t, ndim=2] LU, - np.ndarray[l_int, ndim=1] IPIV, B): - cdef l_int N, NRHS, info - cdef np.ndarray b - - assert_fortran_mat(LU) - - # again: workaround for 1x1-Fortran bug in NumPy < v2.0 - if (not isinstance(B, np.ndarray) or - (B.ndim == 2 and (B.shape[0] > 1 or B.shape[1] > 1) and - not B.flags["F_CONTIGUOUS"])): - raise ValueError("In dgetrs: B must be a Fortran ordered NumPy array") - - b = B N = LU.shape[0] - if b.ndim == 1: - NRHS = 1 - elif b.ndim == 2: - NRHS = b.shape[1] - else: - raise ValueError("In cgetrs: B must be a vector or matrix") - f_lapack.cgetrs_("N", &N, &NRHS, <float complex *>LU.data, &N, - <l_int *>IPIV.data, <float complex *>b.data, &N, - &info) - - assert info == 0, "Argument error in cgetrs" - - return b - -def zgetrs(np.ndarray[np.complex128_t, ndim=2] LU, - np.ndarray[l_int, ndim=1] IPIV, B): - cdef l_int N, NRHS, info - cdef np.ndarray b - - assert_fortran_mat(LU) - - # again: workaround for 1x1-Fortran bug in NumPy < v2.0 - if (not isinstance(B, np.ndarray) or - (B.ndim == 2 and (B.shape[0] > 1 or B.shape[1] > 1) and - not B.flags["F_CONTIGUOUS"])): - raise ValueError("In dgetrs: B must be a Fortran ordered NumPy array") - - b = B - N = LU.shape[0] - if b.ndim == 1: + if B.ndim == 1: NRHS = 1 - elif b.ndim == 2: - NRHS = b.shape[1] - else: - raise ValueError("In zgetrs: B must be a vector or matrix") - - f_lapack.zgetrs_("N", &N, &NRHS, <double complex *>LU.data, &N, - <l_int *>IPIV.data, <double complex *>b.data, &N, - &info) - - assert info == 0, "Argument error in zgetrs" - - return b - -# Wrappers for xGECON + elif B.ndim == 2: + NRHS = B.shape[1] -def sgecon(np.ndarray[np.float32_t, ndim=2] LU, - float normA, char *norm = b"1"): + if scalar is float: + lapack.sgetrs("N", &N, &NRHS, <float *>LU.data, &N, + <l_int *>IPIV.data, <float *>B.data, &N, + &info) + elif scalar is double: + lapack.dgetrs("N", &N, &NRHS, <double *>LU.data, &N, + <l_int *>IPIV.data, <double *>B.data, &N, + &info) + elif scalar is float_complex: + lapack.cgetrs("N", &N, &NRHS, <float complex *>LU.data, &N, + <l_int *>IPIV.data, <float complex *>B.data, &N, + &info) + elif scalar is double_complex: + lapack.zgetrs("N", &N, &NRHS, <double complex *>LU.data, &N, + <l_int *>IPIV.data, <double complex *>B.data, &N, + &info) + + assert info == 0, "Argument error in getrs" + + return B + + +def gecon(np.ndarray[scalar, ndim=2] LU, double normA, char *norm = b"1"): cdef l_int N, info - cdef float rcond - cdef np.ndarray[np.float32_t, ndim=1] work - cdef np.ndarray[l_int, ndim=1] iwork + cdef float srcond, snormA + cdef double drcond - assert_fortran_mat(LU) - - N = LU.shape[0] - work = np.empty(4*N, dtype = np.float32) - iwork = np.empty(N, dtype = f_lapack.l_int_dtype) - - f_lapack.sgecon_(norm, &N, <float *>LU.data, &N, &normA, - &rcond, <float *>work.data, - <l_int *>iwork.data, &info) - - assert info == 0, "Argument error in sgecon" - - return rcond - -def dgecon(np.ndarray[np.float64_t, ndim=2] LU, - double normA, char *norm = b"1"): - cdef l_int N, info - cdef double rcond - cdef np.ndarray[np.float64_t, ndim=1] work - cdef np.ndarray[l_int, ndim=1] iwork + # Parameter checks assert_fortran_mat(LU) + if norm[0] != b"1" and norm[0] != b"I": + raise ValueError("'norm' must be either '1' or 'I'") + if scalar in single_precision: + snormA = normA - N = LU.shape[0] - work = np.empty(4*N, dtype = np.float64) - iwork = np.empty(N, dtype = f_lapack.l_int_dtype) - - f_lapack.dgecon_(norm, &N, <double *>LU.data, &N, &normA, - &rcond, <double *>work.data, - <l_int *>iwork.data, &info) - - assert info == 0, "Argument error in dgecon" - - return rcond - -def cgecon(np.ndarray[np.complex64_t, ndim=2] LU, - float normA, char *norm = b"1"): - cdef l_int N, info - cdef float rcond - cdef np.ndarray[np.complex64_t, ndim=1] work - cdef np.ndarray[np.float32_t, ndim=1] rwork - - assert_fortran_mat(LU) + # Allocate workspaces N = LU.shape[0] - work = np.empty(2*N, dtype = np.complex64) - rwork = np.empty(2*N, dtype = np.float32) - f_lapack.cgecon_(norm, &N, <float complex *>LU.data, &N, &normA, - &rcond, <float complex *>work.data, - <float *>rwork.data, &info) - - assert info == 0, "Argument error in cgecon" - - return rcond + cdef np.ndarray[l_int] iwork + if scalar in floating: + iwork = np.empty(N, dtype=int_dtype) -def zgecon(np.ndarray[np.complex128_t, ndim=2] LU, - double normA, char *norm = b"1"): - cdef l_int N, info - cdef double rcond - cdef np.ndarray[np.complex128_t, ndim=1] work - cdef np.ndarray[np.float64_t, ndim=1] rwork + cdef np.ndarray[scalar] work + if scalar in floating: + work = np.empty(4 * N, dtype=LU.dtype) + else: + work = np.empty(2 * N, dtype=LU.dtype) - assert_fortran_mat(LU) + cdef np.ndarray rwork + if scalar is float_complex: + rwork = np.empty(2 * N, dtype=np.float32) + elif scalar is double_complex: + rwork = np.empty(2 * N, dtype=np.float64) - N = LU.shape[0] - work = np.empty(2*N, dtype = np.complex128) - rwork = np.empty(2*N, dtype = np.float64) + # The actual calculation - f_lapack.zgecon_(norm, &N, <double complex *>LU.data, &N, &normA, - &rcond, <double complex *>work.data, - <double *>rwork.data, &info) + if scalar is float: + lapack.sgecon(norm, &N, <float *>LU.data, &N, &snormA, + &srcond, <float *>work.data, + <l_int *>iwork.data, &info) + elif scalar is double: + lapack.dgecon(norm, &N, <double *>LU.data, &N, &normA, + &drcond, <double *>work.data, + <l_int *>iwork.data, &info) + elif scalar is float_complex: + lapack.cgecon(norm, &N, <float complex *>LU.data, &N, &snormA, + &srcond, <float complex *>work.data, + <float *>rwork.data, &info) + elif scalar is double_complex: + lapack.zgecon(norm, &N, <double complex *>LU.data, &N, &normA, + &drcond, <double complex *>work.data, + <double *>rwork.data, &info) - assert info == 0, "Argument error in zgecon" + assert info == 0, "Argument error in gecon" - return rcond + if scalar in single_precision: + return srcond + else: + return drcond -# Wrappers for xGGEV # Helper function for xGGEV def ggev_postprocess(dtype, alphar, alphai, vl_r=None, vr_r=None): @@ -363,707 +264,363 @@ def ggev_postprocess(dtype, alphar, alphai, vl_r=None, vr_r=None): return (alpha, vl, vr) -def sggev(np.ndarray[np.float32_t, ndim=2] A, - np.ndarray[np.float32_t, ndim=2] B, - left=False, right=True): +def ggev(np.ndarray[scalar, ndim=2] A, np.ndarray[scalar, ndim=2] B, + left=False, right=True): cdef l_int N, info, lwork - cdef char *jobvl - cdef char *jobvr - cdef np.ndarray[np.float32_t, ndim=2] vl_r, vr_r - cdef float *vl_ptr - cdef float *vr_ptr - cdef float qwork - cdef np.ndarray[np.float32_t, ndim=1] work, alphar, alphai, beta - - assert_fortran_mat(A, B) - - N = A.shape[0] - alphar = np.empty(N, dtype = np.float32) - alphai = np.empty(N, dtype = np.float32) - beta = np.empty(N, dtype = np.float32) - - if left: - vl_r = np.empty((N,N), dtype = np.float32, order='F') - vl_ptr = <float *>vl_r.data - jobvl = "V" - else: - vl_r = None - vl_ptr = NULL - jobvl = "N" - - if right: - vr_r = np.empty((N,N), dtype = np.float32, order='F') - vr_ptr = <float *>vr_r.data - jobvr = "V" - else: - vr_r = None - vr_ptr = NULL - jobvr = "N" - - # workspace query - lwork = -1 - - f_lapack.sggev_(jobvl, jobvr, &N, <float *>A.data, &N, - <float *>B.data, &N, - <float *>alphar.data, <float *> alphai.data, - <float *>beta.data, - vl_ptr, &N, vr_ptr, &N, - &qwork, &lwork, &info) - assert info == 0, "Argument error in sggev" + # Parameter checks - lwork = <l_int>qwork - work = np.empty(lwork, dtype = np.float32) - - # Now the real calculation - f_lapack.sggev_(jobvl, jobvr, &N, <float *>A.data, &N, - <float *>B.data, &N, - <float *>alphar.data, <float *> alphai.data, - <float *>beta.data, - vl_ptr, &N, vr_ptr, &N, - <float *>work.data, &lwork, &info) - - if info > 0: - raise LinAlgError("QZ iteration failed to converge in sggev") - - assert info == 0, "Argument error in sggev" - - alpha, vl, vr = ggev_postprocess(np.complex64, alphar, alphai, vl_r, vr_r) + assert_fortran_mat(A, B) - return filter_args((True, True, left, right), (alpha, beta, vl, vr)) + if A.ndim != 2 or A.ndim != 2: + raise ValueError("gen_eig requires both a and be to be matrices") + if A.shape[0] != A.shape[1]: + raise ValueError("gen_eig requires square matrix input") -def dggev(np.ndarray[np.float64_t, ndim=2] A, - np.ndarray[np.float64_t, ndim=2] B, - left=False, right=True): - cdef l_int N, info, lwork - cdef char *jobvl - cdef char *jobvr - cdef np.ndarray[np.float64_t, ndim=2] vl_r, vr_r - cdef double *vl_ptr - cdef double *vr_ptr - cdef double qwork - cdef np.ndarray[np.float64_t, ndim=1] work, alphar, alphai, beta + if A.shape[0] != B.shape[0] or A.shape[1] != B.shape[1]: + raise ValueError("A and B do not have the same shape") - assert_fortran_mat(A, B) + # Allocate workspaces N = A.shape[0] - alphar = np.empty(N, dtype = np.float64) - alphai = np.empty(N, dtype = np.float64) - beta = np.empty(N, dtype = np.float64) - - if left: - vl_r = np.empty((N,N), dtype = np.float64, order='F') - vl_ptr = <double *>vl_r.data - jobvl = "V" - else: - vl_r = None - vl_ptr = NULL - jobvl = "N" - if right: - vr_r = np.empty((N,N), dtype = np.float64, order='F') - vr_ptr = <double *>vr_r.data - jobvr = "V" + cdef np.ndarray[scalar] alphar, alphai + if scalar in cmplx: + alphar = np.empty(N, dtype=A.dtype) + alphai = None else: - vr_r = None - vr_ptr = NULL - jobvr = "N" - - # workspace query - lwork = -1 - - f_lapack.dggev_(jobvl, jobvr, &N, <double *>A.data, &N, - <double *>B.data, &N, - <double *>alphar.data, <double *> alphai.data, - <double *>beta.data, - vl_ptr, &N, vr_ptr, &N, - &qwork, &lwork, &info) - - assert info == 0, "Argument error in dggev" - - lwork = <l_int>qwork - work = np.empty(lwork, dtype = np.float64) - - # Now the real calculation - f_lapack.dggev_(jobvl, jobvr, &N, <double *>A.data, &N, - <double *>B.data, &N, - <double *>alphar.data, <double *> alphai.data, - <double *>beta.data, - vl_ptr, &N, vr_ptr, &N, - <double *>work.data, &lwork, &info) - - if info > 0: - raise LinAlgError("QZ iteration failed to converge in dggev") - - assert info == 0, "Argument error in dggev" + alphar = np.empty(N, dtype=A.dtype) + alphai = np.empty(N, dtype=A.dtype) - alpha, vl, vr = ggev_postprocess(np.complex128, alphar, alphai, vl_r, vr_r) - - return filter_args((True, True, left, right), (alpha, beta, vl, vr)) + cdef np.ndarray[scalar] beta = np.empty(N, dtype=A.dtype) + cdef np.ndarray rwork = None + if scalar is float_complex: + rwork = np.empty(8 * N, dtype=np.float32) + elif scalar is double_complex: + rwork = np.empty(8 * N, dtype=np.float64) -def cggev(np.ndarray[np.complex64_t, ndim=2] A, - np.ndarray[np.complex64_t, ndim=2] B, - left=False, right=True): - cdef l_int N, info, lwork + cdef np.ndarray vl + cdef scalar *vl_ptr cdef char *jobvl - cdef char *jobvr - cdef np.ndarray[np.complex64_t, ndim=2] vl, vr - cdef float complex *vl_ptr - cdef float complex *vr_ptr - cdef float complex qwork - cdef np.ndarray[np.complex64_t, ndim=1] work, alpha, beta - cdef np.ndarray[np.float32_t, ndim=1] rwork - - assert_fortran_mat(A, B) - - N = A.shape[0] - alpha = np.empty(N, dtype = np.complex64) - beta = np.empty(N, dtype = np.complex64) - if left: - vl = np.empty((N,N), dtype = np.complex64, order='F') - vl_ptr = <float complex *>vl.data + vl = np.empty((N,N), dtype=A.dtype, order='F') + vl_ptr = <scalar *>vl.data jobvl = "V" else: + vl = None vl_ptr = NULL jobvl = "N" + cdef np.ndarray vr + cdef scalar *vr_ptr + cdef char *jobvr if right: - vr = np.empty((N,N), dtype = np.complex64, order='F') - vr_ptr = <float complex *>vr.data + vr = np.empty((N,N), dtype=A.dtype, order='F') + vr_ptr = <scalar *>vr.data jobvr = "V" else: + vr = None vr_ptr = NULL jobvr = "N" - rwork = np.empty(8 * N, dtype = np.float32) - - # workspace query + # Workspace query + # Xggev expects &qwork as a <scalar *> (even though it's an integer) lwork = -1 - work = np.empty(1, dtype = np.complex64) + cdef scalar qwork - f_lapack.cggev_(jobvl, jobvr, &N, <float complex *>A.data, &N, - <float complex *>B.data, &N, - <float complex *>alpha.data, <float complex *>beta.data, - vl_ptr, &N, vr_ptr, &N, - &qwork, &lwork, - <float *>rwork.data, &info) + if scalar is float: + lapack.sggev(jobvl, jobvr, &N, <float *>A.data, &N, + <float *>B.data, &N, + <float *>alphar.data, <float *> alphai.data, + <float *>beta.data, + vl_ptr, &N, vr_ptr, &N, + &qwork, &lwork, &info) + elif scalar is double: + lapack.dggev(jobvl, jobvr, &N, <double *>A.data, &N, + <double *>B.data, &N, + <double *>alphar.data, <double *> alphai.data, + <double *>beta.data, + vl_ptr, &N, vr_ptr, &N, + &qwork, &lwork, &info) + elif scalar is float_complex: + lapack.cggev(jobvl, jobvr, &N, <float complex *>A.data, &N, + <float complex *>B.data, &N, + <float complex *>alphar.data, <float complex *>beta.data, + vl_ptr, &N, vr_ptr, &N, + &qwork, &lwork, + <float *>rwork.data, &info) + elif scalar is double_complex: + lapack.zggev(jobvl, jobvr, &N, <double complex *>A.data, &N, + <double complex *>B.data, &N, + <double complex *>alphar.data, <double complex *>beta.data, + vl_ptr, &N, vr_ptr, &N, + &qwork, &lwork, + <double *>rwork.data, &info) - assert info == 0, "Argument error in cggev" + assert info == 0, "Argument error in ggev" - lwork = <l_int>qwork.real + lwork = lwork_from_qwork(qwork) + cdef np.ndarray[scalar] work = np.empty(lwork, dtype=A.dtype) - work = np.empty(lwork, dtype = np.complex64) + # The actual calculation - # Now the real calculation - f_lapack.cggev_(jobvl, jobvr, &N, <float complex *>A.data, &N, - <float complex *>B.data, &N, - <float complex *>alpha.data, <float complex *>beta.data, - vl_ptr, &N, vr_ptr, &N, - <float complex *>work.data, &lwork, - <float *>rwork.data, &info) + if scalar is float: + lapack.sggev(jobvl, jobvr, &N, <float *>A.data, &N, + <float *>B.data, &N, + <float *>alphar.data, <float *> alphai.data, + <float *>beta.data, + vl_ptr, &N, vr_ptr, &N, + <float *>work.data, &lwork, &info) + elif scalar is double: + lapack.dggev(jobvl, jobvr, &N, <double *>A.data, &N, + <double *>B.data, &N, + <double *>alphar.data, <double *> alphai.data, + <double *>beta.data, + vl_ptr, &N, vr_ptr, &N, + <double *>work.data, &lwork, &info) + elif scalar is float_complex: + lapack.cggev(jobvl, jobvr, &N, <float complex *>A.data, &N, + <float complex *>B.data, &N, + <float complex *>alphar.data, <float complex *>beta.data, + vl_ptr, &N, vr_ptr, &N, + <float complex *>work.data, &lwork, + <float *>rwork.data, &info) + elif scalar is double_complex: + lapack.zggev(jobvl, jobvr, &N, <double complex *>A.data, &N, + <double complex *>B.data, &N, + <double complex *>alphar.data, <double complex *>beta.data, + vl_ptr, &N, vr_ptr, &N, + <double complex *>work.data, &lwork, + <double *>rwork.data, &info) if info > 0: - raise LinAlgError("QZ iteration failed to converge in cggev") - - assert info == 0, "Argument error in cggev" - - return filter_args((True, True, left, right), (alpha, beta, vl, vr)) - - -def zggev(np.ndarray[np.complex128_t, ndim=2] A, - np.ndarray[np.complex128_t, ndim=2] B, - left=False, right=True): - cdef l_int N, info, lwork - cdef char *jobvl - cdef char *jobvr - cdef np.ndarray[np.complex128_t, ndim=2] vl, vr - cdef double complex *vl_ptr - cdef double complex *vr_ptr - cdef double complex qwork - cdef np.ndarray[np.complex128_t, ndim=1] work, alpha, beta - cdef np.ndarray[np.float64_t, ndim=1] rwork - - assert_fortran_mat(A, B) + raise LinAlgError("QZ iteration failed to converge in sggev") - N = A.shape[0] - alpha = np.empty(N, dtype = np.complex128) - beta = np.empty(N, dtype = np.complex128) + assert info == 0, "Argument error in ggev" - if left: - vl = np.empty((N,N), dtype = np.complex128, order='F') - vl_ptr = <double complex *>vl.data - jobvl = "V" - else: - vl_ptr = NULL - jobvl = "N" + if scalar is float: + post_dtype = np.complex64 + elif scalar is double: + post_dtype = np.complex128 - if right: - vr = np.empty((N,N), dtype = np.complex128, order='F') - vr_ptr = <double complex *>vr.data - jobvr = "V" - else: - vr_ptr = NULL - jobvr = "N" + cdef np.ndarray alpha + alpha = alphar + if scalar in floating: + alpha, vl, vr = ggev_postprocess(post_dtype, alphar, alphai, vl, vr) - rwork = np.empty(8 * N, dtype = np.float64) + return filter_args((True, True, left, right), (alpha, beta, vl, vr)) - # workspace query - lwork = -1 - work = np.empty(1, dtype = np.complex128) - f_lapack.zggev_(jobvl, jobvr, &N, <double complex *>A.data, &N, - <double complex *>B.data, &N, - <double complex *>alpha.data, <double complex *>beta.data, - vl_ptr, &N, vr_ptr, &N, - &qwork, &lwork, - <double *>rwork.data, &info) +def gees(np.ndarray[scalar, ndim=2] A, calc_q=True, calc_ev=True): + cdef l_int N, lwork, sdim, info - assert info == 0, "Argument error in zggev" + assert_fortran_mat(A) - lwork = <l_int>qwork.real - work = np.empty(lwork, dtype = np.complex128) + if A.ndim != 2: + raise ValueError("Expect matrix as input") - # Now the real calculation - f_lapack.zggev_(jobvl, jobvr, &N, <double complex *>A.data, &N, - <double complex *>B.data, &N, - <double complex *>alpha.data, <double complex *>beta.data, - vl_ptr, &N, vr_ptr, &N, - <double complex *>work.data, &lwork, - <double *>rwork.data, &info) + if A.shape[0] != A.shape[1]: + raise ValueError("Expect square matrix") - if info > 0: - raise LinAlgError("QZ iteration failed to converge in zggev") + # Allocate workspaces - assert info == 0, "Argument error in zggev" + N = A.shape[0] - return filter_args((True, True, left, right), (alpha, beta, vl, vr)) + cdef np.ndarray[scalar] wr, wi + if scalar in cmplx: + wr = np.empty(N, dtype=A.dtype) + wi = None + else: + wr = np.empty(N, dtype=A.dtype) + wi = np.empty(N, dtype=A.dtype) + cdef np.ndarray rwork + if scalar is float_complex: + rwork = np.empty(N, dtype=np.float32) + elif scalar is double_complex: + rwork = np.empty(N, dtype=np.float64) -# Wrapper for xGEES -def sgees(np.ndarray[np.float32_t, ndim=2] A, - calc_q=True, calc_ev=True): - cdef l_int N, lwork, sdim, info cdef char *jobvs - cdef float *vs_ptr - cdef float qwork - cdef np.ndarray[np.float32_t, ndim=2] vs - cdef np.ndarray[np.float32_t] wr, wi, work - - assert_fortran_mat(A) - - N = A.shape[0] - wr = np.empty(N, dtype = np.float32) - wi = np.empty(N, dtype = np.float32) - + cdef scalar *vs_ptr + cdef np.ndarray[scalar, ndim=2] vs if calc_q: - vs = np.empty((N,N), dtype = np.float32, order='F') - vs_ptr = <float *>vs.data + vs = np.empty((N,N), dtype=A.dtype, order='F') + vs_ptr = <scalar *>vs.data jobvs = "V" else: + vs = None vs_ptr = NULL jobvs = "N" - # workspace query + # Workspace query + # Xgees expects &qwork as a <scalar *> (even though it's an integer) lwork = -1 - f_lapack.sgees_(jobvs, "N", NULL, &N, <float *>A.data, &N, - &sdim, <float *>wr.data, <float *>wi.data, vs_ptr, &N, - &qwork, &lwork, NULL, &info) + cdef scalar qwork + + if scalar is float: + lapack.sgees(jobvs, "N", NULL, &N, <float *>A.data, &N, + &sdim, <float *>wr.data, <float *>wi.data, vs_ptr, &N, + &qwork, &lwork, NULL, &info) + elif scalar is double: + lapack.dgees(jobvs, "N", NULL, &N, <double *>A.data, &N, + &sdim, <double *>wr.data, <double *>wi.data, vs_ptr, &N, + &qwork, &lwork, NULL, &info) + elif scalar is float_complex: + lapack.cgees(jobvs, "N", NULL, &N, <float complex *>A.data, &N, + &sdim, <float complex *>wr.data, vs_ptr, &N, + &qwork, &lwork, <float *>rwork.data, NULL, &info) + elif scalar is double_complex: + lapack.zgees(jobvs, "N", NULL, &N, <double complex *>A.data, &N, + &sdim, <double complex *>wr.data, vs_ptr, &N, + &qwork, &lwork, <double *>rwork.data, NULL, &info) assert info == 0, "Argument error in sgees" - lwork = <int>qwork - work = np.empty(lwork, dtype = np.float32) - - # Now the real calculation - f_lapack.sgees_(jobvs, "N", NULL, &N, <float *>A.data, &N, - &sdim, <float *>wr.data, <float *>wi.data, vs_ptr, &N, - <float *>work.data, &lwork, NULL, &info) + lwork = lwork_from_qwork(qwork) + cdef np.ndarray[scalar] work = np.empty(lwork, dtype=A.dtype) + + # The actual calculation + + if scalar is float: + lapack.sgees(jobvs, "N", NULL, &N, <float *>A.data, &N, + &sdim, <float *>wr.data, <float *>wi.data, vs_ptr, &N, + <float *>work.data, &lwork, NULL, &info) + elif scalar is double: + lapack.dgees(jobvs, "N", NULL, &N, <double *>A.data, &N, + &sdim, <double *>wr.data, <double *>wi.data, vs_ptr, &N, + <double *>work.data, &lwork, NULL, &info) + elif scalar is float_complex: + lapack.cgees(jobvs, "N", NULL, &N, <float complex *>A.data, &N, + &sdim, <float complex *>wr.data, vs_ptr, &N, + <float complex *>work.data, &lwork, + <float *>rwork.data, NULL, &info) + elif scalar is double_complex: + lapack.zgees(jobvs, "N", NULL, &N, <double complex *>A.data, &N, + &sdim, <double complex *>wr.data, vs_ptr, &N, + <double complex *>work.data, &lwork, + <double *>rwork.data, NULL, &info) if info > 0: - raise LinAlgError("QR iteration failed to converge in sgees") + raise LinAlgError("QR iteration failed to converge in gees") - assert info == 0, "Argument error in sgees" + assert info == 0, "Argument error in gees" - if wi.nonzero()[0].size: - w = wr + 1j * wi - else: - w = wr + # Real inputs possibly produce complex output + cdef np.ndarray w = maybe_complex[scalar](0, wr, wi) return filter_args((True, calc_q, calc_ev), (A, vs, w)) -def dgees(np.ndarray[np.float64_t, ndim=2] A, - calc_q=True, calc_ev=True): - cdef l_int N, lwork, sdim, info - cdef char *jobvs - cdef double *vs_ptr - cdef double qwork - cdef np.ndarray[np.float64_t, ndim=2] vs - cdef np.ndarray[np.float64_t] wr, wi, work +def trsen(np.ndarray[l_logical] select, + np.ndarray[scalar, ndim=2] T, + np.ndarray[scalar, ndim=2] Q, + calc_ev=True): + cdef l_int N, M, lwork, liwork, qiwork, info - assert_fortran_mat(A) + assert_fortran_mat(T, Q) - N = A.shape[0] - wr = np.empty(N, dtype = np.float64) - wi = np.empty(N, dtype = np.float64) + # Allocate workspaces - if calc_q: - vs = np.empty((N,N), dtype = np.float64, order='F') - vs_ptr = <double *>vs.data - jobvs = "V" - else: - vs_ptr = NULL - jobvs = "N" + N = T.shape[0] - # workspace query - lwork = -1 - f_lapack.dgees_(jobvs, "N", NULL, &N, <double *>A.data, &N, - &sdim, <double *>wr.data, <double *>wi.data, vs_ptr, &N, - &qwork, &lwork, NULL, &info) + cdef np.ndarray[scalar] wr, wi + if scalar in cmplx: + wr = np.empty(N, dtype=T.dtype) + wi = None + else: + wr = np.empty(N, dtype=T.dtype) + wi = np.empty(N, dtype=T.dtype) - assert info == 0, "Argument error in dgees" + cdef char *compq + cdef scalar *q_ptr + if Q is not None: + compq = "V" + q_ptr = <scalar *>Q.data + else: + compq = "N" + q_ptr = NULL - lwork = <int>qwork - work = np.empty(lwork, dtype = np.float64) + # Workspace query + # Xtrsen expects &qwork as a <scalar *> (even though it's an integer) + cdef scalar qwork + lwork = liwork = -1 - # Now the real calculation - f_lapack.dgees_(jobvs, "N", NULL, &N, <double *>A.data, &N, - &sdim, <double *>wr.data, <double *>wi.data, vs_ptr, &N, - <double *>work.data, &lwork, NULL, &info) + if scalar is float: + lapack.strsen("N", compq, <l_logical *>select.data, + &N, <float *>T.data, &N, q_ptr, &N, + <float *>wr.data, <float *>wi.data, &M, NULL, NULL, + &qwork, &lwork, &qiwork, &liwork, &info) + elif scalar is double: + lapack.dtrsen("N", compq, <l_logical *>select.data, + &N, <double *>T.data, &N, q_ptr, &N, + <double *>wr.data, <double *>wi.data, &M, NULL, NULL, + &qwork, &lwork, &qiwork, &liwork, &info) + elif scalar is float_complex: + lapack.ctrsen("N", compq, <l_logical *>select.data, + &N, <float complex *>T.data, &N, q_ptr, &N, + <float complex *>wr.data, &M, NULL, NULL, + &qwork, &lwork, &info) + elif scalar is double_complex: + lapack.ztrsen("N", compq, <l_logical *>select.data, + &N, <double complex *>T.data, &N, q_ptr, &N, + <double complex *>wr.data, &M, NULL, NULL, + &qwork, &lwork, &info) + + assert info == 0, "Argument error in trsen" + + lwork = lwork_from_qwork(qwork) + cdef np.ndarray[scalar] work = np.empty(lwork, dtype=T.dtype) + + cdef np.ndarray[l_int] iwork = None + if scalar in floating: + liwork = qiwork + iwork = np.empty(liwork, dtype=int_dtype) + + # Tha actual calculation + + if scalar is float: + lapack.strsen("N", compq, <l_logical *>select.data, + &N, <float *>T.data, &N, q_ptr, &N, + <float *>wr.data, <float *>wi.data, &M, NULL, NULL, + <float *>work.data, &lwork, + <l_int *>iwork.data, &liwork, &info) + elif scalar is double: + lapack.dtrsen("N", compq, <l_logical *>select.data, + &N, <double *>T.data, &N, q_ptr, &N, + <double *>wr.data, <double *>wi.data, &M, NULL, NULL, + <double *>work.data, &lwork, + <l_int *>iwork.data, &liwork, &info) + elif scalar is float_complex: + lapack.ctrsen("N", compq, <l_logical *>select.data, + &N, <float complex *>T.data, &N, q_ptr, &N, + <float complex *>wr.data, &M, NULL, NULL, + <float complex *>work.data, &lwork, &info) + elif scalar is double_complex: + lapack.ztrsen("N", compq, <l_logical *>select.data, + &N, <double complex *>T.data, &N, q_ptr, &N, + <double complex *>wr.data, &M, NULL, NULL, + <double complex *>work.data, &lwork, &info) if info > 0: - raise LinAlgError("QR iteration failed to converge in dgees") + raise LinAlgError("Reordering failed; problem is very ill-conditioned") - assert info == 0, "Argument error in dgees" + assert info == 0, "Argument error in trsen" - if wi.nonzero()[0].size: - w = wr + 1j * wi - else: - w = wr + # Real inputs possibly produce complex output + cdef np.ndarray w = maybe_complex[scalar](0, wr, wi) - return filter_args((True, calc_q, calc_ev), (A, vs, w)) + return filter_args((True, Q is not None, calc_ev), (T, Q, w)) -def cgees(np.ndarray[np.complex64_t, ndim=2] A, - calc_q=True, calc_ev=True): - cdef l_int N, lwork, sdim, info - cdef char *jobvs - cdef float complex *vs_ptr - cdef float complex qwork - cdef np.ndarray[np.complex64_t, ndim=2] vs - cdef np.ndarray[np.complex64_t] w, work - cdef np.ndarray[np.float32_t] rwork - - assert_fortran_mat(A) - - N = A.shape[0] - w = np.empty(N, dtype = np.complex64) - rwork = np.empty(N, dtype = np.float32) - - if calc_q: - vs = np.empty((N,N), dtype = np.complex64, order='F') - vs_ptr = <float complex *>vs.data - jobvs = "V" - else: - vs_ptr = NULL - jobvs = "N" - - # workspace query - lwork = -1 - f_lapack.cgees_(jobvs, "N", NULL, &N, <float complex *>A.data, &N, - &sdim, <float complex *>w.data, vs_ptr, &N, - &qwork, &lwork, <float *>rwork.data, NULL, &info) - - assert info == 0, "Argument error in cgees" - - lwork = <int>qwork.real - work = np.empty(lwork, dtype = np.complex64) - - # Now the real calculation - f_lapack.cgees_(jobvs, "N", NULL, &N, <float complex *>A.data, &N, - &sdim, <float complex *>w.data, vs_ptr, &N, - <float complex *>work.data, &lwork, - <float *>rwork.data, NULL, &info) - - if info > 0: - raise LinAlgError("QR iteration failed to converge in cgees") - - assert info == 0, "Argument error in cgees" - - return filter_args((True, calc_q, calc_ev), (A, vs, w)) - - -def zgees(np.ndarray[np.complex128_t, ndim=2] A, - calc_q=True, calc_ev=True): - cdef l_int N, lwork, sdim, info - cdef char *jobvs - cdef double complex *vs_ptr - cdef double complex qwork - cdef np.ndarray[np.complex128_t, ndim=2] vs - cdef np.ndarray[np.complex128_t] w, work - cdef np.ndarray[np.float64_t] rwork - - assert_fortran_mat(A) - - N = A.shape[0] - w = np.empty(N, dtype = np.complex128) - rwork = np.empty(N, dtype = np.float64) - - if calc_q: - vs = np.empty((N,N), dtype = np.complex128, order='F') - vs_ptr = <double complex *>vs.data - jobvs = "V" - else: - vs_ptr = NULL - jobvs = "N" - - # workspace query - lwork = -1 - f_lapack.zgees_(jobvs, "N", NULL, &N, <double complex *>A.data, &N, - &sdim, <double complex *>w.data, vs_ptr, &N, - &qwork, &lwork, <double *>rwork.data, NULL, &info) - - assert info == 0, "Argument error in zgees" - - lwork = <int>qwork.real - work = np.empty(lwork, dtype = np.complex128) - - # Now the real calculation - f_lapack.zgees_(jobvs, "N", NULL, &N, <double complex *>A.data, &N, - &sdim, <double complex *>w.data, vs_ptr, &N, - <double complex *>work.data, &lwork, - <double *>rwork.data, NULL, &info) - - if info > 0: - raise LinAlgError("QR iteration failed to converge in zgees") - - assert info == 0, "Argument error in zgees" - - return filter_args((True, calc_q, calc_ev), (A, vs, w)) - -# Wrapper for xTRSEN -def strsen(np.ndarray[l_logical] select, - np.ndarray[np.float32_t, ndim=2] T, - np.ndarray[np.float32_t, ndim=2] Q=None, - calc_ev=True): - cdef l_int N, M, lwork, liwork, qiwork, info - cdef char *compq - cdef float qwork - cdef float *q_ptr - cdef np.ndarray[np.float32_t] wr, wi, work - cdef np.ndarray[l_int] iwork - - assert_fortran_mat(T, Q) - - N = T.shape[0] - wr = np.empty(N, dtype = np.float32) - wi = np.empty(N, dtype = np.float32) - - if Q is not None: - compq = "V" - q_ptr = <float *>Q.data - else: - compq = "N" - q_ptr = NULL - - # workspace query - lwork = liwork = -1 - f_lapack.strsen_("N", compq, <l_logical *>select.data, - &N, <float *>T.data, &N, q_ptr, &N, - <float *>wr.data, <float *>wi.data, &M, NULL, NULL, - &qwork, &lwork, &qiwork, &liwork, &info) - - assert info == 0, "Argument error in strsen" - - lwork = <int>qwork - work = np.empty(lwork, dtype = np.float32) - liwork = qiwork - iwork = np.empty(liwork, dtype = f_lapack.l_int_dtype) - - # Now the real calculation - f_lapack.strsen_("N", compq, <l_logical *>select.data, - &N, <float *>T.data, &N, q_ptr, &N, - <float *>wr.data, <float *>wi.data, &M, NULL, NULL, - <float *>work.data, &lwork, - <int *>iwork.data, &liwork, &info) - - if info > 0: - raise LinAlgError("Reordering failed; problem is very ill-conditioned") - - assert info == 0, "Argument error in strsen" - - if wi.nonzero()[0].size: - w = wr + 1j * wi - else: - w = wr - - return filter_args((True, Q is not None, calc_ev), (T, Q, w)) - - -def dtrsen(np.ndarray[l_logical] select, - np.ndarray[np.float64_t, ndim=2] T, - np.ndarray[np.float64_t, ndim=2] Q=None, - calc_ev=True): - cdef l_int N, M, lwork, liwork, qiwork, info - cdef char *compq - cdef double qwork - cdef double *q_ptr - cdef np.ndarray[np.float64_t] wr, wi, work - cdef np.ndarray[l_int] iwork - - assert_fortran_mat(T, Q) - - N = T.shape[0] - wr = np.empty(N, dtype = np.float64) - wi = np.empty(N, dtype = np.float64) - - if Q is not None: - compq = "V" - q_ptr = <double *>Q.data - else: - compq = "N" - q_ptr = NULL - - # workspace query - lwork = liwork = -1 - f_lapack.dtrsen_("N", compq, <l_logical *>select.data, - &N, <double *>T.data, &N, q_ptr, &N, - <double *>wr.data, <double *>wi.data, &M, NULL, NULL, - &qwork, &lwork, &qiwork, &liwork, &info) - - assert info == 0, "Argument error in dtrsen" - - lwork = <int>qwork - work = np.empty(lwork, dtype = np.float64) - liwork = qiwork - iwork = np.empty(liwork, dtype = f_lapack.l_int_dtype) - - # Now the real calculation - f_lapack.dtrsen_("N", compq, <l_logical *>select.data, - &N, <double *>T.data, &N, q_ptr, &N, - <double *>wr.data, <double *>wi.data, &M, NULL, NULL, - <double *>work.data, &lwork, - <int *>iwork.data, &liwork, &info) - - if info > 0: - raise LinAlgError("Reordering failed; problem is very ill-conditioned") - - assert info == 0, "Argument error in dtrsen" - - if wi.nonzero()[0].size: - w = wr + 1j * wi - else: - w = wr - - return filter_args((True, Q is not None, calc_ev), (T, Q, w)) - - -def ctrsen(np.ndarray[l_logical] select, - np.ndarray[np.complex64_t, ndim=2] T, - np.ndarray[np.complex64_t, ndim=2] Q=None, - calc_ev=True): - cdef l_int N, M, lwork, info - cdef char *compq - cdef float complex qwork - cdef float complex *q_ptr - cdef np.ndarray[np.complex64_t] w, work - - assert_fortran_mat(T, Q) - - N = T.shape[0] - w = np.empty(N, dtype = np.complex64) - - if Q is not None: - compq = "V" - q_ptr = <float complex *>Q.data - else: - compq = "N" - q_ptr = NULL - - # workspace query - lwork = -1 - f_lapack.ctrsen_("N", compq, <l_logical *>select.data, - &N, <float complex *>T.data, &N, q_ptr, &N, - <float complex *>w.data, &M, NULL, NULL, - &qwork, &lwork, &info) - - assert info == 0, "Argument error in ctrsen" - - lwork = <int>qwork.real - work = np.empty(lwork, dtype = np.complex64) - - # Now the real calculation - f_lapack.ctrsen_("N", compq, <l_logical *>select.data, - &N, <float complex *>T.data, &N, q_ptr, &N, - <float complex *>w.data, &M, NULL, NULL, - <float complex *>work.data, &lwork, &info) - - if info > 0: - raise LinAlgError("Reordering failed; problem is very ill-conditioned") - - assert info == 0, "Argument error in ctrsen" - - return filter_args((True, Q is not None, calc_ev), (T, Q, w)) - - -def ztrsen(np.ndarray[l_logical] select, - np.ndarray[np.complex128_t, ndim=2] T, - np.ndarray[np.complex128_t, ndim=2] Q=None, - calc_ev=True): - cdef l_int N, MM, M, lwork, info - cdef char *compq - cdef double complex qwork - cdef double complex *q_ptr - cdef np.ndarray[np.complex128_t] w, work - - assert_fortran_mat(T, Q) - - N = T.shape[0] - w = np.empty(N, dtype = np.complex128) - - if Q is not None: - compq = "V" - q_ptr = <double complex *>Q.data - else: - compq = "N" - q_ptr = NULL - - # workspace query - lwork = -1 - f_lapack.ztrsen_("N", compq, <l_logical *>select.data, - &N, <double complex *>T.data, &N, q_ptr, &N, - <double complex *>w.data, &M, NULL, NULL, - &qwork, &lwork, &info) - - assert info == 0, "Argument error in ztrsen" - - lwork = <int>qwork.real - work = np.empty(lwork, dtype = np.complex128) - - # Now the real calculation - f_lapack.ztrsen_("N", compq, <l_logical *>select.data, - &N, <double complex *>T.data, &N, q_ptr, &N, - <double complex *>w.data, &M, NULL, NULL, - <double complex *>work.data, &lwork, &info) - - if info > 0: - raise LinAlgError("Reordering failed; problem is very ill-conditioned") - - assert info == 0, "Argument error in ztrsen" - - return filter_args((True, Q is not None, calc_ev), (T, Q, w)) - - -# Helper function for xTREVC and xTGEVC -def txevc_postprocess(dtype, T, vreal, np.ndarray[l_logical] select): - cdef int N, M, i, m, indx +# Helper function for xTREVC and xTGEVC +def txevc_postprocess(dtype, T, vreal, np.ndarray[l_logical] select): + cdef int N, M, i, m, indx N = T.shape[0] if select is None: - select = np.ones(N, dtype = f_lapack.l_logical_dtype) + select = np.ones(N, dtype = logical_dtype) selindx = select.nonzero()[0] M = selindx.size @@ -1085,1000 +642,48 @@ def txevc_postprocess(dtype, T, vreal, np.ndarray[l_logical] select): elif k > 0 and T[k,k-1]: # we have the situation of a 2x2 block, and # the eigenvalue with the negative imaginary part desired - v[:, m] = vreal[:, indx] - 1j * vreal[:, indx + 1] - - indx += 2 - else: - # real eigenvalue - v[:, m] = vreal[:, indx] - - indx += 1 - return v - - -# Wrappers for xTREVC -def strevc(np.ndarray[np.float32_t, ndim=2] T, - np.ndarray[np.float32_t, ndim=2] Q=None, - np.ndarray[l_logical] select=None, - left=False, right=True): - cdef l_int N, info, M, MM - cdef char *side - cdef char *howmny - cdef np.ndarray[np.float32_t, ndim=2] vl_r, vr_r - cdef float *vl_r_ptr - cdef float *vr_r_ptr - cdef np.ndarray[l_logical] select_cpy - cdef l_logical *select_ptr - cdef np.ndarray[np.float32_t] work - - assert_fortran_mat(T, Q) - - N = T.shape[0] - work = np.empty(4*N, dtype = np.float32) - - if left and right: - side = "B" - elif left: - side = "L" - elif right: - side = "R" - else: - return - - if select is not None: - howmny = "S" - MM = select.nonzero()[0].size - # Correct for possible additional storage if a single complex - # eigenvalue is selected. - # For that: Figure out the positions of the 2x2 blocks. - cmplxindx = np.diagonal(T, -1).nonzero()[0] - for i in cmplxindx: - if bool(select[i]) != bool(select[i+1]): - MM += 1 - - # Select is overwritten in strevc. - select_cpy = np.array(select, dtype = f_lapack.l_logical_dtype, - order = 'F') - select_ptr = <l_logical *>select_cpy.data - else: - MM = N - select_ptr = NULL - if Q is not None: - howmny = "B" - else: - howmny = "A" - - if left: - if Q is not None and select is None: - vl_r = np.asfortranarray(Q.copy()) - else: - vl_r = np.empty((N, MM), dtype = np.float32, order='F') - vl_r_ptr = <float *>vl_r.data - else: - vl_r_ptr = NULL - - if right: - if Q is not None and select is None: - vr_r = np.asfortranarray(Q.copy()) - else: - vr_r = np.empty((N, MM), dtype = np.float32, order='F') - vr_r_ptr = <float *>vr_r.data - else: - vr_r_ptr = NULL - - f_lapack.strevc_(side, howmny, select_ptr, - &N, <float *>T.data, &N, - vl_r_ptr, &N, vr_r_ptr, &N, &MM, &M, - <float *>work.data, &info) - - assert info == 0, "Argument error in strevc" - assert MM == M, "Unexpected number of eigenvectors returned in strevc" - - if select is not None and Q is not None: - if left: - vl_r = np.asfortranarray(np.dot(Q, vl_r)) - if right: - vr_r = np.asfortranarray(np.dot(Q, vr_r)) - - # If there are complex eigenvalues, we need to postprocess the - # eigenvectors. - if np.diagonal(T, -1).nonzero()[0].size: - if left: - vl = txevc_postprocess(np.complex64, T, vl_r, select) - if right: - vr = txevc_postprocess(np.complex64, T, vr_r, select) - else: - if left: - vl = vl_r - if right: - vr = vr_r - - if left and right: - return (vl, vr) - elif left: - return vl - else: - return vr - - -def dtrevc(np.ndarray[np.float64_t, ndim=2] T, - np.ndarray[np.float64_t, ndim=2] Q=None, - np.ndarray[l_logical] select=None, - left=False, right=True): - cdef l_int N, info, M, MM - cdef char *side - cdef char *howmny - cdef np.ndarray[np.float64_t, ndim=2] vl_r, vr_r - cdef double *vl_r_ptr - cdef double *vr_r_ptr - cdef np.ndarray[l_logical] select_cpy - cdef l_logical *select_ptr - cdef np.ndarray[np.float64_t] work - - assert_fortran_mat(T, Q) - - N = T.shape[0] - work = np.empty(4*N, dtype = np.float64) - - if left and right: - side = "B" - elif left: - side = "L" - elif right: - side = "R" - else: - return - - if select is not None: - howmny = "S" - MM = select.nonzero()[0].size - # Correct for possible additional storage if a single complex - # eigenvalue is selected. - # For that: Figure out the positions of the 2x2 blocks. - cmplxindx = np.diagonal(T, -1).nonzero()[0] - for i in cmplxindx: - if bool(select[i]) != bool(select[i+1]): - MM += 1 - - # Select is overwritten in dtrevc. - select_cpy = np.array(select, dtype = f_lapack.l_logical_dtype, - order = 'F') - select_ptr = <l_logical *>select_cpy.data - else: - MM = N - select_ptr = NULL - if Q is not None: - howmny = "B" - else: - howmny = "A" - - if left: - if Q is not None and select is None: - vl_r = np.asfortranarray(Q.copy()) - else: - vl_r = np.empty((N, MM), dtype = np.float64, order='F') - vl_r_ptr = <double *>vl_r.data - else: - vl_r_ptr = NULL - - if right: - if Q is not None and select is None: - vr_r = np.asfortranarray(Q.copy()) - else: - vr_r = np.empty((N, MM), dtype = np.float64, order='F') - vr_r_ptr = <double *>vr_r.data - else: - vr_r_ptr = NULL - - f_lapack.dtrevc_(side, howmny, select_ptr, - &N, <double *>T.data, &N, - vl_r_ptr, &N, vr_r_ptr, &N, &MM, &M, - <double *>work.data, &info) - - assert info == 0, "Argument error in dtrevc" - assert MM == M, "Unexpected number of eigenvectors returned in dtrevc" - - if select is not None and Q is not None: - if left: - vl_r = np.asfortranarray(np.dot(Q, vl_r)) - if right: - vr_r = np.asfortranarray(np.dot(Q, vr_r)) - - # If there are complex eigenvalues, we need to postprocess the eigenvectors - if np.diagonal(T, -1).nonzero()[0].size: - if left: - vl = txevc_postprocess(np.complex128, T, vl_r, select) - if right: - vr = txevc_postprocess(np.complex128, T, vr_r, select) - else: - if left: - vl = vl_r - if right: - vr = vr_r - - if left and right: - return (vl, vr) - elif left: - return vl - else: - return vr - - -def ctrevc(np.ndarray[np.complex64_t, ndim=2] T, - np.ndarray[np.complex64_t, ndim=2] Q=None, - np.ndarray[l_logical] select=None, - left=False, right=True): - cdef l_int N, info, M, MM - cdef char *side - cdef char *howmny - cdef np.ndarray[np.complex64_t, ndim=2] vl, vr - cdef float complex *vl_ptr - cdef float complex *vr_ptr - cdef l_logical *select_ptr - cdef np.ndarray[np.complex64_t] work - cdef np.ndarray[np.float32_t] rwork - - assert_fortran_mat(T, Q) - - N = T.shape[0] - work = np.empty(2*N, dtype = np.complex64) - rwork = np.empty(N, dtype = np.float32) - - if left and right: - side = "B" - elif left: - side = "L" - elif right: - side = "R" - else: - return - - if select is not None: - howmny = "S" - MM = select.nonzero()[0].size - select_ptr = <l_logical *>select.data - else: - MM = N - select_ptr = NULL - if Q is not None: - howmny = "B" - else: - howmny = "A" - - if left: - if Q is not None and select is None: - vl = np.asfortranarray(Q.copy()) - else: - vl = np.empty((N, MM), dtype = np.complex64, order='F') - vl_ptr = <float complex *>vl.data - else: - vl_ptr = NULL - - if right: - if Q is not None and select is None: - vr = np.asfortranarray(Q.copy()) - else: - vr = np.empty((N, MM), dtype = np.complex64, order='F') - vr_ptr = <float complex *>vr.data - else: - vr_ptr = NULL - - f_lapack.ctrevc_(side, howmny, select_ptr, - &N, <float complex *>T.data, &N, - vl_ptr, &N, vr_ptr, &N, &MM, &M, - <float complex *>work.data, <float *>rwork.data, &info) - - assert info == 0, "Argument error in ctrevc" - assert MM == M, "Unexpected number of eigenvectors returned in ctrevc" - - if select is not None and Q is not None: - if left: - vl = np.asfortranarray(np.dot(Q, vl)) - if right: - vr = np.asfortranarray(np.dot(Q, vr)) - - if left and right: - return (vl, vr) - elif left: - return vl - else: - return vr - - -def ztrevc(np.ndarray[np.complex128_t, ndim=2] T, - np.ndarray[np.complex128_t, ndim=2] Q=None, - np.ndarray[l_logical] select=None, - left=False, right=True): - cdef l_int N, info, M, MM - cdef char *side - cdef char *howmny - cdef np.ndarray[np.complex128_t, ndim=2] vl, vr - cdef double complex *vl_ptr - cdef double complex *vr_ptr - cdef l_logical *select_ptr - cdef np.ndarray[np.complex128_t] work - cdef np.ndarray[np.float64_t] rwork - - assert_fortran_mat(T, Q) - - N = T.shape[0] - work = np.empty(2*N, dtype = np.complex128) - rwork = np.empty(N, dtype = np.float64) - - if left and right: - side = "B" - elif left: - side = "L" - elif right: - side = "R" - else: - return - - if select is not None: - howmny = "S" - MM = select.nonzero()[0].size - select_ptr = <l_logical *>select.data - else: - MM = N - select_ptr = NULL - if Q is not None: - howmny = "B" - else: - howmny = "A" - - if left: - if Q is not None and select is None: - vl = np.asfortranarray(Q.copy()) - else: - vl = np.empty((N, MM), dtype = np.complex128, order='F') - vl_ptr = <double complex *>vl.data - else: - vl_ptr = NULL - - if right: - if Q is not None and select is None: - vr = np.asfortranarray(Q.copy()) - else: - vr = np.empty((N, MM), dtype = np.complex128, order='F') - vr_ptr = <double complex *>vr.data - else: - vr_ptr = NULL - - f_lapack.ztrevc_(side, howmny, select_ptr, - &N, <double complex *>T.data, &N, - vl_ptr, &N, vr_ptr, &N, &MM, &M, - <double complex *>work.data, <double *>rwork.data, &info) - - assert info == 0, "Argument error in ztrevc" - assert MM == M, "Unexpected number of eigenvectors returned in ztrevc" - - if select is not None and Q is not None: - if left: - vl = np.asfortranarray(np.dot(Q, vl)) - if right: - vr = np.asfortranarray(np.dot(Q, vr)) - - if left and right: - return (vl, vr) - elif left: - return vl - else: - return vr - - -# wrappers for xGGES -def sgges(np.ndarray[np.float32_t, ndim=2] A, - np.ndarray[np.float32_t, ndim=2] B, - calc_q=True, calc_z=True, calc_ev=True): - cdef l_int N, lwork, sdim, info - cdef char *jobvsl - cdef char *jobvsr - cdef float *vsl_ptr - cdef float *vsr_ptr - cdef float qwork - cdef np.ndarray[np.float32_t, ndim=2] vsl, vsr - cdef np.ndarray[np.float32_t] alphar, alphai, beta, work - - assert_fortran_mat(A, B) - - N = A.shape[0] - alphar = np.empty(N, dtype = np.float32) - alphai = np.empty(N, dtype = np.float32) - beta = np.empty(N, dtype = np.float32) - - if calc_q: - vsl = np.empty((N,N), dtype = np.float32, order='F') - vsl_ptr = <float *>vsl.data - jobvsl = "V" - else: - vsl = None - vsl_ptr = NULL - jobvsl = "N" - - if calc_z: - vsr = np.empty((N,N), dtype = np.float32, order='F') - vsr_ptr = <float *>vsr.data - jobvsr = "V" - else: - vsr = None - vsr_ptr = NULL - jobvsr = "N" - - # workspace query - lwork = -1 - f_lapack.sgges_(jobvsl, jobvsr, "N", NULL, - &N, <float *>A.data, &N, - <float *>B.data, &N, &sdim, - <float *>alphar.data, <float *>alphai.data, - <float *>beta.data, - vsl_ptr, &N, vsr_ptr, &N, - &qwork, &lwork, NULL, &info) - - assert info == 0, "Argument error in zgees" - - lwork = <int>qwork - work = np.empty(lwork, dtype = np.float32) - - # Now the real calculation - f_lapack.sgges_(jobvsl, jobvsr, "N", NULL, - &N, <float *>A.data, &N, - <float *>B.data, &N, &sdim, - <float *>alphar.data, <float *>alphai.data, - <float *>beta.data, - vsl_ptr, &N, vsr_ptr, &N, - <float *>work.data, &lwork, NULL, &info) - - if info > 0: - raise LinAlgError("QZ iteration failed to converge in sgges") - - assert info == 0, "Argument error in zgees" - - if alphai.nonzero()[0].size: - alpha = alphar + 1j * alphai - else: - alpha = alphar - - return filter_args((True, True, calc_q, calc_z, calc_ev, calc_ev), - (A, B, vsl, vsr, alpha, beta)) - - -def dgges(np.ndarray[np.float64_t, ndim=2] A, - np.ndarray[np.float64_t, ndim=2] B, - calc_q=True, calc_z=True, calc_ev=True): - cdef l_int N, lwork, sdim, info - cdef char *jobvsl - cdef char *jobvsr - cdef double *vsl_ptr - cdef double *vsr_ptr - cdef double qwork - cdef np.ndarray[np.float64_t, ndim=2] vsl, vsr - cdef np.ndarray[np.float64_t] alphar, alphai, beta, work - - assert_fortran_mat(A, B) - - N = A.shape[0] - alphar = np.empty(N, dtype = np.float64) - alphai = np.empty(N, dtype = np.float64) - beta = np.empty(N, dtype = np.float64) - - if calc_q: - vsl = np.empty((N,N), dtype = np.float64, order='F') - vsl_ptr = <double *>vsl.data - jobvsl = "V" - else: - vsl = None - vsl_ptr = NULL - jobvsl = "N" - - if calc_z: - vsr = np.empty((N,N), dtype = np.float64, order='F') - vsr_ptr = <double *>vsr.data - jobvsr = "V" - else: - vsr = None - vsr_ptr = NULL - jobvsr = "N" - - # workspace query - lwork = -1 - f_lapack.dgges_(jobvsl, jobvsr, "N", NULL, - &N, <double *>A.data, &N, - <double *>B.data, &N, &sdim, - <double *>alphar.data, <double *>alphai.data, - <double *>beta.data, - vsl_ptr, &N, vsr_ptr, &N, - &qwork, &lwork, NULL, &info) - - assert info == 0, "Argument error in zgees" - - lwork = <int>qwork - work = np.empty(lwork, dtype = np.float64) - - # Now the real calculation - f_lapack.dgges_(jobvsl, jobvsr, "N", NULL, - &N, <double *>A.data, &N, - <double *>B.data, &N, &sdim, - <double *>alphar.data, <double *>alphai.data, - <double *>beta.data, - vsl_ptr, &N, vsr_ptr, &N, - <double *>work.data, &lwork, NULL, &info) - - if info > 0: - raise LinAlgError("QZ iteration failed to converge in dgges") - - assert info == 0, "Argument error in zgees" - - if alphai.nonzero()[0].size: - alpha = alphar + 1j * alphai - else: - alpha = alphar - - return filter_args((True, True, calc_q, calc_z, calc_ev, calc_ev), - (A, B, vsl, vsr, alpha, beta)) - - -def cgges(np.ndarray[np.complex64_t, ndim=2] A, - np.ndarray[np.complex64_t, ndim=2] B, - calc_q=True, calc_z=True, calc_ev=True): - cdef l_int N, lwork, sdim, info - cdef char *jobvsl - cdef char *jobvsr - cdef float complex *vsl_ptr - cdef float complex *vsr_ptr - cdef float complex qwork - cdef np.ndarray[np.complex64_t, ndim=2] vsl, vsr - cdef np.ndarray[np.complex64_t] alpha, beta, work - cdef np.ndarray[np.float32_t] rwork - - assert_fortran_mat(A, B) - - N = A.shape[0] - alpha = np.empty(N, dtype = np.complex64) - beta = np.empty(N, dtype = np.complex64) - rwork = np.empty(8*N, dtype = np.float32) - - if calc_q: - vsl = np.empty((N,N), dtype = np.complex64, order='F') - vsl_ptr = <float complex *>vsl.data - jobvsl = "V" - else: - vsl = None - vsl_ptr = NULL - jobvsl = "N" - - if calc_z: - vsr = np.empty((N,N), dtype = np.complex64, order='F') - vsr_ptr = <float complex *>vsr.data - jobvsr = "V" - else: - vsr = None - vsr_ptr = NULL - jobvsr = "N" - - # workspace query - lwork = -1 - f_lapack.cgges_(jobvsl, jobvsr, "N", NULL, - &N, <float complex *>A.data, &N, - <float complex *>B.data, &N, &sdim, - <float complex *>alpha.data, <float complex *>beta.data, - vsl_ptr, &N, vsr_ptr, &N, - &qwork, &lwork, <float *>rwork.data, NULL, &info) - - assert info == 0, "Argument error in zgees" - - lwork = <int>qwork.real - work = np.empty(lwork, dtype = np.complex64) - - # Now the real calculation - f_lapack.cgges_(jobvsl, jobvsr, "N", NULL, - &N, <float complex *>A.data, &N, - <float complex *>B.data, &N, &sdim, - <float complex *>alpha.data, <float complex *>beta.data, - vsl_ptr, &N, vsr_ptr, &N, - <float complex *>work.data, &lwork, - <float *>rwork.data, NULL, &info) - - if info > 0: - raise LinAlgError("QZ iteration failed to converge in cgges") - - assert info == 0, "Argument error in zgees" - - return filter_args((True, True, calc_q, calc_z, calc_ev, calc_ev), - (A, B, vsl, vsr, alpha, beta)) - - -def zgges(np.ndarray[np.complex128_t, ndim=2] A, - np.ndarray[np.complex128_t, ndim=2] B, - calc_q=True, calc_z=True, calc_ev=True): - cdef l_int N, lwork, sdim, info - cdef char *jobvsl - cdef char *jobvsr - cdef double complex *vsl_ptr - cdef double complex *vsr_ptr - cdef double complex qwork - cdef np.ndarray[np.complex128_t, ndim=2] vsl, vsr - cdef np.ndarray[np.complex128_t] alpha, beta, work - cdef np.ndarray[np.float64_t] rwork - - assert_fortran_mat(A, B) - - N = A.shape[0] - alpha = np.empty(N, dtype = np.complex128) - beta = np.empty(N, dtype = np.complex128) - rwork = np.empty(8*N, dtype = np.float64) - - if calc_q: - vsl = np.empty((N,N), dtype = np.complex128, order='F') - vsl_ptr = <double complex *>vsl.data - jobvsl = "V" - else: - vsl = None - vsl_ptr = NULL - jobvsl = "N" - - if calc_z: - vsr = np.empty((N,N), dtype = np.complex128, order='F') - vsr_ptr = <double complex *>vsr.data - jobvsr = "V" - else: - vsr = None - vsr_ptr = NULL - jobvsr = "N" - - # workspace query - lwork = -1 - f_lapack.zgges_(jobvsl, jobvsr, "N", NULL, - &N, <double complex *>A.data, &N, - <double complex *>B.data, &N, &sdim, - <double complex *>alpha.data, <double complex *>beta.data, - vsl_ptr, &N, vsr_ptr, &N, - &qwork, &lwork, <double *>rwork.data, NULL, &info) - - assert info == 0, "Argument error in zgees" - - lwork = <int>qwork.real - work = np.empty(lwork, dtype = np.complex128) - - # Now the real calculation - f_lapack.zgges_(jobvsl, jobvsr, "N", NULL, - &N, <double complex *>A.data, &N, - <double complex *>B.data, &N, &sdim, - <double complex *>alpha.data, <double complex *>beta.data, - vsl_ptr, &N, vsr_ptr, &N, - <double complex *>work.data, &lwork, - <double *>rwork.data, NULL, &info) - - if info > 0: - raise LinAlgError("QZ iteration failed to converge in zgges") - - assert info == 0, "Argument error in zgees" - - return filter_args((True, True, calc_q, calc_z, calc_ev, calc_ev), - (A, B, vsl, vsr, alpha, beta)) - - -# wrappers for xTGSEN -def stgsen(np.ndarray[l_logical] select, - np.ndarray[np.float32_t, ndim=2] S, - np.ndarray[np.float32_t, ndim=2] T, - np.ndarray[np.float32_t, ndim=2] Q=None, - np.ndarray[np.float32_t, ndim=2] Z=None, - calc_ev=True): - cdef l_int N, M, lwork, liwork, qiwork, info, ijob - cdef l_logical wantq, wantz - cdef float qwork - cdef float *q_ptr - cdef float *z_ptr - cdef np.ndarray[np.float32_t] alphar, alphai, beta, work - cdef np.ndarray[l_int] iwork - - assert_fortran_mat(S, T, Q, Z) - - N = S.shape[0] - alphar = np.empty(N, dtype = np.float32) - alphai = np.empty(N, dtype = np.float32) - beta = np.empty(N, dtype = np.float32) - ijob = 0 - - if Q is not None: - wantq = 1 - q_ptr = <float *>Q.data - else: - wantq = 0 - q_ptr = NULL - - if Z is not None: - wantz = 1 - z_ptr = <float *>Z.data - else: - wantz = 0 - z_ptr = NULL - - # workspace query - lwork = -1 - liwork = -1 - f_lapack.stgsen_(&ijob, &wantq, &wantz, <l_logical *>select.data, - &N, <float *>S.data, &N, - <float *>T.data, &N, - <float *>alphar.data, <float *>alphai.data, - <float *>beta.data, - q_ptr, &N, z_ptr, &N, &M, NULL, NULL, NULL, - &qwork, &lwork, &qiwork, &liwork, &info) - - assert info == 0, "Argument error in stgsen" - - lwork = <int>qwork - work = np.empty(lwork, dtype = np.float32) - liwork = qiwork - iwork = np.empty(liwork, dtype = int_dtype) - - # Now the real calculation - f_lapack.stgsen_(&ijob, &wantq, &wantz, <l_logical *>select.data, - &N, <float *>S.data, &N, - <float *>T.data, &N, - <float *>alphar.data, <float *>alphai.data, - <float *>beta.data, - q_ptr, &N, z_ptr, &N, &M, NULL, NULL, NULL, - <float *>work.data, &lwork, - <l_int *>iwork.data, &liwork, &info) - - if info > 0: - raise LinAlgError("Reordering failed; problem is very ill-conditioned") - - assert info == 0, "Argument error in stgsen" - - if alphai.nonzero()[0].size: - alpha = alphar + 1j * alphai - else: - alpha = alphar - - return filter_args((True, True, Q is not None, Z is not None, - calc_ev, calc_ev), - (S, T, Q, Z, alpha, beta)) - - -def dtgsen(np.ndarray[l_logical] select, - np.ndarray[np.float64_t, ndim=2] S, - np.ndarray[np.float64_t, ndim=2] T, - np.ndarray[np.float64_t, ndim=2] Q=None, - np.ndarray[np.float64_t, ndim=2] Z=None, - calc_ev=True): - cdef l_int N, M, lwork, liwork, qiwork, info, ijob - cdef l_logical wantq, wantz - cdef double qwork - cdef double *q_ptr - cdef double *z_ptr - cdef np.ndarray[np.float64_t] alphar, alphai, beta, work - cdef np.ndarray[l_int] iwork - - assert_fortran_mat(S, T, Q, Z) - - N = S.shape[0] - alphar = np.empty(N, dtype = np.float64) - alphai = np.empty(N, dtype = np.float64) - beta = np.empty(N, dtype = np.float64) - ijob = 0 - - if Q is not None: - wantq = 1 - q_ptr = <double *>Q.data - else: - wantq = 0 - q_ptr = NULL - - if Z is not None: - wantz = 1 - z_ptr = <double *>Z.data - else: - wantz = 0 - z_ptr = NULL - - # workspace query - lwork = -1 - liwork = -1 - f_lapack.dtgsen_(&ijob, &wantq, &wantz, <l_logical *>select.data, - &N, <double *>S.data, &N, - <double *>T.data, &N, - <double *>alphar.data, <double *>alphai.data, - <double *>beta.data, - q_ptr, &N, z_ptr, &N, &M, NULL, NULL, NULL, - &qwork, &lwork, &qiwork, &liwork, &info) - - assert info == 0, "Argument error in dtgsen" - - lwork = <int>qwork - work = np.empty(lwork, dtype = np.float64) - liwork = qiwork - iwork = np.empty(liwork, dtype = int_dtype) - - # Now the real calculation - f_lapack.dtgsen_(&ijob, &wantq, &wantz, <l_logical *>select.data, - &N, <double *>S.data, &N, - <double *>T.data, &N, - <double *>alphar.data, <double *>alphai.data, - <double *>beta.data, - q_ptr, &N, z_ptr, &N, &M, NULL, NULL, NULL, - <double *>work.data, &lwork, - <l_int *>iwork.data, &liwork, &info) - - if info > 0: - raise LinAlgError("Reordering failed; problem is very ill-conditioned") - - assert info == 0, "Argument error in dtgsen" - - if alphai.nonzero()[0].size: - alpha = alphar + 1j * alphai - else: - alpha = alphar - - return filter_args((True, True, Q is not None, Z is not None, - calc_ev, calc_ev), - (S, T, Q, Z, alpha, beta)) - - -def ctgsen(np.ndarray[l_logical] select, - np.ndarray[np.complex64_t, ndim=2] S, - np.ndarray[np.complex64_t, ndim=2] T, - np.ndarray[np.complex64_t, ndim=2] Q=None, - np.ndarray[np.complex64_t, ndim=2] Z=None, - calc_ev=True): - cdef l_int N, M, lwork, liwork, qiwork, info, ijob - cdef l_logical wantq, wantz - cdef float complex qwork - cdef float complex *q_ptr - cdef float complex *z_ptr - cdef np.ndarray[np.complex64_t] alpha, beta, work - cdef np.ndarray[l_int] iwork - - assert_fortran_mat(S, T, Q, Z) - - N = S.shape[0] - alpha = np.empty(N, dtype = np.complex64) - beta = np.empty(N, dtype = np.complex64) - ijob = 0 - - if Q is not None: - wantq = 1 - q_ptr = <float complex *>Q.data - else: - wantq = 0 - q_ptr = NULL - - if Z is not None: - wantz = 1 - z_ptr = <float complex *>Z.data - else: - wantz = 0 - z_ptr = NULL - - # workspace query - lwork = -1 - liwork = -1 - f_lapack.ctgsen_(&ijob, &wantq, &wantz, <l_logical *>select.data, - &N, <float complex *>S.data, &N, - <float complex *>T.data, &N, - <float complex *>alpha.data, <float complex *>beta.data, - q_ptr, &N, z_ptr, &N, &M, NULL, NULL, NULL, - &qwork, &lwork, &qiwork, &liwork, &info) - - assert info == 0, "Argument error in ctgsen" - - lwork = <int>qwork.real - work = np.empty(lwork, dtype = np.complex64) - liwork = qiwork - iwork = np.empty(liwork, dtype = int_dtype) - - # Now the real calculation - f_lapack.ctgsen_(&ijob, &wantq, &wantz, <l_logical *>select.data, - &N, <float complex *>S.data, &N, - <float complex *>T.data, &N, - <float complex *>alpha.data, <float complex *>beta.data, - q_ptr, &N, z_ptr, &N, &M, NULL, NULL, NULL, - <float complex *>work.data, &lwork, - <l_int *>iwork.data, &liwork, &info) - - if info > 0: - raise LinAlgError("Reordering failed; problem is very ill-conditioned") - - assert info == 0, "Argument error in ctgsen" - - return filter_args((True, True, Q is not None, Z is not None, - calc_ev, calc_ev), - (S, T, Q, Z, alpha, beta)) - - -def ztgsen(np.ndarray[l_logical] select, - np.ndarray[np.complex128_t, ndim=2] S, - np.ndarray[np.complex128_t, ndim=2] T, - np.ndarray[np.complex128_t, ndim=2] Q=None, - np.ndarray[np.complex128_t, ndim=2] Z=None, - calc_ev=True): - cdef l_int N, M, lwork, liwork, qiwork, info, ijob - cdef l_logical wantq, wantz - cdef double complex qwork - cdef double complex *q_ptr - cdef double complex *z_ptr - cdef np.ndarray[np.complex128_t] alpha, beta, work - cdef np.ndarray[l_int] iwork - - assert_fortran_mat(S, T, Q, Z) - - N = S.shape[0] - alpha = np.empty(N, dtype = np.complex128) - beta = np.empty(N, dtype = np.complex128) - ijob = 0 - - if Q is not None: - wantq = 1 - q_ptr = <double complex *>Q.data - else: - wantq = 0 - q_ptr = NULL - - if Z is not None: - wantz = 1 - z_ptr = <double complex *>Z.data - else: - wantz = 0 - z_ptr = NULL - - # workspace query - lwork = -1 - liwork = -1 - f_lapack.ztgsen_(&ijob, &wantq, &wantz, <l_logical *>select.data, - &N, <double complex *>S.data, &N, - <double complex *>T.data, &N, - <double complex *>alpha.data, <double complex *>beta.data, - q_ptr, &N, z_ptr, &N, &M, NULL, NULL, NULL, - &qwork, &lwork, &qiwork, &liwork, &info) - - assert info == 0, "Argument error in ztgsen" - - lwork = <int>qwork.real - work = np.empty(lwork, dtype = np.complex128) - liwork = qiwork - iwork = np.empty(liwork, dtype = int_dtype) - - # Now the real calculation - f_lapack.ztgsen_(&ijob, &wantq, &wantz, <l_logical *>select.data, - &N, <double complex *>S.data, &N, - <double complex *>T.data, &N, - <double complex *>alpha.data, <double complex *>beta.data, - q_ptr, &N, z_ptr, &N, &M, NULL, NULL, NULL, - <double complex *>work.data, &lwork, - <l_int *>iwork.data, &liwork, &info) - - if info > 0: - raise LinAlgError("Reordering failed; problem is very ill-conditioned") + v[:, m] = vreal[:, indx] - 1j * vreal[:, indx + 1] - assert info == 0, "Argument error in ztgsen" + indx += 2 + else: + # real eigenvalue + v[:, m] = vreal[:, indx] - return filter_args((True, True, Q is not None, Z is not None, - calc_ev, calc_ev), - (S, T, Q, Z, alpha, beta)) + indx += 1 + return v -# xTGEVC -def stgevc(np.ndarray[np.float32_t, ndim=2] S, - np.ndarray[np.float32_t, ndim=2] T, - np.ndarray[np.float32_t, ndim=2] Q=None, - np.ndarray[np.float32_t, ndim=2] Z=None, - np.ndarray[l_logical] select=None, - left=False, right=True): +def trevc(np.ndarray[scalar, ndim=2] T, + np.ndarray[scalar, ndim=2] Q, + np.ndarray[l_logical] select, + left=False, right=True): cdef l_int N, info, M, MM cdef char *side cdef char *howmny - cdef np.ndarray[np.float32_t, ndim=2] vl_r, vr_r - cdef float *vl_r_ptr - cdef float *vr_r_ptr - cdef np.ndarray[l_logical] select_cpy - cdef l_logical *select_ptr - cdef np.ndarray[np.float32_t] work - assert_fortran_mat(S, T, Q, Z) + # Parameter checks - N = S.shape[0] - work = np.empty(6*N, dtype = np.float32) + if (T.shape[0] != T.shape[1] or Q.shape[0] != Q.shape[1] + or T.shape[0] != Q.shape[0]): + raise ValueError("Invalid Schur decomposition as input") + + assert_fortran_mat(T, Q) + + # Workspace allocation + + N = T.shape[0] + + cdef np.ndarray[scalar] work + if scalar in floating: + work = np.empty(4 * N, dtype=T.dtype) + else: + work = np.empty(2 * N, dtype=T.dtype) + + cdef np.ndarray rwork = None + if scalar is float_complex: + rwork = np.empty(N, dtype=np.float32) + elif scalar is double_complex: + rwork = np.empty(N, dtype=np.float64) if left and right: side = "B" @@ -2089,78 +694,102 @@ def stgevc(np.ndarray[np.float32_t, ndim=2] S, else: return - backtr = False - + cdef np.ndarray[l_logical] select_cpy + cdef l_logical *select_ptr if select is not None: howmny = "S" MM = select.nonzero()[0].size # Correct for possible additional storage if a single complex # eigenvalue is selected. # For that: Figure out the positions of the 2x2 blocks. - cmplxindx = np.diagonal(S, -1).nonzero()[0] + cmplxindx = np.diagonal(T, -1).nonzero()[0] for i in cmplxindx: if bool(select[i]) != bool(select[i+1]): MM += 1 - # select is overwritten in stgevc - select_cpy = np.array(select, dtype = f_lapack.l_logical_dtype, + # Select is overwritten in strevc. + select_cpy = np.array(select, dtype = logical_dtype, order = 'F') select_ptr = <l_logical *>select_cpy.data else: MM = N select_ptr = NULL - if ((left and right and Q is not None and Z is not None) or - (left and not right and Q is not None) or - (right and not left and Z is not None)): + if Q is not None: howmny = "B" - backtr = True else: howmny = "A" + cdef np.ndarray[scalar, ndim=2] vl_r = None + cdef scalar *vl_r_ptr if left: - if backtr: - vl_r = Q + if Q is not None and select is None: + vl_r = np.asfortranarray(Q.copy()) else: - vl_r = np.empty((N, MM), dtype = np.float32, order='F') - vl_r_ptr = <float *>vl_r.data + vl_r = np.empty((N, MM), dtype=T.dtype, order='F') + vl_r_ptr = <scalar *>vl_r.data else: vl_r_ptr = NULL + cdef np.ndarray[scalar, ndim=2] vr_r = None + cdef scalar *vr_r_ptr if right: - if backtr: - vr_r = Z + if Q is not None and select is None: + vr_r = np.asfortranarray(Q.copy()) else: - vr_r = np.empty((N, MM), dtype = np.float32, order='F') - vr_r_ptr = <float *>vr_r.data + vr_r = np.empty((N, MM), dtype=T.dtype, order='F') + vr_r_ptr = <scalar *>vr_r.data else: vr_r_ptr = NULL - f_lapack.stgevc_(side, howmny, select_ptr, - &N, <float *>S.data, &N, - <float *>T.data, &N, - vl_r_ptr, &N, vr_r_ptr, &N, &MM, &M, - <float *>work.data, &info) - - assert info == 0, "Argument error in stgevc" - assert MM == M, "Unexpected number of eigenvectors returned in stgevc" + # The actual calculation + + if scalar is float: + lapack.strevc(side, howmny, select_ptr, + &N, <float *>T.data, &N, + vl_r_ptr, &N, vr_r_ptr, &N, &MM, &M, + <float *>work.data, &info) + elif scalar is double: + lapack.dtrevc(side, howmny, select_ptr, + &N, <double *>T.data, &N, + vl_r_ptr, &N, vr_r_ptr, &N, &MM, &M, + <double *>work.data, &info) + elif scalar is float_complex: + lapack.ctrevc(side, howmny, select_ptr, + &N, <float complex *>T.data, &N, + vl_r_ptr, &N, vr_r_ptr, &N, &MM, &M, + <float complex *>work.data, <float *>rwork.data, &info) + elif scalar is double_complex: + lapack.ztrevc(side, howmny, select_ptr, + &N, <double complex *>T.data, &N, + vl_r_ptr, &N, vr_r_ptr, &N, &MM, &M, + <double complex *>work.data, <double *>rwork.data, &info) + + assert info == 0, "Argument error in trevc" + assert MM == M, "Unexpected number of eigenvectors returned in strevc" - if not backtr: + if select is not None and Q is not None: if left: vl_r = np.asfortranarray(np.dot(Q, vl_r)) if right: - vr_r = np.asfortranarray(np.dot(Z, vr_r)) + vr_r = np.asfortranarray(np.dot(Q, vr_r)) - # If there are complex eigenvalues, we need to postprocess the eigenvectors - if np.diagonal(S, -1).nonzero()[0].size: - if left: - vl = txevc_postprocess(np.complex64, S, vl_r, select) - if right: - vr = txevc_postprocess(np.complex64, S, vr_r, select) - else: - if left: - vl = vl_r - if right: - vr = vr_r + cdef np.ndarray vl, vr + if left: + vl = vl_r + if right: + vr = vr_r + if scalar in floating: + # If there are complex eigenvalues, we need to postprocess the + # eigenvectors. + if scalar is float: + dtype = np.complex64 + else: + dtype = np.complex128 + if np.diagonal(T, -1).nonzero()[0].size: + if left: + vl = txevc_postprocess(dtype, T, vl_r, select) + if right: + vr = txevc_postprocess(dtype, T, vr_r, select) if left and right: return (vl, vr) @@ -2170,229 +799,340 @@ def stgevc(np.ndarray[np.float32_t, ndim=2] S, return vr -def dtgevc(np.ndarray[np.float64_t, ndim=2] S, - np.ndarray[np.float64_t, ndim=2] T, - np.ndarray[np.float64_t, ndim=2] Q=None, - np.ndarray[np.float64_t, ndim=2] Z=None, - np.ndarray[l_logical] select=None, - left=False, right=True): - cdef l_int N, info, M, MM - cdef char *side - cdef char *howmny - cdef np.ndarray[np.float64_t, ndim=2] vl_r, vr_r - cdef double *vl_r_ptr - cdef double *vr_r_ptr - cdef np.ndarray[l_logical] select_cpy - cdef l_logical *select_ptr - cdef np.ndarray[np.float64_t] work +def gges(np.ndarray[scalar, ndim=2] A, + np.ndarray[scalar, ndim=2] B, + calc_q=True, calc_z=True, calc_ev=True): + cdef l_int N, sdim, info - assert_fortran_mat(S, T, Q, Z) + # Check parameters - N = S.shape[0] - work = np.empty(6*N, dtype = np.float64) + assert_fortran_mat(A, B) - if left and right: - side = "B" - elif left: - side = "L" - elif right: - side = "R" - else: - return + if A.shape[0] != B.shape[1]: + raise ValueError("Expect square matrix A") - backtr = False + if A.shape[0] != B.shape[0] or A.shape[0] != B.shape[1]: + raise ValueError("Shape of B is incompatible with matrix A") - if select is not None: - howmny = "S" - MM = select.nonzero()[0].size - # Correct for possible additional storage if a single complex - # eigenvalue is selected. - # For that: Figure out the positions of the 2x2 blocks. - cmplxindx = np.diagonal(S, -1).nonzero()[0] - for i in cmplxindx: - if bool(select[i]) != bool(select[i+1]): - MM += 1 + # Allocate workspaces - # select is overwritten in dtgevc - select_cpy = np.array(select, dtype = f_lapack.l_logical_dtype, - order = 'F') - select_ptr = <l_logical *>select_cpy.data + N = A.shape[0] + + cdef np.ndarray[scalar] alphar, alphai + if scalar in cmplx: + alphar = np.empty(N, dtype=A.dtype) + alphai = None else: - MM = N - select_ptr = NULL - if ((left and right and Q is not None and Z is not None) or - (left and not right and Q is not None) or - (right and not left and Z is not None)): - howmny = "B" - backtr = True - else: - howmny = "A" + alphar = np.empty(N, dtype=A.dtype) + alphai = np.empty(N, dtype=A.dtype) - if left: - if backtr: - vl_r = Q - else: - vl_r = np.empty((N, MM), dtype = np.float64, order='F') - vl_r_ptr = <double *>vl_r.data + cdef np.ndarray[scalar] beta = np.empty(N, dtype=A.dtype) + + cdef np.ndarray rwork = None + if scalar is float_complex: + rwork = np.empty(8 * N, dtype=np.float32) + elif scalar is double_complex: + rwork = np.empty(8 * N, dtype=np.float64) + + cdef char *jobvsl + cdef scalar *vsl_ptr + cdef np.ndarray[scalar, ndim=2] vsl + if calc_q: + vsl = np.empty((N,N), dtype=A.dtype, order='F') + vsl_ptr = <scalar *>vsl.data + jobvsl = "V" else: - vl_r_ptr = NULL + vsl = None + vsl_ptr = NULL + jobvsl = "N" - if right: - if backtr: - vr_r = Z - else: - vr_r = np.empty((N, MM), dtype = np.float64, order='F') - vr_r_ptr = <double *>vr_r.data + cdef char *jobvsr + cdef scalar *vsr_ptr + cdef np.ndarray[scalar, ndim=2] vsr + if calc_z: + vsr = np.empty((N,N), dtype=A.dtype, order='F') + vsr_ptr = <scalar *>vsr.data + jobvsr = "V" else: - vr_r_ptr = NULL + vsr = None + vsr_ptr = NULL + jobvsr = "N" + + # Workspace query + # Xgges expects &qwork as a <scalar *> (even though it's an integer) + cdef l_int lwork = -1 + cdef scalar qwork + + if scalar is float: + lapack.sgges(jobvsl, jobvsr, "N", NULL, + &N, <float *>A.data, &N, + <float *>B.data, &N, &sdim, + <float *>alphar.data, <float *>alphai.data, + <float *>beta.data, + vsl_ptr, &N, vsr_ptr, &N, + &qwork, &lwork, NULL, &info) + elif scalar is double: + lapack.dgges(jobvsl, jobvsr, "N", NULL, + &N, <double *>A.data, &N, + <double *>B.data, &N, &sdim, + <double *>alphar.data, <double *>alphai.data, + <double *>beta.data, + vsl_ptr, &N, vsr_ptr, &N, + &qwork, &lwork, NULL, &info) + elif scalar is float_complex: + lapack.cgges(jobvsl, jobvsr, "N", NULL, + &N, <float complex *>A.data, &N, + <float complex *>B.data, &N, &sdim, + <float complex *>alphar.data, <float complex *>beta.data, + vsl_ptr, &N, vsr_ptr, &N, + &qwork, &lwork, <float *>rwork.data, NULL, &info) + elif scalar is double_complex: + lapack.zgges(jobvsl, jobvsr, "N", NULL, + &N, <double complex *>A.data, &N, + <double complex *>B.data, &N, &sdim, + <double complex *>alphar.data, <double complex *>beta.data, + vsl_ptr, &N, vsr_ptr, &N, + &qwork, &lwork, <double *>rwork.data, NULL, &info) + + assert info == 0, "Argument error in gges" + + lwork = lwork_from_qwork(qwork) + cdef np.ndarray[scalar] work = np.empty(lwork, dtype=A.dtype) + + # The actual calculation + + if scalar is float: + lapack.sgges(jobvsl, jobvsr, "N", NULL, + &N, <float *>A.data, &N, + <float *>B.data, &N, &sdim, + <float *>alphar.data, <float *>alphai.data, + <float *>beta.data, + vsl_ptr, &N, vsr_ptr, &N, + <float *>work.data, &lwork, NULL, &info) + elif scalar is double: + lapack.dgges(jobvsl, jobvsr, "N", NULL, + &N, <double *>A.data, &N, + <double *>B.data, &N, &sdim, + <double *>alphar.data, <double *>alphai.data, + <double *>beta.data, + vsl_ptr, &N, vsr_ptr, &N, + <double *>work.data, &lwork, NULL, &info) + elif scalar is float_complex: + lapack.cgges(jobvsl, jobvsr, "N", NULL, + &N, <float complex *>A.data, &N, + <float complex *>B.data, &N, &sdim, + <float complex *>alphar.data, <float complex *>beta.data, + vsl_ptr, &N, vsr_ptr, &N, + <float complex *>work.data, &lwork, + <float *>rwork.data, NULL, &info) + elif scalar is double_complex: + lapack.zgges(jobvsl, jobvsr, "N", NULL, + &N, <double complex *>A.data, &N, + <double complex *>B.data, &N, &sdim, + <double complex *>alphar.data, <double complex *>beta.data, + vsl_ptr, &N, vsr_ptr, &N, + <double complex *>work.data, &lwork, + <double *>rwork.data, NULL, &info) - f_lapack.dtgevc_(side, howmny, select_ptr, - &N, <double *>S.data, &N, - <double *>T.data, &N, - vl_r_ptr, &N, vr_r_ptr, &N, &MM, &M, - <double *>work.data, &info) + if info > 0: + raise LinAlgError("QZ iteration failed to converge in gges") - assert info == 0, "Argument error in dtgevc" - assert MM == M, "Unexpected number of eigenvectors returned in dtgevc" + assert info == 0, "Argument error in gges" - if not backtr: - if left: - vl_r = np.asfortranarray(np.dot(Q, vl_r)) - if right: - vr_r = np.asfortranarray(np.dot(Z, vr_r)) + # Real inputs possibly produce complex output + cdef np.ndarray alpha = maybe_complex[scalar](0, alphar, alphai) - # If there are complex eigenvalues, we need to postprocess the - # eigenvectors. - if np.diagonal(S, -1).nonzero()[0].size: - if left: - vl = txevc_postprocess(np.complex128, S, vl_r, select) - if right: - vr = txevc_postprocess(np.complex128, S, vr_r, select) - else: - if left: - vl = vl_r - if right: - vr = vr_r + return filter_args((True, True, calc_q, calc_z, calc_ev, calc_ev), + (A, B, vsl, vsr, alpha, beta)) - if left and right: - return (vl, vr) - elif left: - return vl - else: - return vr +def tgsen(np.ndarray[l_logical] select, + np.ndarray[scalar, ndim=2] S, + np.ndarray[scalar, ndim=2] T, + np.ndarray[scalar, ndim=2] Q, + np.ndarray[scalar, ndim=2] Z, + calc_ev=True): + cdef l_int ijob = 0 + cdef l_int N, M, lwork, liwork, info -def ctgevc(np.ndarray[np.complex64_t, ndim=2] S, - np.ndarray[np.complex64_t, ndim=2] T, - np.ndarray[np.complex64_t, ndim=2] Q=None, - np.ndarray[np.complex64_t, ndim=2] Z=None, - np.ndarray[l_logical] select=None, - left=False, right=True): - cdef l_int N, info, M, MM - cdef char *side - cdef char *howmny - cdef np.ndarray[np.complex64_t, ndim=2] vl, vr - cdef float complex *vl_ptr - cdef float complex *vr_ptr - cdef l_logical *select_ptr - cdef np.ndarray[np.complex64_t] work - cdef np.ndarray[np.float32_t] rwork + # Check parameters + + if ((S.shape[0] != S.shape[1] or T.shape[0] != T.shape[1] or + S.shape[0] != T.shape[0]) or + (Q is not None and (Q.shape[0] != Q.shape[1] or + S.shape[0] != Q.shape[0])) or + (Z is not None and (Z.shape[0] != Z.shape[1] or + S.shape[0] != Z.shape[0]))): + raise ValueError("Invalid Schur decomposition as input") assert_fortran_mat(S, T, Q, Z) + # Allocate workspaces + N = S.shape[0] - work = np.empty(2*N, dtype = np.complex64) - rwork = np.empty(2*N, dtype = np.float32) - if left and right: - side = "B" - elif left: - side = "L" - elif right: - side = "R" + cdef np.ndarray[scalar] alphar, alphai + if scalar in cmplx: + alphar = np.empty(N, dtype=S.dtype) + alphai = None else: - return + alphar = np.empty(N, dtype=S.dtype) + alphai = np.empty(N, dtype=S.dtype) - backtr = False + cdef np.ndarray[scalar] beta + beta = np.empty(N, dtype=S.dtype) - if select is not None: - howmny = "S" - MM = select.nonzero()[0].size - select_ptr = <l_logical *>select.data + cdef l_logical wantq + cdef scalar *q_ptr + if Q is not None: + wantq = 1 + q_ptr = <scalar *>Q.data else: - MM = N - select_ptr = NULL - if ((left and right and Q is not None and Z is not None) or - (left and not right and Q is not None) or - (right and not left and Z is not None)): - howmny = "B" - backtr = True - else: - howmny = "A" + wantq = 0 + q_ptr = NULL - if left: - if backtr: - vl = Q - else: - vl = np.empty((N, MM), dtype = np.complex64, order='F') - vl_ptr = <float complex *>vl.data + cdef l_logical wantz + cdef scalar *z_ptr + if Z is not None: + wantz = 1 + z_ptr = <scalar *>Z.data else: - vl_ptr = NULL + wantz = 0 + z_ptr = NULL - if right: - if backtr: - vr = Z - else: - vr = np.empty((N, MM), dtype = np.complex64, order='F') - vr_ptr = <float complex *>vr.data - else: - vr_ptr = NULL + # Workspace query + # Xtgsen expects &qwork as a <scalar *> (even though it's an integer) + lwork = -1 + liwork = -1 + cdef scalar qwork + cdef l_int qiwork + + if scalar is float: + lapack.stgsen(&ijob, &wantq, &wantz, <l_logical *>select.data, + &N, <float *>S.data, &N, + <float *>T.data, &N, + <float *>alphar.data, <float *>alphai.data, + <float *>beta.data, + q_ptr, &N, z_ptr, &N, &M, NULL, NULL, NULL, + &qwork, &lwork, &qiwork, &liwork, &info) + elif scalar is double: + lapack.dtgsen(&ijob, &wantq, &wantz, <l_logical *>select.data, + &N, <double *>S.data, &N, + <double *>T.data, &N, + <double *>alphar.data, <double *>alphai.data, + <double *>beta.data, + q_ptr, &N, z_ptr, &N, &M, NULL, NULL, NULL, + &qwork, &lwork, &qiwork, &liwork, &info) + elif scalar is float_complex: + lapack.ctgsen(&ijob, &wantq, &wantz, <l_logical *>select.data, + &N, <float complex *>S.data, &N, + <float complex *>T.data, &N, + <float complex *>alphar.data, <float complex *>beta.data, + q_ptr, &N, z_ptr, &N, &M, NULL, NULL, NULL, + &qwork, &lwork, &qiwork, &liwork, &info) + elif scalar is double_complex: + lapack.ztgsen(&ijob, &wantq, &wantz, <l_logical *>select.data, + &N, <double complex *>S.data, &N, + <double complex *>T.data, &N, + <double complex *>alphar.data, <double complex *>beta.data, + q_ptr, &N, z_ptr, &N, &M, NULL, NULL, NULL, + &qwork, &lwork, &qiwork, &liwork, &info) + + assert info == 0, "Argument error in tgsen" + + lwork = lwork_from_qwork(qwork) + cdef np.ndarray[scalar] work = np.empty(lwork, dtype=S.dtype) - f_lapack.ctgevc_(side, howmny, select_ptr, - &N, <float complex *>S.data, &N, - <float complex *>T.data, &N, - vl_ptr, &N, vr_ptr, &N, &MM, &M, - <float complex *>work.data, <float *>rwork.data, &info) + liwork = qiwork + cdef np.ndarray[l_int] iwork = np.empty(liwork, dtype=int_dtype) + + # The actual calculation + + if scalar is float: + lapack.stgsen(&ijob, &wantq, &wantz, <l_logical *>select.data, + &N, <float *>S.data, &N, + <float *>T.data, &N, + <float *>alphar.data, <float *>alphai.data, + <float *>beta.data, + q_ptr, &N, z_ptr, &N, &M, NULL, NULL, NULL, + <float *>work.data, &lwork, + <l_int *>iwork.data, &liwork, &info) + elif scalar is double: + lapack.dtgsen(&ijob, &wantq, &wantz, <l_logical *>select.data, + &N, <double *>S.data, &N, + <double *>T.data, &N, + <double *>alphar.data, <double *>alphai.data, + <double *>beta.data, + q_ptr, &N, z_ptr, &N, &M, NULL, NULL, NULL, + <double *>work.data, &lwork, + <l_int *>iwork.data, &liwork, &info) + elif scalar is float_complex: + lapack.ctgsen(&ijob, &wantq, &wantz, <l_logical *>select.data, + &N, <float complex *>S.data, &N, + <float complex *>T.data, &N, + <float complex *>alphar.data, <float complex *>beta.data, + q_ptr, &N, z_ptr, &N, &M, NULL, NULL, NULL, + <float complex *>work.data, &lwork, + <l_int *>iwork.data, &liwork, &info) + elif scalar is double_complex: + lapack.ztgsen(&ijob, &wantq, &wantz, <l_logical *>select.data, + &N, <double complex *>S.data, &N, + <double complex *>T.data, &N, + <double complex *>alphar.data, <double complex *>beta.data, + q_ptr, &N, z_ptr, &N, &M, NULL, NULL, NULL, + <double complex *>work.data, &lwork, + <l_int *>iwork.data, &liwork, &info) - assert info == 0, "Argument error in ctgevc" - assert MM == M, "Unexpected number of eigenvectors returned in ctgevc" + if info > 0: + raise LinAlgError("Reordering failed; problem is very ill-conditioned") - if not backtr: - if left: - vl = np.asfortranarray(np.dot(Q, vl)) - if right: - vr = np.asfortranarray(np.dot(Z, vr)) + assert info == 0, "Argument error in tgsen" - if left and right: - return (vl, vr) - elif left: - return vl - else: - return vr + # Real inputs possibly produce complex output + cdef np.ndarray alpha = maybe_complex[scalar](0, alphar, alphai) + + return filter_args((True, True, Q is not None, Z is not None, + calc_ev, calc_ev), + (S, T, Q, Z, alpha, beta)) -def ztgevc(np.ndarray[np.complex128_t, ndim=2] S, - np.ndarray[np.complex128_t, ndim=2] T, - np.ndarray[np.complex128_t, ndim=2] Q=None, - np.ndarray[np.complex128_t, ndim=2] Z=None, - np.ndarray[l_logical] select=None, - left=False, right=True): +def tgevc(np.ndarray[scalar, ndim=2] S, + np.ndarray[scalar, ndim=2] T, + np.ndarray[scalar, ndim=2] Q, + np.ndarray[scalar, ndim=2] Z, + np.ndarray[l_logical] select, + left=False, right=True): cdef l_int N, info, M, MM - cdef char *side - cdef char *howmny - cdef np.ndarray[np.complex128_t, ndim=2] vl, vr - cdef double complex *vl_ptr - cdef double complex *vr_ptr - cdef l_logical *select_ptr - cdef np.ndarray[np.complex128_t] work - cdef np.ndarray[np.float64_t] rwork + + # Check parameters + + if ((S.shape[0] != S.shape[1] or T.shape[0] != T.shape[1] or + S.shape[0] != T.shape[0]) or + (Q is not None and (Q.shape[0] != Q.shape[1] or + S.shape[0] != Q.shape[0])) or + (Z is not None and (Z.shape[0] != Z.shape[1] or + S.shape[0] != Z.shape[0]))): + raise ValueError("Invalid Schur decomposition as input") assert_fortran_mat(S, T, Q, Z) + # Allocate workspaces + N = S.shape[0] - work = np.empty(2*N, dtype = np.complex128) - rwork = np.empty(2*N, dtype = np.float64) + cdef np.ndarray[scalar] work + if scalar in floating: + work = np.empty(6 * N, dtype=S.dtype) + else: + work = np.empty(2 * N, dtype=S.dtype) + + cdef np.ndarray rwork = None + if scalar is float_complex: + rwork = np.empty(2 * N, dtype=np.float32) + elif scalar is double_complex: + rwork = np.empty(2 * N, dtype=np.float64) + + cdef char *side if left and right: side = "B" elif left: @@ -2402,12 +1142,26 @@ def ztgevc(np.ndarray[np.complex128_t, ndim=2] S, else: return - backtr = False + cdef l_logical backtr = False + cdef char *howmny + cdef np.ndarray[l_logical] select_cpy = None + cdef l_logical *select_ptr if select is not None: howmny = "S" MM = select.nonzero()[0].size - select_ptr = <l_logical *>select.data + # Correct for possible additional storage if a single complex + # eigenvalue is selected. + # For that: Figure out the positions of the 2x2 blocks. + cmplxindx = np.diagonal(S, -1).nonzero()[0] + for i in cmplxindx: + if bool(select[i]) != bool(select[i+1]): + MM += 1 + + # select is overwritten in tgevc + select_cpy = np.array(select, dtype=logical_dtype, + order = 'F') + select_ptr = <l_logical *>select_cpy.data else: MM = N select_ptr = NULL @@ -2419,38 +1173,78 @@ def ztgevc(np.ndarray[np.complex128_t, ndim=2] S, else: howmny = "A" + cdef np.ndarray[scalar, ndim=2] vl_r + cdef scalar *vl_r_ptr if left: if backtr: - vl = Q + vl_r = Q else: - vl = np.empty((N, MM), dtype = np.complex128, order='F') - vl_ptr = <double complex *>vl.data + vl_r = np.empty((N, MM), dtype=S.dtype, order='F') + vl_r_ptr = <scalar *>vl_r.data else: - vl_ptr = NULL + vl_r_ptr = NULL + cdef np.ndarray[scalar, ndim=2] vr_r + cdef scalar *vr_r_ptr if right: if backtr: - vr = Z + vr_r = Z else: - vr = np.empty((N, MM), dtype = np.complex128, order='F') - vr_ptr = <double complex *>vr.data + vr_r = np.empty((N, MM), dtype=S.dtype, order='F') + vr_r_ptr = <scalar *>vr_r.data else: - vr_ptr = NULL - - f_lapack.ztgevc_(side, howmny, select_ptr, - &N, <double complex *>S.data, &N, - <double complex *>T.data, &N, - vl_ptr, &N, vr_ptr, &N, &MM, &M, - <double complex *>work.data, <double *>rwork.data, &info) + vr_r_ptr = NULL - assert info == 0, "Argument error in ztgevc" - assert MM == M, "Unexpected number of eigenvectors returned in ztgevc" + if scalar is float: + lapack.stgevc(side, howmny, select_ptr, + &N, <float *>S.data, &N, + <float *>T.data, &N, + vl_r_ptr, &N, vr_r_ptr, &N, &MM, &M, + <float *>work.data, &info) + elif scalar is double: + lapack.dtgevc(side, howmny, select_ptr, + &N, <double *>S.data, &N, + <double *>T.data, &N, + vl_r_ptr, &N, vr_r_ptr, &N, &MM, &M, + <double *>work.data, &info) + elif scalar is float_complex: + lapack.ctgevc(side, howmny, select_ptr, + &N, <float complex *>S.data, &N, + <float complex *>T.data, &N, + vl_r_ptr, &N, vr_r_ptr, &N, &MM, &M, + <float complex *>work.data, <float *>rwork.data, &info) + elif scalar is double_complex: + lapack.ztgevc(side, howmny, select_ptr, + &N, <double complex *>S.data, &N, + <double complex *>T.data, &N, + vl_r_ptr, &N, vr_r_ptr, &N, &MM, &M, + <double complex *>work.data, <double *>rwork.data, &info) + + assert info == 0, "Argument error in tgevc" + assert MM == M, "Unexpected number of eigenvectors returned in tgevc" if not backtr: if left: - vl = np.asfortranarray(np.dot(Q, vl)) + vl_r = np.asfortranarray(np.dot(Q, vl_r)) if right: - vr = np.asfortranarray(np.dot(Z, vr)) + vr_r = np.asfortranarray(np.dot(Z, vr_r)) + + # If there are complex eigenvalues, we need to postprocess the eigenvectors + cdef np.ndarray vl, vr + if left: + vl = vl_r + if right: + vr = vr_r + if scalar in floating: + if scalar is float: + dtype = np.complex64 + else: + dtype = np.complex128 + if np.diagonal(S, -1).nonzero()[0].size: + if left: + vl = txevc_postprocess(dtype, S, vl_r, select) + if right: + vr = txevc_postprocess(dtype, S, vr_r, select) if left and right: return (vl, vr) @@ -2480,8 +1274,7 @@ def prepare_for_lapack(overwrite, *args): If an argument is ``None``, it is just passed through and not used to determine the proper LAPACK type. - `prepare_for_lapack` returns a character indicating the proper LAPACK data - type ('s', 'd', 'c', 'z') and a list of properly converted arrays. + Returns a list of properly converted arrays. """ # Make sure we have NumPy arrays @@ -2502,18 +1295,10 @@ def prepare_for_lapack(overwrite, *args): # kind. dtype = np.common_type(*[arr for arr, ovwrt in mats if arr is not None]) - if dtype == np.float32: - lapacktype = 's' - elif dtype == np.float64: - lapacktype = 'd' - elif dtype == np.complex64: - lapacktype = 'c' - elif dtype == np.complex128: - lapacktype = 'z' - else: + if dtype not in (np.float32, np.float64, np.complex64, np.complex128): raise AssertionError("Unexpected data type from common_type") - ret = [ lapacktype ] + ret = [] for npmat, ovwrt in mats: # Now make sure that the array is contiguous, and copy if necessary. if npmat is not None: @@ -2538,4 +1323,7 @@ def prepare_for_lapack(overwrite, *args): ret.append(npmat) - return tuple(ret) + if len(ret) == 1: + return ret[0] + else: + return tuple(ret) diff --git a/setup.py b/setup.py index 197505db81f8a782506519d5a4f70687f713aa84..04d92ea7a23deb14abd64ef07aa4cc7cdb7ddfd4 100755 --- a/setup.py +++ b/setup.py @@ -372,8 +372,9 @@ def search_mumps(): # Conda (via conda-forge). # TODO: remove dependency libs (scotch, metis...) when conda-forge # packaged mumps/scotch are built as properly linked shared libs + # 'openblas' provides Lapack and BLAS symbols ['zmumps', 'mumps_common', 'metis', 'esmumps', 'scotch', - 'scotcherr', 'mpiseq'], + 'scotcherr', 'mpiseq', 'openblas'], ] common_libs = ['pord', 'gfortran'] @@ -384,34 +385,7 @@ def search_mumps(): return [] -def search_lapack(): - """Return the BLAS variant that is installed.""" - lib_sets = [ - # Debian - ['blas', 'lapack'], - # Conda (via conda-forge). Openblas contains lapack symbols - ['openblas', 'gfortran'], - ] - - for libs in lib_sets: - found_libs = search_libs(libs) - if found_libs: - return found_libs - - print('Error: BLAS/LAPACK are required but were not found.', - file=sys.stderr) - sys.exit(1) - - def configure_special_extensions(exts, build_summary): - #### Special config for LAPACK. - lapack = exts['kwant.linalg.lapack'] - if 'libraries' in lapack: - build_summary.append('User-configured LAPACK and BLAS') - else: - lapack['libraries'] = search_lapack() - build_summary.append('Default LAPACK and BLAS') - #### Special config for MUMPS. mumps = exts['kwant.linalg._mumps'] if 'libraries' in mumps: @@ -426,12 +400,6 @@ def configure_special_extensions(exts, build_summary): del exts['kwant.linalg._mumps'] build_summary.append('No MUMPS support') - if mumps: - # Copy config from LAPACK. - for key, value in lapack.items(): - if key not in ['sources', 'depends']: - mumps.setdefault(key, []).extend(value) - return exts @@ -550,8 +518,7 @@ def main(): 'kwant/graph/c_slicer/partitioner.h', 'kwant/graph/c_slicer/slicer.h'])), ('kwant.linalg.lapack', - dict(sources=['kwant/linalg/lapack.pyx'], - depends=['kwant/linalg/f_lapack.pxd'])), + dict(sources=['kwant/linalg/lapack.pyx'])), ('kwant.linalg._mumps', dict(sources=['kwant/linalg/_mumps.pyx'], depends=['kwant/linalg/cmumps.pxd']))]) @@ -567,8 +534,7 @@ def main(): for ext in exts.values(): ext.setdefault('include_dirs', []).append(numpy_include) - aliases = [('lapack', 'kwant.linalg.lapack'), - ('mumps', 'kwant.linalg._mumps')] + aliases = [('mumps', 'kwant.linalg._mumps')] init_cython()