diff options
author | philippe.theveny <philippe.theveny@8a072113-8704-0410-8d35-dd094bca7971> | 2015-02-24 23:50:54 +0000 |
---|---|---|
committer | philippe.theveny <philippe.theveny@8a072113-8704-0410-8d35-dd094bca7971> | 2015-02-24 23:50:54 +0000 |
commit | 6273f536d15680513e8cddfc4d8baa88ad2c64df (patch) | |
tree | a7f3303149eda2542ad7cf05fb470b60872e0161 /SRC | |
parent | c95be035b79cca2ba9e68c961d537344c5390765 (diff) |
Add xGGHD3: blocked Hessenberg reduction, code from Daniel Kressner.
Add xGGES3 and xGGEV3: computation of the Schur form, the Schur vectors, and
the generalized eigenvalues using the blocked Hessenberg reduction.
Diffstat (limited to 'SRC')
-rw-r--r-- | SRC/CMakeLists.txt | 28 | ||||
-rw-r--r-- | SRC/Makefile | 28 | ||||
-rw-r--r-- | SRC/cgges3.f | 597 | ||||
-rw-r--r-- | SRC/cggev3.f | 560 | ||||
-rw-r--r-- | SRC/cgghd3.f | 901 | ||||
-rw-r--r-- | SRC/cunm22.f | 440 | ||||
-rw-r--r-- | SRC/dgges3.f | 674 | ||||
-rw-r--r-- | SRC/dggev3.f | 594 | ||||
-rw-r--r-- | SRC/dgghd3.f | 898 | ||||
-rw-r--r-- | SRC/dorm22.f | 441 | ||||
-rw-r--r-- | SRC/ilaenv.f | 23 | ||||
-rw-r--r-- | SRC/iparmq.f | 109 | ||||
-rw-r--r-- | SRC/sgges3.f | 671 | ||||
-rw-r--r-- | SRC/sggev3.f | 589 | ||||
-rw-r--r-- | SRC/sgghd3.f | 898 | ||||
-rw-r--r-- | SRC/sorm22.f | 441 | ||||
-rw-r--r-- | SRC/zgges3.f | 595 | ||||
-rw-r--r-- | SRC/zggev3.f | 559 | ||||
-rw-r--r-- | SRC/zgghd3.f | 896 | ||||
-rw-r--r-- | SRC/zunm22.f | 440 |
20 files changed, 10338 insertions, 44 deletions
diff --git a/SRC/CMakeLists.txt b/SRC/CMakeLists.txt index d618d6e0..8ea4f5fd 100644 --- a/SRC/CMakeLists.txt +++ b/SRC/CMakeLists.txt @@ -98,8 +98,9 @@ set(SLASRC sgeqp3.f sgeqpf.f sgeqr2.f sgeqr2p.f sgeqrf.f sgeqrfp.f sgerfs.f sgerq2.f sgerqf.f sgesc2.f sgesdd.f sgesv.f sgesvd.f sgesvx.f sgetc2.f sgetf2.f sgetrf.f sgetri.f - sgetrs.f sggbak.f sggbal.f sgges.f sggesx.f sggev.f sggevx.f - sggglm.f sgghrd.f sgglse.f sggqrf.f + sgetrs.f sggbak.f sggbal.f + sgges.f sgges3.f sggesx.f sggev.f sggev3.f sggevx.f + sggglm.f sgghrd.f sgghd3.f sgglse.f sggqrf.f sggrqf.f sggsvd.f sggsvp.f sgtcon.f sgtrfs.f sgtsv.f sgtsvx.f sgttrf.f sgttrs.f sgtts2.f shgeqz.f shsein.f shseqr.f slabrd.f slacon.f slacn2.f @@ -117,7 +118,7 @@ set(SLASRC slatbs.f slatdf.f slatps.f slatrd.f slatrs.f slatrz.f slatzm.f slauu2.f slauum.f sopgtr.f sopmtr.f sorg2l.f sorg2r.f sorgbr.f sorghr.f sorgl2.f sorglq.f sorgql.f sorgqr.f sorgr2.f - sorgrq.f sorgtr.f sorm2l.f sorm2r.f + sorgrq.f sorgtr.f sorm2l.f sorm2r.f sorm22.f sormbr.f sormhr.f sorml2.f sormlq.f sormql.f sormqr.f sormr2.f sormr3.f sormrq.f sormrz.f sormtr.f spbcon.f spbequ.f spbrfs.f spbstf.f spbsv.f spbsvx.f @@ -171,8 +172,9 @@ set(CLASRC cgeqpf.f cgeqr2.f cgeqr2p.f cgeqrf.f cgeqrfp.f cgerfs.f cgerq2.f cgerqf.f cgesc2.f cgesdd.f cgesv.f cgesvd.f cgesvx.f cgetc2.f cgetf2.f cgetrf.f cgetri.f cgetrs.f - cggbak.f cggbal.f cgges.f cggesx.f cggev.f cggevx.f cggglm.f - cgghrd.f cgglse.f cggqrf.f cggrqf.f + cggbak.f cggbal.f + cgges.f cgges3.f cggesx.f cggev.f cggev3.f cggevx.f + cggglm.f cgghrd.f cgghd3.f cgglse.f cggqrf.f cggrqf.f cggsvd.f cggsvp.f cgtcon.f cgtrfs.f cgtsv.f cgtsvx.f cgttrf.f cgttrs.f cgtts2.f chbev.f chbevd.f chbevx.f chbgst.f chbgv.f chbgvd.f chbgvx.f chbtrd.f @@ -220,7 +222,7 @@ set(CLASRC ctptrs.f ctrcon.f ctrevc.f ctrexc.f ctrrfs.f ctrsen.f ctrsna.f ctrsyl.f ctrti2.f ctrtri.f ctrtrs.f ctzrqf.f ctzrzf.f cung2l.f cung2r.f cungbr.f cunghr.f cungl2.f cunglq.f cungql.f cungqr.f cungr2.f - cungrq.f cungtr.f cunm2l.f cunm2r.f cunmbr.f cunmhr.f cunml2.f + cungrq.f cungtr.f cunm2l.f cunm2r.f cunmbr.f cunmhr.f cunml2.f cunm22.f cunmlq.f cunmql.f cunmqr.f cunmr2.f cunmr3.f cunmrq.f cunmrz.f cunmtr.f cupgtr.f cupmtr.f icmax1.f scsum1.f cstemr.f chfrk.f ctfttp.f clanhf.f cpftrf.f cpftri.f cpftrs.f ctfsm.f ctftri.f @@ -254,8 +256,9 @@ set(DLASRC dgeqp3.f dgeqpf.f dgeqr2.f dgeqr2p.f dgeqrf.f dgeqrfp.f dgerfs.f dgerq2.f dgerqf.f dgesc2.f dgesdd.f dgesv.f dgesvd.f dgesvx.f dgetc2.f dgetf2.f dgetrf.f dgetri.f - dgetrs.f dggbak.f dggbal.f dgges.f dggesx.f dggev.f dggevx.f - dggglm.f dgghrd.f dgglse.f dggqrf.f + dgetrs.f dggbak.f dggbal.f + dgges.f dgges3.f dggesx.f dggev.f dggev3.f dggevx.f + dggglm.f dgghrd.f dgghd3.f dgglse.f dggqrf.f dggrqf.f dggsvd.f dggsvp.f dgtcon.f dgtrfs.f dgtsv.f dgtsvx.f dgttrf.f dgttrs.f dgtts2.f dhgeqz.f dhsein.f dhseqr.f dlabrd.f dlacon.f dlacn2.f @@ -273,7 +276,7 @@ set(DLASRC dlatbs.f dlatdf.f dlatps.f dlatrd.f dlatrs.f dlatrz.f dlatzm.f dlauu2.f dlauum.f dopgtr.f dopmtr.f dorg2l.f dorg2r.f dorgbr.f dorghr.f dorgl2.f dorglq.f dorgql.f dorgqr.f dorgr2.f - dorgrq.f dorgtr.f dorm2l.f dorm2r.f + dorgrq.f dorgtr.f dorm2l.f dorm2r.f dorm22.f dormbr.f dormhr.f dorml2.f dormlq.f dormql.f dormqr.f dormr2.f dormr3.f dormrq.f dormrz.f dormtr.f dpbcon.f dpbequ.f dpbrfs.f dpbstf.f dpbsv.f dpbsvx.f @@ -326,8 +329,9 @@ set(ZLASRC zgeqpf.f zgeqr2.f zgeqr2p.f zgeqrf.f zgeqrfp.f zgerfs.f zgerq2.f zgerqf.f zgesc2.f zgesdd.f zgesv.f zgesvd.f zgesvx.f zgetc2.f zgetf2.f zgetrf.f zgetri.f zgetrs.f - zggbak.f zggbal.f zgges.f zggesx.f zggev.f zggevx.f zggglm.f - zgghrd.f zgglse.f zggqrf.f zggrqf.f + zggbak.f zggbal.f + zgges.f zgges3.f zggesx.f zggev.f zggev3.f zggevx.f + zggglm.f zgghrd.f zgghd3.f zgglse.f zggqrf.f zggrqf.f zggsvd.f zggsvp.f zgtcon.f zgtrfs.f zgtsv.f zgtsvx.f zgttrf.f zgttrs.f zgtts2.f zhbev.f zhbevd.f zhbevx.f zhbgst.f zhbgv.f zhbgvd.f zhbgvx.f zhbtrd.f @@ -378,7 +382,7 @@ set(ZLASRC ztptrs.f ztrcon.f ztrevc.f ztrexc.f ztrrfs.f ztrsen.f ztrsna.f ztrsyl.f ztrti2.f ztrtri.f ztrtrs.f ztzrqf.f ztzrzf.f zung2l.f zung2r.f zungbr.f zunghr.f zungl2.f zunglq.f zungql.f zungqr.f zungr2.f - zungrq.f zungtr.f zunm2l.f zunm2r.f zunmbr.f zunmhr.f zunml2.f + zungrq.f zungtr.f zunm2l.f zunm2r.f zunmbr.f zunmhr.f zunml2.f zunm22.f zunmlq.f zunmql.f zunmqr.f zunmr2.f zunmr3.f zunmrq.f zunmrz.f zunmtr.f zupgtr.f zupmtr.f izmax1.f dzsum1.f zstemr.f diff --git a/SRC/Makefile b/SRC/Makefile index f3eaa533..30946da5 100644 --- a/SRC/Makefile +++ b/SRC/Makefile @@ -103,8 +103,9 @@ SLASRC = \ sgeqp3.o sgeqpf.o sgeqr2.o sgeqr2p.o sgeqrf.o sgeqrfp.o sgerfs.o \ sgerq2.o sgerqf.o sgesc2.o sgesdd.o sgesv.o sgesvd.o sgesvx.o \ sgetc2.o sgetf2.o sgetri.o \ - sggbak.o sggbal.o sgges.o sggesx.o sggev.o sggevx.o \ - sggglm.o sgghrd.o sgglse.o sggqrf.o \ + sggbak.o sggbal.o sgges.o sgges3.o sggesx.o \ + sggev.o sggev3.o sggevx.o \ + sggglm.o sgghrd.o sgghd3.o sgglse.o sggqrf.o \ sggrqf.o sggsvd.o sggsvp.o sgtcon.o sgtrfs.o sgtsv.o \ sgtsvx.o sgttrf.o sgttrs.o sgtts2.o shgeqz.o \ shsein.o shseqr.o slabrd.o slacon.o slacn2.o \ @@ -122,7 +123,7 @@ SLASRC = \ slatbs.o slatdf.o slatps.o slatrd.o slatrs.o slatrz.o slatzm.o \ slauu2.o slauum.o sopgtr.o sopmtr.o sorg2l.o sorg2r.o \ sorgbr.o sorghr.o sorgl2.o sorglq.o sorgql.o sorgqr.o sorgr2.o \ - sorgrq.o sorgtr.o sorm2l.o sorm2r.o \ + sorgrq.o sorgtr.o sorm2l.o sorm2r.o sorm22.o \ sormbr.o sormhr.o sorml2.o sormlq.o sormql.o sormqr.o sormr2.o \ sormr3.o sormrq.o sormrz.o sormtr.o spbcon.o spbequ.o spbrfs.o \ spbstf.o spbsv.o spbsvx.o \ @@ -178,8 +179,9 @@ CLASRC = \ cgeqpf.o cgeqr2.o cgeqr2p.o cgeqrf.o cgeqrfp.o cgerfs.o \ cgerq2.o cgerqf.o cgesc2.o cgesdd.o cgesv.o cgesvd.o \ cgesvx.o cgetc2.o cgetf2.o cgetri.o \ - cggbak.o cggbal.o cgges.o cggesx.o cggev.o cggevx.o cggglm.o \ - cgghrd.o cgglse.o cggqrf.o cggrqf.o \ + cggbak.o cggbal.o cgges.o cgges3.o cggesx.o \ + cggev.o cggev3.o cggevx.o \ + cgghrd.o cgghd3.o cgglse.o cggqrf.o cggrqf.o \ cggsvd.o cggsvp.o \ cgtcon.o cgtrfs.o cgtsv.o cgtsvx.o cgttrf.o cgttrs.o cgtts2.o chbev.o \ chbevd.o chbevx.o chbgst.o chbgv.o chbgvd.o chbgvx.o chbtrd.o \ @@ -227,7 +229,7 @@ CLASRC = \ ctptrs.o ctrcon.o ctrevc.o ctrexc.o ctrrfs.o ctrsen.o ctrsna.o \ ctrsyl.o ctrti2.o ctrtri.o ctrtrs.o ctzrqf.o ctzrzf.o cung2l.o cung2r.o \ cungbr.o cunghr.o cungl2.o cunglq.o cungql.o cungqr.o cungr2.o \ - cungrq.o cungtr.o cunm2l.o cunm2r.o cunmbr.o cunmhr.o cunml2.o \ + cungrq.o cungtr.o cunm2l.o cunm2r.o cunmbr.o cunmhr.o cunml2.o cunm22.o \ cunmlq.o cunmql.o cunmqr.o cunmr2.o cunmr3.o cunmrq.o cunmrz.o \ cunmtr.o cupgtr.o cupmtr.o icmax1.o scsum1.o cstemr.o \ chfrk.o ctfttp.o clanhf.o cpftrf.o cpftri.o cpftrs.o ctfsm.o ctftri.o \ @@ -263,8 +265,9 @@ DLASRC = \ dgeqp3.o dgeqpf.o dgeqr2.o dgeqr2p.o dgeqrf.o dgeqrfp.o dgerfs.o \ dgerq2.o dgerqf.o dgesc2.o dgesdd.o dgesv.o dgesvd.o dgesvx.o \ dgetc2.o dgetf2.o dgetrf.o dgetri.o \ - dgetrs.o dggbak.o dggbal.o dgges.o dggesx.o dggev.o dggevx.o \ - dggglm.o dgghrd.o dgglse.o dggqrf.o \ + dgetrs.o dggbak.o dggbal.o dgges.o dgges3.o dggesx.o \ + dggev.o dggev3.o dggevx.o \ + dggglm.o dgghrd.o dgghd3.o dgglse.o dggqrf.o \ dggrqf.o dggsvd.o dggsvp.o dgtcon.o dgtrfs.o dgtsv.o \ dgtsvx.o dgttrf.o dgttrs.o dgtts2.o dhgeqz.o \ dhsein.o dhseqr.o dlabrd.o dlacon.o dlacn2.o \ @@ -282,7 +285,7 @@ DLASRC = \ dlatbs.o dlatdf.o dlatps.o dlatrd.o dlatrs.o dlatrz.o dlatzm.o dlauu2.o \ dlauum.o dopgtr.o dopmtr.o dorg2l.o dorg2r.o \ dorgbr.o dorghr.o dorgl2.o dorglq.o dorgql.o dorgqr.o dorgr2.o \ - dorgrq.o dorgtr.o dorm2l.o dorm2r.o \ + dorgrq.o dorgtr.o dorm2l.o dorm2r.o dorm22.o \ dormbr.o dormhr.o dorml2.o dormlq.o dormql.o dormqr.o dormr2.o \ dormr3.o dormrq.o dormrz.o dormtr.o dpbcon.o dpbequ.o dpbrfs.o \ dpbstf.o dpbsv.o dpbsvx.o \ @@ -337,8 +340,9 @@ ZLASRC = \ zgeqpf.o zgeqr2.o zgeqr2p.o zgeqrf.o zgeqrfp.o zgerfs.o zgerq2.o zgerqf.o \ zgesc2.o zgesdd.o zgesv.o zgesvd.o zgesvx.o zgetc2.o zgetf2.o zgetrf.o \ zgetri.o zgetrs.o \ - zggbak.o zggbal.o zgges.o zggesx.o zggev.o zggevx.o zggglm.o \ - zgghrd.o zgglse.o zggqrf.o zggrqf.o \ + zggbak.o zggbal.o zgges.o zgges3.o zggesx.o \ + zggev.o zggev3.o zggevx.o zggglm.o \ + zgghrd.o zgghd3.o zgglse.o zggqrf.o zggrqf.o \ zggsvd.o zggsvp.o \ zgtcon.o zgtrfs.o zgtsv.o zgtsvx.o zgttrf.o zgttrs.o zgtts2.o zhbev.o \ zhbevd.o zhbevx.o zhbgst.o zhbgv.o zhbgvd.o zhbgvx.o zhbtrd.o \ @@ -389,7 +393,7 @@ ZLASRC = \ ztptrs.o ztrcon.o ztrevc.o ztrexc.o ztrrfs.o ztrsen.o ztrsna.o \ ztrsyl.o ztrti2.o ztrtri.o ztrtrs.o ztzrqf.o ztzrzf.o zung2l.o \ zung2r.o zungbr.o zunghr.o zungl2.o zunglq.o zungql.o zungqr.o zungr2.o \ - zungrq.o zungtr.o zunm2l.o zunm2r.o zunmbr.o zunmhr.o zunml2.o \ + zungrq.o zungtr.o zunm2l.o zunm2r.o zunmbr.o zunmhr.o zunml2.o zunm22.o \ zunmlq.o zunmql.o zunmqr.o zunmr2.o zunmr3.o zunmrq.o zunmrz.o \ zunmtr.o zupgtr.o \ zupmtr.o izmax1.o dzsum1.o zstemr.o \ diff --git a/SRC/cgges3.f b/SRC/cgges3.f new file mode 100644 index 00000000..ab603de5 --- /dev/null +++ b/SRC/cgges3.f @@ -0,0 +1,597 @@ +*> \brief <b> CGGES3 computes the eigenvalues, the Schur form, and, optionally, the matrix of Schur vectors for GE matrices (blocked algorithm)</b> +* +* =========== DOCUMENTATION =========== +* +* Online html documentation available at +* http://www.netlib.org/lapack/explore-html/ +* +*> \htmlonly +*> Download CGGES3 + dependencies +*> <a href="http://www.netlib.org/cgi-bin/netlibfiles.tgz?format=tgz&filename=/lapack/lapack_routine/cgges3.f"> +*> [TGZ]</a> +*> <a href="http://www.netlib.org/cgi-bin/netlibfiles.zip?format=zip&filename=/lapack/lapack_routine/cgges3.f"> +*> [ZIP]</a> +*> <a href="http://www.netlib.org/cgi-bin/netlibfiles.txt?format=txt&filename=/lapack/lapack_routine/cgges3.f"> +*> [TXT]</a> +*> \endhtmlonly +* +* Definition: +* =========== +* +* SUBROUTINE CGGES3( JOBVSL, JOBVSR, SORT, SELCTG, N, A, LDA, B, +* $ LDB, SDIM, ALPHA, BETA, VSL, LDVSL, VSR, LDVSR, +* $ WORK, LWORK, RWORK, BWORK, INFO ) +* +* .. Scalar Arguments .. +* CHARACTER JOBVSL, JOBVSR, SORT +* INTEGER INFO, LDA, LDB, LDVSL, LDVSR, LWORK, N, SDIM +* .. +* .. Array Arguments .. +* LOGICAL BWORK( * ) +* REAL RWORK( * ) +* COMPLEX A( LDA, * ), ALPHA( * ), B( LDB, * ), +* $ BETA( * ), VSL( LDVSL, * ), VSR( LDVSR, * ), +* $ WORK( * ) +* .. +* .. Function Arguments .. +* LOGICAL SELCTG +* EXTERNAL SELCTG +* .. +* +* +*> \par Purpose: +* ============= +*> +*> \verbatim +*> +*> CGGES3 computes for a pair of N-by-N complex nonsymmetric matrices +*> (A,B), the generalized eigenvalues, the generalized complex Schur +*> form (S, T), and optionally left and/or right Schur vectors (VSL +*> and VSR). This gives the generalized Schur factorization +*> +*> (A,B) = ( (VSL)*S*(VSR)**H, (VSL)*T*(VSR)**H ) +*> +*> where (VSR)**H is the conjugate-transpose of VSR. +*> +*> Optionally, it also orders the eigenvalues so that a selected cluster +*> of eigenvalues appears in the leading diagonal blocks of the upper +*> triangular matrix S and the upper triangular matrix T. The leading +*> columns of VSL and VSR then form an unitary basis for the +*> corresponding left and right eigenspaces (deflating subspaces). +*> +*> (If only the generalized eigenvalues are needed, use the driver +*> CGGEV instead, which is faster.) +*> +*> A generalized eigenvalue for a pair of matrices (A,B) is a scalar w +*> or a ratio alpha/beta = w, such that A - w*B is singular. It is +*> usually represented as the pair (alpha,beta), as there is a +*> reasonable interpretation for beta=0, and even for both being zero. +*> +*> A pair of matrices (S,T) is in generalized complex Schur form if S +*> and T are upper triangular and, in addition, the diagonal elements +*> of T are non-negative real numbers. +*> \endverbatim +* +* Arguments: +* ========== +* +*> \param[in] JOBVSL +*> \verbatim +*> JOBVSL is CHARACTER*1 +*> = 'N': do not compute the left Schur vectors; +*> = 'V': compute the left Schur vectors. +*> \endverbatim +*> +*> \param[in] JOBVSR +*> \verbatim +*> JOBVSR is CHARACTER*1 +*> = 'N': do not compute the right Schur vectors; +*> = 'V': compute the right Schur vectors. +*> \endverbatim +*> +*> \param[in] SORT +*> \verbatim +*> SORT is CHARACTER*1 +*> Specifies whether or not to order the eigenvalues on the +*> diagonal of the generalized Schur form. +*> = 'N': Eigenvalues are not ordered; +*> = 'S': Eigenvalues are ordered (see SELCTG). +*> \endverbatim +*> +*> \param[in] SELCTG +*> \verbatim +*> SELCTG is a LOGICAL FUNCTION of two COMPLEX arguments +*> SELCTG must be declared EXTERNAL in the calling subroutine. +*> If SORT = 'N', SELCTG is not referenced. +*> If SORT = 'S', SELCTG is used to select eigenvalues to sort +*> to the top left of the Schur form. +*> An eigenvalue ALPHA(j)/BETA(j) is selected if +*> SELCTG(ALPHA(j),BETA(j)) is true. +*> +*> Note that a selected complex eigenvalue may no longer satisfy +*> SELCTG(ALPHA(j),BETA(j)) = .TRUE. after ordering, since +*> ordering may change the value of complex eigenvalues +*> (especially if the eigenvalue is ill-conditioned), in this +*> case INFO is set to N+2 (See INFO below). +*> \endverbatim +*> +*> \param[in] N +*> \verbatim +*> N is INTEGER +*> The order of the matrices A, B, VSL, and VSR. N >= 0. +*> \endverbatim +*> +*> \param[in,out] A +*> \verbatim +*> A is COMPLEX array, dimension (LDA, N) +*> On entry, the first of the pair of matrices. +*> On exit, A has been overwritten by its generalized Schur +*> form S. +*> \endverbatim +*> +*> \param[in] LDA +*> \verbatim +*> LDA is INTEGER +*> The leading dimension of A. LDA >= max(1,N). +*> \endverbatim +*> +*> \param[in,out] B +*> \verbatim +*> B is COMPLEX array, dimension (LDB, N) +*> On entry, the second of the pair of matrices. +*> On exit, B has been overwritten by its generalized Schur +*> form T. +*> \endverbatim +*> +*> \param[in] LDB +*> \verbatim +*> LDB is INTEGER +*> The leading dimension of B. LDB >= max(1,N). +*> \endverbatim +*> +*> \param[out] SDIM +*> \verbatim +*> SDIM is INTEGER +*> If SORT = 'N', SDIM = 0. +*> If SORT = 'S', SDIM = number of eigenvalues (after sorting) +*> for which SELCTG is true. +*> \endverbatim +*> +*> \param[out] ALPHA +*> \verbatim +*> ALPHA is COMPLEX array, dimension (N) +*> \endverbatim +*> +*> \param[out] BETA +*> \verbatim +*> BETA is COMPLEX array, dimension (N) +*> On exit, ALPHA(j)/BETA(j), j=1,...,N, will be the +*> generalized eigenvalues. ALPHA(j), j=1,...,N and BETA(j), +*> j=1,...,N are the diagonals of the complex Schur form (A,B) +*> output by CGGES3. The BETA(j) will be non-negative real. +*> +*> Note: the quotients ALPHA(j)/BETA(j) may easily over- or +*> underflow, and BETA(j) may even be zero. Thus, the user +*> should avoid naively computing the ratio alpha/beta. +*> However, ALPHA will be always less than and usually +*> comparable with norm(A) in magnitude, and BETA always less +*> than and usually comparable with norm(B). +*> \endverbatim +*> +*> \param[out] VSL +*> \verbatim +*> VSL is COMPLEX array, dimension (LDVSL,N) +*> If JOBVSL = 'V', VSL will contain the left Schur vectors. +*> Not referenced if JOBVSL = 'N'. +*> \endverbatim +*> +*> \param[in] LDVSL +*> \verbatim +*> LDVSL is INTEGER +*> The leading dimension of the matrix VSL. LDVSL >= 1, and +*> if JOBVSL = 'V', LDVSL >= N. +*> \endverbatim +*> +*> \param[out] VSR +*> \verbatim +*> VSR is COMPLEX array, dimension (LDVSR,N) +*> If JOBVSR = 'V', VSR will contain the right Schur vectors. +*> Not referenced if JOBVSR = 'N'. +*> \endverbatim +*> +*> \param[in] LDVSR +*> \verbatim +*> LDVSR is INTEGER +*> The leading dimension of the matrix VSR. LDVSR >= 1, and +*> if JOBVSR = 'V', LDVSR >= N. +*> \endverbatim +*> +*> \param[out] WORK +*> \verbatim +*> WORK is COMPLEX array, dimension (MAX(1,LWORK)) +*> On exit, if INFO = 0, WORK(1) returns the optimal LWORK. +*> \endverbatim +*> +*> \param[in] LWORK +*> \verbatim +*> LWORK is INTEGER +*> The dimension of the array WORK. +*> +*> If LWORK = -1, then a workspace query is assumed; the routine +*> only calculates the optimal size of the WORK array, returns +*> this value as the first entry of the WORK array, and no error +*> message related to LWORK is issued by XERBLA. +*> \endverbatim +*> +*> \param[out] RWORK +*> \verbatim +*> RWORK is REAL array, dimension (8*N) +*> \endverbatim +*> +*> \param[out] BWORK +*> \verbatim +*> BWORK is LOGICAL array, dimension (N) +*> Not referenced if SORT = 'N'. +*> \endverbatim +*> +*> \param[out] INFO +*> \verbatim +*> INFO is INTEGER +*> = 0: successful exit +*> < 0: if INFO = -i, the i-th argument had an illegal value. +*> =1,...,N: +*> The QZ iteration failed. (A,B) are not in Schur +*> form, but ALPHA(j) and BETA(j) should be correct for +*> j=INFO+1,...,N. +*> > N: =N+1: other than QZ iteration failed in CHGEQZ +*> =N+2: after reordering, roundoff changed values of +*> some complex eigenvalues so that leading +*> eigenvalues in the Generalized Schur form no +*> longer satisfy SELCTG=.TRUE. This could also +*> be caused due to scaling. +*> =N+3: reordering failed in CTGSEN. +*> \endverbatim +* +* Authors: +* ======== +* +*> \author Univ. of Tennessee +*> \author Univ. of California Berkeley +*> \author Univ. of Colorado Denver +*> \author NAG Ltd. +* +*> \date January 2015 +* +*> \ingroup complexGEeigen +* +* ===================================================================== + SUBROUTINE CGGES3( JOBVSL, JOBVSR, SORT, SELCTG, N, A, LDA, B, + $ LDB, SDIM, ALPHA, BETA, VSL, LDVSL, VSR, LDVSR, + $ WORK, LWORK, RWORK, BWORK, INFO ) +* +* -- LAPACK driver routine (version 3.6.0) -- +* -- LAPACK is a software package provided by Univ. of Tennessee, -- +* -- Univ. of California Berkeley, Univ. of Colorado Denver and NAG Ltd..-- +* January 2015 +* +* .. Scalar Arguments .. + CHARACTER JOBVSL, JOBVSR, SORT + INTEGER INFO, LDA, LDB, LDVSL, LDVSR, LWORK, N, SDIM +* .. +* .. Array Arguments .. + LOGICAL BWORK( * ) + REAL RWORK( * ) + COMPLEX A( LDA, * ), ALPHA( * ), B( LDB, * ), + $ BETA( * ), VSL( LDVSL, * ), VSR( LDVSR, * ), + $ WORK( * ) +* .. +* .. Function Arguments .. + LOGICAL SELCTG + EXTERNAL SELCTG +* .. +* +* ===================================================================== +* +* .. Parameters .. + REAL ZERO, ONE + PARAMETER ( ZERO = 0.0E0, ONE = 1.0E0 ) + COMPLEX CZERO, CONE + PARAMETER ( CZERO = ( 0.0E0, 0.0E0 ), + $ CONE = ( 1.0E0, 0.0E0 ) ) +* .. +* .. Local Scalars .. + LOGICAL CURSL, ILASCL, ILBSCL, ILVSL, ILVSR, LASTSL, + $ LQUERY, WANTST + INTEGER I, ICOLS, IERR, IHI, IJOBVL, IJOBVR, ILEFT, + $ ILO, IRIGHT, IROWS, IRWRK, ITAU, IWRK, LWKOPT + REAL ANRM, ANRMTO, BIGNUM, BNRM, BNRMTO, EPS, PVSL, + $ PVSR, SMLNUM +* .. +* .. Local Arrays .. + INTEGER IDUM( 1 ) + REAL DIF( 2 ) +* .. +* .. External Subroutines .. + EXTERNAL CGEQRF, CGGBAK, CGGBAL, CGGHD3, CHGEQZ, CLACPY, + $ CLASCL, CLASET, CTGSEN, CUNGQR, CUNMQR, SLABAD, + $ XERBLA +* .. +* .. External Functions .. + LOGICAL LSAME + REAL CLANGE, SLAMCH + EXTERNAL LSAME, CLANGE, SLAMCH +* .. +* .. Intrinsic Functions .. + INTRINSIC MAX, SQRT +* .. +* .. Executable Statements .. +* +* Decode the input arguments +* + IF( LSAME( JOBVSL, 'N' ) ) THEN + IJOBVL = 1 + ILVSL = .FALSE. + ELSE IF( LSAME( JOBVSL, 'V' ) ) THEN + IJOBVL = 2 + ILVSL = .TRUE. + ELSE + IJOBVL = -1 + ILVSL = .FALSE. + END IF +* + IF( LSAME( JOBVSR, 'N' ) ) THEN + IJOBVR = 1 + ILVSR = .FALSE. + ELSE IF( LSAME( JOBVSR, 'V' ) ) THEN + IJOBVR = 2 + ILVSR = .TRUE. + ELSE + IJOBVR = -1 + ILVSR = .FALSE. + END IF +* + WANTST = LSAME( SORT, 'S' ) +* +* Test the input arguments +* + INFO = 0 + LQUERY = ( LWORK.EQ.-1 ) + IF( IJOBVL.LE.0 ) THEN + INFO = -1 + ELSE IF( IJOBVR.LE.0 ) THEN + INFO = -2 + ELSE IF( ( .NOT.WANTST ) .AND. ( .NOT.LSAME( SORT, 'N' ) ) ) THEN + INFO = -3 + ELSE IF( N.LT.0 ) THEN + INFO = -5 + ELSE IF( LDA.LT.MAX( 1, N ) ) THEN + INFO = -7 + ELSE IF( LDB.LT.MAX( 1, N ) ) THEN + INFO = -9 + ELSE IF( LDVSL.LT.1 .OR. ( ILVSL .AND. LDVSL.LT.N ) ) THEN + INFO = -14 + ELSE IF( LDVSR.LT.1 .OR. ( ILVSR .AND. LDVSR.LT.N ) ) THEN + INFO = -16 + ELSE IF( LWORK.LT.MAX( 1, 2*N ) .AND. .NOT.LQUERY ) THEN + INFO = -18 + END IF +* +* Compute workspace +* + IF( INFO.EQ.0 ) THEN + CALL CGEQRF( N, N, B, LDB, WORK, WORK, -1, IERR ) + LWKOPT = MAX( 1, N + INT ( WORK( 1 ) ) ) + CALL CUNMQR( 'L', 'C', N, N, N, B, LDB, WORK, A, LDA, WORK, + $ -1, IERR ) + LWKOPT = MAX( LWKOPT, N + INT ( WORK( 1 ) ) ) + IF( ILVSL ) THEN + CALL CUNGQR( N, N, N, VSL, LDVSL, WORK, WORK, -1, + $ IERR ) + LWKOPT = MAX( LWKOPT, N + INT ( WORK( 1 ) ) ) + END IF + CALL CGGHD3( JOBVSL, JOBVSR, N, 1, N, A, LDA, B, LDB, VSL, + $ LDVSL, VSR, LDVSR, WORK, -1, IERR ) + LWKOPT = MAX( LWKOPT, N + INT ( WORK( 1 ) ) ) + CALL CHGEQZ( 'S', JOBVSL, JOBVSR, N, 1, N, A, LDA, B, LDB, + $ ALPHA, BETA, VSL, LDVSL, VSR, LDVSR, WORK, -1, + $ WORK, IERR ) + LWKOPT = MAX( LWKOPT, INT ( WORK( 1 ) ) ) + IF( WANTST ) THEN + CALL CTGSEN( 0, ILVSL, ILVSR, WORK, N, A, LDA, B, LDB, + $ ALPHA, BETA, VSL, LDVSL, VSR, LDVSR, SDIM, + $ PVSL, PVSR, DIF, WORK, -1, IDUM, 1, IERR ) + LWKOPT = MAX( LWKOPT, INT ( WORK( 1 ) ) ) + END IF + WORK( 1 ) = CMPLX( LWKOPT ) + END IF + +* + IF( INFO.NE.0 ) THEN + CALL XERBLA( 'CGGES3 ', -INFO ) + RETURN + ELSE IF( LQUERY ) THEN + RETURN + END IF +* +* Quick return if possible +* + IF( N.EQ.0 ) THEN + SDIM = 0 + RETURN + END IF +* +* Get machine constants +* + EPS = SLAMCH( 'P' ) + SMLNUM = SLAMCH( 'S' ) + BIGNUM = ONE / SMLNUM + CALL SLABAD( SMLNUM, BIGNUM ) + SMLNUM = SQRT( SMLNUM ) / EPS + BIGNUM = ONE / SMLNUM +* +* Scale A if max element outside range [SMLNUM,BIGNUM] +* + ANRM = CLANGE( 'M', N, N, A, LDA, RWORK ) + ILASCL = .FALSE. + IF( ANRM.GT.ZERO .AND. ANRM.LT.SMLNUM ) THEN + ANRMTO = SMLNUM + ILASCL = .TRUE. + ELSE IF( ANRM.GT.BIGNUM ) THEN + ANRMTO = BIGNUM + ILASCL = .TRUE. + END IF +* + IF( ILASCL ) + $ CALL CLASCL( 'G', 0, 0, ANRM, ANRMTO, N, N, A, LDA, IERR ) +* +* Scale B if max element outside range [SMLNUM,BIGNUM] +* + BNRM = CLANGE( 'M', N, N, B, LDB, RWORK ) + ILBSCL = .FALSE. + IF( BNRM.GT.ZERO .AND. BNRM.LT.SMLNUM ) THEN + BNRMTO = SMLNUM + ILBSCL = .TRUE. + ELSE IF( BNRM.GT.BIGNUM ) THEN + BNRMTO = BIGNUM + ILBSCL = .TRUE. + END IF +* + IF( ILBSCL ) + $ CALL CLASCL( 'G', 0, 0, BNRM, BNRMTO, N, N, B, LDB, IERR ) +* +* Permute the matrix to make it more nearly triangular +* + ILEFT = 1 + IRIGHT = N + 1 + IRWRK = IRIGHT + N + CALL CGGBAL( 'P', N, A, LDA, B, LDB, ILO, IHI, RWORK( ILEFT ), + $ RWORK( IRIGHT ), RWORK( IRWRK ), IERR ) +* +* Reduce B to triangular form (QR decomposition of B) +* + IROWS = IHI + 1 - ILO + ICOLS = N + 1 - ILO + ITAU = 1 + IWRK = ITAU + IROWS + CALL CGEQRF( IROWS, ICOLS, B( ILO, ILO ), LDB, WORK( ITAU ), + $ WORK( IWRK ), LWORK+1-IWRK, IERR ) +* +* Apply the orthogonal transformation to matrix A +* + CALL CUNMQR( 'L', 'C', IROWS, ICOLS, IROWS, B( ILO, ILO ), LDB, + $ WORK( ITAU ), A( ILO, ILO ), LDA, WORK( IWRK ), + $ LWORK+1-IWRK, IERR ) +* +* Initialize VSL +* + IF( ILVSL ) THEN + CALL CLASET( 'Full', N, N, CZERO, CONE, VSL, LDVSL ) + IF( IROWS.GT.1 ) THEN + CALL CLACPY( 'L', IROWS-1, IROWS-1, B( ILO+1, ILO ), LDB, + $ VSL( ILO+1, ILO ), LDVSL ) + END IF + CALL CUNGQR( IROWS, IROWS, IROWS, VSL( ILO, ILO ), LDVSL, + $ WORK( ITAU ), WORK( IWRK ), LWORK+1-IWRK, IERR ) + END IF +* +* Initialize VSR +* + IF( ILVSR ) + $ CALL CLASET( 'Full', N, N, CZERO, CONE, VSR, LDVSR ) +* +* Reduce to generalized Hessenberg form +* + CALL CGGHD3( JOBVSL, JOBVSR, N, ILO, IHI, A, LDA, B, LDB, VSL, + $ LDVSL, VSR, LDVSR, WORK( IWRK ), LWORK+1-IWRK, IERR ) +* + SDIM = 0 +* +* Perform QZ algorithm, computing Schur vectors if desired +* + IWRK = ITAU + CALL CHGEQZ( 'S', JOBVSL, JOBVSR, N, ILO, IHI, A, LDA, B, LDB, + $ ALPHA, BETA, VSL, LDVSL, VSR, LDVSR, WORK( IWRK ), + $ LWORK+1-IWRK, RWORK( IRWRK ), IERR ) + IF( IERR.NE.0 ) THEN + IF( IERR.GT.0 .AND. IERR.LE.N ) THEN + INFO = IERR + ELSE IF( IERR.GT.N .AND. IERR.LE.2*N ) THEN + INFO = IERR - N + ELSE + INFO = N + 1 + END IF + GO TO 30 + END IF +* +* Sort eigenvalues ALPHA/BETA if desired +* + IF( WANTST ) THEN +* +* Undo scaling on eigenvalues before selecting +* + IF( ILASCL ) + $ CALL CLASCL( 'G', 0, 0, ANRM, ANRMTO, N, 1, ALPHA, N, IERR ) + IF( ILBSCL ) + $ CALL CLASCL( 'G', 0, 0, BNRM, BNRMTO, N, 1, BETA, N, IERR ) +* +* Select eigenvalues +* + DO 10 I = 1, N + BWORK( I ) = SELCTG( ALPHA( I ), BETA( I ) ) + 10 CONTINUE +* + CALL CTGSEN( 0, ILVSL, ILVSR, BWORK, N, A, LDA, B, LDB, ALPHA, + $ BETA, VSL, LDVSL, VSR, LDVSR, SDIM, PVSL, PVSR, + $ DIF, WORK( IWRK ), LWORK-IWRK+1, IDUM, 1, IERR ) + IF( IERR.EQ.1 ) + $ INFO = N + 3 +* + END IF +* +* Apply back-permutation to VSL and VSR +* + IF( ILVSL ) + $ CALL CGGBAK( 'P', 'L', N, ILO, IHI, RWORK( ILEFT ), + $ RWORK( IRIGHT ), N, VSL, LDVSL, IERR ) + IF( ILVSR ) + $ CALL CGGBAK( 'P', 'R', N, ILO, IHI, RWORK( ILEFT ), + $ RWORK( IRIGHT ), N, VSR, LDVSR, IERR ) +* +* Undo scaling +* + IF( ILASCL ) THEN + CALL CLASCL( 'U', 0, 0, ANRMTO, ANRM, N, N, A, LDA, IERR ) + CALL CLASCL( 'G', 0, 0, ANRMTO, ANRM, N, 1, ALPHA, N, IERR ) + END IF +* + IF( ILBSCL ) THEN + CALL CLASCL( 'U', 0, 0, BNRMTO, BNRM, N, N, B, LDB, IERR ) + CALL CLASCL( 'G', 0, 0, BNRMTO, BNRM, N, 1, BETA, N, IERR ) + END IF +* + IF( WANTST ) THEN +* +* Check if reordering is correct +* + LASTSL = .TRUE. + SDIM = 0 + DO 20 I = 1, N + CURSL = SELCTG( ALPHA( I ), BETA( I ) ) + IF( CURSL ) + $ SDIM = SDIM + 1 + IF( CURSL .AND. .NOT.LASTSL ) + $ INFO = N + 2 + LASTSL = CURSL + 20 CONTINUE +* + END IF +* + 30 CONTINUE +* + WORK( 1 ) = CMPLX( LWKOPT ) +* + RETURN +* +* End of CGGES3 +* + END diff --git a/SRC/cggev3.f b/SRC/cggev3.f new file mode 100644 index 00000000..5d8c7f8a --- /dev/null +++ b/SRC/cggev3.f @@ -0,0 +1,560 @@ +*> \brief <b> CGGEV3 computes the eigenvalues and, optionally, the left and/or right eigenvectors for GE matrices (blocked algorithm)</b> +* +* =========== DOCUMENTATION =========== +* +* Online html documentation available at +* http://www.netlib.org/lapack/explore-html/ +* +*> \htmlonly +*> Download CGGEV3 + dependencies +*> <a href="http://www.netlib.org/cgi-bin/netlibfiles.tgz?format=tgz&filename=/lapack/lapack_routine/cggev3.f"> +*> [TGZ]</a> +*> <a href="http://www.netlib.org/cgi-bin/netlibfiles.zip?format=zip&filename=/lapack/lapack_routine/cggev3.f"> +*> [ZIP]</a> +*> <a href="http://www.netlib.org/cgi-bin/netlibfiles.txt?format=txt&filename=/lapack/lapack_routine/cggev3.f"> +*> [TXT]</a> +*> \endhtmlonly +* +* Definition: +* =========== +* +* SUBROUTINE CGGEV3( JOBVL, JOBVR, N, A, LDA, B, LDB, ALPHA, BETA, +* $ VL, LDVL, VR, LDVR, WORK, LWORK, RWORK, INFO ) +* +* .. Scalar Arguments .. +* CHARACTER JOBVL, JOBVR +* INTEGER INFO, LDA, LDB, LDVL, LDVR, LWORK, N +* .. +* .. Array Arguments .. +* REAL RWORK( * ) +* COMPLEX A( LDA, * ), ALPHA( * ), B( LDB, * ), +* $ BETA( * ), VL( LDVL, * ), VR( LDVR, * ), +* $ WORK( * ) +* .. +* +* +*> \par Purpose: +* ============= +*> +*> \verbatim +*> +*> CGGEV3 computes for a pair of N-by-N complex nonsymmetric matrices +*> (A,B), the generalized eigenvalues, and optionally, the left and/or +*> right generalized eigenvectors. +*> +*> A generalized eigenvalue for a pair of matrices (A,B) is a scalar +*> lambda or a ratio alpha/beta = lambda, such that A - lambda*B is +*> singular. It is usually represented as the pair (alpha,beta), as +*> there is a reasonable interpretation for beta=0, and even for both +*> being zero. +*> +*> The right generalized eigenvector v(j) corresponding to the +*> generalized eigenvalue lambda(j) of (A,B) satisfies +*> +*> A * v(j) = lambda(j) * B * v(j). +*> +*> The left generalized eigenvector u(j) corresponding to the +*> generalized eigenvalues lambda(j) of (A,B) satisfies +*> +*> u(j)**H * A = lambda(j) * u(j)**H * B +*> +*> where u(j)**H is the conjugate-transpose of u(j). +*> \endverbatim +* +* Arguments: +* ========== +* +*> \param[in] JOBVL +*> \verbatim +*> JOBVL is CHARACTER*1 +*> = 'N': do not compute the left generalized eigenvectors; +*> = 'V': compute the left generalized eigenvectors. +*> \endverbatim +*> +*> \param[in] JOBVR +*> \verbatim +*> JOBVR is CHARACTER*1 +*> = 'N': do not compute the right generalized eigenvectors; +*> = 'V': compute the right generalized eigenvectors. +*> \endverbatim +*> +*> \param[in] N +*> \verbatim +*> N is INTEGER +*> The order of the matrices A, B, VL, and VR. N >= 0. +*> \endverbatim +*> +*> \param[in,out] A +*> \verbatim +*> A is COMPLEX array, dimension (LDA, N) +*> On entry, the matrix A in the pair (A,B). +*> On exit, A has been overwritten. +*> \endverbatim +*> +*> \param[in] LDA +*> \verbatim +*> LDA is INTEGER +*> The leading dimension of A. LDA >= max(1,N). +*> \endverbatim +*> +*> \param[in,out] B +*> \verbatim +*> B is COMPLEX array, dimension (LDB, N) +*> On entry, the matrix B in the pair (A,B). +*> On exit, B has been overwritten. +*> \endverbatim +*> +*> \param[in] LDB +*> \verbatim +*> LDB is INTEGER +*> The leading dimension of B. LDB >= max(1,N). +*> \endverbatim +*> +*> \param[out] ALPHA +*> \verbatim +*> ALPHA is COMPLEX array, dimension (N) +*> \endverbatim +*> +*> \param[out] BETA +*> \verbatim +*> BETA is COMPLEX array, dimension (N) +*> On exit, ALPHA(j)/BETA(j), j=1,...,N, will be the +*> generalized eigenvalues. +*> +*> Note: the quotients ALPHA(j)/BETA(j) may easily over- or +*> underflow, and BETA(j) may even be zero. Thus, the user +*> should avoid naively computing the ratio alpha/beta. +*> However, ALPHA will be always less than and usually +*> comparable with norm(A) in magnitude, and BETA always less +*> than and usually comparable with norm(B). +*> \endverbatim +*> +*> \param[out] VL +*> \verbatim +*> VL is COMPLEX array, dimension (LDVL,N) +*> If JOBVL = 'V', the left generalized eigenvectors u(j) are +*> stored one after another in the columns of VL, in the same +*> order as their eigenvalues. +*> Each eigenvector is scaled so the largest component has +*> abs(real part) + abs(imag. part) = 1. +*> Not referenced if JOBVL = 'N'. +*> \endverbatim +*> +*> \param[in] LDVL +*> \verbatim +*> LDVL is INTEGER +*> The leading dimension of the matrix VL. LDVL >= 1, and +*> if JOBVL = 'V', LDVL >= N. +*> \endverbatim +*> +*> \param[out] VR +*> \verbatim +*> VR is COMPLEX array, dimension (LDVR,N) +*> If JOBVR = 'V', the right generalized eigenvectors v(j) are +*> stored one after another in the columns of VR, in the same +*> order as their eigenvalues. +*> Each eigenvector is scaled so the largest component has +*> abs(real part) + abs(imag. part) = 1. +*> Not referenced if JOBVR = 'N'. +*> \endverbatim +*> +*> \param[in] LDVR +*> \verbatim +*> LDVR is INTEGER +*> The leading dimension of the matrix VR. LDVR >= 1, and +*> if JOBVR = 'V', LDVR >= N. +*> \endverbatim +*> +*> \param[out] WORK +*> \verbatim +*> WORK is COMPLEX array, dimension (MAX(1,LWORK)) +*> On exit, if INFO = 0, WORK(1) returns the optimal LWORK. +*> \endverbatim +*> +*> \param[in] LWORK +*> \verbatim +*> LWORK is INTEGER +*> The dimension of the array WORK. +*> +*> If LWORK = -1, then a workspace query is assumed; the routine +*> only calculates the optimal size of the WORK array, returns +*> this value as the first entry of the WORK array, and no error +*> message related to LWORK is issued by XERBLA. +*> \endverbatim +*> +*> \param[out] RWORK +*> \verbatim +*> RWORK is REAL array, dimension (8*N) +*> \endverbatim +*> +*> \param[out] INFO +*> \verbatim +*> INFO is INTEGER +*> = 0: successful exit +*> < 0: if INFO = -i, the i-th argument had an illegal value. +*> =1,...,N: +*> The QZ iteration failed. No eigenvectors have been +*> calculated, but ALPHA(j) and BETA(j) should be +*> correct for j=INFO+1,...,N. +*> > N: =N+1: other then QZ iteration failed in SHGEQZ, +*> =N+2: error return from STGEVC. +*> \endverbatim +* +* Authors: +* ======== +* +*> \author Univ. of Tennessee +*> \author Univ. of California Berkeley +*> \author Univ. of Colorado Denver +*> \author NAG Ltd. +* +*> \date January 2015 +* +*> \ingroup complexGEeigen +* +* ===================================================================== + SUBROUTINE CGGEV3( JOBVL, JOBVR, N, A, LDA, B, LDB, ALPHA, BETA, + $ VL, LDVL, VR, LDVR, WORK, LWORK, RWORK, INFO ) +* +* -- LAPACK driver routine (version 3.6.0) -- +* -- LAPACK is a software package provided by Univ. of Tennessee, -- +* -- Univ. of California Berkeley, Univ. of Colorado Denver and NAG Ltd..-- +* January 2015 +* +* .. Scalar Arguments .. + CHARACTER JOBVL, JOBVR + INTEGER INFO, LDA, LDB, LDVL, LDVR, LWORK, N +* .. +* .. Array Arguments .. + REAL RWORK( * ) + COMPLEX A( LDA, * ), ALPHA( * ), B( LDB, * ), + $ BETA( * ), VL( LDVL, * ), VR( LDVR, * ), + $ WORK( * ) +* .. +* +* ===================================================================== +* +* .. Parameters .. + REAL ZERO, ONE + PARAMETER ( ZERO = 0.0E0, ONE = 1.0E0 ) + COMPLEX CZERO, CONE + PARAMETER ( CZERO = ( 0.0E0, 0.0E0 ), + $ CONE = ( 1.0E0, 0.0E0 ) ) +* .. +* .. Local Scalars .. + LOGICAL ILASCL, ILBSCL, ILV, ILVL, ILVR, LQUERY + CHARACTER CHTEMP + INTEGER ICOLS, IERR, IHI, IJOBVL, IJOBVR, ILEFT, ILO, + $ IN, IRIGHT, IROWS, IRWRK, ITAU, IWRK, JC, JR, + $ LWKMIN, LWKOPT + REAL ANRM, ANRMTO, BIGNUM, BNRM, BNRMTO, EPS, + $ SMLNUM, TEMP + COMPLEX X +* .. +* .. Local Arrays .. + LOGICAL LDUMMA( 1 ) +* .. +* .. External Subroutines .. + EXTERNAL CGEQRF, CGGBAK, CGGBAL, CGGHD3, CHGEQZ, CLACPY, + $ CLASCL, CLASET, CTGEVC, CUNGQR, CUNMQR, SLABAD, + $ XERBLA +* .. +* .. External Functions .. + LOGICAL LSAME + REAL CLANGE, SLAMCH + EXTERNAL LSAME, CLANGE, SLAMCH +* .. +* .. Intrinsic Functions .. + INTRINSIC ABS, AIMAG, MAX, REAL, SQRT +* .. +* .. Statement Functions .. + REAL ABS1 +* .. +* .. Statement Function definitions .. + ABS1( X ) = ABS( REAL( X ) ) + ABS( AIMAG( X ) ) +* .. +* .. Executable Statements .. +* +* Decode the input arguments +* + IF( LSAME( JOBVL, 'N' ) ) THEN + IJOBVL = 1 + ILVL = .FALSE. + ELSE IF( LSAME( JOBVL, 'V' ) ) THEN + IJOBVL = 2 + ILVL = .TRUE. + ELSE + IJOBVL = -1 + ILVL = .FALSE. + END IF +* + IF( LSAME( JOBVR, 'N' ) ) THEN + IJOBVR = 1 + ILVR = .FALSE. + ELSE IF( LSAME( JOBVR, 'V' ) ) THEN + IJOBVR = 2 + ILVR = .TRUE. + ELSE + IJOBVR = -1 + ILVR = .FALSE. + END IF + ILV = ILVL .OR. ILVR +* +* Test the input arguments +* + INFO = 0 + LQUERY = ( LWORK.EQ.-1 ) + IF( IJOBVL.LE.0 ) THEN + INFO = -1 + ELSE IF( IJOBVR.LE.0 ) THEN + INFO = -2 + ELSE IF( N.LT.0 ) THEN + INFO = -3 + ELSE IF( LDA.LT.MAX( 1, N ) ) THEN + INFO = -5 + ELSE IF( LDB.LT.MAX( 1, N ) ) THEN + INFO = -7 + ELSE IF( LDVL.LT.1 .OR. ( ILVL .AND. LDVL.LT.N ) ) THEN + INFO = -11 + ELSE IF( LDVR.LT.1 .OR. ( ILVR .AND. LDVR.LT.N ) ) THEN + INFO = -13 + ELSE IF( LWORK.LT.MAX( 1, 2*N ) .AND. .NOT.LQUERY ) THEN + INFO = -15 + END IF +* +* Compute workspace +* + IF( INFO.EQ.0 ) THEN + CALL CGEQRF( N, N, B, LDB, WORK, WORK, -1, IERR ) + LWKOPT = MAX( N, N+INT( WORK( 1 ) ) ) + CALL CUNMQR( 'L', 'C', N, N, N, B, LDB, WORK, A, LDA, WORK, + $ -1, IERR ) + LWKOPT = MAX( LWKOPT, N+INT( WORK( 1 ) ) ) + IF( ILVL ) THEN + CALL CUNGQR( N, N, N, VL, LDVL, WORK, WORK, -1, IERR ) + LWKOPT = MAX( LWKOPT, N+INT( WORK( 1 ) ) ) + END IF + IF( ILV ) THEN + CALL CGGHD3( JOBVL, JOBVR, N, 1, N, A, LDA, B, LDB, VL, + $ LDVL, VR, LDVR, WORK, -1, IERR ) + LWKOPT = MAX( LWKOPT, N+INT( WORK( 1 ) ) ) + CALL CHGEQZ( 'S', JOBVL, JOBVR, N, 1, N, A, LDA, B, LDB, + $ ALPHA, BETA, VL, LDVL, VR, LDVR, WORK, + $ -1, WORK, IERR ) + LWKOPT = MAX( LWKOPT, N+INT( WORK( 1 ) ) ) + ELSE + CALL CGGHD3( 'N', 'N', N, 1, N, A, LDA, B, LDB, VL, LDVL, + $ VR, LDVR, WORK, -1, IERR ) + LWKOPT = MAX( LWKOPT, N+INT( WORK( 1 ) ) ) + CALL CHGEQZ( 'E', JOBVL, JOBVR, N, 1, N, A, LDA, B, LDB, + $ ALPHA, BETA, VL, LDVL, VR, LDVR, WORK, + $ -1, WORK, IERR ) + LWKOPT = MAX( LWKOPT, N+INT( WORK( 1 ) ) ) + END IF + WORK( 1 ) = CMPLX( LWKOPT ) + END IF +* + IF( INFO.NE.0 ) THEN + CALL XERBLA( 'CGGEV3 ', -INFO ) + RETURN + ELSE IF( LQUERY ) THEN + RETURN + END IF +* +* Quick return if possible +* + IF( N.EQ.0 ) + $ RETURN +* +* Get machine constants +* + EPS = SLAMCH( 'E' )*SLAMCH( 'B' ) + SMLNUM = SLAMCH( 'S' ) + BIGNUM = ONE / SMLNUM + CALL SLABAD( SMLNUM, BIGNUM ) + SMLNUM = SQRT( SMLNUM ) / EPS + BIGNUM = ONE / SMLNUM +* +* Scale A if max element outside range [SMLNUM,BIGNUM] +* + ANRM = CLANGE( 'M', N, N, A, LDA, RWORK ) + ILASCL = .FALSE. + IF( ANRM.GT.ZERO .AND. ANRM.LT.SMLNUM ) THEN + ANRMTO = SMLNUM + ILASCL = .TRUE. + ELSE IF( ANRM.GT.BIGNUM ) THEN + ANRMTO = BIGNUM + ILASCL = .TRUE. + END IF + IF( ILASCL ) + $ CALL CLASCL( 'G', 0, 0, ANRM, ANRMTO, N, N, A, LDA, IERR ) +* +* Scale B if max element outside range [SMLNUM,BIGNUM] +* + BNRM = CLANGE( 'M', N, N, B, LDB, RWORK ) + ILBSCL = .FALSE. + IF( BNRM.GT.ZERO .AND. BNRM.LT.SMLNUM ) THEN + BNRMTO = SMLNUM + ILBSCL = .TRUE. + ELSE IF( BNRM.GT.BIGNUM ) THEN + BNRMTO = BIGNUM + ILBSCL = .TRUE. + END IF + IF( ILBSCL ) + $ CALL CLASCL( 'G', 0, 0, BNRM, BNRMTO, N, N, B, LDB, IERR ) +* +* Permute the matrices A, B to isolate eigenvalues if possible +* + ILEFT = 1 + IRIGHT = N + 1 + IRWRK = IRIGHT + N + CALL CGGBAL( 'P', N, A, LDA, B, LDB, ILO, IHI, RWORK( ILEFT ), + $ RWORK( IRIGHT ), RWORK( IRWRK ), IERR ) +* +* Reduce B to triangular form (QR decomposition of B) +* + IROWS = IHI + 1 - ILO + IF( ILV ) THEN + ICOLS = N + 1 - ILO + ELSE + ICOLS = IROWS + END IF + ITAU = 1 + IWRK = ITAU + IROWS + CALL CGEQRF( IROWS, ICOLS, B( ILO, ILO ), LDB, WORK( ITAU ), + $ WORK( IWRK ), LWORK+1-IWRK, IERR ) +* +* Apply the orthogonal transformation to matrix A +* + CALL CUNMQR( 'L', 'C', IROWS, ICOLS, IROWS, B( ILO, ILO ), LDB, + $ WORK( ITAU ), A( ILO, ILO ), LDA, WORK( IWRK ), + $ LWORK+1-IWRK, IERR ) +* +* Initialize VL +* + IF( ILVL ) THEN + CALL CLASET( 'Full', N, N, CZERO, CONE, VL, LDVL ) + IF( IROWS.GT.1 ) THEN + CALL CLACPY( 'L', IROWS-1, IROWS-1, B( ILO+1, ILO ), LDB, + $ VL( ILO+1, ILO ), LDVL ) + END IF + CALL CUNGQR( IROWS, IROWS, IROWS, VL( ILO, ILO ), LDVL, + $ WORK( ITAU ), WORK( IWRK ), LWORK+1-IWRK, IERR ) + END IF +* +* Initialize VR +* + IF( ILVR ) + $ CALL CLASET( 'Full', N, N, CZERO, CONE, VR, LDVR ) +* +* Reduce to generalized Hessenberg form +* + IF( ILV ) THEN +* +* Eigenvectors requested -- work on whole matrix. +* + CALL CGGHD3( JOBVL, JOBVR, N, ILO, IHI, A, LDA, B, LDB, VL, + $ LDVL, VR, LDVR, WORK( IWRK ), LWORK+1-IWRK, + $ IERR ) + ELSE + CALL CGGHD3( 'N', 'N', IROWS, 1, IROWS, A( ILO, ILO ), LDA, + $ B( ILO, ILO ), LDB, VL, LDVL, VR, LDVR, + $ WORK( IWRK ), LWORK+1-IWRK, IERR ) + END IF +* +* Perform QZ algorithm (Compute eigenvalues, and optionally, the +* Schur form and Schur vectors) +* + IWRK = ITAU + IF( ILV ) THEN + CHTEMP = 'S' + ELSE + CHTEMP = 'E' + END IF + CALL CHGEQZ( CHTEMP, JOBVL, JOBVR, N, ILO, IHI, A, LDA, B, LDB, + $ ALPHA, BETA, VL, LDVL, VR, LDVR, WORK( IWRK ), + $ LWORK+1-IWRK, RWORK( IRWRK ), IERR ) + IF( IERR.NE.0 ) THEN + IF( IERR.GT.0 .AND. IERR.LE.N ) THEN + INFO = IERR + ELSE IF( IERR.GT.N .AND. IERR.LE.2*N ) THEN + INFO = IERR - N + ELSE + INFO = N + 1 + END IF + GO TO 70 + END IF +* +* Compute Eigenvectors +* + IF( ILV ) THEN + IF( ILVL ) THEN + IF( ILVR ) THEN + CHTEMP = 'B' + ELSE + CHTEMP = 'L' + END IF + ELSE + CHTEMP = 'R' + END IF +* + CALL CTGEVC( CHTEMP, 'B', LDUMMA, N, A, LDA, B, LDB, VL, LDVL, + $ VR, LDVR, N, IN, WORK( IWRK ), RWORK( IRWRK ), + $ IERR ) + IF( IERR.NE.0 ) THEN + INFO = N + 2 + GO TO 70 + END IF +* +* Undo balancing on VL and VR and normalization +* + IF( ILVL ) THEN + CALL CGGBAK( 'P', 'L', N, ILO, IHI, RWORK( ILEFT ), + $ RWORK( IRIGHT ), N, VL, LDVL, IERR ) + DO 30 JC = 1, N + TEMP = ZERO + DO 10 JR = 1, N + TEMP = MAX( TEMP, ABS1( VL( JR, JC ) ) ) + 10 CONTINUE + IF( TEMP.LT.SMLNUM ) + $ GO TO 30 + TEMP = ONE / TEMP + DO 20 JR = 1, N + VL( JR, JC ) = VL( JR, JC )*TEMP + 20 CONTINUE + 30 CONTINUE + END IF + IF( ILVR ) THEN + CALL CGGBAK( 'P', 'R', N, ILO, IHI, RWORK( ILEFT ), + $ RWORK( IRIGHT ), N, VR, LDVR, IERR ) + DO 60 JC = 1, N + TEMP = ZERO + DO 40 JR = 1, N + TEMP = MAX( TEMP, ABS1( VR( JR, JC ) ) ) + 40 CONTINUE + IF( TEMP.LT.SMLNUM ) + $ GO TO 60 + TEMP = ONE / TEMP + DO 50 JR = 1, N + VR( JR, JC ) = VR( JR, JC )*TEMP + 50 CONTINUE + 60 CONTINUE + END IF + END IF +* +* Undo scaling if necessary +* + 70 CONTINUE +* + IF( ILASCL ) + $ CALL CLASCL( 'G', 0, 0, ANRMTO, ANRM, N, 1, ALPHA, N, IERR ) +* + IF( ILBSCL ) + $ CALL CLASCL( 'G', 0, 0, BNRMTO, BNRM, N, 1, BETA, N, IERR ) +* + WORK( 1 ) = CMPLX( LWKOPT ) + RETURN +* +* End of CGGEV3 +* + END diff --git a/SRC/cgghd3.f b/SRC/cgghd3.f new file mode 100644 index 00000000..347d799f --- /dev/null +++ b/SRC/cgghd3.f @@ -0,0 +1,901 @@ +*> \brief \b CGGHD3 +* +* =========== DOCUMENTATION =========== +* +* Online html documentation available at +* http://www.netlib.org/lapack/explore-html/ +* +*> \htmlonly +*> Download CGGHD3 + dependencies +*> <a href="http://www.netlib.org/cgi-bin/netlibfiles.tgz?format=tgz&filename=/lapack/lapack_routine/cgghd3.f"> +*> [TGZ]</a> +*> <a href="http://www.netlib.org/cgi-bin/netlibfiles.zip?format=zip&filename=/lapack/lapack_routine/cgghd3.f"> +*> [ZIP]</a> +*> <a href="http://www.netlib.org/cgi-bin/netlibfiles.txt?format=txt&filename=/lapack/lapack_routine/cgghd3.f"> +*> [TXT]</a> +*> \endhtmlonly +* +* Definition: +* =========== +* +* SUBROUTINE CGGHD3( COMPQ, COMPZ, N, ILO, IHI, A, LDA, B, LDB, Q, +* $ LDQ, Z, LDZ, WORK, LWORK, INFO ) +* +* .. Scalar Arguments .. +* CHARACTER COMPQ, COMPZ +* INTEGER IHI, ILO, INFO, LDA, LDB, LDQ, LDZ, N, LWORK +* .. +* .. Array Arguments .. +* COMPLEX A( LDA, * ), B( LDB, * ), Q( LDQ, * ), +* $ Z( LDZ, * ), WORK( * ) +* .. +* +* +*> \par Purpose: +* ============= +*> +*> \verbatim +*> +*> +*> CGGHD3 reduces a pair of complex matrices (A,B) to generalized upper +*> Hessenberg form using unitary transformations, where A is a +*> general matrix and B is upper triangular. The form of the +*> generalized eigenvalue problem is +*> A*x = lambda*B*x, +*> and B is typically made upper triangular by computing its QR +*> factorization and moving the unitary matrix Q to the left side +*> of the equation. +*> +*> This subroutine simultaneously reduces A to a Hessenberg matrix H: +*> Q**H*A*Z = H +*> and transforms B to another upper triangular matrix T: +*> Q**H*B*Z = T +*> in order to reduce the problem to its standard form +*> H*y = lambda*T*y +*> where y = Z**H*x. +*> +*> The unitary matrices Q and Z are determined as products of Givens +*> rotations. They may either be formed explicitly, or they may be +*> postmultiplied into input matrices Q1 and Z1, so that +*> +*> Q1 * A * Z1**H = (Q1*Q) * H * (Z1*Z)**H +*> +*> Q1 * B * Z1**H = (Q1*Q) * T * (Z1*Z)**H +*> +*> If Q1 is the unitary matrix from the QR factorization of B in the +*> original equation A*x = lambda*B*x, then CGGHD3 reduces the original +*> problem to generalized Hessenberg form. +*> +*> This is a blocked variant of CGGHRD, using matrix-matrix +*> multiplications for parts of the computation to enhance performance. +*> \endverbatim +* +* Arguments: +* ========== +* +*> \param[in] COMPQ +*> \verbatim +*> COMPQ is CHARACTER*1 +*> = 'N': do not compute Q; +*> = 'I': Q is initialized to the unit matrix, and the +*> unitary matrix Q is returned; +*> = 'V': Q must contain a unitary matrix Q1 on entry, +*> and the product Q1*Q is returned. +*> \endverbatim +*> +*> \param[in] COMPZ +*> \verbatim +*> COMPZ is CHARACTER*1 +*> = 'N': do not compute Z; +*> = 'I': Z is initialized to the unit matrix, and the +*> unitary matrix Z is returned; +*> = 'V': Z must contain a unitary matrix Z1 on entry, +*> and the product Z1*Z is returned. +*> \endverbatim +*> +*> \param[in] N +*> \verbatim +*> N is INTEGER +*> The order of the matrices A and B. N >= 0. +*> \endverbatim +*> +*> \param[in] ILO +*> \verbatim +*> ILO is INTEGER +*> \endverbatim +*> +*> \param[in] IHI +*> \verbatim +*> IHI is INTEGER +*> +*> ILO and IHI mark the rows and columns of A which are to be +*> reduced. It is assumed that A is already upper triangular +*> in rows and columns 1:ILO-1 and IHI+1:N. ILO and IHI are +*> normally set by a previous call to CGGBAL; otherwise they +*> should be set to 1 and N respectively. +*> 1 <= ILO <= IHI <= N, if N > 0; ILO=1 and IHI=0, if N=0. +*> \endverbatim +*> +*> \param[in,out] A +*> \verbatim +*> A is COMPLEX array, dimension (LDA, N) +*> On entry, the N-by-N general matrix to be reduced. +*> On exit, the upper triangle and the first subdiagonal of A +*> are overwritten with the upper Hessenberg matrix H, and the +*> rest is set to zero. +*> \endverbatim +*> +*> \param[in] LDA +*> \verbatim +*> LDA is INTEGER +*> The leading dimension of the array A. LDA >= max(1,N). +*> \endverbatim +*> +*> \param[in,out] B +*> \verbatim +*> B is COMPLEX array, dimension (LDB, N) +*> On entry, the N-by-N upper triangular matrix B. +*> On exit, the upper triangular matrix T = Q**H B Z. The +*> elements below the diagonal are set to zero. +*> \endverbatim +*> +*> \param[in] LDB +*> \verbatim +*> LDB is INTEGER +*> The leading dimension of the array B. LDB >= max(1,N). +*> \endverbatim +*> +*> \param[in,out] Q +*> \verbatim +*> Q is COMPLEX array, dimension (LDQ, N) +*> On entry, if COMPQ = 'V', the unitary matrix Q1, typically +*> from the QR factorization of B. +*> On exit, if COMPQ='I', the unitary matrix Q, and if +*> COMPQ = 'V', the product Q1*Q. +*> Not referenced if COMPQ='N'. +*> \endverbatim +*> +*> \param[in] LDQ +*> \verbatim +*> LDQ is INTEGER +*> The leading dimension of the array Q. +*> LDQ >= N if COMPQ='V' or 'I'; LDQ >= 1 otherwise. +*> \endverbatim +*> +*> \param[in,out] Z +*> \verbatim +*> Z is COMPLEX array, dimension (LDZ, N) +*> On entry, if COMPZ = 'V', the unitary matrix Z1. +*> On exit, if COMPZ='I', the unitary matrix Z, and if +*> COMPZ = 'V', the product Z1*Z. +*> Not referenced if COMPZ='N'. +*> \endverbatim +*> +*> \param[in] LDZ +*> \verbatim +*> LDZ is INTEGER +*> The leading dimension of the array Z. +*> LDZ >= N if COMPZ='V' or 'I'; LDZ >= 1 otherwise. +*> \endverbatim +*> +*> \param[out] WORK +*> \verbatim +*> WORK is COMPLEX array, dimension (LWORK) +*> On exit, if INFO = 0, WORK(1) returns the optimal LWORK. +*> \endverbatim +*> +*> \param[in] LWORK +*> \verbatim +*> LWORK is INTEGER +*> The length of the array WORK. LWORK >= 1. +*> For optimum performance LWORK >= 6*N*NB, where NB is the +*> optimal blocksize. +*> +*> If LWORK = -1, then a workspace query is assumed; the routine +*> only calculates the optimal size of the WORK array, returns +*> this value as the first entry of the WORK array, and no error +*> message related to LWORK is issued by XERBLA. +*> \endverbatim +*> +*> \param[out] INFO +*> \verbatim +*> INFO is INTEGER +*> = 0: successful exit. +*> < 0: if INFO = -i, the i-th argument had an illegal value. +*> \endverbatim +* +* Authors: +* ======== +* +*> \author Univ. of Tennessee +*> \author Univ. of California Berkeley +*> \author Univ. of Colorado Denver +*> \author NAG Ltd. +* +*> \date January 2015 +* +*> \ingroup complexOTHERcomputational +* +*> \par Further Details: +* ===================== +*> +*> \verbatim +*> +*> This routine reduces A to Hessenberg form and maintains B in +*> using a blocked variant of Moler and Stewart's original algorithm, +*> as described by Kagstrom, Kressner, Quintana-Orti, and Quintana-Orti +*> (BIT 2008). +*> \endverbatim +*> +* ===================================================================== + SUBROUTINE CGGHD3( COMPQ, COMPZ, N, ILO, IHI, A, LDA, B, LDB, Q, + $ LDQ, Z, LDZ, WORK, LWORK, INFO ) +* +* -- LAPACK computational routine (version 3.6.0) -- +* -- LAPACK is a software package provided by Univ. of Tennessee, -- +* -- Univ. of California Berkeley, Univ. of Colorado Denver and NAG Ltd..-- +* January 2015 +* +* + IMPLICIT NONE +* +* .. Scalar Arguments .. + CHARACTER COMPQ, COMPZ + INTEGER IHI, ILO, INFO, LDA, LDB, LDQ, LDZ, N, LWORK +* .. +* .. Array Arguments .. + COMPLEX A( LDA, * ), B( LDB, * ), Q( LDQ, * ), + $ Z( LDZ, * ), WORK( * ) +* .. +* +* ===================================================================== +* +* .. Parameters .. + COMPLEX CONE, CZERO + PARAMETER ( CONE = ( 1.0E+0, 0.0E+0 ), + $ CZERO = ( 0.0E+0, 0.0E+0 ) ) +* .. +* .. Local Scalars .. + LOGICAL BLK22, INITQ, INITZ, LQUERY, WANTQ, WANTZ + CHARACTER*1 COMPQ2, COMPZ2 + INTEGER COLA, I, IERR, J, J0, JCOL, JJ, JROW, K, + $ KACC22, LEN, LWKOPT, N2NB, NB, NBLST, NBMIN, + $ NH, NNB, NX, PPW, PPWO, PW, TOP, TOPQ + REAL C + COMPLEX C1, C2, CTEMP, S, S1, S2, TEMP, TEMP1, TEMP2, + $ TEMP3 +* .. +* .. External Functions .. + LOGICAL LSAME + INTEGER ILAENV + EXTERNAL ILAENV, LSAME +* .. +* .. External Subroutines .. + EXTERNAL CGGHRD, CLARTG, CLASET, CUNM22, CROT, XERBLA +* .. +* .. Intrinsic Functions .. + INTRINSIC REAL, CMPLX, CONJG, MAX +* .. +* .. Executable Statements .. +* +* Decode and test the input parameters. +* + INFO = 0 + NB = ILAENV( 1, 'CGGHD3', ' ', N, ILO, IHI, -1 ) + LWKOPT = 6*N*NB + WORK( 1 ) = CMPLX( LWKOPT ) + INITQ = LSAME( COMPQ, 'I' ) + WANTQ = INITQ .OR. LSAME( COMPQ, 'V' ) + INITZ = LSAME( COMPZ, 'I' ) + WANTZ = INITZ .OR. LSAME( COMPZ, 'V' ) + LQUERY = ( LWORK.EQ.-1 ) +* + IF( .NOT.LSAME( COMPQ, 'N' ) .AND. .NOT.WANTQ ) THEN + INFO = -1 + ELSE IF( .NOT.LSAME( COMPZ, 'N' ) .AND. .NOT.WANTZ ) THEN + INFO = -2 + ELSE IF( N.LT.0 ) THEN + INFO = -3 + ELSE IF( ILO.LT.1 ) THEN + INFO = -4 + ELSE IF( IHI.GT.N .OR. IHI.LT.ILO-1 ) THEN + INFO = -5 + ELSE IF( LDA.LT.MAX( 1, N ) ) THEN + INFO = -7 + ELSE IF( LDB.LT.MAX( 1, N ) ) THEN + INFO = -9 + ELSE IF( ( WANTQ .AND. LDQ.LT.N ) .OR. LDQ.LT.1 ) THEN + INFO = -11 + ELSE IF( ( WANTZ .AND. LDZ.LT.N ) .OR. LDZ.LT.1 ) THEN + INFO = -13 + ELSE IF( LWORK.LT.1 .AND. .NOT.LQUERY ) THEN + INFO = -15 + END IF + IF( INFO.NE.0 ) THEN + CALL XERBLA( 'CGGHD3', -INFO ) + RETURN + ELSE IF( LQUERY ) THEN + RETURN + END IF +* +* Initialize Q and Z if desired. +* + IF( INITQ ) + $ CALL CLASET( 'All', N, N, CZERO, CONE, Q, LDQ ) + IF( INITZ ) + $ CALL CLASET( 'All', N, N, CZERO, CONE, Z, LDZ ) +* +* Zero out lower triangle of B. +* + IF( N.GT.1 ) + $ CALL CLASET( 'Lower', N-1, N-1, CZERO, CZERO, B(2, 1), LDB ) +* +* Quick return if possible +* + NH = IHI - ILO + 1 + IF( NH.LE.1 ) THEN + WORK( 1 ) = CONE + RETURN + END IF +* +* Determine the blocksize. +* + NBMIN = ILAENV( 2, 'CGGHD3', ' ', N, ILO, IHI, -1 ) + IF( NB.GT.1 .AND. NB.LT.NH ) THEN +* +* Determine when to use unblocked instead of blocked code. +* + NX = MAX( NB, ILAENV( 3, 'CGGHD3', ' ', N, ILO, IHI, -1 ) ) + IF( NX.LT.NH ) THEN +* +* Determine if workspace is large enough for blocked code. +* + IF( LWORK.LT.LWKOPT ) THEN +* +* Not enough workspace to use optimal NB: determine the +* minimum value of NB, and reduce NB or force use of +* unblocked code. +* + NBMIN = MAX( 2, ILAENV( 2, 'CGGHD3', ' ', N, ILO, IHI, + $ -1 ) ) + IF( LWORK.GE.6*N*NBMIN ) THEN + NB = LWORK / ( 6*N ) + ELSE + NB = 1 + END IF + END IF + END IF + END IF +* + IF( NB.LT.NBMIN .OR. NB.GE.NH ) THEN +* +* Use unblocked code below +* + JCOL = ILO +* + ELSE +* +* Use blocked code +* + KACC22 = ILAENV( 16, 'CGGHD3', ' ', N, ILO, IHI, -1 ) + BLK22 = KACC22.EQ.2 + DO JCOL = ILO, IHI-2, NB + NNB = MIN( NB, IHI-JCOL-1 ) +* +* Initialize small unitary factors that will hold the +* accumulated Givens rotations in workspace. +* N2NB denotes the number of 2*NNB-by-2*NNB factors +* NBLST denotes the (possibly smaller) order of the last +* factor. +* + N2NB = ( IHI-JCOL-1 ) / NNB - 1 + NBLST = IHI - JCOL - N2NB*NNB + CALL CLASET( 'All', NBLST, NBLST, CZERO, CONE, WORK, NBLST ) + PW = NBLST * NBLST + 1 + DO I = 1, N2NB + CALL CLASET( 'All', 2*NNB, 2*NNB, CZERO, CONE, + $ WORK( PW ), 2*NNB ) + PW = PW + 4*NNB*NNB + END DO +* +* Reduce columns JCOL:JCOL+NNB-1 of A to Hessenberg form. +* + DO J = JCOL, JCOL+NNB-1 +* +* Reduce Jth column of A. Store cosines and sines in Jth +* column of A and B, respectively. +* + DO I = IHI, J+2, -1 + TEMP = A( I-1, J ) + CALL CLARTG( TEMP, A( I, J ), C, S, A( I-1, J ) ) + A( I, J ) = CMPLX( C ) + B( I, J ) = S + END DO +* +* Accumulate Givens rotations into workspace array. +* + PPW = ( NBLST + 1 )*( NBLST - 2 ) - J + JCOL + 1 + LEN = 2 + J - JCOL + JROW = J + N2NB*NNB + 2 + DO I = IHI, JROW, -1 + CTEMP = A( I, J ) + S = B( I, J ) + DO JJ = PPW, PPW+LEN-1 + TEMP = WORK( JJ + NBLST ) + WORK( JJ + NBLST ) = CTEMP*TEMP - S*WORK( JJ ) + WORK( JJ ) = CONJG( S )*TEMP + CTEMP*WORK( JJ ) + END DO + LEN = LEN + 1 + PPW = PPW - NBLST - 1 + END DO +* + PPWO = NBLST*NBLST + ( NNB+J-JCOL-1 )*2*NNB + NNB + J0 = JROW - NNB + DO JROW = J0, J+2, -NNB + PPW = PPWO + LEN = 2 + J - JCOL + DO I = JROW+NNB-1, JROW, -1 + CTEMP = A( I, J ) + S = B( I, J ) + DO JJ = PPW, PPW+LEN-1 + TEMP = WORK( JJ + 2*NNB ) + WORK( JJ + 2*NNB ) = CTEMP*TEMP - S*WORK( JJ ) + WORK( JJ ) = CONJG( S )*TEMP + CTEMP*WORK( JJ ) + END DO + LEN = LEN + 1 + PPW = PPW - 2*NNB - 1 + END DO + PPWO = PPWO + 4*NNB*NNB + END DO +* +* TOP denotes the number of top rows in A and B that will +* not be updated during the next steps. +* + IF( JCOL.LE.2 ) THEN + TOP = 0 + ELSE + TOP = JCOL + END IF +* +* Propagate transformations through B and replace stored +* left sines/cosines by right sines/cosines. +* + DO JJ = N, J+1, -1 +* +* Update JJth column of B. +* + DO I = MIN( JJ+1, IHI ), J+2, -1 + CTEMP = A( I, J ) + S = B( I, J ) + TEMP = B( I, JJ ) + B( I, JJ ) = CTEMP*TEMP - CONJG( S )*B( I-1, JJ ) + B( I-1, JJ ) = S*TEMP + CTEMP*B( I-1, JJ ) + END DO +* +* Annihilate B( JJ+1, JJ ). +* + IF( JJ.LT.IHI ) THEN + TEMP = B( JJ+1, JJ+1 ) + CALL CLARTG( TEMP, B( JJ+1, JJ ), C, S, + $ B( JJ+1, JJ+1 ) ) + B( JJ+1, JJ ) = CZERO + CALL CROT( JJ-TOP, B( TOP+1, JJ+1 ), 1, + $ B( TOP+1, JJ ), 1, C, S ) + A( JJ+1, J ) = CMPLX( C ) + B( JJ+1, J ) = -CONJG( S ) + END IF + END DO +* +* Update A by transformations from right. +* + JJ = MOD( IHI-J-1, 3 ) + DO I = IHI-J-3, JJ+1, -3 + CTEMP = A( J+1+I, J ) + S = -B( J+1+I, J ) + C1 = A( J+2+I, J ) + S1 = -B( J+2+I, J ) + C2 = A( J+3+I, J ) + S2 = -B( J+3+I, J ) +* + DO K = TOP+1, IHI + TEMP = A( K, J+I ) + TEMP1 = A( K, J+I+1 ) + TEMP2 = A( K, J+I+2 ) + TEMP3 = A( K, J+I+3 ) + A( K, J+I+3 ) = C2*TEMP3 + CONJG( S2 )*TEMP2 + TEMP2 = -S2*TEMP3 + C2*TEMP2 + A( K, J+I+2 ) = C1*TEMP2 + CONJG( S1 )*TEMP1 + TEMP1 = -S1*TEMP2 + C1*TEMP1 + A( K, J+I+1 ) = CTEMP*TEMP1 + CONJG( S )*TEMP + A( K, J+I ) = -S*TEMP1 + CTEMP*TEMP + END DO + END DO +* + IF( JJ.GT.0 ) THEN + DO I = JJ, 1, -1 + C = DBLE( A( J+1+I, J ) ) + CALL CROT( IHI-TOP, A( TOP+1, J+I+1 ), 1, + $ A( TOP+1, J+I ), 1, C, + $ -CONJG( B( J+1+I, J ) ) ) + END DO + END IF +* +* Update (J+1)th column of A by transformations from left. +* + IF ( J .LT. JCOL + NNB - 1 ) THEN + LEN = 1 + J - JCOL +* +* Multiply with the trailing accumulated unitary +* matrix, which takes the form +* +* [ U11 U12 ] +* U = [ ], +* [ U21 U22 ] +* +* where U21 is a LEN-by-LEN matrix and U12 is lower +* triangular. +* + JROW = IHI - NBLST + 1 + CALL CGEMV( 'Conjugate', NBLST, LEN, CONE, WORK, + $ NBLST, A( JROW, J+1 ), 1, CZERO, + $ WORK( PW ), 1 ) + PPW = PW + LEN + DO I = JROW, JROW+NBLST-LEN-1 + WORK( PPW ) = A( I, J+1 ) + PPW = PPW + 1 + END DO + CALL CTRMV( 'Lower', 'Conjugate', 'Non-unit', + $ NBLST-LEN, WORK( LEN*NBLST + 1 ), NBLST, + $ WORK( PW+LEN ), 1 ) + CALL CGEMV( 'Conjugate', LEN, NBLST-LEN, CONE, + $ WORK( (LEN+1)*NBLST - LEN + 1 ), NBLST, + $ A( JROW+NBLST-LEN, J+1 ), 1, CONE, + $ WORK( PW+LEN ), 1 ) + PPW = PW + DO I = JROW, JROW+NBLST-1 + A( I, J+1 ) = WORK( PPW ) + PPW = PPW + 1 + END DO +* +* Multiply with the other accumulated unitary +* matrices, which take the form +* +* [ U11 U12 0 ] +* [ ] +* U = [ U21 U22 0 ], +* [ ] +* [ 0 0 I ] +* +* where I denotes the (NNB-LEN)-by-(NNB-LEN) identity +* matrix, U21 is a LEN-by-LEN upper triangular matrix +* and U12 is an NNB-by-NNB lower triangular matrix. +* + PPWO = 1 + NBLST*NBLST + J0 = JROW - NNB + DO JROW = J0, JCOL+1, -NNB + PPW = PW + LEN + DO I = JROW, JROW+NNB-1 + WORK( PPW ) = A( I, J+1 ) + PPW = PPW + 1 + END DO + PPW = PW + DO I = JROW+NNB, JROW+NNB+LEN-1 + WORK( PPW ) = A( I, J+1 ) + PPW = PPW + 1 + END DO + CALL CTRMV( 'Upper', 'Conjugate', 'Non-unit', LEN, + $ WORK( PPWO + NNB ), 2*NNB, WORK( PW ), + $ 1 ) + CALL CTRMV( 'Lower', 'Conjugate', 'Non-unit', NNB, + $ WORK( PPWO + 2*LEN*NNB ), + $ 2*NNB, WORK( PW + LEN ), 1 ) + CALL CGEMV( 'Conjugate', NNB, LEN, CONE, + $ WORK( PPWO ), 2*NNB, A( JROW, J+1 ), 1, + $ CONE, WORK( PW ), 1 ) + CALL CGEMV( 'Conjugate', LEN, NNB, CONE, + $ WORK( PPWO + 2*LEN*NNB + NNB ), 2*NNB, + $ A( JROW+NNB, J+1 ), 1, CONE, + $ WORK( PW+LEN ), 1 ) + PPW = PW + DO I = JROW, JROW+LEN+NNB-1 + A( I, J+1 ) = WORK( PPW ) + PPW = PPW + 1 + END DO + PPWO = PPWO + 4*NNB*NNB + END DO + END IF + END DO +* +* Apply accumulated unitary matrices to A. +* + COLA = N - JCOL - NNB + 1 + J = IHI - NBLST + 1 + CALL CGEMM( 'Conjugate', 'No Transpose', NBLST, + $ COLA, NBLST, CONE, WORK, NBLST, + $ A( J, JCOL+NNB ), LDA, CZERO, WORK( PW ), + $ NBLST ) + CALL CLACPY( 'All', NBLST, COLA, WORK( PW ), NBLST, + $ A( J, JCOL+NNB ), LDA ) + PPWO = NBLST*NBLST + 1 + J0 = J - NNB + DO J = J0, JCOL+1, -NNB + IF ( BLK22 ) THEN +* +* Exploit the structure of +* +* [ U11 U12 ] +* U = [ ] +* [ U21 U22 ], +* +* where all blocks are NNB-by-NNB, U21 is upper +* triangular and U12 is lower triangular. +* + CALL CUNM22( 'Left', 'Conjugate', 2*NNB, COLA, NNB, + $ NNB, WORK( PPWO ), 2*NNB, + $ A( J, JCOL+NNB ), LDA, WORK( PW ), + $ LWORK-PW+1, IERR ) + ELSE +* +* Ignore the structure of U. +* + CALL CGEMM( 'Conjugate', 'No Transpose', 2*NNB, + $ COLA, 2*NNB, CONE, WORK( PPWO ), 2*NNB, + $ A( J, JCOL+NNB ), LDA, CZERO, WORK( PW ), + $ 2*NNB ) + CALL CLACPY( 'All', 2*NNB, COLA, WORK( PW ), 2*NNB, + $ A( J, JCOL+NNB ), LDA ) + END IF + PPWO = PPWO + 4*NNB*NNB + END DO +* +* Apply accumulated unitary matrices to Q. +* + IF( WANTQ ) THEN + J = IHI - NBLST + 1 + IF ( INITQ ) THEN + TOPQ = MAX( 2, J - JCOL + 1 ) + NH = IHI - TOPQ + 1 + ELSE + TOPQ = 1 + NH = N + END IF + CALL CGEMM( 'No Transpose', 'No Transpose', NH, + $ NBLST, NBLST, CONE, Q( TOPQ, J ), LDQ, + $ WORK, NBLST, CZERO, WORK( PW ), NH ) + CALL CLACPY( 'All', NH, NBLST, WORK( PW ), NH, + $ Q( TOPQ, J ), LDQ ) + PPWO = NBLST*NBLST + 1 + J0 = J - NNB + DO J = J0, JCOL+1, -NNB + IF ( INITQ ) THEN + TOPQ = MAX( 2, J - JCOL + 1 ) + NH = IHI - TOPQ + 1 + END IF + IF ( BLK22 ) THEN +* +* Exploit the structure of U. +* + CALL CUNM22( 'Right', 'No Transpose', NH, 2*NNB, + $ NNB, NNB, WORK( PPWO ), 2*NNB, + $ Q( TOPQ, J ), LDQ, WORK( PW ), + $ LWORK-PW+1, IERR ) + ELSE +* +* Ignore the structure of U. +* + CALL CGEMM( 'No Transpose', 'No Transpose', NH, + $ 2*NNB, 2*NNB, CONE, Q( TOPQ, J ), LDQ, + $ WORK( PPWO ), 2*NNB, CZERO, WORK( PW ), + $ NH ) + CALL CLACPY( 'All', NH, 2*NNB, WORK( PW ), NH, + $ Q( TOPQ, J ), LDQ ) + END IF + PPWO = PPWO + 4*NNB*NNB + END DO + END IF +* +* Accumulate right Givens rotations if required. +* + IF ( WANTZ .OR. TOP.GT.0 ) THEN +* +* Initialize small unitary factors that will hold the +* accumulated Givens rotations in workspace. +* + CALL CLASET( 'All', NBLST, NBLST, CZERO, CONE, WORK, + $ NBLST ) + PW = NBLST * NBLST + 1 + DO I = 1, N2NB + CALL CLASET( 'All', 2*NNB, 2*NNB, CZERO, CONE, + $ WORK( PW ), 2*NNB ) + PW = PW + 4*NNB*NNB + END DO +* +* Accumulate Givens rotations into workspace array. +* + DO J = JCOL, JCOL+NNB-1 + PPW = ( NBLST + 1 )*( NBLST - 2 ) - J + JCOL + 1 + LEN = 2 + J - JCOL + JROW = J + N2NB*NNB + 2 + DO I = IHI, JROW, -1 + CTEMP = A( I, J ) + A( I, J ) = CZERO + S = B( I, J ) + B( I, J ) = CZERO + DO JJ = PPW, PPW+LEN-1 + TEMP = WORK( JJ + NBLST ) + WORK( JJ + NBLST ) = CTEMP*TEMP - + $ CONJG( S )*WORK( JJ ) + WORK( JJ ) = S*TEMP + CTEMP*WORK( JJ ) + END DO + LEN = LEN + 1 + PPW = PPW - NBLST - 1 + END DO +* + PPWO = NBLST*NBLST + ( NNB+J-JCOL-1 )*2*NNB + NNB + J0 = JROW - NNB + DO JROW = J0, J+2, -NNB + PPW = PPWO + LEN = 2 + J - JCOL + DO I = JROW+NNB-1, JROW, -1 + CTEMP = A( I, J ) + A( I, J ) = CZERO + S = B( I, J ) + B( I, J ) = CZERO + DO JJ = PPW, PPW+LEN-1 + TEMP = WORK( JJ + 2*NNB ) + WORK( JJ + 2*NNB ) = CTEMP*TEMP - + $ CONJG( S )*WORK( JJ ) + WORK( JJ ) = S*TEMP + CTEMP*WORK( JJ ) + END DO + LEN = LEN + 1 + PPW = PPW - 2*NNB - 1 + END DO + PPWO = PPWO + 4*NNB*NNB + END DO + END DO + ELSE + + DO J = ILO, ILO+NNB + DO I = J+2, IHI + A( I, J ) = CZERO + B( I, J ) = CZERO + END DO + END DO + END IF +* +* Apply accumulated unitary matrices to A and B. +* + IF ( TOP.GT.0 ) THEN + J = IHI - NBLST + 1 + CALL CGEMM( 'No Transpose', 'No Transpose', TOP, + $ NBLST, NBLST, CONE, A( 1, J ), LDA, + $ WORK, NBLST, CZERO, WORK( PW ), TOP ) + CALL CLACPY( 'All', TOP, NBLST, WORK( PW ), TOP, + $ A( 1, J ), LDA ) + PPWO = NBLST*NBLST + 1 + J0 = J - NNB + DO J = J0, JCOL+1, -NNB + IF ( BLK22 ) THEN +* +* Exploit the structure of U. +* + CALL CUNM22( 'Right', 'No Transpose', TOP, 2*NNB, + $ NNB, NNB, WORK( PPWO ), 2*NNB, + $ A( 1, J ), LDA, WORK( PW ), + $ LWORK-PW+1, IERR ) + ELSE +* +* Ignore the structure of U. +* + CALL CGEMM( 'No Transpose', 'No Transpose', TOP, + $ 2*NNB, 2*NNB, CONE, A( 1, J ), LDA, + $ WORK( PPWO ), 2*NNB, CZERO, + $ WORK( PW ), TOP ) + CALL CLACPY( 'All', TOP, 2*NNB, WORK( PW ), TOP, + $ A( 1, J ), LDA ) + END IF + PPWO = PPWO + 4*NNB*NNB + END DO +* + J = IHI - NBLST + 1 + CALL CGEMM( 'No Transpose', 'No Transpose', TOP, + $ NBLST, NBLST, CONE, B( 1, J ), LDB, + $ WORK, NBLST, CZERO, WORK( PW ), TOP ) + CALL CLACPY( 'All', TOP, NBLST, WORK( PW ), TOP, + $ B( 1, J ), LDB ) + PPWO = NBLST*NBLST + 1 + J0 = J - NNB + DO J = J0, JCOL+1, -NNB + IF ( BLK22 ) THEN +* +* Exploit the structure of U. +* + CALL CUNM22( 'Right', 'No Transpose', TOP, 2*NNB, + $ NNB, NNB, WORK( PPWO ), 2*NNB, + $ B( 1, J ), LDA, WORK( PW ), + $ LWORK-PW+1, IERR ) + ELSE +* +* Ignore the structure of U. +* + CALL CGEMM( 'No Transpose', 'No Transpose', TOP, + $ 2*NNB, 2*NNB, CONE, B( 1, J ), LDB, + $ WORK( PPWO ), 2*NNB, CZERO, + $ WORK( PW ), TOP ) + CALL CLACPY( 'All', TOP, 2*NNB, WORK( PW ), TOP, + $ B( 1, J ), LDB ) + END IF + PPWO = PPWO + 4*NNB*NNB + END DO + END IF +* +* Apply accumulated unitary matrices to Z. +* + IF( WANTZ ) THEN + J = IHI - NBLST + 1 + IF ( INITQ ) THEN + TOPQ = MAX( 2, J - JCOL + 1 ) + NH = IHI - TOPQ + 1 + ELSE + TOPQ = 1 + NH = N + END IF + CALL CGEMM( 'No Transpose', 'No Transpose', NH, + $ NBLST, NBLST, CONE, Z( TOPQ, J ), LDZ, + $ WORK, NBLST, CZERO, WORK( PW ), NH ) + CALL CLACPY( 'All', NH, NBLST, WORK( PW ), NH, + $ Z( TOPQ, J ), LDZ ) + PPWO = NBLST*NBLST + 1 + J0 = J - NNB + DO J = J0, JCOL+1, -NNB + IF ( INITQ ) THEN + TOPQ = MAX( 2, J - JCOL + 1 ) + NH = IHI - TOPQ + 1 + END IF + IF ( BLK22 ) THEN +* +* Exploit the structure of U. +* + CALL CUNM22( 'Right', 'No Transpose', NH, 2*NNB, + $ NNB, NNB, WORK( PPWO ), 2*NNB, + $ Z( TOPQ, J ), LDZ, WORK( PW ), + $ LWORK-PW+1, IERR ) + ELSE +* +* Ignore the structure of U. +* + CALL CGEMM( 'No Transpose', 'No Transpose', NH, + $ 2*NNB, 2*NNB, CONE, Z( TOPQ, J ), LDZ, + $ WORK( PPWO ), 2*NNB, CZERO, WORK( PW ), + $ NH ) + CALL CLACPY( 'All', NH, 2*NNB, WORK( PW ), NH, + $ Z( TOPQ, J ), LDZ ) + END IF + PPWO = PPWO + 4*NNB*NNB + END DO + END IF + END DO + END IF +* +* Use unblocked code to reduce the rest of the matrix +* Avoid re-initialization of modified Q and Z. +* + COMPQ2 = COMPQ + COMPZ2 = COMPZ + IF ( JCOL.NE.ILO ) THEN + IF ( WANTQ ) + $ COMPQ2 = 'V' + IF ( WANTZ ) + $ COMPZ2 = 'V' + END IF +* + IF ( JCOL.LT.IHI ) + $ CALL CGGHRD( COMPQ2, COMPZ2, N, JCOL, IHI, A, LDA, B, LDB, Q, + $ LDQ, Z, LDZ, IERR ) + WORK( 1 ) = CMPLX( LWKOPT ) +* + RETURN +* +* End of CGGHD3 +* + END diff --git a/SRC/cunm22.f b/SRC/cunm22.f new file mode 100644 index 00000000..85c2269a --- /dev/null +++ b/SRC/cunm22.f @@ -0,0 +1,440 @@ +*> \brief \b CUNM22 multiplies a general matrix by a banded unitary matrix. +* +* =========== DOCUMENTATION =========== +* +* Online html documentation available at +* http://www.netlib.org/lapack/explore-html/ +* +*> \htmlonly +*> Download CUNM22 + dependencies +*> <a href="http://www.netlib.org/cgi-bin/netlibfiles.tgz?format=tgz&filename=/lapack/lapack_routine/cunm22.f"> +*> [TGZ]</a> +*> <a href="http://www.netlib.org/cgi-bin/netlibfiles.zip?format=zip&filename=/lapack/lapack_routine/cunm22.f"> +*> [ZIP]</a> +*> <a href="http://www.netlib.org/cgi-bin/netlibfiles.txt?format=txt&filename=/lapack/lapack_routine/cunm22.f"> +*> [TXT]</a> +*> \endhtmlonly +* +* Definition: +* =========== +* +* SUBROUTINE CUNM22( SIDE, TRANS, M, N, N1, N2, Q, LDQ, C, LDC, +* $ WORK, LWORK, INFO ) +* +* .. Scalar Arguments .. +* CHARACTER SIDE, TRANS +* INTEGER M, N, N1, N2, LDQ, LDC, LWORK, INFO +* .. +* .. Array Arguments .. +* COMPLEX Q( LDQ, * ), C( LDC, * ), WORK( * ) +* .. +* +*> \par Purpose +* ============ +*> +*> \verbatim +*> +*> CUNM22 overwrites the general complex M-by-N matrix C with +*> +*> SIDE = 'L' SIDE = 'R' +*> TRANS = 'N': Q * C C * Q +*> TRANS = 'C': Q**H * C C * Q**H +*> +*> where Q is a complex unitary matrix of order NQ, with NQ = M if +*> SIDE = 'L' and NQ = N if SIDE = 'R'. +*> The unitary matrix Q processes a 2-by-2 block structure +*> +*> [ Q11 Q12 ] +*> Q = [ ] +*> [ Q21 Q22 ], +*> +*> where Q12 is an N1-by-N1 lower triangular matrix and Q21 is an +*> N2-by-N2 upper triangular matrix. +*> \endverbatim +* +* Arguments +* ========= +* +*> \param[in] SIDE +*> \verbatim +*> SIDE is CHARACTER*1 +*> = 'L': apply Q or Q**H from the Left; +*> = 'R': apply Q or Q**H from the Right. +*> \endverbatim +*> +*> \param[in] TRANS +*> \verbatim +*> TRANS is CHARACTER*1 +*> = 'N': apply Q (No transpose); +*> = 'C': apply Q**H (Conjugate transpose). +*> \endverbatim +*> +*> \param[in] M +*> \verbatim +*> M is INTEGER +*> The number of rows of the matrix C. M >= 0. +*> \endverbatim +*> +*> \param[in] N +*> \verbatim +*> N is INTEGER +*> The number of columns of the matrix C. N >= 0. +*> \endverbatim +*> +*> \param[in] N1 +*> \param[in] N2 +*> \verbatim +*> N1 is INTEGER +*> N2 is INTEGER +*> The dimension of Q12 and Q21, respectively. N1, N2 >= 0. +*> The following requirement must be satisfied: +*> N1 + N2 = M if SIDE = 'L' and N1 + N2 = N if SIDE = 'R'. +*> \endverbatim +*> +*> \param[in] Q +*> \verbatim +*> Q is COMPLEX array, dimension +*> (LDQ,M) if SIDE = 'L' +*> (LDQ,N) if SIDE = 'R' +*> \endverbatim +*> +*> \param[in] LDQ +*> \verbatim +*> LDQ is INTEGER +*> The leading dimension of the array Q. +*> LDQ >= max(1,M) if SIDE = 'L'; LDQ >= max(1,N) if SIDE = 'R'. +*> \endverbatim +*> +*> \param[in,out] C +*> \verbatim +*> C is COMPLEX array, dimension (LDC,N) +*> On entry, the M-by-N matrix C. +*> On exit, C is overwritten by Q*C or Q**H*C or C*Q**H or C*Q. +*> \endverbatim +*> +*> \param[in] LDC +*> \verbatim +*> LDC is INTEGER +*> The leading dimension of the array C. LDC >= max(1,M). +*> \endverbatim +*> +*> \param[out] WORK +*> \verbatim +*> WORK is COMPLEX array, dimension (MAX(1,LWORK)) +*> On exit, if INFO = 0, WORK(1) returns the optimal LWORK. +*> \endverbatim +*> +*> \param[in] LWORK +*> \verbatim +*> LWORK is INTEGER +*> The dimension of the array WORK. +*> If SIDE = 'L', LWORK >= max(1,N); +*> if SIDE = 'R', LWORK >= max(1,M). +*> For optimum performance LWORK >= M*N. +*> +*> If LWORK = -1, then a workspace query is assumed; the routine +*> only calculates the optimal size of the WORK array, returns +*> this value as the first entry of the WORK array, and no error +*> message related to LWORK is issued by XERBLA. +*> \endverbatim +*> +*> \param[out] INFO +*> \verbatim +*> INFO is INTEGER +*> = 0: successful exit +*> < 0: if INFO = -i, the i-th argument had an illegal value +*> \endverbatim +* +* +* Authors: +* ======== +* +*> \author Univ. of Tennessee +*> \author Univ. of California Berkeley +*> \author Univ. of Colorado Denver +*> \author NAG Ltd. +* +*> \date January 2015 +* +*> \ingroup complexOTHERcomputational +* +* ===================================================================== + SUBROUTINE CUNM22( SIDE, TRANS, M, N, N1, N2, Q, LDQ, C, LDC, + $ WORK, LWORK, INFO ) +* +* -- LAPACK computational routine (version 3.6.0) -- +* -- LAPACK is a software package provided by Univ. of Tennessee, -- +* -- Univ. of California Berkeley, Univ. of Colorado Denver and NAG Ltd..-- +* January 2015 +* + IMPLICIT NONE +* +* .. Scalar Arguments .. + CHARACTER SIDE, TRANS + INTEGER M, N, N1, N2, LDQ, LDC, LWORK, INFO +* .. +* .. Array Arguments .. + COMPLEX Q( LDQ, * ), C( LDC, * ), WORK( * ) +* .. +* +* ===================================================================== +* +* .. Parameters .. + COMPLEX ONE + PARAMETER ( ONE = ( 1.0E+0, 0.0E+0 ) ) +* +* .. Local Scalars .. + LOGICAL LEFT, LQUERY, NOTRAN + INTEGER I, LDWORK, LEN, LWKOPT, NB, NQ, NW +* .. +* .. External Functions .. + LOGICAL LSAME + EXTERNAL LSAME +* .. +* .. External Subroutines .. + EXTERNAL CGEMM, CLACPY, CTRMM, XERBLA +* .. +* .. Intrinsic Functions .. + INTRINSIC CMPLX, MAX, MIN +* .. +* .. Executable Statements .. +* +* Test the input arguments +* + INFO = 0 + LEFT = LSAME( SIDE, 'L' ) + NOTRAN = LSAME( TRANS, 'N' ) + LQUERY = ( LWORK.EQ.-1 ) +* +* NQ is the order of Q; +* NW is the minimum dimension of WORK. +* + IF( LEFT ) THEN + NQ = M + ELSE + NQ = N + END IF + NW = NQ + IF( N1.EQ.0 .OR. N2.EQ.0 ) NW = 1 + IF( .NOT.LEFT .AND. .NOT.LSAME( SIDE, 'R' ) ) THEN + INFO = -1 + ELSE IF( .NOT.LSAME( TRANS, 'N' ) .AND. .NOT.LSAME( TRANS, 'C' ) ) + $ THEN + INFO = -2 + ELSE IF( M.LT.0 ) THEN + INFO = -3 + ELSE IF( N.LT.0 ) THEN + INFO = -4 + ELSE IF( N1.LT.0 .OR. N1+N2.NE.NQ ) THEN + INFO = -5 + ELSE IF( N2.LT.0 ) THEN + INFO = -6 + ELSE IF( LDQ.LT.MAX( 1, NQ ) ) THEN + INFO = -8 + ELSE IF( LDC.LT.MAX( 1, M ) ) THEN + INFO = -10 + ELSE IF( LWORK.LT.NW .AND. .NOT.LQUERY ) THEN + INFO = -12 + END IF +* + IF( INFO.EQ.0 ) THEN + LWKOPT = M*N + WORK( 1 ) = CMPLX( LWKOPT ) + END IF +* + IF( INFO.NE.0 ) THEN + CALL XERBLA( 'CUNM22', -INFO ) + RETURN + ELSE IF( LQUERY ) THEN + RETURN + END IF +* +* Quick return if possible +* + IF( M.EQ.0 .OR. N.EQ.0 ) THEN + WORK( 1 ) = 1 + RETURN + END IF +* +* Degenerate cases (N1 = 0 or N2 = 0) are handled using CTRMM. +* + IF( N1.EQ.0 ) THEN + CALL CTRMM( SIDE, 'Upper', TRANS, 'Non-Unit', M, N, ONE, + $ Q, LDQ, C, LDC ) + WORK( 1 ) = ONE + RETURN + ELSE IF( N2.EQ.0 ) THEN + CALL CTRMM( SIDE, 'Lower', TRANS, 'Non-Unit', M, N, ONE, + $ Q, LDQ, C, LDC ) + WORK( 1 ) = ONE + RETURN + END IF +* +* Compute the largest chunk size available from the workspace. +* + NB = MAX( 1, MIN( LWORK, LWKOPT ) / NQ ) +* + IF( LEFT ) THEN + IF( NOTRAN ) THEN + DO I = 1, N, NB + LEN = MIN( NB, N-I+1 ) + LDWORK = M +* +* Multiply bottom part of C by Q12. +* + CALL CLACPY( 'All', N1, LEN, C( N2+1, I ), LDC, WORK, + $ LDWORK ) + CALL CTRMM( 'Left', 'Lower', 'No Transpose', 'Non-Unit', + $ N1, LEN, ONE, Q( 1, N2+1 ), LDQ, WORK, + $ LDWORK ) +* +* Multiply top part of C by Q11. +* + CALL CGEMM( 'No Transpose', 'No Transpose', N1, LEN, N2, + $ ONE, Q, LDQ, C( 1, I ), LDC, ONE, WORK, + $ LDWORK ) +* +* Multiply top part of C by Q21. +* + CALL CLACPY( 'All', N2, LEN, C( 1, I ), LDC, + $ WORK( N1+1 ), LDWORK ) + CALL CTRMM( 'Left', 'Upper', 'No Transpose', 'Non-Unit', + $ N2, LEN, ONE, Q( N1+1, 1 ), LDQ, + $ WORK( N1+1 ), LDWORK ) +* +* Multiply bottom part of C by Q22. +* + CALL CGEMM( 'No Transpose', 'No Transpose', N2, LEN, N1, + $ ONE, Q( N1+1, N2+1 ), LDQ, C( N2+1, I ), LDC, + $ ONE, WORK( N1+1 ), LDWORK ) +* +* Copy everything back. +* + CALL CLACPY( 'All', M, LEN, WORK, LDWORK, C( 1, I ), + $ LDC ) + END DO + ELSE + DO I = 1, N, NB + LEN = MIN( NB, N-I+1 ) + LDWORK = M +* +* Multiply bottom part of C by Q21**H. +* + CALL CLACPY( 'All', N2, LEN, C( N1+1, I ), LDC, WORK, + $ LDWORK ) + CALL CTRMM( 'Left', 'Upper', 'Conjugate', 'Non-Unit', + $ N2, LEN, ONE, Q( N1+1, 1 ), LDQ, WORK, + $ LDWORK ) +* +* Multiply top part of C by Q11**H. +* + CALL CGEMM( 'Conjugate', 'No Transpose', N2, LEN, N1, + $ ONE, Q, LDQ, C( 1, I ), LDC, ONE, WORK, + $ LDWORK ) +* +* Multiply top part of C by Q12**H. +* + CALL CLACPY( 'All', N1, LEN, C( 1, I ), LDC, + $ WORK( N2+1 ), LDWORK ) + CALL CTRMM( 'Left', 'Lower', 'Conjugate', 'Non-Unit', + $ N1, LEN, ONE, Q( 1, N2+1 ), LDQ, + $ WORK( N2+1 ), LDWORK ) +* +* Multiply bottom part of C by Q22**H. +* + CALL CGEMM( 'Conjugate', 'No Transpose', N1, LEN, N2, + $ ONE, Q( N1+1, N2+1 ), LDQ, C( N1+1, I ), LDC, + $ ONE, WORK( N2+1 ), LDWORK ) +* +* Copy everything back. +* + CALL CLACPY( 'All', M, LEN, WORK, LDWORK, C( 1, I ), + $ LDC ) + END DO + END IF + ELSE + IF( NOTRAN ) THEN + DO I = 1, M, NB + LEN = MIN( NB, M-I+1 ) + LDWORK = LEN +* +* Multiply right part of C by Q21. +* + CALL CLACPY( 'All', LEN, N2, C( I, N1+1 ), LDC, WORK, + $ LDWORK ) + CALL CTRMM( 'Right', 'Upper', 'No Transpose', 'Non-Unit', + $ LEN, N2, ONE, Q( N1+1, 1 ), LDQ, WORK, + $ LDWORK ) +* +* Multiply left part of C by Q11. +* + CALL CGEMM( 'No Transpose', 'No Transpose', LEN, N2, N1, + $ ONE, C( I, 1 ), LDC, Q, LDQ, ONE, WORK, + $ LDWORK ) +* +* Multiply left part of C by Q12. +* + CALL CLACPY( 'All', LEN, N1, C( I, 1 ), LDC, + $ WORK( 1 + N2*LDWORK ), LDWORK ) + CALL CTRMM( 'Right', 'Lower', 'No Transpose', 'Non-Unit', + $ LEN, N1, ONE, Q( 1, N2+1 ), LDQ, + $ WORK( 1 + N2*LDWORK ), LDWORK ) +* +* Multiply right part of C by Q22. +* + CALL CGEMM( 'No Transpose', 'No Transpose', LEN, N1, N2, + $ ONE, C( I, N1+1 ), LDC, Q( N1+1, N2+1 ), LDQ, + $ ONE, WORK( 1 + N2*LDWORK ), LDWORK ) +* +* Copy everything back. +* + CALL CLACPY( 'All', LEN, N, WORK, LDWORK, C( I, 1 ), + $ LDC ) + END DO + ELSE + DO I = 1, M, NB + LEN = MIN( NB, M-I+1 ) + LDWORK = LEN +* +* Multiply right part of C by Q12**H. +* + CALL CLACPY( 'All', LEN, N1, C( I, N2+1 ), LDC, WORK, + $ LDWORK ) + CALL CTRMM( 'Right', 'Lower', 'Conjugate', 'Non-Unit', + $ LEN, N1, ONE, Q( 1, N2+1 ), LDQ, WORK, + $ LDWORK ) +* +* Multiply left part of C by Q11**H. +* + CALL CGEMM( 'No Transpose', 'Conjugate', LEN, N1, N2, + $ ONE, C( I, 1 ), LDC, Q, LDQ, ONE, WORK, + $ LDWORK ) +* +* Multiply left part of C by Q21**H. +* + CALL CLACPY( 'All', LEN, N2, C( I, 1 ), LDC, + $ WORK( 1 + N1*LDWORK ), LDWORK ) + CALL CTRMM( 'Right', 'Upper', 'Conjugate', 'Non-Unit', + $ LEN, N2, ONE, Q( N1+1, 1 ), LDQ, + $ WORK( 1 + N1*LDWORK ), LDWORK ) +* +* Multiply right part of C by Q22**H. +* + CALL CGEMM( 'No Transpose', 'Conjugate', LEN, N2, N1, + $ ONE, C( I, N2+1 ), LDC, Q( N1+1, N2+1 ), LDQ, + $ ONE, WORK( 1 + N1*LDWORK ), LDWORK ) +* +* Copy everything back. +* + CALL CLACPY( 'All', LEN, N, WORK, LDWORK, C( I, 1 ), + $ LDC ) + END DO + END IF + END IF +* + WORK( 1 ) = CMPLX( LWKOPT ) + RETURN +* +* End of CUNM22 +* + END diff --git a/SRC/dgges3.f b/SRC/dgges3.f new file mode 100644 index 00000000..41d2ea0e --- /dev/null +++ b/SRC/dgges3.f @@ -0,0 +1,674 @@ +*> \brief <b> DGGES3 computes the eigenvalues, the Schur form, and, optionally, the matrix of Schur vectors for GE matrices (blocked algorithm)</b> +* +* =========== DOCUMENTATION =========== +* +* Online html documentation available at +* http://www.netlib.org/lapack/explore-html/ +* +*> \htmlonly +*> Download DGGES3 + dependencies +*> <a href="http://www.netlib.org/cgi-bin/netlibfiles.tgz?format=tgz&filename=/lapack/lapack_routine/dgges3.f"> +*> [TGZ]</a> +*> <a href="http://www.netlib.org/cgi-bin/netlibfiles.zip?format=zip&filename=/lapack/lapack_routine/dgges3.f"> +*> [ZIP]</a> +*> <a href="http://www.netlib.org/cgi-bin/netlibfiles.txt?format=txt&filename=/lapack/lapack_routine/dgges3.f"> +*> [TXT]</a> +*> \endhtmlonly +* +* Definition: +* =========== +* +* SUBROUTINE DGGES3( JOBVSL, JOBVSR, SORT, SELCTG, N, A, LDA, B, LDB, +* SDIM, ALPHAR, ALPHAI, BETA, VSL, LDVSL, VSR, +* LDVSR, WORK, LWORK, BWORK, INFO ) +* +* .. Scalar Arguments .. +* CHARACTER JOBVSL, JOBVSR, SORT +* INTEGER INFO, LDA, LDB, LDVSL, LDVSR, LWORK, N, SDIM +* .. +* .. Array Arguments .. +* LOGICAL BWORK( * ) +* DOUBLE PRECISION A( LDA, * ), ALPHAI( * ), ALPHAR( * ), +* $ B( LDB, * ), BETA( * ), VSL( LDVSL, * ), +* $ VSR( LDVSR, * ), WORK( * ) +* .. +* .. Function Arguments .. +* LOGICAL SELCTG +* EXTERNAL SELCTG +* .. +* +* +*> \par Purpose: +* ============= +*> +*> \verbatim +*> +*> DGGES3 computes for a pair of N-by-N real nonsymmetric matrices (A,B), +*> the generalized eigenvalues, the generalized real Schur form (S,T), +*> optionally, the left and/or right matrices of Schur vectors (VSL and +*> VSR). This gives the generalized Schur factorization +*> +*> (A,B) = ( (VSL)*S*(VSR)**T, (VSL)*T*(VSR)**T ) +*> +*> Optionally, it also orders the eigenvalues so that a selected cluster +*> of eigenvalues appears in the leading diagonal blocks of the upper +*> quasi-triangular matrix S and the upper triangular matrix T.The +*> leading columns of VSL and VSR then form an orthonormal basis for the +*> corresponding left and right eigenspaces (deflating subspaces). +*> +*> (If only the generalized eigenvalues are needed, use the driver +*> DGGEV instead, which is faster.) +*> +*> A generalized eigenvalue for a pair of matrices (A,B) is a scalar w +*> or a ratio alpha/beta = w, such that A - w*B is singular. It is +*> usually represented as the pair (alpha,beta), as there is a +*> reasonable interpretation for beta=0 or both being zero. +*> +*> A pair of matrices (S,T) is in generalized real Schur form if T is +*> upper triangular with non-negative diagonal and S is block upper +*> triangular with 1-by-1 and 2-by-2 blocks. 1-by-1 blocks correspond +*> to real generalized eigenvalues, while 2-by-2 blocks of S will be +*> "standardized" by making the corresponding elements of T have the +*> form: +*> [ a 0 ] +*> [ 0 b ] +*> +*> and the pair of corresponding 2-by-2 blocks in S and T will have a +*> complex conjugate pair of generalized eigenvalues. +*> +*> \endverbatim +* +* Arguments: +* ========== +* +*> \param[in] JOBVSL +*> \verbatim +*> JOBVSL is CHARACTER*1 +*> = 'N': do not compute the left Schur vectors; +*> = 'V': compute the left Schur vectors. +*> \endverbatim +*> +*> \param[in] JOBVSR +*> \verbatim +*> JOBVSR is CHARACTER*1 +*> = 'N': do not compute the right Schur vectors; +*> = 'V': compute the right Schur vectors. +*> \endverbatim +*> +*> \param[in] SORT +*> \verbatim +*> SORT is CHARACTER*1 +*> Specifies whether or not to order the eigenvalues on the +*> diagonal of the generalized Schur form. +*> = 'N': Eigenvalues are not ordered; +*> = 'S': Eigenvalues are ordered (see SELCTG); +*> \endverbatim +*> +*> \param[in] SELCTG +*> \verbatim +*> SELCTG is a LOGICAL FUNCTION of three DOUBLE PRECISION arguments +*> SELCTG must be declared EXTERNAL in the calling subroutine. +*> If SORT = 'N', SELCTG is not referenced. +*> If SORT = 'S', SELCTG is used to select eigenvalues to sort +*> to the top left of the Schur form. +*> An eigenvalue (ALPHAR(j)+ALPHAI(j))/BETA(j) is selected if +*> SELCTG(ALPHAR(j),ALPHAI(j),BETA(j)) is true; i.e. if either +*> one of a complex conjugate pair of eigenvalues is selected, +*> then both complex eigenvalues are selected. +*> +*> Note that in the ill-conditioned case, a selected complex +*> eigenvalue may no longer satisfy SELCTG(ALPHAR(j),ALPHAI(j), +*> BETA(j)) = .TRUE. after ordering. INFO is to be set to N+2 +*> in this case. +*> \endverbatim +*> +*> \param[in] N +*> \verbatim +*> N is INTEGER +*> The order of the matrices A, B, VSL, and VSR. N >= 0. +*> \endverbatim +*> +*> \param[in,out] A +*> \verbatim +*> A is DOUBLE PRECISION array, dimension (LDA, N) +*> On entry, the first of the pair of matrices. +*> On exit, A has been overwritten by its generalized Schur +*> form S. +*> \endverbatim +*> +*> \param[in] LDA +*> \verbatim +*> LDA is INTEGER +*> The leading dimension of A. LDA >= max(1,N). +*> \endverbatim +*> +*> \param[in,out] B +*> \verbatim +*> B is DOUBLE PRECISION array, dimension (LDB, N) +*> On entry, the second of the pair of matrices. +*> On exit, B has been overwritten by its generalized Schur +*> form T. +*> \endverbatim +*> +*> \param[in] LDB +*> \verbatim +*> LDB is INTEGER +*> The leading dimension of B. LDB >= max(1,N). +*> \endverbatim +*> +*> \param[out] SDIM +*> \verbatim +*> SDIM is INTEGER +*> If SORT = 'N', SDIM = 0. +*> If SORT = 'S', SDIM = number of eigenvalues (after sorting) +*> for which SELCTG is true. (Complex conjugate pairs for which +*> SELCTG is true for either eigenvalue count as 2.) +*> \endverbatim +*> +*> \param[out] ALPHAR +*> \verbatim +*> ALPHAR is DOUBLE PRECISION array, dimension (N) +*> \endverbatim +*> +*> \param[out] ALPHAI +*> \verbatim +*> ALPHAI is DOUBLE PRECISION array, dimension (N) +*> \endverbatim +*> +*> \param[out] BETA +*> \verbatim +*> BETA is DOUBLE PRECISION array, dimension (N) +*> On exit, (ALPHAR(j) + ALPHAI(j)*i)/BETA(j), j=1,...,N, will +*> be the generalized eigenvalues. ALPHAR(j) + ALPHAI(j)*i, +*> and BETA(j),j=1,...,N are the diagonals of the complex Schur +*> form (S,T) that would result if the 2-by-2 diagonal blocks of +*> the real Schur form of (A,B) were further reduced to +*> triangular form using 2-by-2 complex unitary transformations. +*> If ALPHAI(j) is zero, then the j-th eigenvalue is real; if +*> positive, then the j-th and (j+1)-st eigenvalues are a +*> complex conjugate pair, with ALPHAI(j+1) negative. +*> +*> Note: the quotients ALPHAR(j)/BETA(j) and ALPHAI(j)/BETA(j) +*> may easily over- or underflow, and BETA(j) may even be zero. +*> Thus, the user should avoid naively computing the ratio. +*> However, ALPHAR and ALPHAI will be always less than and +*> usually comparable with norm(A) in magnitude, and BETA always +*> less than and usually comparable with norm(B). +*> \endverbatim +*> +*> \param[out] VSL +*> \verbatim +*> VSL is DOUBLE PRECISION array, dimension (LDVSL,N) +*> If JOBVSL = 'V', VSL will contain the left Schur vectors. +*> Not referenced if JOBVSL = 'N'. +*> \endverbatim +*> +*> \param[in] LDVSL +*> \verbatim +*> LDVSL is INTEGER +*> The leading dimension of the matrix VSL. LDVSL >=1, and +*> if JOBVSL = 'V', LDVSL >= N. +*> \endverbatim +*> +*> \param[out] VSR +*> \verbatim +*> VSR is DOUBLE PRECISION array, dimension (LDVSR,N) +*> If JOBVSR = 'V', VSR will contain the right Schur vectors. +*> Not referenced if JOBVSR = 'N'. +*> \endverbatim +*> +*> \param[in] LDVSR +*> \verbatim +*> LDVSR is INTEGER +*> The leading dimension of the matrix VSR. LDVSR >= 1, and +*> if JOBVSR = 'V', LDVSR >= N. +*> \endverbatim +*> +*> \param[out] WORK +*> \verbatim +*> WORK is DOUBLE PRECISION array, dimension (MAX(1,LWORK)) +*> On exit, if INFO = 0, WORK(1) returns the optimal LWORK. +*> \endverbatim +*> +*> \param[in] LWORK +*> \verbatim +*> LWORK is INTEGER +*> The dimension of the array WORK. +*> +*> If LWORK = -1, then a workspace query is assumed; the routine +*> only calculates the optimal size of the WORK array, returns +*> this value as the first entry of the WORK array, and no error +*> message related to LWORK is issued by XERBLA. +*> \endverbatim +*> +*> \param[out] BWORK +*> \verbatim +*> BWORK is LOGICAL array, dimension (N) +*> Not referenced if SORT = 'N'. +*> \endverbatim +*> +*> \param[out] INFO +*> \verbatim +*> INFO is INTEGER +*> = 0: successful exit +*> < 0: if INFO = -i, the i-th argument had an illegal value. +*> = 1,...,N: +*> The QZ iteration failed. (A,B) are not in Schur +*> form, but ALPHAR(j), ALPHAI(j), and BETA(j) should +*> be correct for j=INFO+1,...,N. +*> > N: =N+1: other than QZ iteration failed in DHGEQZ. +*> =N+2: after reordering, roundoff changed values of +*> some complex eigenvalues so that leading +*> eigenvalues in the Generalized Schur form no +*> longer satisfy SELCTG=.TRUE. This could also +*> be caused due to scaling. +*> =N+3: reordering failed in DTGSEN. +*> \endverbatim +* +* Authors: +* ======== +* +*> \author Univ. of Tennessee +*> \author Univ. of California Berkeley +*> \author Univ. of Colorado Denver +*> \author NAG Ltd. +* +*> \date January 2015 +* +*> \ingroup doubleGEeigen +* +* ===================================================================== + SUBROUTINE DGGES3( JOBVSL, JOBVSR, SORT, SELCTG, N, A, LDA, B, + $ LDB, SDIM, ALPHAR, ALPHAI, BETA, VSL, LDVSL, + $ VSR, LDVSR, WORK, LWORK, BWORK, INFO ) +* +* -- LAPACK driver routine (version 3.6.0) -- +* -- LAPACK is a software package provided by Univ. of Tennessee, -- +* -- Univ. of California Berkeley, Univ. of Colorado Denver and NAG Ltd..-- +* January 2015 +* +* .. Scalar Arguments .. + CHARACTER JOBVSL, JOBVSR, SORT + INTEGER INFO, LDA, LDB, LDVSL, LDVSR, LWORK, N, SDIM +* .. +* .. Array Arguments .. + LOGICAL BWORK( * ) + DOUBLE PRECISION A( LDA, * ), ALPHAI( * ), ALPHAR( * ), + $ B( LDB, * ), BETA( * ), VSL( LDVSL, * ), + $ VSR( LDVSR, * ), WORK( * ) +* .. +* .. Function Arguments .. + LOGICAL SELCTG + EXTERNAL SELCTG +* .. +* +* ===================================================================== +* +* .. Parameters .. + DOUBLE PRECISION ZERO, ONE + PARAMETER ( ZERO = 0.0D+0, ONE = 1.0D+0 ) +* .. +* .. Local Scalars .. + LOGICAL CURSL, ILASCL, ILBSCL, ILVSL, ILVSR, LASTSL, + $ LQUERY, LST2SL, WANTST + INTEGER I, ICOLS, IERR, IHI, IJOBVL, IJOBVR, ILEFT, + $ ILO, IP, IRIGHT, IROWS, ITAU, IWRK, LWKOPT + DOUBLE PRECISION ANRM, ANRMTO, BIGNUM, BNRM, BNRMTO, EPS, PVSL, + $ PVSR, SAFMAX, SAFMIN, SMLNUM +* .. +* .. Local Arrays .. + INTEGER IDUM( 1 ) + DOUBLE PRECISION DIF( 2 ) +* .. +* .. External Subroutines .. + EXTERNAL DGEQRF, DGGBAK, DGGBAL, DGGHD3, DHGEQZ, DLABAD, + $ DLACPY, DLASCL, DLASET, DORGQR, DORMQR, DTGSEN, + $ XERBLA +* .. +* .. External Functions .. + LOGICAL LSAME + DOUBLE PRECISION DLAMCH, DLANGE + EXTERNAL LSAME, DLAMCH, DLANGE +* .. +* .. Intrinsic Functions .. + INTRINSIC ABS, MAX, SQRT +* .. +* .. Executable Statements .. +* +* Decode the input arguments +* + IF( LSAME( JOBVSL, 'N' ) ) THEN + IJOBVL = 1 + ILVSL = .FALSE. + ELSE IF( LSAME( JOBVSL, 'V' ) ) THEN + IJOBVL = 2 + ILVSL = .TRUE. + ELSE + IJOBVL = -1 + ILVSL = .FALSE. + END IF +* + IF( LSAME( JOBVSR, 'N' ) ) THEN + IJOBVR = 1 + ILVSR = .FALSE. + ELSE IF( LSAME( JOBVSR, 'V' ) ) THEN + IJOBVR = 2 + ILVSR = .TRUE. + ELSE + IJOBVR = -1 + ILVSR = .FALSE. + END IF +* + WANTST = LSAME( SORT, 'S' ) +* +* Test the input arguments +* + INFO = 0 + LQUERY = ( LWORK.EQ.-1 ) + IF( IJOBVL.LE.0 ) THEN + INFO = -1 + ELSE IF( IJOBVR.LE.0 ) THEN + INFO = -2 + ELSE IF( ( .NOT.WANTST ) .AND. ( .NOT.LSAME( SORT, 'N' ) ) ) THEN + INFO = -3 + ELSE IF( N.LT.0 ) THEN + INFO = -5 + ELSE IF( LDA.LT.MAX( 1, N ) ) THEN + INFO = -7 + ELSE IF( LDB.LT.MAX( 1, N ) ) THEN + INFO = -9 + ELSE IF( LDVSL.LT.1 .OR. ( ILVSL .AND. LDVSL.LT.N ) ) THEN + INFO = -15 + ELSE IF( LDVSR.LT.1 .OR. ( ILVSR .AND. LDVSR.LT.N ) ) THEN + INFO = -17 + ELSE IF( LWORK.LT.6*N+16 .AND. .NOT.LQUERY ) THEN + INFO = -19 + END IF +* +* Compute workspace +* + IF( INFO.EQ.0 ) THEN + CALL DGEQRF( N, N, B, LDB, WORK, WORK, -1, IERR ) + LWKOPT = MAX( 6*N+16, 3*N+INT( WORK ( 1 ) ) ) + CALL DORMQR( 'L', 'T', N, N, N, B, LDB, WORK, A, LDA, WORK, + $ -1, IERR ) + LWKOPT = MAX( LWKOPT, 3*N+INT( WORK ( 1 ) ) ) + IF( ILVSL ) THEN + CALL DORGQR( N, N, N, VSL, LDVSL, WORK, WORK, -1, IERR ) + LWKOPT = MAX( LWKOPT, 3*N+INT( WORK ( 1 ) ) ) + END IF + CALL DGGHD3( JOBVSL, JOBVSR, N, 1, N, A, LDA, B, LDB, VSL, + $ LDVSL, VSR, LDVSR, WORK, -1, IERR ) + LWKOPT = MAX( LWKOPT, 3*N+INT( WORK ( 1 ) ) ) + CALL DHGEQZ( 'S', JOBVSL, JOBVSR, N, 1, N, A, LDA, B, LDB, + $ ALPHAR, ALPHAI, BETA, VSL, LDVSL, VSR, LDVSR, + $ WORK, -1, IERR ) + LWKOPT = MAX( LWKOPT, 2*N+INT( WORK ( 1 ) ) ) + IF( WANTST ) THEN + CALL DTGSEN( 0, ILVSL, ILVSR, BWORK, N, A, LDA, B, LDB, + $ ALPHAR, ALPHAI, BETA, VSL, LDVSL, VSR, LDVSR, + $ SDIM, PVSL, PVSR, DIF, WORK, -1, IDUM, 1, + $ IERR ) + LWKOPT = MAX( LWKOPT, 2*N+INT( WORK ( 1 ) ) ) + END IF + WORK( 1 ) = LWKOPT + END IF +* + IF( INFO.NE.0 ) THEN + CALL XERBLA( 'DGGES3 ', -INFO ) + RETURN + ELSE IF( LQUERY ) THEN + RETURN + END IF +* +* Quick return if possible +* + IF( N.EQ.0 ) THEN + SDIM = 0 + RETURN + END IF +* +* Get machine constants +* + EPS = DLAMCH( 'P' ) + SAFMIN = DLAMCH( 'S' ) + SAFMAX = ONE / SAFMIN + CALL DLABAD( SAFMIN, SAFMAX ) + SMLNUM = SQRT( SAFMIN ) / EPS + BIGNUM = ONE / SMLNUM +* +* Scale A if max element outside range [SMLNUM,BIGNUM] +* + ANRM = DLANGE( 'M', N, N, A, LDA, WORK ) + ILASCL = .FALSE. + IF( ANRM.GT.ZERO .AND. ANRM.LT.SMLNUM ) THEN + ANRMTO = SMLNUM + ILASCL = .TRUE. + ELSE IF( ANRM.GT.BIGNUM ) THEN + ANRMTO = BIGNUM + ILASCL = .TRUE. + END IF + IF( ILASCL ) + $ CALL DLASCL( 'G', 0, 0, ANRM, ANRMTO, N, N, A, LDA, IERR ) +* +* Scale B if max element outside range [SMLNUM,BIGNUM] +* + BNRM = DLANGE( 'M', N, N, B, LDB, WORK ) + ILBSCL = .FALSE. + IF( BNRM.GT.ZERO .AND. BNRM.LT.SMLNUM ) THEN + BNRMTO = SMLNUM + ILBSCL = .TRUE. + ELSE IF( BNRM.GT.BIGNUM ) THEN + BNRMTO = BIGNUM + ILBSCL = .TRUE. + END IF + IF( ILBSCL ) + $ CALL DLASCL( 'G', 0, 0, BNRM, BNRMTO, N, N, B, LDB, IERR ) +* +* Permute the matrix to make it more nearly triangular +* + ILEFT = 1 + IRIGHT = N + 1 + IWRK = IRIGHT + N + CALL DGGBAL( 'P', N, A, LDA, B, LDB, ILO, IHI, WORK( ILEFT ), + $ WORK( IRIGHT ), WORK( IWRK ), IERR ) +* +* Reduce B to triangular form (QR decomposition of B) +* + IROWS = IHI + 1 - ILO + ICOLS = N + 1 - ILO + ITAU = IWRK + IWRK = ITAU + IROWS + CALL DGEQRF( IROWS, ICOLS, B( ILO, ILO ), LDB, WORK( ITAU ), + $ WORK( IWRK ), LWORK+1-IWRK, IERR ) +* +* Apply the orthogonal transformation to matrix A +* + CALL DORMQR( 'L', 'T', IROWS, ICOLS, IROWS, B( ILO, ILO ), LDB, + $ WORK( ITAU ), A( ILO, ILO ), LDA, WORK( IWRK ), + $ LWORK+1-IWRK, IERR ) +* +* Initialize VSL +* + IF( ILVSL ) THEN + CALL DLASET( 'Full', N, N, ZERO, ONE, VSL, LDVSL ) + IF( IROWS.GT.1 ) THEN + CALL DLACPY( 'L', IROWS-1, IROWS-1, B( ILO+1, ILO ), LDB, + $ VSL( ILO+1, ILO ), LDVSL ) + END IF + CALL DORGQR( IROWS, IROWS, IROWS, VSL( ILO, ILO ), LDVSL, + $ WORK( ITAU ), WORK( IWRK ), LWORK+1-IWRK, IERR ) + END IF +* +* Initialize VSR +* + IF( ILVSR ) + $ CALL DLASET( 'Full', N, N, ZERO, ONE, VSR, LDVSR ) +* +* Reduce to generalized Hessenberg form +* + CALL DGGHD3( JOBVSL, JOBVSR, N, ILO, IHI, A, LDA, B, LDB, VSL, + $ LDVSL, VSR, LDVSR, WORK( IWRK ), LWORK+1-IWRK, + $ IERR ) +* +* Perform QZ algorithm, computing Schur vectors if desired +* + IWRK = ITAU + CALL DHGEQZ( 'S', JOBVSL, JOBVSR, N, ILO, IHI, A, LDA, B, LDB, + $ ALPHAR, ALPHAI, BETA, VSL, LDVSL, VSR, LDVSR, + $ WORK( IWRK ), LWORK+1-IWRK, IERR ) + IF( IERR.NE.0 ) THEN + IF( IERR.GT.0 .AND. IERR.LE.N ) THEN + INFO = IERR + ELSE IF( IERR.GT.N .AND. IERR.LE.2*N ) THEN + INFO = IERR - N + ELSE + INFO = N + 1 + END IF + GO TO 50 + END IF +* +* Sort eigenvalues ALPHA/BETA if desired +* + SDIM = 0 + IF( WANTST ) THEN +* +* Undo scaling on eigenvalues before SELCTGing +* + IF( ILASCL ) THEN + CALL DLASCL( 'G', 0, 0, ANRMTO, ANRM, N, 1, ALPHAR, N, + $ IERR ) + CALL DLASCL( 'G', 0, 0, ANRMTO, ANRM, N, 1, ALPHAI, N, + $ IERR ) + END IF + IF( ILBSCL ) + $ CALL DLASCL( 'G', 0, 0, BNRMTO, BNRM, N, 1, BETA, N, IERR ) +* +* Select eigenvalues +* + DO 10 I = 1, N + BWORK( I ) = SELCTG( ALPHAR( I ), ALPHAI( I ), BETA( I ) ) + 10 CONTINUE +* + CALL DTGSEN( 0, ILVSL, ILVSR, BWORK, N, A, LDA, B, LDB, ALPHAR, + $ ALPHAI, BETA, VSL, LDVSL, VSR, LDVSR, SDIM, PVSL, + $ PVSR, DIF, WORK( IWRK ), LWORK-IWRK+1, IDUM, 1, + $ IERR ) + IF( IERR.EQ.1 ) + $ INFO = N + 3 +* + END IF +* +* Apply back-permutation to VSL and VSR +* + IF( ILVSL ) + $ CALL DGGBAK( 'P', 'L', N, ILO, IHI, WORK( ILEFT ), + $ WORK( IRIGHT ), N, VSL, LDVSL, IERR ) +* + IF( ILVSR ) + $ CALL DGGBAK( 'P', 'R', N, ILO, IHI, WORK( ILEFT ), + $ WORK( IRIGHT ), N, VSR, LDVSR, IERR ) +* +* Check if unscaling would cause over/underflow, if so, rescale +* (ALPHAR(I),ALPHAI(I),BETA(I)) so BETA(I) is on the order of +* B(I,I) and ALPHAR(I) and ALPHAI(I) are on the order of A(I,I) +* + IF( ILASCL ) THEN + DO 20 I = 1, N + IF( ALPHAI( I ).NE.ZERO ) THEN + IF( ( ALPHAR( I ) / SAFMAX ).GT.( ANRMTO / ANRM ) .OR. + $ ( SAFMIN / ALPHAR( I ) ).GT.( ANRM / ANRMTO ) ) THEN + WORK( 1 ) = ABS( A( I, I ) / ALPHAR( I ) ) + BETA( I ) = BETA( I )*WORK( 1 ) + ALPHAR( I ) = ALPHAR( I )*WORK( 1 ) + ALPHAI( I ) = ALPHAI( I )*WORK( 1 ) + ELSE IF( ( ALPHAI( I ) / SAFMAX ).GT. + $ ( ANRMTO / ANRM ) .OR. + $ ( SAFMIN / ALPHAI( I ) ).GT.( ANRM / ANRMTO ) ) + $ THEN + WORK( 1 ) = ABS( A( I, I+1 ) / ALPHAI( I ) ) + BETA( I ) = BETA( I )*WORK( 1 ) + ALPHAR( I ) = ALPHAR( I )*WORK( 1 ) + ALPHAI( I ) = ALPHAI( I )*WORK( 1 ) + END IF + END IF + 20 CONTINUE + END IF +* + IF( ILBSCL ) THEN + DO 30 I = 1, N + IF( ALPHAI( I ).NE.ZERO ) THEN + IF( ( BETA( I ) / SAFMAX ).GT.( BNRMTO / BNRM ) .OR. + $ ( SAFMIN / BETA( I ) ).GT.( BNRM / BNRMTO ) ) THEN + WORK( 1 ) = ABS( B( I, I ) / BETA( I ) ) + BETA( I ) = BETA( I )*WORK( 1 ) + ALPHAR( I ) = ALPHAR( I )*WORK( 1 ) + ALPHAI( I ) = ALPHAI( I )*WORK( 1 ) + END IF + END IF + 30 CONTINUE + END IF +* +* Undo scaling +* + IF( ILASCL ) THEN + CALL DLASCL( 'H', 0, 0, ANRMTO, ANRM, N, N, A, LDA, IERR ) + CALL DLASCL( 'G', 0, 0, ANRMTO, ANRM, N, 1, ALPHAR, N, IERR ) + CALL DLASCL( 'G', 0, 0, ANRMTO, ANRM, N, 1, ALPHAI, N, IERR ) + END IF +* + IF( ILBSCL ) THEN + CALL DLASCL( 'U', 0, 0, BNRMTO, BNRM, N, N, B, LDB, IERR ) + CALL DLASCL( 'G', 0, 0, BNRMTO, BNRM, N, 1, BETA, N, IERR ) + END IF +* + IF( WANTST ) THEN +* +* Check if reordering is correct +* + LASTSL = .TRUE. + LST2SL = .TRUE. + SDIM = 0 + IP = 0 + DO 40 I = 1, N + CURSL = SELCTG( ALPHAR( I ), ALPHAI( I ), BETA( I ) ) + IF( ALPHAI( I ).EQ.ZERO ) THEN + IF( CURSL ) + $ SDIM = SDIM + 1 + IP = 0 + IF( CURSL .AND. .NOT.LASTSL ) + $ INFO = N + 2 + ELSE + IF( IP.EQ.1 ) THEN +* +* Last eigenvalue of conjugate pair +* + CURSL = CURSL .OR. LASTSL + LASTSL = CURSL + IF( CURSL ) + $ SDIM = SDIM + 2 + IP = -1 + IF( CURSL .AND. .NOT.LST2SL ) + $ INFO = N + 2 + ELSE +* +* First eigenvalue of conjugate pair +* + IP = 1 + END IF + END IF + LST2SL = LASTSL + LASTSL = CURSL + 40 CONTINUE +* + END IF +* + 50 CONTINUE +* + WORK( 1 ) = LWKOPT +* + RETURN +* +* End of DGGES3 +* + END diff --git a/SRC/dggev3.f b/SRC/dggev3.f new file mode 100644 index 00000000..43a853df --- /dev/null +++ b/SRC/dggev3.f @@ -0,0 +1,594 @@ +*> \brief <b> DGGEV3 computes the eigenvalues and, optionally, the left and/or right eigenvectors for GE matrices (blocked algorithm)</b> +* +* =========== DOCUMENTATION =========== +* +* Online html documentation available at +* http://www.netlib.org/lapack/explore-html/ +* +*> \htmlonly +*> Download DGGEV3 + dependencies +*> <a href="http://www.netlib.org/cgi-bin/netlibfiles.tgz?format=tgz&filename=/lapack/lapack_routine/dggev3.f"> +*> [TGZ]</a> +*> <a href="http://www.netlib.org/cgi-bin/netlibfiles.zip?format=zip&filename=/lapack/lapack_routine/dggev3.f"> +*> [ZIP]</a> +*> <a href="http://www.netlib.org/cgi-bin/netlibfiles.txt?format=txt&filename=/lapack/lapack_routine/dggev3.f"> +*> [TXT]</a> +*> \endhtmlonly +* +* Definition: +* =========== +* +* SUBROUTINE DGGEV3( JOBVL, JOBVR, N, A, LDA, B, LDB, ALPHAR, +* $ ALPHAI, BETA, VL, LDVL, VR, LDVR, WORK, LWORK, +* $ INFO ) +* +* .. Scalar Arguments .. +* CHARACTER JOBVL, JOBVR +* INTEGER INFO, LDA, LDB, LDVL, LDVR, LWORK, N +* .. +* .. Array Arguments .. +* DOUBLE PRECISION A( LDA, * ), ALPHAI( * ), ALPHAR( * ), +* $ B( LDB, * ), BETA( * ), VL( LDVL, * ), +* $ VR( LDVR, * ), WORK( * ) +* .. +* +* +*> \par Purpose: +* ============= +*> +*> \verbatim +*> +*> DGGEV3 computes for a pair of N-by-N real nonsymmetric matrices (A,B) +*> the generalized eigenvalues, and optionally, the left and/or right +*> generalized eigenvectors. +*> +*> A generalized eigenvalue for a pair of matrices (A,B) is a scalar +*> lambda or a ratio alpha/beta = lambda, such that A - lambda*B is +*> singular. It is usually represented as the pair (alpha,beta), as +*> there is a reasonable interpretation for beta=0, and even for both +*> being zero. +*> +*> The right eigenvector v(j) corresponding to the eigenvalue lambda(j) +*> of (A,B) satisfies +*> +*> A * v(j) = lambda(j) * B * v(j). +*> +*> The left eigenvector u(j) corresponding to the eigenvalue lambda(j) +*> of (A,B) satisfies +*> +*> u(j)**H * A = lambda(j) * u(j)**H * B . +*> +*> where u(j)**H is the conjugate-transpose of u(j). +*> +*> \endverbatim +* +* Arguments: +* ========== +* +*> \param[in] JOBVL +*> \verbatim +*> JOBVL is CHARACTER*1 +*> = 'N': do not compute the left generalized eigenvectors; +*> = 'V': compute the left generalized eigenvectors. +*> \endverbatim +*> +*> \param[in] JOBVR +*> \verbatim +*> JOBVR is CHARACTER*1 +*> = 'N': do not compute the right generalized eigenvectors; +*> = 'V': compute the right generalized eigenvectors. +*> \endverbatim +*> +*> \param[in] N +*> \verbatim +*> N is INTEGER +*> The order of the matrices A, B, VL, and VR. N >= 0. +*> \endverbatim +*> +*> \param[in,out] A +*> \verbatim +*> A is DOUBLE PRECISION array, dimension (LDA, N) +*> On entry, the matrix A in the pair (A,B). +*> On exit, A has been overwritten. +*> \endverbatim +*> +*> \param[in] LDA +*> \verbatim +*> LDA is INTEGER +*> The leading dimension of A. LDA >= max(1,N). +*> \endverbatim +*> +*> \param[in,out] B +*> \verbatim +*> B is DOUBLE PRECISION array, dimension (LDB, N) +*> On entry, the matrix B in the pair (A,B). +*> On exit, B has been overwritten. +*> \endverbatim +*> +*> \param[in] LDB +*> \verbatim +*> LDB is INTEGER +*> The leading dimension of B. LDB >= max(1,N). +*> \endverbatim +*> +*> \param[out] ALPHAR +*> \verbatim +*> ALPHAR is DOUBLE PRECISION array, dimension (N) +*> \endverbatim +*> +*> \param[out] ALPHAI +*> \verbatim +*> ALPHAI is DOUBLE PRECISION array, dimension (N) +*> \endverbatim +*> +*> \param[out] BETA +*> \verbatim +*> BETA is DOUBLE PRECISION array, dimension (N) +*> On exit, (ALPHAR(j) + ALPHAI(j)*i)/BETA(j), j=1,...,N, will +*> be the generalized eigenvalues. If ALPHAI(j) is zero, then +*> the j-th eigenvalue is real; if positive, then the j-th and +*> (j+1)-st eigenvalues are a complex conjugate pair, with +*> ALPHAI(j+1) negative. +*> +*> Note: the quotients ALPHAR(j)/BETA(j) and ALPHAI(j)/BETA(j) +*> may easily over- or underflow, and BETA(j) may even be zero. +*> Thus, the user should avoid naively computing the ratio +*> alpha/beta. However, ALPHAR and ALPHAI will be always less +*> than and usually comparable with norm(A) in magnitude, and +*> BETA always less than and usually comparable with norm(B). +*> \endverbatim +*> +*> \param[out] VL +*> \verbatim +*> VL is DOUBLE PRECISION array, dimension (LDVL,N) +*> If JOBVL = 'V', the left eigenvectors u(j) are stored one +*> after another in the columns of VL, in the same order as +*> their eigenvalues. If the j-th eigenvalue is real, then +*> u(j) = VL(:,j), the j-th column of VL. If the j-th and +*> (j+1)-th eigenvalues form a complex conjugate pair, then +*> u(j) = VL(:,j)+i*VL(:,j+1) and u(j+1) = VL(:,j)-i*VL(:,j+1). +*> Each eigenvector is scaled so the largest component has +*> abs(real part)+abs(imag. part)=1. +*> Not referenced if JOBVL = 'N'. +*> \endverbatim +*> +*> \param[in] LDVL +*> \verbatim +*> LDVL is INTEGER +*> The leading dimension of the matrix VL. LDVL >= 1, and +*> if JOBVL = 'V', LDVL >= N. +*> \endverbatim +*> +*> \param[out] VR +*> \verbatim +*> VR is DOUBLE PRECISION array, dimension (LDVR,N) +*> If JOBVR = 'V', the right eigenvectors v(j) are stored one +*> after another in the columns of VR, in the same order as +*> their eigenvalues. If the j-th eigenvalue is real, then +*> v(j) = VR(:,j), the j-th column of VR. If the j-th and +*> (j+1)-th eigenvalues form a complex conjugate pair, then +*> v(j) = VR(:,j)+i*VR(:,j+1) and v(j+1) = VR(:,j)-i*VR(:,j+1). +*> Each eigenvector is scaled so the largest component has +*> abs(real part)+abs(imag. part)=1. +*> Not referenced if JOBVR = 'N'. +*> \endverbatim +*> +*> \param[in] LDVR +*> \verbatim +*> LDVR is INTEGER +*> The leading dimension of the matrix VR. LDVR >= 1, and +*> if JOBVR = 'V', LDVR >= N. +*> \endverbatim +*> +*> \param[out] WORK +*> \verbatim +*> WORK is DOUBLE PRECISION array, dimension (MAX(1,LWORK)) +*> On exit, if INFO = 0, WORK(1) returns the optimal LWORK. +*> \endverbatim +*> +*> \param[in] LWORK +*> \verbatim +*> LWORK is INTEGER +*> +*> If LWORK = -1, then a workspace query is assumed; the routine +*> only calculates the optimal size of the WORK array, returns +*> this value as the first entry of the WORK array, and no error +*> message related to LWORK is issued by XERBLA. +*> \endverbatim +*> +*> \param[out] INFO +*> \verbatim +*> INFO is INTEGER +*> = 0: successful exit +*> < 0: if INFO = -i, the i-th argument had an illegal value. +*> = 1,...,N: +*> The QZ iteration failed. No eigenvectors have been +*> calculated, but ALPHAR(j), ALPHAI(j), and BETA(j) +*> should be correct for j=INFO+1,...,N. +*> > N: =N+1: other than QZ iteration failed in DHGEQZ. +*> =N+2: error return from DTGEVC. +*> \endverbatim +* +* Authors: +* ======== +* +*> \author Univ. of Tennessee +*> \author Univ. of California Berkeley +*> \author Univ. of Colorado Denver +*> \author NAG Ltd. +* +*> \date January 2015 +* +*> \ingroup doubleGEeigen +* +* ===================================================================== + SUBROUTINE DGGEV3( JOBVL, JOBVR, N, A, LDA, B, LDB, ALPHAR, + $ ALPHAI, BETA, VL, LDVL, VR, LDVR, WORK, LWORK, + $ INFO ) +* +* -- LAPACK driver routine (version 3.6.0) -- +* -- LAPACK is a software package provided by Univ. of Tennessee, -- +* -- Univ. of California Berkeley, Univ. of Colorado Denver and NAG Ltd..-- +* January 2015 +* +* .. Scalar Arguments .. + CHARACTER JOBVL, JOBVR + INTEGER INFO, LDA, LDB, LDVL, LDVR, LWORK, N +* .. +* .. Array Arguments .. + DOUBLE PRECISION A( LDA, * ), ALPHAI( * ), ALPHAR( * ), + $ B( LDB, * ), BETA( * ), VL( LDVL, * ), + $ VR( LDVR, * ), WORK( * ) +* .. +* +* ===================================================================== +* +* .. Parameters .. + DOUBLE PRECISION ZERO, ONE + PARAMETER ( ZERO = 0.0D+0, ONE = 1.0D+0 ) +* .. +* .. Local Scalars .. + LOGICAL ILASCL, ILBSCL, ILV, ILVL, ILVR, LQUERY + CHARACTER CHTEMP + INTEGER ICOLS, IERR, IHI, IJOBVL, IJOBVR, ILEFT, ILO, + $ IN, IRIGHT, IROWS, ITAU, IWRK, JC, JR, LWKOPT + DOUBLE PRECISION ANRM, ANRMTO, BIGNUM, BNRM, BNRMTO, EPS, + $ SMLNUM, TEMP +* .. +* .. Local Arrays .. + LOGICAL LDUMMA( 1 ) +* .. +* .. External Subroutines .. + EXTERNAL DGEQRF, DGGBAK, DGGBAL, DGGHD3, DHGEQZ, DLABAD, + $ DLACPY, DLASCL, DLASET, DORGQR, DORMQR, DTGEVC, + $ XERBLA +* .. +* .. External Functions .. + LOGICAL LSAME + DOUBLE PRECISION DLAMCH, DLANGE + EXTERNAL LSAME, DLAMCH, DLANGE +* .. +* .. Intrinsic Functions .. + INTRINSIC ABS, MAX, SQRT +* .. +* .. Executable Statements .. +* +* Decode the input arguments +* + IF( LSAME( JOBVL, 'N' ) ) THEN + IJOBVL = 1 + ILVL = .FALSE. + ELSE IF( LSAME( JOBVL, 'V' ) ) THEN + IJOBVL = 2 + ILVL = .TRUE. + ELSE + IJOBVL = -1 + ILVL = .FALSE. + END IF +* + IF( LSAME( JOBVR, 'N' ) ) THEN + IJOBVR = 1 + ILVR = .FALSE. + ELSE IF( LSAME( JOBVR, 'V' ) ) THEN + IJOBVR = 2 + ILVR = .TRUE. + ELSE + IJOBVR = -1 + ILVR = .FALSE. + END IF + ILV = ILVL .OR. ILVR +* +* Test the input arguments +* + INFO = 0 + LQUERY = ( LWORK.EQ.-1 ) + IF( IJOBVL.LE.0 ) THEN + INFO = -1 + ELSE IF( IJOBVR.LE.0 ) THEN + INFO = -2 + ELSE IF( N.LT.0 ) THEN + INFO = -3 + ELSE IF( LDA.LT.MAX( 1, N ) ) THEN + INFO = -5 + ELSE IF( LDB.LT.MAX( 1, N ) ) THEN + INFO = -7 + ELSE IF( LDVL.LT.1 .OR. ( ILVL .AND. LDVL.LT.N ) ) THEN + INFO = -12 + ELSE IF( LDVR.LT.1 .OR. ( ILVR .AND. LDVR.LT.N ) ) THEN + INFO = -14 + ELSE IF( LWORK.LT.MAX( 1, 8*N ) .AND. .NOT.LQUERY ) THEN + INFO = -16 + END IF +* +* Compute workspace +* + IF( INFO.EQ.0 ) THEN + CALL DGEQRF( N, N, B, LDB, WORK, WORK, -1, IERR ) + LWKOPT = MAX(1, 8*N, 3*N+INT( WORK( 1 ) ) ) + CALL DORMQR( 'L', 'T', N, N, N, B, LDB, WORK, A, LDA, WORK, -1, + $ IERR ) + LWKOPT = MAX( LWKOPT, 3*N+INT( WORK ( 1 ) ) ) + IF( ILVL ) THEN + CALL DORGQR( N, N, N, VL, LDVL, WORK, WORK, -1, IERR ) + LWKOPT = MAX( LWKOPT, 3*N+INT( WORK ( 1 ) ) ) + END IF + IF( ILV ) THEN + CALL DGGHD3( JOBVL, JOBVR, N, 1, N, A, LDA, B, LDB, VL, + $ LDVL, VR, LDVR, WORK, -1, IERR ) + LWKOPT = MAX( LWKOPT, 3*N+INT( WORK ( 1 ) ) ) + CALL DHGEQZ( 'S', JOBVL, JOBVR, N, 1, N, A, LDA, B, LDB, + $ ALPHAR, ALPHAI, BETA, VL, LDVL, VR, LDVR, + $ WORK, -1, IERR ) + LWKOPT = MAX( LWKOPT, 2*N+INT( WORK ( 1 ) ) ) + ELSE + CALL DGGHD3( 'N', 'N', N, 1, N, A, LDA, B, LDB, VL, LDVL, + $ VR, LDVR, WORK, -1, IERR ) + LWKOPT = MAX( LWKOPT, 3*N+INT( WORK ( 1 ) ) ) + CALL DHGEQZ( 'E', JOBVL, JOBVR, N, 1, N, A, LDA, B, LDB, + $ ALPHAR, ALPHAI, BETA, VL, LDVL, VR, LDVR, + $ WORK, -1, IERR ) + LWKOPT = MAX( LWKOPT, 2*N+INT( WORK ( 1 ) ) ) + END IF + + WORK( 1 ) = LWKOPT + END IF +* + IF( INFO.NE.0 ) THEN + CALL XERBLA( 'DGGEV3 ', -INFO ) + RETURN + ELSE IF( LQUERY ) THEN + RETURN + END IF +* +* Quick return if possible +* + IF( N.EQ.0 ) + $ RETURN +* +* Get machine constants +* + EPS = DLAMCH( 'P' ) + SMLNUM = DLAMCH( 'S' ) + BIGNUM = ONE / SMLNUM + CALL DLABAD( SMLNUM, BIGNUM ) + SMLNUM = SQRT( SMLNUM ) / EPS + BIGNUM = ONE / SMLNUM +* +* Scale A if max element outside range [SMLNUM,BIGNUM] +* + ANRM = DLANGE( 'M', N, N, A, LDA, WORK ) + ILASCL = .FALSE. + IF( ANRM.GT.ZERO .AND. ANRM.LT.SMLNUM ) THEN + ANRMTO = SMLNUM + ILASCL = .TRUE. + ELSE IF( ANRM.GT.BIGNUM ) THEN + ANRMTO = BIGNUM + ILASCL = .TRUE. + END IF + IF( ILASCL ) + $ CALL DLASCL( 'G', 0, 0, ANRM, ANRMTO, N, N, A, LDA, IERR ) +* +* Scale B if max element outside range [SMLNUM,BIGNUM] +* + BNRM = DLANGE( 'M', N, N, B, LDB, WORK ) + ILBSCL = .FALSE. + IF( BNRM.GT.ZERO .AND. BNRM.LT.SMLNUM ) THEN + BNRMTO = SMLNUM + ILBSCL = .TRUE. + ELSE IF( BNRM.GT.BIGNUM ) THEN + BNRMTO = BIGNUM + ILBSCL = .TRUE. + END IF + IF( ILBSCL ) + $ CALL DLASCL( 'G', 0, 0, BNRM, BNRMTO, N, N, B, LDB, IERR ) +* +* Permute the matrices A, B to isolate eigenvalues if possible +* + ILEFT = 1 + IRIGHT = N + 1 + IWRK = IRIGHT + N + CALL DGGBAL( 'P', N, A, LDA, B, LDB, ILO, IHI, WORK( ILEFT ), + $ WORK( IRIGHT ), WORK( IWRK ), IERR ) +* +* Reduce B to triangular form (QR decomposition of B) +* + IROWS = IHI + 1 - ILO + IF( ILV ) THEN + ICOLS = N + 1 - ILO + ELSE + ICOLS = IROWS + END IF + ITAU = IWRK + IWRK = ITAU + IROWS + CALL DGEQRF( IROWS, ICOLS, B( ILO, ILO ), LDB, WORK( ITAU ), + $ WORK( IWRK ), LWORK+1-IWRK, IERR ) +* +* Apply the orthogonal transformation to matrix A +* + CALL DORMQR( 'L', 'T', IROWS, ICOLS, IROWS, B( ILO, ILO ), LDB, + $ WORK( ITAU ), A( ILO, ILO ), LDA, WORK( IWRK ), + $ LWORK+1-IWRK, IERR ) +* +* Initialize VL +* + IF( ILVL ) THEN + CALL DLASET( 'Full', N, N, ZERO, ONE, VL, LDVL ) + IF( IROWS.GT.1 ) THEN + CALL DLACPY( 'L', IROWS-1, IROWS-1, B( ILO+1, ILO ), LDB, + $ VL( ILO+1, ILO ), LDVL ) + END IF + CALL DORGQR( IROWS, IROWS, IROWS, VL( ILO, ILO ), LDVL, + $ WORK( ITAU ), WORK( IWRK ), LWORK+1-IWRK, IERR ) + END IF +* +* Initialize VR +* + IF( ILVR ) + $ CALL DLASET( 'Full', N, N, ZERO, ONE, VR, LDVR ) +* +* Reduce to generalized Hessenberg form +* + IF( ILV ) THEN +* +* Eigenvectors requested -- work on whole matrix. +* + CALL DGGHD3( JOBVL, JOBVR, N, ILO, IHI, A, LDA, B, LDB, VL, + $ LDVL, VR, LDVR, WORK( IWRK ), LWORK+1-IWRK, IERR ) + ELSE + CALL DGGHD3( 'N', 'N', IROWS, 1, IROWS, A( ILO, ILO ), LDA, + $ B( ILO, ILO ), LDB, VL, LDVL, VR, LDVR, + $ WORK( IWRK ), LWORK+1-IWRK, IERR ) + END IF +* +* Perform QZ algorithm (Compute eigenvalues, and optionally, the +* Schur forms and Schur vectors) +* + IWRK = ITAU + IF( ILV ) THEN + CHTEMP = 'S' + ELSE + CHTEMP = 'E' + END IF + CALL DHGEQZ( CHTEMP, JOBVL, JOBVR, N, ILO, IHI, A, LDA, B, LDB, + $ ALPHAR, ALPHAI, BETA, VL, LDVL, VR, LDVR, + $ WORK( IWRK ), LWORK+1-IWRK, IERR ) + IF( IERR.NE.0 ) THEN + IF( IERR.GT.0 .AND. IERR.LE.N ) THEN + INFO = IERR + ELSE IF( IERR.GT.N .AND. IERR.LE.2*N ) THEN + INFO = IERR - N + ELSE + INFO = N + 1 + END IF + GO TO 110 + END IF +* +* Compute Eigenvectors +* + IF( ILV ) THEN + IF( ILVL ) THEN + IF( ILVR ) THEN + CHTEMP = 'B' + ELSE + CHTEMP = 'L' + END IF + ELSE + CHTEMP = 'R' + END IF + CALL DTGEVC( CHTEMP, 'B', LDUMMA, N, A, LDA, B, LDB, VL, LDVL, + $ VR, LDVR, N, IN, WORK( IWRK ), IERR ) + IF( IERR.NE.0 ) THEN + INFO = N + 2 + GO TO 110 + END IF +* +* Undo balancing on VL and VR and normalization +* + IF( ILVL ) THEN + CALL DGGBAK( 'P', 'L', N, ILO, IHI, WORK( ILEFT ), + $ WORK( IRIGHT ), N, VL, LDVL, IERR ) + DO 50 JC = 1, N + IF( ALPHAI( JC ).LT.ZERO ) + $ GO TO 50 + TEMP = ZERO + IF( ALPHAI( JC ).EQ.ZERO ) THEN + DO 10 JR = 1, N + TEMP = MAX( TEMP, ABS( VL( JR, JC ) ) ) + 10 CONTINUE + ELSE + DO 20 JR = 1, N + TEMP = MAX( TEMP, ABS( VL( JR, JC ) )+ + $ ABS( VL( JR, JC+1 ) ) ) + 20 CONTINUE + END IF + IF( TEMP.LT.SMLNUM ) + $ GO TO 50 + TEMP = ONE / TEMP + IF( ALPHAI( JC ).EQ.ZERO ) THEN + DO 30 JR = 1, N + VL( JR, JC ) = VL( JR, JC )*TEMP + 30 CONTINUE + ELSE + DO 40 JR = 1, N + VL( JR, JC ) = VL( JR, JC )*TEMP + VL( JR, JC+1 ) = VL( JR, JC+1 )*TEMP + 40 CONTINUE + END IF + 50 CONTINUE + END IF + IF( ILVR ) THEN + CALL DGGBAK( 'P', 'R', N, ILO, IHI, WORK( ILEFT ), + $ WORK( IRIGHT ), N, VR, LDVR, IERR ) + DO 100 JC = 1, N + IF( ALPHAI( JC ).LT.ZERO ) + $ GO TO 100 + TEMP = ZERO + IF( ALPHAI( JC ).EQ.ZERO ) THEN + DO 60 JR = 1, N + TEMP = MAX( TEMP, ABS( VR( JR, JC ) ) ) + 60 CONTINUE + ELSE + DO 70 JR = 1, N + TEMP = MAX( TEMP, ABS( VR( JR, JC ) )+ + $ ABS( VR( JR, JC+1 ) ) ) + 70 CONTINUE + END IF + IF( TEMP.LT.SMLNUM ) + $ GO TO 100 + TEMP = ONE / TEMP + IF( ALPHAI( JC ).EQ.ZERO ) THEN + DO 80 JR = 1, N + VR( JR, JC ) = VR( JR, JC )*TEMP + 80 CONTINUE + ELSE + DO 90 JR = 1, N + VR( JR, JC ) = VR( JR, JC )*TEMP + VR( JR, JC+1 ) = VR( JR, JC+1 )*TEMP + 90 CONTINUE + END IF + 100 CONTINUE + END IF +* +* End of eigenvector calculation +* + END IF +* +* Undo scaling if necessary +* + 110 CONTINUE +* + IF( ILASCL ) THEN + CALL DLASCL( 'G', 0, 0, ANRMTO, ANRM, N, 1, ALPHAR, N, IERR ) + CALL DLASCL( 'G', 0, 0, ANRMTO, ANRM, N, 1, ALPHAI, N, IERR ) + END IF +* + IF( ILBSCL ) THEN + CALL DLASCL( 'G', 0, 0, BNRMTO, BNRM, N, 1, BETA, N, IERR ) + END IF +* + WORK( 1 ) = LWKOPT + RETURN +* +* End of DGGEV3 +* + END diff --git a/SRC/dgghd3.f b/SRC/dgghd3.f new file mode 100644 index 00000000..7bed5cc4 --- /dev/null +++ b/SRC/dgghd3.f @@ -0,0 +1,898 @@ +*> \brief \b DGGHD3 +* +* =========== DOCUMENTATION =========== +* +* Online html documentation available at +* http://www.netlib.org/lapack/explore-html/ +* +*> \htmlonly +*> Download DGGHD3 + dependencies +*> <a href="http://www.netlib.org/cgi-bin/netlibfiles.tgz?format=tgz&filename=/lapack/lapack_routine/dgghd3.f"> +*> [TGZ]</a> +*> <a href="http://www.netlib.org/cgi-bin/netlibfiles.zip?format=zip&filename=/lapack/lapack_routine/dgghd3.f"> +*> [ZIP]</a> +*> <a href="http://www.netlib.org/cgi-bin/netlibfiles.txt?format=txt&filename=/lapack/lapack_routine/dgghd3.f"> +*> [TXT]</a> +*> \endhtmlonly +* +* Definition: +* =========== +* +* SUBROUTINE DGGHD3( COMPQ, COMPZ, N, ILO, IHI, A, LDA, B, LDB, Q, +* LDQ, Z, LDZ, WORK, LWORK, INFO ) +* +* .. Scalar Arguments .. +* CHARACTER COMPQ, COMPZ +* INTEGER IHI, ILO, INFO, LDA, LDB, LDQ, LDZ, N, LWORK +* .. +* .. Array Arguments .. +* DOUBLE PRECISION A( LDA, * ), B( LDB, * ), Q( LDQ, * ), +* $ Z( LDZ, * ), WORK( * ) +* .. +* +* +*> \par Purpose: +* ============= +*> +*> \verbatim +*> +*> DGGHD3 reduces a pair of real matrices (A,B) to generalized upper +*> Hessenberg form using orthogonal transformations, where A is a +*> general matrix and B is upper triangular. The form of the +*> generalized eigenvalue problem is +*> A*x = lambda*B*x, +*> and B is typically made upper triangular by computing its QR +*> factorization and moving the orthogonal matrix Q to the left side +*> of the equation. +*> +*> This subroutine simultaneously reduces A to a Hessenberg matrix H: +*> Q**T*A*Z = H +*> and transforms B to another upper triangular matrix T: +*> Q**T*B*Z = T +*> in order to reduce the problem to its standard form +*> H*y = lambda*T*y +*> where y = Z**T*x. +*> +*> The orthogonal matrices Q and Z are determined as products of Givens +*> rotations. They may either be formed explicitly, or they may be +*> postmultiplied into input matrices Q1 and Z1, so that +*> +*> Q1 * A * Z1**T = (Q1*Q) * H * (Z1*Z)**T +*> +*> Q1 * B * Z1**T = (Q1*Q) * T * (Z1*Z)**T +*> +*> If Q1 is the orthogonal matrix from the QR factorization of B in the +*> original equation A*x = lambda*B*x, then DGGHD3 reduces the original +*> problem to generalized Hessenberg form. +*> +*> This is a blocked variant of DGGHRD, using matrix-matrix +*> multiplications for parts of the computation to enhance performance. +*> \endverbatim +* +* Arguments: +* ========== +* +*> \param[in] COMPQ +*> \verbatim +*> COMPQ is CHARACTER*1 +*> = 'N': do not compute Q; +*> = 'I': Q is initialized to the unit matrix, and the +*> orthogonal matrix Q is returned; +*> = 'V': Q must contain an orthogonal matrix Q1 on entry, +*> and the product Q1*Q is returned. +*> \endverbatim +*> +*> \param[in] COMPZ +*> \verbatim +*> COMPZ is CHARACTER*1 +*> = 'N': do not compute Z; +*> = 'I': Z is initialized to the unit matrix, and the +*> orthogonal matrix Z is returned; +*> = 'V': Z must contain an orthogonal matrix Z1 on entry, +*> and the product Z1*Z is returned. +*> \endverbatim +*> +*> \param[in] N +*> \verbatim +*> N is INTEGER +*> The order of the matrices A and B. N >= 0. +*> \endverbatim +*> +*> \param[in] ILO +*> \verbatim +*> ILO is INTEGER +*> \endverbatim +*> +*> \param[in] IHI +*> \verbatim +*> IHI is INTEGER +*> +*> ILO and IHI mark the rows and columns of A which are to be +*> reduced. It is assumed that A is already upper triangular +*> in rows and columns 1:ILO-1 and IHI+1:N. ILO and IHI are +*> normally set by a previous call to DGGBAL; otherwise they +*> should be set to 1 and N respectively. +*> 1 <= ILO <= IHI <= N, if N > 0; ILO=1 and IHI=0, if N=0. +*> \endverbatim +*> +*> \param[in,out] A +*> \verbatim +*> A is DOUBLE PRECISION array, dimension (LDA, N) +*> On entry, the N-by-N general matrix to be reduced. +*> On exit, the upper triangle and the first subdiagonal of A +*> are overwritten with the upper Hessenberg matrix H, and the +*> rest is set to zero. +*> \endverbatim +*> +*> \param[in] LDA +*> \verbatim +*> LDA is INTEGER +*> The leading dimension of the array A. LDA >= max(1,N). +*> \endverbatim +*> +*> \param[in,out] B +*> \verbatim +*> B is DOUBLE PRECISION array, dimension (LDB, N) +*> On entry, the N-by-N upper triangular matrix B. +*> On exit, the upper triangular matrix T = Q**T B Z. The +*> elements below the diagonal are set to zero. +*> \endverbatim +*> +*> \param[in] LDB +*> \verbatim +*> LDB is INTEGER +*> The leading dimension of the array B. LDB >= max(1,N). +*> \endverbatim +*> +*> \param[in,out] Q +*> \verbatim +*> Q is DOUBLE PRECISION array, dimension (LDQ, N) +*> On entry, if COMPQ = 'V', the orthogonal matrix Q1, +*> typically from the QR factorization of B. +*> On exit, if COMPQ='I', the orthogonal matrix Q, and if +*> COMPQ = 'V', the product Q1*Q. +*> Not referenced if COMPQ='N'. +*> \endverbatim +*> +*> \param[in] LDQ +*> \verbatim +*> LDQ is INTEGER +*> The leading dimension of the array Q. +*> LDQ >= N if COMPQ='V' or 'I'; LDQ >= 1 otherwise. +*> \endverbatim +*> +*> \param[in,out] Z +*> \verbatim +*> Z is DOUBLE PRECISION array, dimension (LDZ, N) +*> On entry, if COMPZ = 'V', the orthogonal matrix Z1. +*> On exit, if COMPZ='I', the orthogonal matrix Z, and if +*> COMPZ = 'V', the product Z1*Z. +*> Not referenced if COMPZ='N'. +*> \endverbatim +*> +*> \param[in] LDZ +*> \verbatim +*> LDZ is INTEGER +*> The leading dimension of the array Z. +*> LDZ >= N if COMPZ='V' or 'I'; LDZ >= 1 otherwise. +*> \endverbatim +*> +*> \param[out] WORK +*> \verbatim +*> WORK is DOUBLE PRECISION array, dimension (LWORK) +*> On exit, if INFO = 0, WORK(1) returns the optimal LWORK. +*> \endverbatim +*> +*> \param[in] LWORK +*> \verbatim +*> LWORK is INTEGER +*> The length of the array WORK. LWORK >= 1. +*> For optimum performance LWORK >= 6*N*NB, where NB is the +*> optimal blocksize. +*> +*> If LWORK = -1, then a workspace query is assumed; the routine +*> only calculates the optimal size of the WORK array, returns +*> this value as the first entry of the WORK array, and no error +*> message related to LWORK is issued by XERBLA. +*> \endverbatim +*> +*> \param[out] INFO +*> \verbatim +*> INFO is INTEGER +*> = 0: successful exit. +*> < 0: if INFO = -i, the i-th argument had an illegal value. +*> \endverbatim +* +* Authors: +* ======== +* +*> \author Univ. of Tennessee +*> \author Univ. of California Berkeley +*> \author Univ. of Colorado Denver +*> \author NAG Ltd. +* +*> \date January 2015 +* +*> \ingroup doubleOTHERcomputational +* +*> \par Further Details: +* ===================== +*> +*> \verbatim +*> +*> This routine reduces A to Hessenberg form and maintains B in +*> using a blocked variant of Moler and Stewart's original algorithm, +*> as described by Kagstrom, Kressner, Quintana-Orti, and Quintana-Orti +*> (BIT 2008). +*> \endverbatim +*> +* ===================================================================== + SUBROUTINE DGGHD3( COMPQ, COMPZ, N, ILO, IHI, A, LDA, B, LDB, Q, + $ LDQ, Z, LDZ, WORK, LWORK, INFO ) +* +* -- LAPACK computational routine (version 3.6.0) -- +* -- LAPACK is a software package provided by Univ. of Tennessee, -- +* -- Univ. of California Berkeley, Univ. of Colorado Denver and NAG Ltd..-- +* January 2015 +* + IMPLICIT NONE +* +* .. Scalar Arguments .. + CHARACTER COMPQ, COMPZ + INTEGER IHI, ILO, INFO, LDA, LDB, LDQ, LDZ, N, LWORK +* .. +* .. Array Arguments .. + DOUBLE PRECISION A( LDA, * ), B( LDB, * ), Q( LDQ, * ), + $ Z( LDZ, * ), WORK( * ) +* .. +* +* ===================================================================== +* +* .. Parameters .. + DOUBLE PRECISION ZERO, ONE + PARAMETER ( ZERO = 0.0D+0, ONE = 1.0D+0 ) +* .. +* .. Local Scalars .. + LOGICAL BLK22, INITQ, INITZ, LQUERY, WANTQ, WANTZ + CHARACTER*1 COMPQ2, COMPZ2 + INTEGER COLA, I, IERR, J, J0, JCOL, JJ, JROW, K, + $ KACC22, LEN, LWKOPT, N2NB, NB, NBLST, NBMIN, + $ NH, NNB, NX, PPW, PPWO, PW, TOP, TOPQ + DOUBLE PRECISION C, C1, C2, S, S1, S2, TEMP, TEMP1, TEMP2, TEMP3 +* .. +* .. External Functions .. + LOGICAL LSAME + INTEGER ILAENV + EXTERNAL ILAENV, LSAME +* .. +* .. External Subroutines .. + EXTERNAL DGGHRD, DLARTG, DLASET, DORM22, DROT, XERBLA +* .. +* .. Intrinsic Functions .. + INTRINSIC DBLE, MAX +* .. +* .. Executable Statements .. +* +* Decode and test the input parameters. +* + INFO = 0 + NB = ILAENV( 1, 'DGGHD3', ' ', N, ILO, IHI, -1 ) + LWKOPT = 6*N*NB + WORK( 1 ) = DBLE( LWKOPT ) + INITQ = LSAME( COMPQ, 'I' ) + WANTQ = INITQ .OR. LSAME( COMPQ, 'V' ) + INITZ = LSAME( COMPZ, 'I' ) + WANTZ = INITZ .OR. LSAME( COMPZ, 'V' ) + LQUERY = ( LWORK.EQ.-1 ) +* + IF( .NOT.LSAME( COMPQ, 'N' ) .AND. .NOT.WANTQ ) THEN + INFO = -1 + ELSE IF( .NOT.LSAME( COMPZ, 'N' ) .AND. .NOT.WANTZ ) THEN + INFO = -2 + ELSE IF( N.LT.0 ) THEN + INFO = -3 + ELSE IF( ILO.LT.1 ) THEN + INFO = -4 + ELSE IF( IHI.GT.N .OR. IHI.LT.ILO-1 ) THEN + INFO = -5 + ELSE IF( LDA.LT.MAX( 1, N ) ) THEN + INFO = -7 + ELSE IF( LDB.LT.MAX( 1, N ) ) THEN + INFO = -9 + ELSE IF( ( WANTQ .AND. LDQ.LT.N ) .OR. LDQ.LT.1 ) THEN + INFO = -11 + ELSE IF( ( WANTZ .AND. LDZ.LT.N ) .OR. LDZ.LT.1 ) THEN + INFO = -13 + ELSE IF( LWORK.LT.1 .AND. .NOT.LQUERY ) THEN + INFO = -15 + END IF + IF( INFO.NE.0 ) THEN + CALL XERBLA( 'DGGHD3', -INFO ) + RETURN + ELSE IF( LQUERY ) THEN + RETURN + END IF +* +* Initialize Q and Z if desired. +* + IF( INITQ ) + $ CALL DLASET( 'All', N, N, ZERO, ONE, Q, LDQ ) + IF( INITZ ) + $ CALL DLASET( 'All', N, N, ZERO, ONE, Z, LDZ ) +* +* Zero out lower triangle of B. +* + IF( N.GT.1 ) + $ CALL DLASET( 'Lower', N-1, N-1, ZERO, ZERO, B(2, 1), LDB ) +* +* Quick return if possible +* + NH = IHI - ILO + 1 + IF( NH.LE.1 ) THEN + WORK( 1 ) = ONE + RETURN + END IF +* +* Determine the blocksize. +* + NBMIN = ILAENV( 2, 'DGGHD3', ' ', N, ILO, IHI, -1 ) + IF( NB.GT.1 .AND. NB.LT.NH ) THEN +* +* Determine when to use unblocked instead of blocked code. +* + NX = MAX( NB, ILAENV( 3, 'DGGHD3', ' ', N, ILO, IHI, -1 ) ) + IF( NX.LT.NH ) THEN +* +* Determine if workspace is large enough for blocked code. +* + IF( LWORK.LT.LWKOPT ) THEN +* +* Not enough workspace to use optimal NB: determine the +* minimum value of NB, and reduce NB or force use of +* unblocked code. +* + NBMIN = MAX( 2, ILAENV( 2, 'DGGHD3', ' ', N, ILO, IHI, + $ -1 ) ) + IF( LWORK.GE.6*N*NBMIN ) THEN + NB = LWORK / ( 6*N ) + ELSE + NB = 1 + END IF + END IF + END IF + END IF +* + IF( NB.LT.NBMIN .OR. NB.GE.NH ) THEN +* +* Use unblocked code below +* + JCOL = ILO +* + ELSE +* +* Use blocked code +* + KACC22 = ILAENV( 16, 'DGGHD3', ' ', N, ILO, IHI, -1 ) + BLK22 = KACC22.EQ.2 + DO JCOL = ILO, IHI-2, NB + NNB = MIN( NB, IHI-JCOL-1 ) +* +* Initialize small orthogonal factors that will hold the +* accumulated Givens rotations in workspace. +* N2NB denotes the number of 2*NNB-by-2*NNB factors +* NBLST denotes the (possibly smaller) order of the last +* factor. +* + N2NB = ( IHI-JCOL-1 ) / NNB - 1 + NBLST = IHI - JCOL - N2NB*NNB + CALL DLASET( 'All', NBLST, NBLST, ZERO, ONE, WORK, NBLST ) + PW = NBLST * NBLST + 1 + DO I = 1, N2NB + CALL DLASET( 'All', 2*NNB, 2*NNB, ZERO, ONE, + $ WORK( PW ), 2*NNB ) + PW = PW + 4*NNB*NNB + END DO +* +* Reduce columns JCOL:JCOL+NNB-1 of A to Hessenberg form. +* + DO J = JCOL, JCOL+NNB-1 +* +* Reduce Jth column of A. Store cosines and sines in Jth +* column of A and B, respectively. +* + DO I = IHI, J+2, -1 + TEMP = A( I-1, J ) + CALL DLARTG( TEMP, A( I, J ), C, S, A( I-1, J ) ) + A( I, J ) = C + B( I, J ) = S + END DO +* +* Accumulate Givens rotations into workspace array. +* + PPW = ( NBLST + 1 )*( NBLST - 2 ) - J + JCOL + 1 + LEN = 2 + J - JCOL + JROW = J + N2NB*NNB + 2 + DO I = IHI, JROW, -1 + C = A( I, J ) + S = B( I, J ) + DO JJ = PPW, PPW+LEN-1 + TEMP = WORK( JJ + NBLST ) + WORK( JJ + NBLST ) = C*TEMP - S*WORK( JJ ) + WORK( JJ ) = S*TEMP + C*WORK( JJ ) + END DO + LEN = LEN + 1 + PPW = PPW - NBLST - 1 + END DO +* + PPWO = NBLST*NBLST + ( NNB+J-JCOL-1 )*2*NNB + NNB + J0 = JROW - NNB + DO JROW = J0, J+2, -NNB + PPW = PPWO + LEN = 2 + J - JCOL + DO I = JROW+NNB-1, JROW, -1 + C = A( I, J ) + S = B( I, J ) + DO JJ = PPW, PPW+LEN-1 + TEMP = WORK( JJ + 2*NNB ) + WORK( JJ + 2*NNB ) = C*TEMP - S*WORK( JJ ) + WORK( JJ ) = S*TEMP + C*WORK( JJ ) + END DO + LEN = LEN + 1 + PPW = PPW - 2*NNB - 1 + END DO + PPWO = PPWO + 4*NNB*NNB + END DO +* +* TOP denotes the number of top rows in A and B that will +* not be updated during the next steps. +* + IF( JCOL.LE.2 ) THEN + TOP = 0 + ELSE + TOP = JCOL + END IF +* +* Propagate transformations through B and replace stored +* left sines/cosines by right sines/cosines. +* + DO JJ = N, J+1, -1 +* +* Update JJth column of B. +* + DO I = MIN( JJ+1, IHI ), J+2, -1 + C = A( I, J ) + S = B( I, J ) + TEMP = B( I, JJ ) + B( I, JJ ) = C*TEMP - S*B( I-1, JJ ) + B( I-1, JJ ) = S*TEMP + C*B( I-1, JJ ) + END DO +* +* Annihilate B( JJ+1, JJ ). +* + IF( JJ.LT.IHI ) THEN + TEMP = B( JJ+1, JJ+1 ) + CALL DLARTG( TEMP, B( JJ+1, JJ ), C, S, + $ B( JJ+1, JJ+1 ) ) + B( JJ+1, JJ ) = ZERO + CALL DROT( JJ-TOP, B( TOP+1, JJ+1 ), 1, + $ B( TOP+1, JJ ), 1, C, S ) + A( JJ+1, J ) = C + B( JJ+1, J ) = -S + END IF + END DO +* +* Update A by transformations from right. +* Explicit loop unrolling provides better performance +* compared to DLASR. +* CALL DLASR( 'Right', 'Variable', 'Backward', IHI-TOP, +* $ IHI-J, A( J+2, J ), B( J+2, J ), +* $ A( TOP+1, J+1 ), LDA ) +* + JJ = MOD( IHI-J-1, 3 ) + DO I = IHI-J-3, JJ+1, -3 + C = A( J+1+I, J ) + S = -B( J+1+I, J ) + C1 = A( J+2+I, J ) + S1 = -B( J+2+I, J ) + C2 = A( J+3+I, J ) + S2 = -B( J+3+I, J ) +* + DO K = TOP+1, IHI + TEMP = A( K, J+I ) + TEMP1 = A( K, J+I+1 ) + TEMP2 = A( K, J+I+2 ) + TEMP3 = A( K, J+I+3 ) + A( K, J+I+3 ) = C2*TEMP3 + S2*TEMP2 + TEMP2 = -S2*TEMP3 + C2*TEMP2 + A( K, J+I+2 ) = C1*TEMP2 + S1*TEMP1 + TEMP1 = -S1*TEMP2 + C1*TEMP1 + A( K, J+I+1 ) = C*TEMP1 + S*TEMP + A( K, J+I ) = -S*TEMP1 + C*TEMP + END DO + END DO +* + IF( JJ.GT.0 ) THEN + DO I = JJ, 1, -1 + CALL DROT( IHI-TOP, A( TOP+1, J+I+1 ), 1, + $ A( TOP+1, J+I ), 1, A( J+1+I, J ), + $ -B( J+1+I, J ) ) + END DO + END IF +* +* Update (J+1)th column of A by transformations from left. +* + IF ( J .LT. JCOL + NNB - 1 ) THEN + LEN = 1 + J - JCOL +* +* Multiply with the trailing accumulated orthogonal +* matrix, which takes the form +* +* [ U11 U12 ] +* U = [ ], +* [ U21 U22 ] +* +* where U21 is a LEN-by-LEN matrix and U12 is lower +* triangular. +* + JROW = IHI - NBLST + 1 + CALL DGEMV( 'Transpose', NBLST, LEN, ONE, WORK, + $ NBLST, A( JROW, J+1 ), 1, ZERO, + $ WORK( PW ), 1 ) + PPW = PW + LEN + DO I = JROW, JROW+NBLST-LEN-1 + WORK( PPW ) = A( I, J+1 ) + PPW = PPW + 1 + END DO + CALL DTRMV( 'Lower', 'Transpose', 'Non-unit', + $ NBLST-LEN, WORK( LEN*NBLST + 1 ), NBLST, + $ WORK( PW+LEN ), 1 ) + CALL DGEMV( 'Transpose', LEN, NBLST-LEN, ONE, + $ WORK( (LEN+1)*NBLST - LEN + 1 ), NBLST, + $ A( JROW+NBLST-LEN, J+1 ), 1, ONE, + $ WORK( PW+LEN ), 1 ) + PPW = PW + DO I = JROW, JROW+NBLST-1 + A( I, J+1 ) = WORK( PPW ) + PPW = PPW + 1 + END DO +* +* Multiply with the other accumulated orthogonal +* matrices, which take the form +* +* [ U11 U12 0 ] +* [ ] +* U = [ U21 U22 0 ], +* [ ] +* [ 0 0 I ] +* +* where I denotes the (NNB-LEN)-by-(NNB-LEN) identity +* matrix, U21 is a LEN-by-LEN upper triangular matrix +* and U12 is an NNB-by-NNB lower triangular matrix. +* + PPWO = 1 + NBLST*NBLST + J0 = JROW - NNB + DO JROW = J0, JCOL+1, -NNB + PPW = PW + LEN + DO I = JROW, JROW+NNB-1 + WORK( PPW ) = A( I, J+1 ) + PPW = PPW + 1 + END DO + PPW = PW + DO I = JROW+NNB, JROW+NNB+LEN-1 + WORK( PPW ) = A( I, J+1 ) + PPW = PPW + 1 + END DO + CALL DTRMV( 'Upper', 'Transpose', 'Non-unit', LEN, + $ WORK( PPWO + NNB ), 2*NNB, WORK( PW ), + $ 1 ) + CALL DTRMV( 'Lower', 'Transpose', 'Non-unit', NNB, + $ WORK( PPWO + 2*LEN*NNB ), + $ 2*NNB, WORK( PW + LEN ), 1 ) + CALL DGEMV( 'Transpose', NNB, LEN, ONE, + $ WORK( PPWO ), 2*NNB, A( JROW, J+1 ), 1, + $ ONE, WORK( PW ), 1 ) + CALL DGEMV( 'Transpose', LEN, NNB, ONE, + $ WORK( PPWO + 2*LEN*NNB + NNB ), 2*NNB, + $ A( JROW+NNB, J+1 ), 1, ONE, + $ WORK( PW+LEN ), 1 ) + PPW = PW + DO I = JROW, JROW+LEN+NNB-1 + A( I, J+1 ) = WORK( PPW ) + PPW = PPW + 1 + END DO + PPWO = PPWO + 4*NNB*NNB + END DO + END IF + END DO +* +* Apply accumulated orthogonal matrices to A. +* + COLA = N - JCOL - NNB + 1 + J = IHI - NBLST + 1 + CALL DGEMM( 'Transpose', 'No Transpose', NBLST, + $ COLA, NBLST, ONE, WORK, NBLST, + $ A( J, JCOL+NNB ), LDA, ZERO, WORK( PW ), + $ NBLST ) + CALL DLACPY( 'All', NBLST, COLA, WORK( PW ), NBLST, + $ A( J, JCOL+NNB ), LDA ) + PPWO = NBLST*NBLST + 1 + J0 = J - NNB + DO J = J0, JCOL+1, -NNB + IF ( BLK22 ) THEN +* +* Exploit the structure of +* +* [ U11 U12 ] +* U = [ ] +* [ U21 U22 ], +* +* where all blocks are NNB-by-NNB, U21 is upper +* triangular and U12 is lower triangular. +* + CALL DORM22( 'Left', 'Transpose', 2*NNB, COLA, NNB, + $ NNB, WORK( PPWO ), 2*NNB, + $ A( J, JCOL+NNB ), LDA, WORK( PW ), + $ LWORK-PW+1, IERR ) + ELSE +* +* Ignore the structure of U. +* + CALL DGEMM( 'Transpose', 'No Transpose', 2*NNB, + $ COLA, 2*NNB, ONE, WORK( PPWO ), 2*NNB, + $ A( J, JCOL+NNB ), LDA, ZERO, WORK( PW ), + $ 2*NNB ) + CALL DLACPY( 'All', 2*NNB, COLA, WORK( PW ), 2*NNB, + $ A( J, JCOL+NNB ), LDA ) + END IF + PPWO = PPWO + 4*NNB*NNB + END DO +* +* Apply accumulated orthogonal matrices to Q. +* + IF( WANTQ ) THEN + J = IHI - NBLST + 1 + IF ( INITQ ) THEN + TOPQ = MAX( 2, J - JCOL + 1 ) + NH = IHI - TOPQ + 1 + ELSE + TOPQ = 1 + NH = N + END IF + CALL DGEMM( 'No Transpose', 'No Transpose', NH, + $ NBLST, NBLST, ONE, Q( TOPQ, J ), LDQ, + $ WORK, NBLST, ZERO, WORK( PW ), NH ) + CALL DLACPY( 'All', NH, NBLST, WORK( PW ), NH, + $ Q( TOPQ, J ), LDQ ) + PPWO = NBLST*NBLST + 1 + J0 = J - NNB + DO J = J0, JCOL+1, -NNB + IF ( INITQ ) THEN + TOPQ = MAX( 2, J - JCOL + 1 ) + NH = IHI - TOPQ + 1 + END IF + IF ( BLK22 ) THEN +* +* Exploit the structure of U. +* + CALL DORM22( 'Right', 'No Transpose', NH, 2*NNB, + $ NNB, NNB, WORK( PPWO ), 2*NNB, + $ Q( TOPQ, J ), LDQ, WORK( PW ), + $ LWORK-PW+1, IERR ) + ELSE +* +* Ignore the structure of U. +* + CALL DGEMM( 'No Transpose', 'No Transpose', NH, + $ 2*NNB, 2*NNB, ONE, Q( TOPQ, J ), LDQ, + $ WORK( PPWO ), 2*NNB, ZERO, WORK( PW ), + $ NH ) + CALL DLACPY( 'All', NH, 2*NNB, WORK( PW ), NH, + $ Q( TOPQ, J ), LDQ ) + END IF + PPWO = PPWO + 4*NNB*NNB + END DO + END IF +* +* Accumulate right Givens rotations if required. +* + IF ( WANTZ .OR. TOP.GT.0 ) THEN +* +* Initialize small orthogonal factors that will hold the +* accumulated Givens rotations in workspace. +* + CALL DLASET( 'All', NBLST, NBLST, ZERO, ONE, WORK, + $ NBLST ) + PW = NBLST * NBLST + 1 + DO I = 1, N2NB + CALL DLASET( 'All', 2*NNB, 2*NNB, ZERO, ONE, + $ WORK( PW ), 2*NNB ) + PW = PW + 4*NNB*NNB + END DO +* +* Accumulate Givens rotations into workspace array. +* + DO J = JCOL, JCOL+NNB-1 + PPW = ( NBLST + 1 )*( NBLST - 2 ) - J + JCOL + 1 + LEN = 2 + J - JCOL + JROW = J + N2NB*NNB + 2 + DO I = IHI, JROW, -1 + C = A( I, J ) + A( I, J ) = ZERO + S = B( I, J ) + B( I, J ) = ZERO + DO JJ = PPW, PPW+LEN-1 + TEMP = WORK( JJ + NBLST ) + WORK( JJ + NBLST ) = C*TEMP - S*WORK( JJ ) + WORK( JJ ) = S*TEMP + C*WORK( JJ ) + END DO + LEN = LEN + 1 + PPW = PPW - NBLST - 1 + END DO +* + PPWO = NBLST*NBLST + ( NNB+J-JCOL-1 )*2*NNB + NNB + J0 = JROW - NNB + DO JROW = J0, J+2, -NNB + PPW = PPWO + LEN = 2 + J - JCOL + DO I = JROW+NNB-1, JROW, -1 + C = A( I, J ) + A( I, J ) = ZERO + S = B( I, J ) + B( I, J ) = ZERO + DO JJ = PPW, PPW+LEN-1 + TEMP = WORK( JJ + 2*NNB ) + WORK( JJ + 2*NNB ) = C*TEMP - S*WORK( JJ ) + WORK( JJ ) = S*TEMP + C*WORK( JJ ) + END DO + LEN = LEN + 1 + PPW = PPW - 2*NNB - 1 + END DO + PPWO = PPWO + 4*NNB*NNB + END DO + END DO + ELSE + + DO J = ILO, ILO+NNB + DO I = J+2, IHI + A( I, J ) = ZERO + B( I, J ) = ZERO + END DO + END DO + END IF +* +* Apply accumulated orthogonal matrices to A and B. +* + IF ( TOP.GT.0 ) THEN + J = IHI - NBLST + 1 + CALL DGEMM( 'No Transpose', 'No Transpose', TOP, + $ NBLST, NBLST, ONE, A( 1, J ), LDA, + $ WORK, NBLST, ZERO, WORK( PW ), TOP ) + CALL DLACPY( 'All', TOP, NBLST, WORK( PW ), TOP, + $ A( 1, J ), LDA ) + PPWO = NBLST*NBLST + 1 + J0 = J - NNB + DO J = J0, JCOL+1, -NNB + IF ( BLK22 ) THEN +* +* Exploit the structure of U. +* + CALL DORM22( 'Right', 'No Transpose', TOP, 2*NNB, + $ NNB, NNB, WORK( PPWO ), 2*NNB, + $ A( 1, J ), LDA, WORK( PW ), + $ LWORK-PW+1, IERR ) + ELSE +* +* Ignore the structure of U. +* + CALL DGEMM( 'No Transpose', 'No Transpose', TOP, + $ 2*NNB, 2*NNB, ONE, A( 1, J ), LDA, + $ WORK( PPWO ), 2*NNB, ZERO, + $ WORK( PW ), TOP ) + CALL DLACPY( 'All', TOP, 2*NNB, WORK( PW ), TOP, + $ A( 1, J ), LDA ) + END IF + PPWO = PPWO + 4*NNB*NNB + END DO +* + J = IHI - NBLST + 1 + CALL DGEMM( 'No Transpose', 'No Transpose', TOP, + $ NBLST, NBLST, ONE, B( 1, J ), LDB, + $ WORK, NBLST, ZERO, WORK( PW ), TOP ) + CALL DLACPY( 'All', TOP, NBLST, WORK( PW ), TOP, + $ B( 1, J ), LDB ) + PPWO = NBLST*NBLST + 1 + J0 = J - NNB + DO J = J0, JCOL+1, -NNB + IF ( BLK22 ) THEN +* +* Exploit the structure of U. +* + CALL DORM22( 'Right', 'No Transpose', TOP, 2*NNB, + $ NNB, NNB, WORK( PPWO ), 2*NNB, + $ B( 1, J ), LDA, WORK( PW ), + $ LWORK-PW+1, IERR ) + ELSE +* +* Ignore the structure of U. +* + CALL DGEMM( 'No Transpose', 'No Transpose', TOP, + $ 2*NNB, 2*NNB, ONE, B( 1, J ), LDB, + $ WORK( PPWO ), 2*NNB, ZERO, + $ WORK( PW ), TOP ) + CALL DLACPY( 'All', TOP, 2*NNB, WORK( PW ), TOP, + $ B( 1, J ), LDB ) + END IF + PPWO = PPWO + 4*NNB*NNB + END DO + END IF +* +* Apply accumulated orthogonal matrices to Z. +* + IF( WANTZ ) THEN + J = IHI - NBLST + 1 + IF ( INITQ ) THEN + TOPQ = MAX( 2, J - JCOL + 1 ) + NH = IHI - TOPQ + 1 + ELSE + TOPQ = 1 + NH = N + END IF + CALL DGEMM( 'No Transpose', 'No Transpose', NH, + $ NBLST, NBLST, ONE, Z( TOPQ, J ), LDZ, + $ WORK, NBLST, ZERO, WORK( PW ), NH ) + CALL DLACPY( 'All', NH, NBLST, WORK( PW ), NH, + $ Z( TOPQ, J ), LDZ ) + PPWO = NBLST*NBLST + 1 + J0 = J - NNB + DO J = J0, JCOL+1, -NNB + IF ( INITQ ) THEN + TOPQ = MAX( 2, J - JCOL + 1 ) + NH = IHI - TOPQ + 1 + END IF + IF ( BLK22 ) THEN +* +* Exploit the structure of U. +* + CALL DORM22( 'Right', 'No Transpose', NH, 2*NNB, + $ NNB, NNB, WORK( PPWO ), 2*NNB, + $ Z( TOPQ, J ), LDZ, WORK( PW ), + $ LWORK-PW+1, IERR ) + ELSE +* +* Ignore the structure of U. +* + CALL DGEMM( 'No Transpose', 'No Transpose', NH, + $ 2*NNB, 2*NNB, ONE, Z( TOPQ, J ), LDZ, + $ WORK( PPWO ), 2*NNB, ZERO, WORK( PW ), + $ NH ) + CALL DLACPY( 'All', NH, 2*NNB, WORK( PW ), NH, + $ Z( TOPQ, J ), LDZ ) + END IF + PPWO = PPWO + 4*NNB*NNB + END DO + END IF + END DO + END IF +* +* Use unblocked code to reduce the rest of the matrix +* Avoid re-initialization of modified Q and Z. +* + COMPQ2 = COMPQ + COMPZ2 = COMPZ + IF ( JCOL.NE.ILO ) THEN + IF ( WANTQ ) + $ COMPQ2 = 'V' + IF ( WANTZ ) + $ COMPZ2 = 'V' + END IF +* + IF ( JCOL.LT.IHI ) + $ CALL DGGHRD( COMPQ2, COMPZ2, N, JCOL, IHI, A, LDA, B, LDB, Q, + $ LDQ, Z, LDZ, IERR ) + WORK( 1 ) = DBLE( LWKOPT ) +* + RETURN +* +* End of DGGHD3 +* + END diff --git a/SRC/dorm22.f b/SRC/dorm22.f new file mode 100644 index 00000000..ac79e1e7 --- /dev/null +++ b/SRC/dorm22.f @@ -0,0 +1,441 @@ +*> \brief \b DORM22 multiplies a general matrix by a banded orthogonal matrix. +* +* =========== DOCUMENTATION =========== +* +* Online html documentation available at +* http://www.netlib.org/lapack/explore-html/ +* +*> \htmlonly +*> Download DORM22 + dependencies +*> <a href="http://www.netlib.org/cgi-bin/netlibfiles.tgz?format=tgz&filename=/lapack/lapack_routine/dorm22.f"> +*> [TGZ]</a> +*> <a href="http://www.netlib.org/cgi-bin/netlibfiles.zip?format=zip&filename=/lapack/lapack_routine/dorm22.f"> +*> [ZIP]</a> +*> <a href="http://www.netlib.org/cgi-bin/netlibfiles.txt?format=txt&filename=/lapack/lapack_routine/dorm22.f"> +*> [TXT]</a> +*> \endhtmlonly +* +* Definition: +* =========== +* +* SUBROUTINE DORM22( SIDE, TRANS, M, N, N1, N2, Q, LDQ, C, LDC, +* $ WORK, LWORK, INFO ) +* +* .. Scalar Arguments .. +* CHARACTER SIDE, TRANS +* INTEGER M, N, N1, N2, LDQ, LDC, LWORK, INFO +* .. +* .. Array Arguments .. +* DOUBLE PRECISION Q( LDQ, * ), C( LDC, * ), WORK( * ) +* .. +* +*> \par Purpose +* ============ +*> +*> \verbatim +*> +*> +*> DORM22 overwrites the general real M-by-N matrix C with +*> +*> SIDE = 'L' SIDE = 'R' +*> TRANS = 'N': Q * C C * Q +*> TRANS = 'T': Q**T * C C * Q**T +*> +*> where Q is a real orthogonal matrix of order NQ, with NQ = M if +*> SIDE = 'L' and NQ = N if SIDE = 'R'. +*> The orthogonal matrix Q processes a 2-by-2 block structure +*> +*> [ Q11 Q12 ] +*> Q = [ ] +*> [ Q21 Q22 ], +*> +*> where Q12 is an N1-by-N1 lower triangular matrix and Q21 is an +*> N2-by-N2 upper triangular matrix. +*> \endverbatim +* +* Arguments +* ========= +* +*> \param[in] SIDE +*> \verbatim +*> SIDE is CHARACTER*1 +*> = 'L': apply Q or Q**T from the Left; +*> = 'R': apply Q or Q**T from the Right. +*> \endverbatim +*> +*> \param[in] TRANS +*> \verbatim +*> TRANS is CHARACTER*1 +*> = 'N': apply Q (No transpose); +*> = 'C': apply Q**T (Conjugate transpose). +*> \endverbatim +*> +*> \param[in] M +*> \verbatim +*> M is INTEGER +*> The number of rows of the matrix C. M >= 0. +*> \endverbatim +*> +*> \param[in] N +*> \verbatim +*> N is INTEGER +*> The number of columns of the matrix C. N >= 0. +*> \endverbatim +*> +*> \param[in] N1 +*> \param[in] N2 +*> \verbatim +*> N1 is INTEGER +*> N2 is INTEGER +*> The dimension of Q12 and Q21, respectively. N1, N2 >= 0. +*> The following requirement must be satisfied: +*> N1 + N2 = M if SIDE = 'L' and N1 + N2 = N if SIDE = 'R'. +*> \endverbatim +*> +*> \param[in] Q +*> \verbatim +*> Q is DOUBLE PRECISION array, dimension +*> (LDQ,M) if SIDE = 'L' +*> (LDQ,N) if SIDE = 'R' +*> \endverbatim +*> +*> \param[in] LDQ +*> \verbatim +*> LDQ is INTEGER +*> The leading dimension of the array Q. +*> LDQ >= max(1,M) if SIDE = 'L'; LDQ >= max(1,N) if SIDE = 'R'. +*> \endverbatim +*> +*> \param[in,out] C +*> \verbatim +*> C is DOUBLE PRECISION array, dimension (LDC,N) +*> On entry, the M-by-N matrix C. +*> On exit, C is overwritten by Q*C or Q**T*C or C*Q**T or C*Q. +*> \endverbatim +*> +*> \param[in] LDC +*> \verbatim +*> LDC is INTEGER +*> The leading dimension of the array C. LDC >= max(1,M). +*> \endverbatim +*> +*> \param[out] WORK +*> \verbatim +*> WORK is DOUBLE PRECISION array, dimension (MAX(1,LWORK)) +*> On exit, if INFO = 0, WORK(1) returns the optimal LWORK. +*> \endverbatim +*> +*> \param[in] LWORK +*> \verbatim +*> LWORK is INTEGER +*> The dimension of the array WORK. +*> If SIDE = 'L', LWORK >= max(1,N); +*> if SIDE = 'R', LWORK >= max(1,M). +*> For optimum performance LWORK >= M*N. +*> +*> If LWORK = -1, then a workspace query is assumed; the routine +*> only calculates the optimal size of the WORK array, returns +*> this value as the first entry of the WORK array, and no error +*> message related to LWORK is issued by XERBLA. +*> \endverbatim +*> +*> \param[out] INFO +*> \verbatim +*> INFO is INTEGER +*> = 0: successful exit +*> < 0: if INFO = -i, the i-th argument had an illegal value +*> \endverbatim +* +* +* Authors: +* ======== +* +*> \author Univ. of Tennessee +*> \author Univ. of California Berkeley +*> \author Univ. of Colorado Denver +*> \author NAG Ltd. +* +*> \date January 2015 +* +*> \ingroup complexOTHERcomputational +* +* ===================================================================== + SUBROUTINE DORM22( SIDE, TRANS, M, N, N1, N2, Q, LDQ, C, LDC, + $ WORK, LWORK, INFO ) +* +* -- LAPACK computational routine (version 3.6.0) -- +* -- LAPACK is a software package provided by Univ. of Tennessee, -- +* -- Univ. of California Berkeley, Univ. of Colorado Denver and NAG Ltd..-- +* January 2015 +* + IMPLICIT NONE +* +* .. Scalar Arguments .. + CHARACTER SIDE, TRANS + INTEGER M, N, N1, N2, LDQ, LDC, LWORK, INFO +* .. +* .. Array Arguments .. + DOUBLE PRECISION Q( LDQ, * ), C( LDC, * ), WORK( * ) +* .. +* +* ===================================================================== +* +* .. Parameters .. + DOUBLE PRECISION ONE + PARAMETER ( ONE = 1.0D+0 ) +* +* .. Local Scalars .. + LOGICAL LEFT, LQUERY, NOTRAN + INTEGER I, LDWORK, LEN, LWKOPT, NB, NQ, NW +* .. +* .. External Functions .. + LOGICAL LSAME + EXTERNAL LSAME +* .. +* .. External Subroutines .. + EXTERNAL DGEMM, DLACPY, DTRMM, XERBLA +* .. +* .. Intrinsic Functions .. + INTRINSIC DBLE, MAX, MIN +* .. +* .. Executable Statements .. +* +* Test the input arguments +* + INFO = 0 + LEFT = LSAME( SIDE, 'L' ) + NOTRAN = LSAME( TRANS, 'N' ) + LQUERY = ( LWORK.EQ.-1 ) +* +* NQ is the order of Q; +* NW is the minimum dimension of WORK. +* + IF( LEFT ) THEN + NQ = M + ELSE + NQ = N + END IF + NW = NQ + IF( N1.EQ.0 .OR. N2.EQ.0 ) NW = 1 + IF( .NOT.LEFT .AND. .NOT.LSAME( SIDE, 'R' ) ) THEN + INFO = -1 + ELSE IF( .NOT.LSAME( TRANS, 'N' ) .AND. .NOT.LSAME( TRANS, 'T' ) ) + $ THEN + INFO = -2 + ELSE IF( M.LT.0 ) THEN + INFO = -3 + ELSE IF( N.LT.0 ) THEN + INFO = -4 + ELSE IF( N1.LT.0 .OR. N1+N2.NE.NQ ) THEN + INFO = -5 + ELSE IF( N2.LT.0 ) THEN + INFO = -6 + ELSE IF( LDQ.LT.MAX( 1, NQ ) ) THEN + INFO = -8 + ELSE IF( LDC.LT.MAX( 1, M ) ) THEN + INFO = -10 + ELSE IF( LWORK.LT.NW .AND. .NOT.LQUERY ) THEN + INFO = -12 + END IF +* + IF( INFO.EQ.0 ) THEN + LWKOPT = M*N + WORK( 1 ) = DBLE( LWKOPT ) + END IF +* + IF( INFO.NE.0 ) THEN + CALL XERBLA( 'DORM22', -INFO ) + RETURN + ELSE IF( LQUERY ) THEN + RETURN + END IF +* +* Quick return if possible +* + IF( M.EQ.0 .OR. N.EQ.0 ) THEN + WORK( 1 ) = 1 + RETURN + END IF +* +* Degenerate cases (N1 = 0 or N2 = 0) are handled using DTRMM. +* + IF( N1.EQ.0 ) THEN + CALL DTRMM( SIDE, 'Upper', TRANS, 'Non-Unit', M, N, ONE, + $ Q, LDQ, C, LDC ) + WORK( 1 ) = ONE + RETURN + ELSE IF( N2.EQ.0 ) THEN + CALL DTRMM( SIDE, 'Lower', TRANS, 'Non-Unit', M, N, ONE, + $ Q, LDQ, C, LDC ) + WORK( 1 ) = ONE + RETURN + END IF +* +* Compute the largest chunk size available from the workspace. +* + NB = MAX( 1, MIN( LWORK, LWKOPT ) / NQ ) +* + IF( LEFT ) THEN + IF( NOTRAN ) THEN + DO I = 1, N, NB + LEN = MIN( NB, N-I+1 ) + LDWORK = M +* +* Multiply bottom part of C by Q12. +* + CALL DLACPY( 'All', N1, LEN, C( N2+1, I ), LDC, WORK, + $ LDWORK ) + CALL DTRMM( 'Left', 'Lower', 'No Transpose', 'Non-Unit', + $ N1, LEN, ONE, Q( 1, N2+1 ), LDQ, WORK, + $ LDWORK ) +* +* Multiply top part of C by Q11. +* + CALL DGEMM( 'No Transpose', 'No Transpose', N1, LEN, N2, + $ ONE, Q, LDQ, C( 1, I ), LDC, ONE, WORK, + $ LDWORK ) +* +* Multiply top part of C by Q21. +* + CALL DLACPY( 'All', N2, LEN, C( 1, I ), LDC, + $ WORK( N1+1 ), LDWORK ) + CALL DTRMM( 'Left', 'Upper', 'No Transpose', 'Non-Unit', + $ N2, LEN, ONE, Q( N1+1, 1 ), LDQ, + $ WORK( N1+1 ), LDWORK ) +* +* Multiply bottom part of C by Q22. +* + CALL DGEMM( 'No Transpose', 'No Transpose', N2, LEN, N1, + $ ONE, Q( N1+1, N2+1 ), LDQ, C( N2+1, I ), LDC, + $ ONE, WORK( N1+1 ), LDWORK ) +* +* Copy everything back. +* + CALL DLACPY( 'All', M, LEN, WORK, LDWORK, C( 1, I ), + $ LDC ) + END DO + ELSE + DO I = 1, N, NB + LEN = MIN( NB, N-I+1 ) + LDWORK = M +* +* Multiply bottom part of C by Q21**T. +* + CALL DLACPY( 'All', N2, LEN, C( N1+1, I ), LDC, WORK, + $ LDWORK ) + CALL DTRMM( 'Left', 'Upper', 'Transpose', 'Non-Unit', + $ N2, LEN, ONE, Q( N1+1, 1 ), LDQ, WORK, + $ LDWORK ) +* +* Multiply top part of C by Q11**T. +* + CALL DGEMM( 'Transpose', 'No Transpose', N2, LEN, N1, + $ ONE, Q, LDQ, C( 1, I ), LDC, ONE, WORK, + $ LDWORK ) +* +* Multiply top part of C by Q12**T. +* + CALL DLACPY( 'All', N1, LEN, C( 1, I ), LDC, + $ WORK( N2+1 ), LDWORK ) + CALL DTRMM( 'Left', 'Lower', 'Transpose', 'Non-Unit', + $ N1, LEN, ONE, Q( 1, N2+1 ), LDQ, + $ WORK( N2+1 ), LDWORK ) +* +* Multiply bottom part of C by Q22**T. +* + CALL DGEMM( 'Transpose', 'No Transpose', N1, LEN, N2, + $ ONE, Q( N1+1, N2+1 ), LDQ, C( N1+1, I ), LDC, + $ ONE, WORK( N2+1 ), LDWORK ) +* +* Copy everything back. +* + CALL DLACPY( 'All', M, LEN, WORK, LDWORK, C( 1, I ), + $ LDC ) + END DO + END IF + ELSE + IF( NOTRAN ) THEN + DO I = 1, M, NB + LEN = MIN( NB, M-I+1 ) + LDWORK = LEN +* +* Multiply right part of C by Q21. +* + CALL DLACPY( 'All', LEN, N2, C( I, N1+1 ), LDC, WORK, + $ LDWORK ) + CALL DTRMM( 'Right', 'Upper', 'No Transpose', 'Non-Unit', + $ LEN, N2, ONE, Q( N1+1, 1 ), LDQ, WORK, + $ LDWORK ) +* +* Multiply left part of C by Q11. +* + CALL DGEMM( 'No Transpose', 'No Transpose', LEN, N2, N1, + $ ONE, C( I, 1 ), LDC, Q, LDQ, ONE, WORK, + $ LDWORK ) +* +* Multiply left part of C by Q12. +* + CALL DLACPY( 'All', LEN, N1, C( I, 1 ), LDC, + $ WORK( 1 + N2*LDWORK ), LDWORK ) + CALL DTRMM( 'Right', 'Lower', 'No Transpose', 'Non-Unit', + $ LEN, N1, ONE, Q( 1, N2+1 ), LDQ, + $ WORK( 1 + N2*LDWORK ), LDWORK ) +* +* Multiply right part of C by Q22. +* + CALL DGEMM( 'No Transpose', 'No Transpose', LEN, N1, N2, + $ ONE, C( I, N1+1 ), LDC, Q( N1+1, N2+1 ), LDQ, + $ ONE, WORK( 1 + N2*LDWORK ), LDWORK ) +* +* Copy everything back. +* + CALL DLACPY( 'All', LEN, N, WORK, LDWORK, C( I, 1 ), + $ LDC ) + END DO + ELSE + DO I = 1, M, NB + LEN = MIN( NB, M-I+1 ) + LDWORK = LEN +* +* Multiply right part of C by Q12**T. +* + CALL DLACPY( 'All', LEN, N1, C( I, N2+1 ), LDC, WORK, + $ LDWORK ) + CALL DTRMM( 'Right', 'Lower', 'Transpose', 'Non-Unit', + $ LEN, N1, ONE, Q( 1, N2+1 ), LDQ, WORK, + $ LDWORK ) +* +* Multiply left part of C by Q11**T. +* + CALL DGEMM( 'No Transpose', 'Transpose', LEN, N1, N2, + $ ONE, C( I, 1 ), LDC, Q, LDQ, ONE, WORK, + $ LDWORK ) +* +* Multiply left part of C by Q21**T. +* + CALL DLACPY( 'All', LEN, N2, C( I, 1 ), LDC, + $ WORK( 1 + N1*LDWORK ), LDWORK ) + CALL DTRMM( 'Right', 'Upper', 'Transpose', 'Non-Unit', + $ LEN, N2, ONE, Q( N1+1, 1 ), LDQ, + $ WORK( 1 + N1*LDWORK ), LDWORK ) +* +* Multiply right part of C by Q22**T. +* + CALL DGEMM( 'No Transpose', 'Transpose', LEN, N2, N1, + $ ONE, C( I, N2+1 ), LDC, Q( N1+1, N2+1 ), LDQ, + $ ONE, WORK( 1 + N1*LDWORK ), LDWORK ) +* +* Copy everything back. +* + CALL DLACPY( 'All', LEN, N, WORK, LDWORK, C( I, 1 ), + $ LDC ) + END DO + END IF + END IF +* + WORK( 1 ) = DBLE( LWKOPT ) + RETURN +* +* End of DORM22 +* + END diff --git a/SRC/ilaenv.f b/SRC/ilaenv.f index 867464de..010b5ed6 100644 --- a/SRC/ilaenv.f +++ b/SRC/ilaenv.f @@ -82,7 +82,7 @@ *> =10: ieee NaN arithmetic can be trusted not to trap *> =11: infinity arithmetic can be trusted not to trap *> 12 <= ISPEC <= 16: -*> xHSEQR or one of its subroutines, +*> xHSEQR or related subroutines, *> see IPARMQ for detailed explanation *> \endverbatim *> @@ -410,6 +410,15 @@ IF( C3.EQ.'EBZ' ) THEN NB = 1 END IF + ELSE IF( C2.EQ.'GG' ) THEN + NB = 32 + IF( C3.EQ.'HD3' ) THEN + IF( SNAME ) THEN + NB = 32 + ELSE + NB = 32 + END IF + END IF END IF ILAENV = NB RETURN @@ -488,6 +497,11 @@ NBMIN = 2 END IF END IF + ELSE IF( C2.EQ.'GG' ) THEN + NBMIN = 2 + IF( C3.EQ.'HD3' ) THEN + NBMIN = 2 + END IF END IF ILAENV = NBMIN RETURN @@ -542,6 +556,11 @@ NX = 128 END IF END IF + ELSE IF( C2.EQ.'GG' ) THEN + NX = 128 + IF( C3.EQ.'HD3' ) THEN + NX = 128 + END IF END IF ILAENV = NX RETURN @@ -614,7 +633,7 @@ * 160 CONTINUE * -* 12 <= ISPEC <= 16: xHSEQR or one of its subroutines. +* 12 <= ISPEC <= 16: xHSEQR or related subroutines. * ILAENV = IPARMQ( ISPEC, NAME, OPTS, N1, N2, N3, N4 ) RETURN diff --git a/SRC/iparmq.f b/SRC/iparmq.f index bd5bd7a0..581e1cb1 100644 --- a/SRC/iparmq.f +++ b/SRC/iparmq.f @@ -31,7 +31,8 @@ *> \verbatim *> *> This program sets problem and machine dependent parameters -*> useful for xHSEQR and its subroutines. It is called whenever +*> useful for xHSEQR and related subroutines for eigenvalue +*> problems. It is called whenever *> ILAENV is called with 12 <= ISPEC <= 16 *> \endverbatim * @@ -75,19 +76,26 @@ *> *> ISPEC=16: (IACC22) IPARMQ is set to 0, 1 or 2 with the *> following meanings. -*> 0: During the multi-shift QR sweep, -*> xLAQR5 does not accumulate reflections and -*> does not use matrix-matrix multiply to -*> update the far-from-diagonal matrix -*> entries. -*> 1: During the multi-shift QR sweep, -*> xLAQR5 and/or xLAQRaccumulates reflections and uses -*> matrix-matrix multiply to update the +*> 0: During the multi-shift QR/QZ sweep, +*> blocked eigenvalue reordering, blocked +*> Hessenberg-triangular reduction, +*> reflections and/or rotations are not +*> accumulated when updating the *> far-from-diagonal matrix entries. -*> 2: During the multi-shift QR sweep. -*> xLAQR5 accumulates reflections and takes -*> advantage of 2-by-2 block structure during -*> matrix-matrix multiplies. +*> 1: During the multi-shift QR/QZ sweep, +*> blocked eigenvalue reordering, blocked +*> Hessenberg-triangular reduction, +*> reflections and/or rotations are +*> accumulated, and matrix-matrix +*> multiplication is used to update the +*> far-from-diagonal matrix entries. +*> 2: During the multi-shift QR/QZ sweep, +*> blocked eigenvalue reordering, blocked +*> Hessenberg-triangular reduction, +*> reflections and/or rotations are +*> accumulated, and 2-by-2 block structure +*> is exploited during matrix-matrix +*> multiplies. *> (If xTRMM is slower than xGEMM, then *> IPARMQ(ISPEC=16)=1 may be more efficient than *> IPARMQ(ISPEC=16)=2 despite the greater level of @@ -236,6 +244,8 @@ * .. * .. Local Scalars .. INTEGER NH, NS + INTEGER I, IC, IZ + CHARACTER SUBNAM*6 * .. * .. Intrinsic Functions .. INTRINSIC LOG, MAX, MOD, NINT, REAL @@ -305,11 +315,74 @@ * . by making this choice dependent also upon the * . NH=IHI-ILO+1. * - IPARMQ = 0 - IF( NS.GE.KACMIN ) - $ IPARMQ = 1 - IF( NS.GE.K22MIN ) - $ IPARMQ = 2 +* +* Convert NAME to upper case if the first character is lower case. +* + ILAENV = 0 + SUBNAM = NAME + IC = ICHAR( SUBNAM( 1: 1 ) ) + IZ = ICHAR( 'Z' ) + IF( IZ.EQ.90 .OR. IZ.EQ.122 ) THEN +* +* ASCII character set +* + IF( IC.GE.97 .AND. IC.LE.122 ) THEN + SUBNAM( 1: 1 ) = CHAR( IC-32 ) + DO I = 2, 6 + IC = ICHAR( SUBNAM( I: I ) ) + IF( IC.GE.97 .AND. IC.LE.122 ) + $ SUBNAM( I: I ) = CHAR( IC-32 ) + END DO + END IF +* + ELSE IF( IZ.EQ.233 .OR. IZ.EQ.169 ) THEN +* +* EBCDIC character set +* + IF( ( IC.GE.129 .AND. IC.LE.137 ) .OR. + $ ( IC.GE.145 .AND. IC.LE.153 ) .OR. + $ ( IC.GE.162 .AND. IC.LE.169 ) ) THEN + SUBNAM( 1: 1 ) = CHAR( IC+64 ) + DO I = 2, 6 + IC = ICHAR( SUBNAM( I: I ) ) + IF( ( IC.GE.129 .AND. IC.LE.137 ) .OR. + $ ( IC.GE.145 .AND. IC.LE.153 ) .OR. + $ ( IC.GE.162 .AND. IC.LE.169 ) )SUBNAM( I: + $ I ) = CHAR( IC+64 ) + END DO + END IF +* + ELSE IF( IZ.EQ.218 .OR. IZ.EQ.250 ) THEN +* +* Prime machines: ASCII+128 +* + IF( IC.GE.225 .AND. IC.LE.250 ) THEN + SUBNAM( 1: 1 ) = CHAR( IC-32 ) + DO I = 2, 6 + IC = ICHAR( SUBNAM( I: I ) ) + IF( IC.GE.225 .AND. IC.LE.250 ) + $ SUBNAM( I: I ) = CHAR( IC-32 ) + END DO + END IF + END IF +* + IF( SUBNAM( 2:6 ).EQ.'GGHRD' .OR. + $ SUBNAM( 2:6 ).EQ.'GGHD3' ) THEN + IPARMQ = 1 + IF( NH.GE.K22MIN ) + $ IPARMQ = 2 + ELSE IF ( SUBNAM( 4:6 ).EQ.'EXC' ) THEN + IF( NH.GE.KACMIN ) + $ IPARMQ = 1 + IF( NH.GE.K22MIN ) + $ IPARMQ = 2 + ELSE IF ( SUBNAM( 2:6 ).EQ.'HSEQR' .OR. + $ SUBNAM( 2:5 ).EQ.'LAQR' ) THEN + IF( NS.GE.KACMIN ) + $ IPARMQ = 1 + IF( NS.GE.K22MIN ) + $ IPARMQ = 2 + END IF * ELSE * ===== invalid value of ispec ===== diff --git a/SRC/sgges3.f b/SRC/sgges3.f new file mode 100644 index 00000000..81ab96c4 --- /dev/null +++ b/SRC/sgges3.f @@ -0,0 +1,671 @@ +*> \brief <b> SGGES3 computes the eigenvalues, the Schur form, and, optionally, the matrix of Schur vectors for GE matrices (blocked algorithm)</b> +* +* =========== DOCUMENTATION =========== +* +* Online html documentation available at +* http://www.netlib.org/lapack/explore-html/ +* +*> \htmlonly +*> Download SGGES3 + dependencies +*> <a href="http://www.netlib.org/cgi-bin/netlibfiles.tgz?format=tgz&filename=/lapack/lapack_routine/sgges3.f"> +*> [TGZ]</a> +*> <a href="http://www.netlib.org/cgi-bin/netlibfiles.zip?format=zip&filename=/lapack/lapack_routine/sgges3.f"> +*> [ZIP]</a> +*> <a href="http://www.netlib.org/cgi-bin/netlibfiles.txt?format=txt&filename=/lapack/lapack_routine/sgges3.f"> +*> [TXT]</a> +*> \endhtmlonly +* +* Definition: +* =========== +* +* SUBROUTINE SGGES3( JOBVSL, JOBVSR, SORT, SELCTG, N, A, LDA, B, +* $ LDB, SDIM, ALPHAR, ALPHAI, BETA, VSL, LDVSL, +* $ VSR, LDVSR, WORK, LWORK, BWORK, INFO ) +* +* .. Scalar Arguments .. +* CHARACTER JOBVSL, JOBVSR, SORT +* INTEGER INFO, LDA, LDB, LDVSL, LDVSR, LWORK, N, SDIM +* .. +* .. Array Arguments .. +* LOGICAL BWORK( * ) +* REAL A( LDA, * ), ALPHAI( * ), ALPHAR( * ), +* $ B( LDB, * ), BETA( * ), VSL( LDVSL, * ), +* $ VSR( LDVSR, * ), WORK( * ) +* .. +* .. Function Arguments .. +* LOGICAL SELCTG +* EXTERNAL SELCTG +* .. +* +* +*> \par Purpose: +* ============= +*> +*> \verbatim +*> +*> SGGES3 computes for a pair of N-by-N real nonsymmetric matrices (A,B), +*> the generalized eigenvalues, the generalized real Schur form (S,T), +*> optionally, the left and/or right matrices of Schur vectors (VSL and +*> VSR). This gives the generalized Schur factorization +*> +*> (A,B) = ( (VSL)*S*(VSR)**T, (VSL)*T*(VSR)**T ) +*> +*> Optionally, it also orders the eigenvalues so that a selected cluster +*> of eigenvalues appears in the leading diagonal blocks of the upper +*> quasi-triangular matrix S and the upper triangular matrix T.The +*> leading columns of VSL and VSR then form an orthonormal basis for the +*> corresponding left and right eigenspaces (deflating subspaces). +*> +*> (If only the generalized eigenvalues are needed, use the driver +*> SGGEV instead, which is faster.) +*> +*> A generalized eigenvalue for a pair of matrices (A,B) is a scalar w +*> or a ratio alpha/beta = w, such that A - w*B is singular. It is +*> usually represented as the pair (alpha,beta), as there is a +*> reasonable interpretation for beta=0 or both being zero. +*> +*> A pair of matrices (S,T) is in generalized real Schur form if T is +*> upper triangular with non-negative diagonal and S is block upper +*> triangular with 1-by-1 and 2-by-2 blocks. 1-by-1 blocks correspond +*> to real generalized eigenvalues, while 2-by-2 blocks of S will be +*> "standardized" by making the corresponding elements of T have the +*> form: +*> [ a 0 ] +*> [ 0 b ] +*> +*> and the pair of corresponding 2-by-2 blocks in S and T will have a +*> complex conjugate pair of generalized eigenvalues. +*> +*> \endverbatim +* +* Arguments: +* ========== +* +*> \param[in] JOBVSL +*> \verbatim +*> JOBVSL is CHARACTER*1 +*> = 'N': do not compute the left Schur vectors; +*> = 'V': compute the left Schur vectors. +*> \endverbatim +*> +*> \param[in] JOBVSR +*> \verbatim +*> JOBVSR is CHARACTER*1 +*> = 'N': do not compute the right Schur vectors; +*> = 'V': compute the right Schur vectors. +*> \endverbatim +*> +*> \param[in] SORT +*> \verbatim +*> SORT is CHARACTER*1 +*> Specifies whether or not to order the eigenvalues on the +*> diagonal of the generalized Schur form. +*> = 'N': Eigenvalues are not ordered; +*> = 'S': Eigenvalues are ordered (see SELCTG); +*> \endverbatim +*> +*> \param[in] SELCTG +*> \verbatim +*> SELCTG is a LOGICAL FUNCTION of three REAL arguments +*> SELCTG must be declared EXTERNAL in the calling subroutine. +*> If SORT = 'N', SELCTG is not referenced. +*> If SORT = 'S', SELCTG is used to select eigenvalues to sort +*> to the top left of the Schur form. +*> An eigenvalue (ALPHAR(j)+ALPHAI(j))/BETA(j) is selected if +*> SELCTG(ALPHAR(j),ALPHAI(j),BETA(j)) is true; i.e. if either +*> one of a complex conjugate pair of eigenvalues is selected, +*> then both complex eigenvalues are selected. +*> +*> Note that in the ill-conditioned case, a selected complex +*> eigenvalue may no longer satisfy SELCTG(ALPHAR(j),ALPHAI(j), +*> BETA(j)) = .TRUE. after ordering. INFO is to be set to N+2 +*> in this case. +*> \endverbatim +*> +*> \param[in] N +*> \verbatim +*> N is INTEGER +*> The order of the matrices A, B, VSL, and VSR. N >= 0. +*> \endverbatim +*> +*> \param[in,out] A +*> \verbatim +*> A is REAL array, dimension (LDA, N) +*> On entry, the first of the pair of matrices. +*> On exit, A has been overwritten by its generalized Schur +*> form S. +*> \endverbatim +*> +*> \param[in] LDA +*> \verbatim +*> LDA is INTEGER +*> The leading dimension of A. LDA >= max(1,N). +*> \endverbatim +*> +*> \param[in,out] B +*> \verbatim +*> B is REAL array, dimension (LDB, N) +*> On entry, the second of the pair of matrices. +*> On exit, B has been overwritten by its generalized Schur +*> form T. +*> \endverbatim +*> +*> \param[in] LDB +*> \verbatim +*> LDB is INTEGER +*> The leading dimension of B. LDB >= max(1,N). +*> \endverbatim +*> +*> \param[out] SDIM +*> \verbatim +*> SDIM is INTEGER +*> If SORT = 'N', SDIM = 0. +*> If SORT = 'S', SDIM = number of eigenvalues (after sorting) +*> for which SELCTG is true. (Complex conjugate pairs for which +*> SELCTG is true for either eigenvalue count as 2.) +*> \endverbatim +*> +*> \param[out] ALPHAR +*> \verbatim +*> ALPHAR is REAL array, dimension (N) +*> \endverbatim +*> +*> \param[out] ALPHAI +*> \verbatim +*> ALPHAI is REAL array, dimension (N) +*> \endverbatim +*> +*> \param[out] BETA +*> \verbatim +*> BETA is REAL array, dimension (N) +*> On exit, (ALPHAR(j) + ALPHAI(j)*i)/BETA(j), j=1,...,N, will +*> be the generalized eigenvalues. ALPHAR(j) + ALPHAI(j)*i, +*> and BETA(j),j=1,...,N are the diagonals of the complex Schur +*> form (S,T) that would result if the 2-by-2 diagonal blocks of +*> the real Schur form of (A,B) were further reduced to +*> triangular form using 2-by-2 complex unitary transformations. +*> If ALPHAI(j) is zero, then the j-th eigenvalue is real; if +*> positive, then the j-th and (j+1)-st eigenvalues are a +*> complex conjugate pair, with ALPHAI(j+1) negative. +*> +*> Note: the quotients ALPHAR(j)/BETA(j) and ALPHAI(j)/BETA(j) +*> may easily over- or underflow, and BETA(j) may even be zero. +*> Thus, the user should avoid naively computing the ratio. +*> However, ALPHAR and ALPHAI will be always less than and +*> usually comparable with norm(A) in magnitude, and BETA always +*> less than and usually comparable with norm(B). +*> \endverbatim +*> +*> \param[out] VSL +*> \verbatim +*> VSL is REAL array, dimension (LDVSL,N) +*> If JOBVSL = 'V', VSL will contain the left Schur vectors. +*> Not referenced if JOBVSL = 'N'. +*> \endverbatim +*> +*> \param[in] LDVSL +*> \verbatim +*> LDVSL is INTEGER +*> The leading dimension of the matrix VSL. LDVSL >=1, and +*> if JOBVSL = 'V', LDVSL >= N. +*> \endverbatim +*> +*> \param[out] VSR +*> \verbatim +*> VSR is REAL array, dimension (LDVSR,N) +*> If JOBVSR = 'V', VSR will contain the right Schur vectors. +*> Not referenced if JOBVSR = 'N'. +*> \endverbatim +*> +*> \param[in] LDVSR +*> \verbatim +*> LDVSR is INTEGER +*> The leading dimension of the matrix VSR. LDVSR >= 1, and +*> if JOBVSR = 'V', LDVSR >= N. +*> \endverbatim +*> +*> \param[out] WORK +*> \verbatim +*> WORK is REAL array, dimension (MAX(1,LWORK)) +*> On exit, if INFO = 0, WORK(1) returns the optimal LWORK. +*> \endverbatim +*> +*> \param[in] LWORK +*> \verbatim +*> LWORK is INTEGER +*> The dimension of the array WORK. +*> +*> If LWORK = -1, then a workspace query is assumed; the routine +*> only calculates the optimal size of the WORK array, returns +*> this value as the first entry of the WORK array, and no error +*> message related to LWORK is issued by XERBLA. +*> \endverbatim +*> +*> \param[out] BWORK +*> \verbatim +*> BWORK is LOGICAL array, dimension (N) +*> Not referenced if SORT = 'N'. +*> \endverbatim +*> +*> \param[out] INFO +*> \verbatim +*> INFO is INTEGER +*> = 0: successful exit +*> < 0: if INFO = -i, the i-th argument had an illegal value. +*> = 1,...,N: +*> The QZ iteration failed. (A,B) are not in Schur +*> form, but ALPHAR(j), ALPHAI(j), and BETA(j) should +*> be correct for j=INFO+1,...,N. +*> > N: =N+1: other than QZ iteration failed in SHGEQZ. +*> =N+2: after reordering, roundoff changed values of +*> some complex eigenvalues so that leading +*> eigenvalues in the Generalized Schur form no +*> longer satisfy SELCTG=.TRUE. This could also +*> be caused due to scaling. +*> =N+3: reordering failed in STGSEN. +*> \endverbatim +* +* Authors: +* ======== +* +*> \author Univ. of Tennessee +*> \author Univ. of California Berkeley +*> \author Univ. of Colorado Denver +*> \author NAG Ltd. +* +*> \date January 2015 +* +*> \ingroup realGEeigen +* +* ===================================================================== + SUBROUTINE SGGES3( JOBVSL, JOBVSR, SORT, SELCTG, N, A, LDA, B, + $ LDB, SDIM, ALPHAR, ALPHAI, BETA, VSL, LDVSL, + $ VSR, LDVSR, WORK, LWORK, BWORK, INFO ) +* +* -- LAPACK driver routine (version 3.6.0) -- +* -- LAPACK is a software package provided by Univ. of Tennessee, -- +* -- Univ. of California Berkeley, Univ. of Colorado Denver and NAG Ltd..-- +* January 2015 +* +* .. Scalar Arguments .. + CHARACTER JOBVSL, JOBVSR, SORT + INTEGER INFO, LDA, LDB, LDVSL, LDVSR, LWORK, N, SDIM +* .. +* .. Array Arguments .. + LOGICAL BWORK( * ) + REAL A( LDA, * ), ALPHAI( * ), ALPHAR( * ), + $ B( LDB, * ), BETA( * ), VSL( LDVSL, * ), + $ VSR( LDVSR, * ), WORK( * ) +* .. +* .. Function Arguments .. + LOGICAL SELCTG + EXTERNAL SELCTG +* .. +* +* ===================================================================== +* +* .. Parameters .. + REAL ZERO, ONE + PARAMETER ( ZERO = 0.0E+0, ONE = 1.0E+0 ) +* .. +* .. Local Scalars .. + LOGICAL CURSL, ILASCL, ILBSCL, ILVSL, ILVSR, LASTSL, + $ LQUERY, LST2SL, WANTST + INTEGER I, ICOLS, IERR, IHI, IJOBVL, IJOBVR, ILEFT, + $ ILO, IP, IRIGHT, IROWS, ITAU, IWRK, LWKOPT + REAL ANRM, ANRMTO, BIGNUM, BNRM, BNRMTO, EPS, PVSL, + $ PVSR, SAFMAX, SAFMIN, SMLNUM +* .. +* .. Local Arrays .. + INTEGER IDUM( 1 ) + REAL DIF( 2 ) +* .. +* .. External Subroutines .. + EXTERNAL SGEQRF, SGGBAK, SGGBAL, SGGHD3, SHGEQZ, SLABAD, + $ SLACPY, SLASCL, SLASET, SORGQR, SORMQR, STGSEN, + $ XERBLA +* .. +* .. External Functions .. + LOGICAL LSAME + REAL SLAMCH, SLANGE + EXTERNAL LSAME, SLAMCH, SLANGE +* .. +* .. Intrinsic Functions .. + INTRINSIC ABS, MAX, SQRT +* .. +* .. Executable Statements .. +* +* Decode the input arguments +* + IF( LSAME( JOBVSL, 'N' ) ) THEN + IJOBVL = 1 + ILVSL = .FALSE. + ELSE IF( LSAME( JOBVSL, 'V' ) ) THEN + IJOBVL = 2 + ILVSL = .TRUE. + ELSE + IJOBVL = -1 + ILVSL = .FALSE. + END IF +* + IF( LSAME( JOBVSR, 'N' ) ) THEN + IJOBVR = 1 + ILVSR = .FALSE. + ELSE IF( LSAME( JOBVSR, 'V' ) ) THEN + IJOBVR = 2 + ILVSR = .TRUE. + ELSE + IJOBVR = -1 + ILVSR = .FALSE. + END IF +* + WANTST = LSAME( SORT, 'S' ) +* +* Test the input arguments +* + INFO = 0 + LQUERY = ( LWORK.EQ.-1 ) + IF( IJOBVL.LE.0 ) THEN + INFO = -1 + ELSE IF( IJOBVR.LE.0 ) THEN + INFO = -2 + ELSE IF( ( .NOT.WANTST ) .AND. ( .NOT.LSAME( SORT, 'N' ) ) ) THEN + INFO = -3 + ELSE IF( N.LT.0 ) THEN + INFO = -5 + ELSE IF( LDA.LT.MAX( 1, N ) ) THEN + INFO = -7 + ELSE IF( LDB.LT.MAX( 1, N ) ) THEN + INFO = -9 + ELSE IF( LDVSL.LT.1 .OR. ( ILVSL .AND. LDVSL.LT.N ) ) THEN + INFO = -15 + ELSE IF( LDVSR.LT.1 .OR. ( ILVSR .AND. LDVSR.LT.N ) ) THEN + INFO = -17 + ELSE IF( LWORK.LT.6*N+16 .AND. .NOT.LQUERY ) THEN + INFO = -19 + END IF +* +* Compute workspace +* + IF( INFO.EQ.0 ) THEN + CALL SGEQRF( N, N, B, LDB, WORK, WORK, -1, IERR ) + LWKOPT = MAX( 6*N+16, 3*N+INT( WORK( 1 ) ) ) + CALL SORMQR( 'L', 'T', N, N, N, B, LDB, WORK, A, LDA, WORK, + $ -1, IERR ) + LWKOPT = MAX( LWKOPT, 3*N+INT( WORK( 1 ) ) ) + IF( ILVSL ) THEN + CALL SORGQR( N, N, N, VSL, LDVSL, WORK, WORK, -1, IERR ) + LWKOPT = MAX( LWKOPT, 3*N+INT( WORK( 1 ) ) ) + END IF + CALL SGGHD3( JOBVSL, JOBVSR, N, 1, N, A, LDA, B, LDB, VSL, + $ LDVSL, VSR, LDVSR, WORK, -1, IERR ) + LWKOPT = MAX( LWKOPT, 3*N+INT( WORK( 1 ) ) ) + CALL SHGEQZ( 'S', JOBVSL, JOBVSR, N, 1, N, A, LDA, B, LDB, + $ ALPHAR, ALPHAI, BETA, VSL, LDVSL, VSR, LDVSR, + $ WORK, -1, IERR ) + LWKOPT = MAX( LWKOPT, 2*N+INT( WORK( 1 ) ) ) + IF( WANTST ) THEN + CALL STGSEN( 0, ILVSL, ILVSR, BWORK, N, A, LDA, B, LDB, + $ ALPHAR, ALPHAI, BETA, VSL, LDVSL, VSR, LDVSR, + $ SDIM, PVSL, PVSR, DIF, WORK, -1, IDUM, 1, + $ IERR ) + LWKOPT = MAX( LWKOPT, 2*N+INT( WORK( 1 ) ) ) + END IF + WORK( 1 ) = LWKOPT + END IF +* + IF( INFO.NE.0 ) THEN + CALL XERBLA( 'SGGES3 ', -INFO ) + RETURN + ELSE IF( LQUERY ) THEN + RETURN + END IF +* +* Quick return if possible +* + IF( N.EQ.0 ) THEN + SDIM = 0 + RETURN + END IF +* +* Get machine constants +* + EPS = SLAMCH( 'P' ) + SAFMIN = SLAMCH( 'S' ) + SAFMAX = ONE / SAFMIN + CALL SLABAD( SAFMIN, SAFMAX ) + SMLNUM = SQRT( SAFMIN ) / EPS + BIGNUM = ONE / SMLNUM +* +* Scale A if max element outside range [SMLNUM,BIGNUM] +* + ANRM = SLANGE( 'M', N, N, A, LDA, WORK ) + ILASCL = .FALSE. + IF( ANRM.GT.ZERO .AND. ANRM.LT.SMLNUM ) THEN + ANRMTO = SMLNUM + ILASCL = .TRUE. + ELSE IF( ANRM.GT.BIGNUM ) THEN + ANRMTO = BIGNUM + ILASCL = .TRUE. + END IF + IF( ILASCL ) + $ CALL SLASCL( 'G', 0, 0, ANRM, ANRMTO, N, N, A, LDA, IERR ) +* +* Scale B if max element outside range [SMLNUM,BIGNUM] +* + BNRM = SLANGE( 'M', N, N, B, LDB, WORK ) + ILBSCL = .FALSE. + IF( BNRM.GT.ZERO .AND. BNRM.LT.SMLNUM ) THEN + BNRMTO = SMLNUM + ILBSCL = .TRUE. + ELSE IF( BNRM.GT.BIGNUM ) THEN + BNRMTO = BIGNUM + ILBSCL = .TRUE. + END IF + IF( ILBSCL ) + $ CALL SLASCL( 'G', 0, 0, BNRM, BNRMTO, N, N, B, LDB, IERR ) +* +* Permute the matrix to make it more nearly triangular +* + ILEFT = 1 + IRIGHT = N + 1 + IWRK = IRIGHT + N + CALL SGGBAL( 'P', N, A, LDA, B, LDB, ILO, IHI, WORK( ILEFT ), + $ WORK( IRIGHT ), WORK( IWRK ), IERR ) +* +* Reduce B to triangular form (QR decomposition of B) +* + IROWS = IHI + 1 - ILO + ICOLS = N + 1 - ILO + ITAU = IWRK + IWRK = ITAU + IROWS + CALL SGEQRF( IROWS, ICOLS, B( ILO, ILO ), LDB, WORK( ITAU ), + $ WORK( IWRK ), LWORK+1-IWRK, IERR ) +* +* Apply the orthogonal transformation to matrix A +* + CALL SORMQR( 'L', 'T', IROWS, ICOLS, IROWS, B( ILO, ILO ), LDB, + $ WORK( ITAU ), A( ILO, ILO ), LDA, WORK( IWRK ), + $ LWORK+1-IWRK, IERR ) +* +* Initialize VSL +* + IF( ILVSL ) THEN + CALL SLASET( 'Full', N, N, ZERO, ONE, VSL, LDVSL ) + IF( IROWS.GT.1 ) THEN + CALL SLACPY( 'L', IROWS-1, IROWS-1, B( ILO+1, ILO ), LDB, + $ VSL( ILO+1, ILO ), LDVSL ) + END IF + CALL SORGQR( IROWS, IROWS, IROWS, VSL( ILO, ILO ), LDVSL, + $ WORK( ITAU ), WORK( IWRK ), LWORK+1-IWRK, IERR ) + END IF +* +* Initialize VSR +* + IF( ILVSR ) + $ CALL SLASET( 'Full', N, N, ZERO, ONE, VSR, LDVSR ) +* +* Reduce to generalized Hessenberg form +* + CALL SGGHD3( JOBVSL, JOBVSR, N, ILO, IHI, A, LDA, B, LDB, VSL, + $ LDVSL, VSR, LDVSR, WORK( IWRK ), LWORK+1-IWRK, IERR ) +* +* Perform QZ algorithm, computing Schur vectors if desired +* + IWRK = ITAU + CALL SHGEQZ( 'S', JOBVSL, JOBVSR, N, ILO, IHI, A, LDA, B, LDB, + $ ALPHAR, ALPHAI, BETA, VSL, LDVSL, VSR, LDVSR, + $ WORK( IWRK ), LWORK+1-IWRK, IERR ) + IF( IERR.NE.0 ) THEN + IF( IERR.GT.0 .AND. IERR.LE.N ) THEN + INFO = IERR + ELSE IF( IERR.GT.N .AND. IERR.LE.2*N ) THEN + INFO = IERR - N + ELSE + INFO = N + 1 + END IF + GO TO 40 + END IF +* +* Sort eigenvalues ALPHA/BETA if desired +* + SDIM = 0 + IF( WANTST ) THEN +* +* Undo scaling on eigenvalues before SELCTGing +* + IF( ILASCL ) THEN + CALL SLASCL( 'G', 0, 0, ANRMTO, ANRM, N, 1, ALPHAR, N, + $ IERR ) + CALL SLASCL( 'G', 0, 0, ANRMTO, ANRM, N, 1, ALPHAI, N, + $ IERR ) + END IF + IF( ILBSCL ) + $ CALL SLASCL( 'G', 0, 0, BNRMTO, BNRM, N, 1, BETA, N, IERR ) +* +* Select eigenvalues +* + DO 10 I = 1, N + BWORK( I ) = SELCTG( ALPHAR( I ), ALPHAI( I ), BETA( I ) ) + 10 CONTINUE +* + CALL STGSEN( 0, ILVSL, ILVSR, BWORK, N, A, LDA, B, LDB, ALPHAR, + $ ALPHAI, BETA, VSL, LDVSL, VSR, LDVSR, SDIM, PVSL, + $ PVSR, DIF, WORK( IWRK ), LWORK-IWRK+1, IDUM, 1, + $ IERR ) + IF( IERR.EQ.1 ) + $ INFO = N + 3 +* + END IF +* +* Apply back-permutation to VSL and VSR +* + IF( ILVSL ) + $ CALL SGGBAK( 'P', 'L', N, ILO, IHI, WORK( ILEFT ), + $ WORK( IRIGHT ), N, VSL, LDVSL, IERR ) +* + IF( ILVSR ) + $ CALL SGGBAK( 'P', 'R', N, ILO, IHI, WORK( ILEFT ), + $ WORK( IRIGHT ), N, VSR, LDVSR, IERR ) +* +* Check if unscaling would cause over/underflow, if so, rescale +* (ALPHAR(I),ALPHAI(I),BETA(I)) so BETA(I) is on the order of +* B(I,I) and ALPHAR(I) and ALPHAI(I) are on the order of A(I,I) +* + IF( ILASCL )THEN + DO 50 I = 1, N + IF( ALPHAI( I ).NE.ZERO ) THEN + IF( ( ALPHAR( I )/SAFMAX ).GT.( ANRMTO/ANRM ) .OR. + $ ( SAFMIN/ALPHAR( I ) ).GT.( ANRM/ANRMTO ) ) THEN + WORK( 1 ) = ABS( A( I, I )/ALPHAR( I ) ) + BETA( I ) = BETA( I )*WORK( 1 ) + ALPHAR( I ) = ALPHAR( I )*WORK( 1 ) + ALPHAI( I ) = ALPHAI( I )*WORK( 1 ) + ELSE IF( ( ALPHAI( I )/SAFMAX ).GT.( ANRMTO/ANRM ) .OR. + $ ( SAFMIN/ALPHAI( I ) ).GT.( ANRM/ANRMTO ) ) THEN + WORK( 1 ) = ABS( A( I, I+1 )/ALPHAI( I ) ) + BETA( I ) = BETA( I )*WORK( 1 ) + ALPHAR( I ) = ALPHAR( I )*WORK( 1 ) + ALPHAI( I ) = ALPHAI( I )*WORK( 1 ) + END IF + END IF + 50 CONTINUE + END IF +* + IF( ILBSCL )THEN + DO 60 I = 1, N + IF( ALPHAI( I ).NE.ZERO ) THEN + IF( ( BETA( I )/SAFMAX ).GT.( BNRMTO/BNRM ) .OR. + $ ( SAFMIN/BETA( I ) ).GT.( BNRM/BNRMTO ) ) THEN + WORK( 1 ) = ABS(B( I, I )/BETA( I )) + BETA( I ) = BETA( I )*WORK( 1 ) + ALPHAR( I ) = ALPHAR( I )*WORK( 1 ) + ALPHAI( I ) = ALPHAI( I )*WORK( 1 ) + END IF + END IF + 60 CONTINUE + END IF +* +* Undo scaling +* + IF( ILASCL ) THEN + CALL SLASCL( 'H', 0, 0, ANRMTO, ANRM, N, N, A, LDA, IERR ) + CALL SLASCL( 'G', 0, 0, ANRMTO, ANRM, N, 1, ALPHAR, N, IERR ) + CALL SLASCL( 'G', 0, 0, ANRMTO, ANRM, N, 1, ALPHAI, N, IERR ) + END IF +* + IF( ILBSCL ) THEN + CALL SLASCL( 'U', 0, 0, BNRMTO, BNRM, N, N, B, LDB, IERR ) + CALL SLASCL( 'G', 0, 0, BNRMTO, BNRM, N, 1, BETA, N, IERR ) + END IF +* + IF( WANTST ) THEN +* +* Check if reordering is correct +* + LASTSL = .TRUE. + LST2SL = .TRUE. + SDIM = 0 + IP = 0 + DO 30 I = 1, N + CURSL = SELCTG( ALPHAR( I ), ALPHAI( I ), BETA( I ) ) + IF( ALPHAI( I ).EQ.ZERO ) THEN + IF( CURSL ) + $ SDIM = SDIM + 1 + IP = 0 + IF( CURSL .AND. .NOT.LASTSL ) + $ INFO = N + 2 + ELSE + IF( IP.EQ.1 ) THEN +* +* Last eigenvalue of conjugate pair +* + CURSL = CURSL .OR. LASTSL + LASTSL = CURSL + IF( CURSL ) + $ SDIM = SDIM + 2 + IP = -1 + IF( CURSL .AND. .NOT.LST2SL ) + $ INFO = N + 2 + ELSE +* +* First eigenvalue of conjugate pair +* + IP = 1 + END IF + END IF + LST2SL = LASTSL + LASTSL = CURSL + 30 CONTINUE +* + END IF +* + 40 CONTINUE +* + WORK( 1 ) = LWKOPT +* + RETURN +* +* End of SGGES3 +* + END diff --git a/SRC/sggev3.f b/SRC/sggev3.f new file mode 100644 index 00000000..7a253ad1 --- /dev/null +++ b/SRC/sggev3.f @@ -0,0 +1,589 @@ +*> \brief <b> SGGEV3 computes the eigenvalues and, optionally, the left and/or right eigenvectors for GE matrices (blocked algorithm)</b> +* +* =========== DOCUMENTATION =========== +* +* Online html documentation available at +* http://www.netlib.org/lapack/explore-html/ +* +*> \htmlonly +*> Download SGGEV3 + dependencies +*> <a href="http://www.netlib.org/cgi-bin/netlibfiles.tgz?format=tgz&filename=/lapack/lapack_routine/sggev3.f"> +*> [TGZ]</a> +*> <a href="http://www.netlib.org/cgi-bin/netlibfiles.zip?format=zip&filename=/lapack/lapack_routine/sggev3.f"> +*> [ZIP]</a> +*> <a href="http://www.netlib.org/cgi-bin/netlibfiles.txt?format=txt&filename=/lapack/lapack_routine/sggev3.f"> +*> [TXT]</a> +*> \endhtmlonly +* +* Definition: +* =========== +* +* SUBROUTINE SGGEV3( JOBVL, JOBVR, N, A, LDA, B, LDB, ALPHAR, +* $ ALPHAI, BETA, VL, LDVL, VR, LDVR, WORK, LWORK, +* $ INFO ) +* +* .. Scalar Arguments .. +* CHARACTER JOBVL, JOBVR +* INTEGER INFO, LDA, LDB, LDVL, LDVR, LWORK, N +* .. +* .. Array Arguments .. +* REAL A( LDA, * ), ALPHAI( * ), ALPHAR( * ), +* $ B( LDB, * ), BETA( * ), VL( LDVL, * ), +* $ VR( LDVR, * ), WORK( * ) +* .. +* +* +*> \par Purpose: +* ============= +*> +*> \verbatim +*> +*> SGGEV3 computes for a pair of N-by-N real nonsymmetric matrices (A,B) +*> the generalized eigenvalues, and optionally, the left and/or right +*> generalized eigenvectors. +*> +*> A generalized eigenvalue for a pair of matrices (A,B) is a scalar +*> lambda or a ratio alpha/beta = lambda, such that A - lambda*B is +*> singular. It is usually represented as the pair (alpha,beta), as +*> there is a reasonable interpretation for beta=0, and even for both +*> being zero. +*> +*> The right eigenvector v(j) corresponding to the eigenvalue lambda(j) +*> of (A,B) satisfies +*> +*> A * v(j) = lambda(j) * B * v(j). +*> +*> The left eigenvector u(j) corresponding to the eigenvalue lambda(j) +*> of (A,B) satisfies +*> +*> u(j)**H * A = lambda(j) * u(j)**H * B . +*> +*> where u(j)**H is the conjugate-transpose of u(j). +*> +*> \endverbatim +* +* Arguments: +* ========== +* +*> \param[in] JOBVL +*> \verbatim +*> JOBVL is CHARACTER*1 +*> = 'N': do not compute the left generalized eigenvectors; +*> = 'V': compute the left generalized eigenvectors. +*> \endverbatim +*> +*> \param[in] JOBVR +*> \verbatim +*> JOBVR is CHARACTER*1 +*> = 'N': do not compute the right generalized eigenvectors; +*> = 'V': compute the right generalized eigenvectors. +*> \endverbatim +*> +*> \param[in] N +*> \verbatim +*> N is INTEGER +*> The order of the matrices A, B, VL, and VR. N >= 0. +*> \endverbatim +*> +*> \param[in,out] A +*> \verbatim +*> A is REAL array, dimension (LDA, N) +*> On entry, the matrix A in the pair (A,B). +*> On exit, A has been overwritten. +*> \endverbatim +*> +*> \param[in] LDA +*> \verbatim +*> LDA is INTEGER +*> The leading dimension of A. LDA >= max(1,N). +*> \endverbatim +*> +*> \param[in,out] B +*> \verbatim +*> B is REAL array, dimension (LDB, N) +*> On entry, the matrix B in the pair (A,B). +*> On exit, B has been overwritten. +*> \endverbatim +*> +*> \param[in] LDB +*> \verbatim +*> LDB is INTEGER +*> The leading dimension of B. LDB >= max(1,N). +*> \endverbatim +*> +*> \param[out] ALPHAR +*> \verbatim +*> ALPHAR is REAL array, dimension (N) +*> \endverbatim +*> +*> \param[out] ALPHAI +*> \verbatim +*> ALPHAI is REAL array, dimension (N) +*> \endverbatim +*> +*> \param[out] BETA +*> \verbatim +*> BETA is REAL array, dimension (N) +*> On exit, (ALPHAR(j) + ALPHAI(j)*i)/BETA(j), j=1,...,N, will +*> be the generalized eigenvalues. If ALPHAI(j) is zero, then +*> the j-th eigenvalue is real; if positive, then the j-th and +*> (j+1)-st eigenvalues are a complex conjugate pair, with +*> ALPHAI(j+1) negative. +*> +*> Note: the quotients ALPHAR(j)/BETA(j) and ALPHAI(j)/BETA(j) +*> may easily over- or underflow, and BETA(j) may even be zero. +*> Thus, the user should avoid naively computing the ratio +*> alpha/beta. However, ALPHAR and ALPHAI will be always less +*> than and usually comparable with norm(A) in magnitude, and +*> BETA always less than and usually comparable with norm(B). +*> \endverbatim +*> +*> \param[out] VL +*> \verbatim +*> VL is REAL array, dimension (LDVL,N) +*> If JOBVL = 'V', the left eigenvectors u(j) are stored one +*> after another in the columns of VL, in the same order as +*> their eigenvalues. If the j-th eigenvalue is real, then +*> u(j) = VL(:,j), the j-th column of VL. If the j-th and +*> (j+1)-th eigenvalues form a complex conjugate pair, then +*> u(j) = VL(:,j)+i*VL(:,j+1) and u(j+1) = VL(:,j)-i*VL(:,j+1). +*> Each eigenvector is scaled so the largest component has +*> abs(real part)+abs(imag. part)=1. +*> Not referenced if JOBVL = 'N'. +*> \endverbatim +*> +*> \param[in] LDVL +*> \verbatim +*> LDVL is INTEGER +*> The leading dimension of the matrix VL. LDVL >= 1, and +*> if JOBVL = 'V', LDVL >= N. +*> \endverbatim +*> +*> \param[out] VR +*> \verbatim +*> VR is REAL array, dimension (LDVR,N) +*> If JOBVR = 'V', the right eigenvectors v(j) are stored one +*> after another in the columns of VR, in the same order as +*> their eigenvalues. If the j-th eigenvalue is real, then +*> v(j) = VR(:,j), the j-th column of VR. If the j-th and +*> (j+1)-th eigenvalues form a complex conjugate pair, then +*> v(j) = VR(:,j)+i*VR(:,j+1) and v(j+1) = VR(:,j)-i*VR(:,j+1). +*> Each eigenvector is scaled so the largest component has +*> abs(real part)+abs(imag. part)=1. +*> Not referenced if JOBVR = 'N'. +*> \endverbatim +*> +*> \param[in] LDVR +*> \verbatim +*> LDVR is INTEGER +*> The leading dimension of the matrix VR. LDVR >= 1, and +*> if JOBVR = 'V', LDVR >= N. +*> \endverbatim +*> +*> \param[out] WORK +*> \verbatim +*> WORK is REAL array, dimension (MAX(1,LWORK)) +*> On exit, if INFO = 0, WORK(1) returns the optimal LWORK. +*> \endverbatim +*> +*> \param[in] LWORK +*> \verbatim +*> LWORK is INTEGER +*> +*> If LWORK = -1, then a workspace query is assumed; the routine +*> only calculates the optimal size of the WORK array, returns +*> this value as the first entry of the WORK array, and no error +*> message related to LWORK is issued by XERBLA. +*> \endverbatim +*> +*> \param[out] INFO +*> \verbatim +*> INFO is INTEGER +*> = 0: successful exit +*> < 0: if INFO = -i, the i-th argument had an illegal value. +*> = 1,...,N: +*> The QZ iteration failed. No eigenvectors have been +*> calculated, but ALPHAR(j), ALPHAI(j), and BETA(j) +*> should be correct for j=INFO+1,...,N. +*> > N: =N+1: other than QZ iteration failed in SHGEQZ. +*> =N+2: error return from STGEVC. +*> \endverbatim +* +* Authors: +* ======== +* +*> \author Univ. of Tennessee +*> \author Univ. of California Berkeley +*> \author Univ. of Colorado Denver +*> \author NAG Ltd. +* +*> \date January 2015 +* +*> \ingroup realGEeigen +* +* ===================================================================== + SUBROUTINE SGGEV3( JOBVL, JOBVR, N, A, LDA, B, LDB, ALPHAR, + $ ALPHAI, BETA, VL, LDVL, VR, LDVR, WORK, LWORK, + $ INFO ) +* +* -- LAPACK driver routine (version 3.6.0) -- +* -- LAPACK is a software package provided by Univ. of Tennessee, -- +* -- Univ. of California Berkeley, Univ. of Colorado Denver and NAG Ltd..-- +* January 2015 +* +* .. Scalar Arguments .. + CHARACTER JOBVL, JOBVR + INTEGER INFO, LDA, LDB, LDVL, LDVR, LWORK, N +* .. +* .. Array Arguments .. + REAL A( LDA, * ), ALPHAI( * ), ALPHAR( * ), + $ B( LDB, * ), BETA( * ), VL( LDVL, * ), + $ VR( LDVR, * ), WORK( * ) +* .. +* +* ===================================================================== +* +* .. Parameters .. + REAL ZERO, ONE + PARAMETER ( ZERO = 0.0E+0, ONE = 1.0E+0 ) +* .. +* .. Local Scalars .. + LOGICAL ILASCL, ILBSCL, ILV, ILVL, ILVR, LQUERY + CHARACTER CHTEMP + INTEGER ICOLS, IERR, IHI, IJOBVL, IJOBVR, ILEFT, ILO, + $ IN, IRIGHT, IROWS, ITAU, IWRK, JC, JR, LWKOPT + REAL ANRM, ANRMTO, BIGNUM, BNRM, BNRMTO, EPS, + $ SMLNUM, TEMP +* .. +* .. Local Arrays .. + LOGICAL LDUMMA( 1 ) +* .. +* .. External Subroutines .. + EXTERNAL SGEQRF, SGGBAK, SGGBAL, SGGHD3, SHGEQZ, SLABAD, + $ SLACPY, SLASCL, SLASET, SORGQR, SORMQR, STGEVC, + $ XERBLA +* .. +* .. External Functions .. + LOGICAL LSAME + REAL SLAMCH, SLANGE + EXTERNAL LSAME, SLAMCH, SLANGE +* .. +* .. Intrinsic Functions .. + INTRINSIC ABS, MAX, SQRT +* .. +* .. Executable Statements .. +* +* Decode the input arguments +* + IF( LSAME( JOBVL, 'N' ) ) THEN + IJOBVL = 1 + ILVL = .FALSE. + ELSE IF( LSAME( JOBVL, 'V' ) ) THEN + IJOBVL = 2 + ILVL = .TRUE. + ELSE + IJOBVL = -1 + ILVL = .FALSE. + END IF +* + IF( LSAME( JOBVR, 'N' ) ) THEN + IJOBVR = 1 + ILVR = .FALSE. + ELSE IF( LSAME( JOBVR, 'V' ) ) THEN + IJOBVR = 2 + ILVR = .TRUE. + ELSE + IJOBVR = -1 + ILVR = .FALSE. + END IF + ILV = ILVL .OR. ILVR +* +* Test the input arguments +* + INFO = 0 + LQUERY = ( LWORK.EQ.-1 ) + IF( IJOBVL.LE.0 ) THEN + INFO = -1 + ELSE IF( IJOBVR.LE.0 ) THEN + INFO = -2 + ELSE IF( N.LT.0 ) THEN + INFO = -3 + ELSE IF( LDA.LT.MAX( 1, N ) ) THEN + INFO = -5 + ELSE IF( LDB.LT.MAX( 1, N ) ) THEN + INFO = -7 + ELSE IF( LDVL.LT.1 .OR. ( ILVL .AND. LDVL.LT.N ) ) THEN + INFO = -12 + ELSE IF( LDVR.LT.1 .OR. ( ILVR .AND. LDVR.LT.N ) ) THEN + INFO = -14 + ELSE IF( LWORK.LT.MAX( 1, 8*N ) .AND. .NOT.LQUERY ) THEN + INFO = -16 + END IF +* +* Compute workspace +* + IF( INFO.EQ.0 ) THEN + CALL SGEQRF( N, N, B, LDB, WORK, WORK, -1, IERR ) + LWKOPT = MAX( 1, 8*N, 3*N+INT ( WORK( 1 ) ) ) + CALL SORMQR( 'L', 'T', N, N, N, B, LDB, WORK, A, LDA, WORK, + $ -1, IERR ) + LWKOPT = MAX( LWKOPT, 3*N+INT ( WORK( 1 ) ) ) + CALL SGGHD3( JOBVL, JOBVR, N, 1, N, A, LDA, B, LDB, VL, LDVL, + $ VR, LDVR, WORK, -1, IERR ) + LWKOPT = MAX( LWKOPT, 3*N+INT ( WORK( 1 ) ) ) + IF( ILVL ) THEN + CALL SORGQR( N, N, N, VL, LDVL, WORK, WORK, -1, IERR ) + LWKOPT = MAX( LWKOPT, 3*N+INT ( WORK( 1 ) ) ) + CALL SHGEQZ( 'S', JOBVL, JOBVR, N, 1, N, A, LDA, B, LDB, + $ ALPHAR, ALPHAI, BETA, VL, LDVL, VR, LDVR, + $ WORK, -1, IERR ) + LWKOPT = MAX( LWKOPT, 2*N+INT ( WORK( 1 ) ) ) + ELSE + CALL SHGEQZ( 'E', JOBVL, JOBVR, N, 1, N, A, LDA, B, LDB, + $ ALPHAR, ALPHAI, BETA, VL, LDVL, VR, LDVR, + $ WORK, -1, IERR ) + LWKOPT = MAX( LWKOPT, 2*N+INT ( WORK( 1 ) ) ) + END IF + WORK( 1 ) = REAL( LWKOPT ) +* + END IF +* + IF( INFO.NE.0 ) THEN + CALL XERBLA( 'SGGEV3 ', -INFO ) + RETURN + ELSE IF( LQUERY ) THEN + RETURN + END IF +* +* Quick return if possible +* + IF( N.EQ.0 ) + $ RETURN +* +* Get machine constants +* + EPS = SLAMCH( 'P' ) + SMLNUM = SLAMCH( 'S' ) + BIGNUM = ONE / SMLNUM + CALL SLABAD( SMLNUM, BIGNUM ) + SMLNUM = SQRT( SMLNUM ) / EPS + BIGNUM = ONE / SMLNUM +* +* Scale A if max element outside range [SMLNUM,BIGNUM] +* + ANRM = SLANGE( 'M', N, N, A, LDA, WORK ) + ILASCL = .FALSE. + IF( ANRM.GT.ZERO .AND. ANRM.LT.SMLNUM ) THEN + ANRMTO = SMLNUM + ILASCL = .TRUE. + ELSE IF( ANRM.GT.BIGNUM ) THEN + ANRMTO = BIGNUM + ILASCL = .TRUE. + END IF + IF( ILASCL ) + $ CALL SLASCL( 'G', 0, 0, ANRM, ANRMTO, N, N, A, LDA, IERR ) +* +* Scale B if max element outside range [SMLNUM,BIGNUM] +* + BNRM = SLANGE( 'M', N, N, B, LDB, WORK ) + ILBSCL = .FALSE. + IF( BNRM.GT.ZERO .AND. BNRM.LT.SMLNUM ) THEN + BNRMTO = SMLNUM + ILBSCL = .TRUE. + ELSE IF( BNRM.GT.BIGNUM ) THEN + BNRMTO = BIGNUM + ILBSCL = .TRUE. + END IF + IF( ILBSCL ) + $ CALL SLASCL( 'G', 0, 0, BNRM, BNRMTO, N, N, B, LDB, IERR ) +* +* Permute the matrices A, B to isolate eigenvalues if possible +* + ILEFT = 1 + IRIGHT = N + 1 + IWRK = IRIGHT + N + CALL SGGBAL( 'P', N, A, LDA, B, LDB, ILO, IHI, WORK( ILEFT ), + $ WORK( IRIGHT ), WORK( IWRK ), IERR ) +* +* Reduce B to triangular form (QR decomposition of B) +* + IROWS = IHI + 1 - ILO + IF( ILV ) THEN + ICOLS = N + 1 - ILO + ELSE + ICOLS = IROWS + END IF + ITAU = IWRK + IWRK = ITAU + IROWS + CALL SGEQRF( IROWS, ICOLS, B( ILO, ILO ), LDB, WORK( ITAU ), + $ WORK( IWRK ), LWORK+1-IWRK, IERR ) +* +* Apply the orthogonal transformation to matrix A +* + CALL SORMQR( 'L', 'T', IROWS, ICOLS, IROWS, B( ILO, ILO ), LDB, + $ WORK( ITAU ), A( ILO, ILO ), LDA, WORK( IWRK ), + $ LWORK+1-IWRK, IERR ) +* +* Initialize VL +* + IF( ILVL ) THEN + CALL SLASET( 'Full', N, N, ZERO, ONE, VL, LDVL ) + IF( IROWS.GT.1 ) THEN + CALL SLACPY( 'L', IROWS-1, IROWS-1, B( ILO+1, ILO ), LDB, + $ VL( ILO+1, ILO ), LDVL ) + END IF + CALL SORGQR( IROWS, IROWS, IROWS, VL( ILO, ILO ), LDVL, + $ WORK( ITAU ), WORK( IWRK ), LWORK+1-IWRK, IERR ) + END IF +* +* Initialize VR +* + IF( ILVR ) + $ CALL SLASET( 'Full', N, N, ZERO, ONE, VR, LDVR ) +* +* Reduce to generalized Hessenberg form +* + IF( ILV ) THEN +* +* Eigenvectors requested -- work on whole matrix. +* + CALL SGGHD3( JOBVL, JOBVR, N, ILO, IHI, A, LDA, B, LDB, VL, + $ LDVL, VR, LDVR, WORK( IWRK ), LWORK+1-IWRK, IERR ) + ELSE + CALL SGGHD3( 'N', 'N', IROWS, 1, IROWS, A( ILO, ILO ), LDA, + $ B( ILO, ILO ), LDB, VL, LDVL, VR, LDVR, + $ WORK( IWRK ), LWORK+1-IWRK, IERR ) + END IF +* +* Perform QZ algorithm (Compute eigenvalues, and optionally, the +* Schur forms and Schur vectors) +* + IWRK = ITAU + IF( ILV ) THEN + CHTEMP = 'S' + ELSE + CHTEMP = 'E' + END IF + CALL SHGEQZ( CHTEMP, JOBVL, JOBVR, N, ILO, IHI, A, LDA, B, LDB, + $ ALPHAR, ALPHAI, BETA, VL, LDVL, VR, LDVR, + $ WORK( IWRK ), LWORK+1-IWRK, IERR ) + IF( IERR.NE.0 ) THEN + IF( IERR.GT.0 .AND. IERR.LE.N ) THEN + INFO = IERR + ELSE IF( IERR.GT.N .AND. IERR.LE.2*N ) THEN + INFO = IERR - N + ELSE + INFO = N + 1 + END IF + GO TO 110 + END IF +* +* Compute Eigenvectors +* + IF( ILV ) THEN + IF( ILVL ) THEN + IF( ILVR ) THEN + CHTEMP = 'B' + ELSE + CHTEMP = 'L' + END IF + ELSE + CHTEMP = 'R' + END IF + CALL STGEVC( CHTEMP, 'B', LDUMMA, N, A, LDA, B, LDB, VL, LDVL, + $ VR, LDVR, N, IN, WORK( IWRK ), IERR ) + IF( IERR.NE.0 ) THEN + INFO = N + 2 + GO TO 110 + END IF +* +* Undo balancing on VL and VR and normalization +* + IF( ILVL ) THEN + CALL SGGBAK( 'P', 'L', N, ILO, IHI, WORK( ILEFT ), + $ WORK( IRIGHT ), N, VL, LDVL, IERR ) + DO 50 JC = 1, N + IF( ALPHAI( JC ).LT.ZERO ) + $ GO TO 50 + TEMP = ZERO + IF( ALPHAI( JC ).EQ.ZERO ) THEN + DO 10 JR = 1, N + TEMP = MAX( TEMP, ABS( VL( JR, JC ) ) ) + 10 CONTINUE + ELSE + DO 20 JR = 1, N + TEMP = MAX( TEMP, ABS( VL( JR, JC ) )+ + $ ABS( VL( JR, JC+1 ) ) ) + 20 CONTINUE + END IF + IF( TEMP.LT.SMLNUM ) + $ GO TO 50 + TEMP = ONE / TEMP + IF( ALPHAI( JC ).EQ.ZERO ) THEN + DO 30 JR = 1, N + VL( JR, JC ) = VL( JR, JC )*TEMP + 30 CONTINUE + ELSE + DO 40 JR = 1, N + VL( JR, JC ) = VL( JR, JC )*TEMP + VL( JR, JC+1 ) = VL( JR, JC+1 )*TEMP + 40 CONTINUE + END IF + 50 CONTINUE + END IF + IF( ILVR ) THEN + CALL SGGBAK( 'P', 'R', N, ILO, IHI, WORK( ILEFT ), + $ WORK( IRIGHT ), N, VR, LDVR, IERR ) + DO 100 JC = 1, N + IF( ALPHAI( JC ).LT.ZERO ) + $ GO TO 100 + TEMP = ZERO + IF( ALPHAI( JC ).EQ.ZERO ) THEN + DO 60 JR = 1, N + TEMP = MAX( TEMP, ABS( VR( JR, JC ) ) ) + 60 CONTINUE + ELSE + DO 70 JR = 1, N + TEMP = MAX( TEMP, ABS( VR( JR, JC ) )+ + $ ABS( VR( JR, JC+1 ) ) ) + 70 CONTINUE + END IF + IF( TEMP.LT.SMLNUM ) + $ GO TO 100 + TEMP = ONE / TEMP + IF( ALPHAI( JC ).EQ.ZERO ) THEN + DO 80 JR = 1, N + VR( JR, JC ) = VR( JR, JC )*TEMP + 80 CONTINUE + ELSE + DO 90 JR = 1, N + VR( JR, JC ) = VR( JR, JC )*TEMP + VR( JR, JC+1 ) = VR( JR, JC+1 )*TEMP + 90 CONTINUE + END IF + 100 CONTINUE + END IF +* +* End of eigenvector calculation +* + END IF +* +* Undo scaling if necessary +* + 110 CONTINUE +* + IF( ILASCL ) THEN + CALL SLASCL( 'G', 0, 0, ANRMTO, ANRM, N, 1, ALPHAR, N, IERR ) + CALL SLASCL( 'G', 0, 0, ANRMTO, ANRM, N, 1, ALPHAI, N, IERR ) + END IF +* + IF( ILBSCL ) THEN + CALL SLASCL( 'G', 0, 0, BNRMTO, BNRM, N, 1, BETA, N, IERR ) + END IF +* + WORK( 1 ) = REAL( LWKOPT ) + RETURN +* +* End of SGGEV3 +* + END diff --git a/SRC/sgghd3.f b/SRC/sgghd3.f new file mode 100644 index 00000000..bf91f559 --- /dev/null +++ b/SRC/sgghd3.f @@ -0,0 +1,898 @@ +*> \brief \b SGGHD3 +* +* =========== DOCUMENTATION =========== +* +* Online html documentation available at +* http://www.netlib.org/lapack/explore-html/ +* +*> \htmlonly +*> Download SGGHRD + dependencies +*> <a href="http://www.netlib.org/cgi-bin/netlibfiles.tgz?format=tgz&filename=/lapack/lapack_routine/sgghd3.f"> +*> [TGZ]</a> +*> <a href="http://www.netlib.org/cgi-bin/netlibfiles.zip?format=zip&filename=/lapack/lapack_routine/sgghd3.f"> +*> [ZIP]</a> +*> <a href="http://www.netlib.org/cgi-bin/netlibfiles.txt?format=txt&filename=/lapack/lapack_routine/sgghd3.f"> +*> [TXT]</a> +*> \endhtmlonly +* +* Definition: +* =========== +* +* SUBROUTINE SGGHD3( COMPQ, COMPZ, N, ILO, IHI, A, LDA, B, LDB, Q, +* LDQ, Z, LDZ, WORK, LWORK, INFO ) +* +* .. Scalar Arguments .. +* CHARACTER COMPQ, COMPZ +* INTEGER IHI, ILO, INFO, LDA, LDB, LDQ, LDZ, N, LWORK +* .. +* .. Array Arguments .. +* REAL A( LDA, * ), B( LDB, * ), Q( LDQ, * ), +* $ Z( LDZ, * ), WORK( * ) +* .. +* +* +*> \par Purpose: +* ============= +*> +*> \verbatim +*> +*> SGGHD3 reduces a pair of real matrices (A,B) to generalized upper +*> Hessenberg form using orthogonal transformations, where A is a +*> general matrix and B is upper triangular. The form of the +*> generalized eigenvalue problem is +*> A*x = lambda*B*x, +*> and B is typically made upper triangular by computing its QR +*> factorization and moving the orthogonal matrix Q to the left side +*> of the equation. +*> +*> This subroutine simultaneously reduces A to a Hessenberg matrix H: +*> Q**T*A*Z = H +*> and transforms B to another upper triangular matrix T: +*> Q**T*B*Z = T +*> in order to reduce the problem to its standard form +*> H*y = lambda*T*y +*> where y = Z**T*x. +*> +*> The orthogonal matrices Q and Z are determined as products of Givens +*> rotations. They may either be formed explicitly, or they may be +*> postmultiplied into input matrices Q1 and Z1, so that +*> +*> Q1 * A * Z1**T = (Q1*Q) * H * (Z1*Z)**T +*> +*> Q1 * B * Z1**T = (Q1*Q) * T * (Z1*Z)**T +*> +*> If Q1 is the orthogonal matrix from the QR factorization of B in the +*> original equation A*x = lambda*B*x, then SGGHD3 reduces the original +*> problem to generalized Hessenberg form. +*> +*> This is a blocked variant of SGGHRD, using matrix-matrix +*> multiplications for parts of the computation to enhance performance. +*> \endverbatim +* +* Arguments: +* ========== +* +*> \param[in] COMPQ +*> \verbatim +*> COMPQ is CHARACTER*1 +*> = 'N': do not compute Q; +*> = 'I': Q is initialized to the unit matrix, and the +*> orthogonal matrix Q is returned; +*> = 'V': Q must contain an orthogonal matrix Q1 on entry, +*> and the product Q1*Q is returned. +*> \endverbatim +*> +*> \param[in] COMPZ +*> \verbatim +*> COMPZ is CHARACTER*1 +*> = 'N': do not compute Z; +*> = 'I': Z is initialized to the unit matrix, and the +*> orthogonal matrix Z is returned; +*> = 'V': Z must contain an orthogonal matrix Z1 on entry, +*> and the product Z1*Z is returned. +*> \endverbatim +*> +*> \param[in] N +*> \verbatim +*> N is INTEGER +*> The order of the matrices A and B. N >= 0. +*> \endverbatim +*> +*> \param[in] ILO +*> \verbatim +*> ILO is INTEGER +*> \endverbatim +*> +*> \param[in] IHI +*> \verbatim +*> IHI is INTEGER +*> +*> ILO and IHI mark the rows and columns of A which are to be +*> reduced. It is assumed that A is already upper triangular +*> in rows and columns 1:ILO-1 and IHI+1:N. ILO and IHI are +*> normally set by a previous call to SGGBAL; otherwise they +*> should be set to 1 and N respectively. +*> 1 <= ILO <= IHI <= N, if N > 0; ILO=1 and IHI=0, if N=0. +*> \endverbatim +*> +*> \param[in,out] A +*> \verbatim +*> A is REAL array, dimension (LDA, N) +*> On entry, the N-by-N general matrix to be reduced. +*> On exit, the upper triangle and the first subdiagonal of A +*> are overwritten with the upper Hessenberg matrix H, and the +*> rest is set to zero. +*> \endverbatim +*> +*> \param[in] LDA +*> \verbatim +*> LDA is INTEGER +*> The leading dimension of the array A. LDA >= max(1,N). +*> \endverbatim +*> +*> \param[in,out] B +*> \verbatim +*> B is REAL array, dimension (LDB, N) +*> On entry, the N-by-N upper triangular matrix B. +*> On exit, the upper triangular matrix T = Q**T B Z. The +*> elements below the diagonal are set to zero. +*> \endverbatim +*> +*> \param[in] LDB +*> \verbatim +*> LDB is INTEGER +*> The leading dimension of the array B. LDB >= max(1,N). +*> \endverbatim +*> +*> \param[in,out] Q +*> \verbatim +*> Q is REAL array, dimension (LDQ, N) +*> On entry, if COMPQ = 'V', the orthogonal matrix Q1, +*> typically from the QR factorization of B. +*> On exit, if COMPQ='I', the orthogonal matrix Q, and if +*> COMPQ = 'V', the product Q1*Q. +*> Not referenced if COMPQ='N'. +*> \endverbatim +*> +*> \param[in] LDQ +*> \verbatim +*> LDQ is INTEGER +*> The leading dimension of the array Q. +*> LDQ >= N if COMPQ='V' or 'I'; LDQ >= 1 otherwise. +*> \endverbatim +*> +*> \param[in,out] Z +*> \verbatim +*> Z is REAL array, dimension (LDZ, N) +*> On entry, if COMPZ = 'V', the orthogonal matrix Z1. +*> On exit, if COMPZ='I', the orthogonal matrix Z, and if +*> COMPZ = 'V', the product Z1*Z. +*> Not referenced if COMPZ='N'. +*> \endverbatim +*> +*> \param[in] LDZ +*> \verbatim +*> LDZ is INTEGER +*> The leading dimension of the array Z. +*> LDZ >= N if COMPZ='V' or 'I'; LDZ >= 1 otherwise. +*> \endverbatim +*> +*> \param[out] WORK +*> \verbatim +*> WORK is REAL array, dimension (LWORK) +*> On exit, if INFO = 0, WORK(1) returns the optimal LWORK. +*> \endverbatim +*> +*> \param[in] LWORK +*> \verbatim +*> LWORK is INTEGER +*> The length of the array WORK. LWORK >= 1. +*> For optimum performance LWORK >= 6*N*NB, where NB is the +*> optimal blocksize. +*> +*> If LWORK = -1, then a workspace query is assumed; the routine +*> only calculates the optimal size of the WORK array, returns +*> this value as the first entry of the WORK array, and no error +*> message related to LWORK is issued by XERBLA. +*> \endverbatim +*> +*> \param[out] INFO +*> \verbatim +*> INFO is INTEGER +*> = 0: successful exit. +*> < 0: if INFO = -i, the i-th argument had an illegal value. +*> \endverbatim +* +* Authors: +* ======== +* +*> \author Univ. of Tennessee +*> \author Univ. of California Berkeley +*> \author Univ. of Colorado Denver +*> \author NAG Ltd. +* +*> \date January 2015 +* +*> \ingroup realOTHERcomputational +* +*> \par Further Details: +* ===================== +*> +*> \verbatim +*> +*> This routine reduces A to Hessenberg form and maintains B in +*> using a blocked variant of Moler and Stewart's original algorithm, +*> as described by Kagstrom, Kressner, Quintana-Orti, and Quintana-Orti +*> (BIT 2008). +*> \endverbatim +*> +* ===================================================================== + SUBROUTINE SGGHD3( COMPQ, COMPZ, N, ILO, IHI, A, LDA, B, LDB, Q, + $ LDQ, Z, LDZ, WORK, LWORK, INFO ) +* +* -- LAPACK computational routine (version 3.6.0) -- +* -- LAPACK is a software package provided by Univ. of Tennessee, -- +* -- Univ. of California Berkeley, Univ. of Colorado Denver and NAG Ltd..-- +* January 2015 +* + IMPLICIT NONE +* +* .. Scalar Arguments .. + CHARACTER COMPQ, COMPZ + INTEGER IHI, ILO, INFO, LDA, LDB, LDQ, LDZ, N, LWORK +* .. +* .. Array Arguments .. + REAL A( LDA, * ), B( LDB, * ), Q( LDQ, * ), + $ Z( LDZ, * ), WORK( * ) +* .. +* +* ===================================================================== +* +* .. Parameters .. + REAL ZERO, ONE + PARAMETER ( ZERO = 0.0E+0, ONE = 1.0E+0 ) +* .. +* .. Local Scalars .. + LOGICAL BLK22, INITQ, INITZ, LQUERY, WANTQ, WANTZ + CHARACTER*1 COMPQ2, COMPZ2 + INTEGER COLA, I, IERR, J, J0, JCOL, JJ, JROW, K, + $ KACC22, LEN, LWKOPT, N2NB, NB, NBLST, NBMIN, + $ NH, NNB, NX, PPW, PPWO, PW, TOP, TOPQ + REAL C, C1, C2, S, S1, S2, TEMP, TEMP1, TEMP2, TEMP3 +* .. +* .. External Functions .. + LOGICAL LSAME + INTEGER ILAENV + EXTERNAL ILAENV, LSAME +* .. +* .. External Subroutines .. + EXTERNAL SGGHRD, SLARTG, SLASET, SORM22, SROT, XERBLA +* .. +* .. Intrinsic Functions .. + INTRINSIC REAL, MAX +* .. +* .. Executable Statements .. +* +* Decode and test the input parameters. +* + INFO = 0 + NB = ILAENV( 1, 'SGGHD3', ' ', N, ILO, IHI, -1 ) + LWKOPT = 6*N*NB + WORK( 1 ) = REAL( LWKOPT ) + INITQ = LSAME( COMPQ, 'I' ) + WANTQ = INITQ .OR. LSAME( COMPQ, 'V' ) + INITZ = LSAME( COMPZ, 'I' ) + WANTZ = INITZ .OR. LSAME( COMPZ, 'V' ) + LQUERY = ( LWORK.EQ.-1 ) +* + IF( .NOT.LSAME( COMPQ, 'N' ) .AND. .NOT.WANTQ ) THEN + INFO = -1 + ELSE IF( .NOT.LSAME( COMPZ, 'N' ) .AND. .NOT.WANTZ ) THEN + INFO = -2 + ELSE IF( N.LT.0 ) THEN + INFO = -3 + ELSE IF( ILO.LT.1 ) THEN + INFO = -4 + ELSE IF( IHI.GT.N .OR. IHI.LT.ILO-1 ) THEN + INFO = -5 + ELSE IF( LDA.LT.MAX( 1, N ) ) THEN + INFO = -7 + ELSE IF( LDB.LT.MAX( 1, N ) ) THEN + INFO = -9 + ELSE IF( ( WANTQ .AND. LDQ.LT.N ) .OR. LDQ.LT.1 ) THEN + INFO = -11 + ELSE IF( ( WANTZ .AND. LDZ.LT.N ) .OR. LDZ.LT.1 ) THEN + INFO = -13 + ELSE IF( LWORK.LT.1 .AND. .NOT.LQUERY ) THEN + INFO = -15 + END IF + IF( INFO.NE.0 ) THEN + CALL XERBLA( 'SGGHD3', -INFO ) + RETURN + ELSE IF( LQUERY ) THEN + RETURN + END IF +* +* Initialize Q and Z if desired. +* + IF( INITQ ) + $ CALL SLASET( 'All', N, N, ZERO, ONE, Q, LDQ ) + IF( INITZ ) + $ CALL SLASET( 'All', N, N, ZERO, ONE, Z, LDZ ) +* +* Zero out lower triangle of B. +* + IF( N.GT.1 ) + $ CALL SLASET( 'Lower', N-1, N-1, ZERO, ZERO, B(2, 1), LDB ) +* +* Quick return if possible +* + NH = IHI - ILO + 1 + IF( NH.LE.1 ) THEN + WORK( 1 ) = ONE + RETURN + END IF +* +* Determine the blocksize. +* + NBMIN = ILAENV( 2, 'SGGHD3', ' ', N, ILO, IHI, -1 ) + IF( NB.GT.1 .AND. NB.LT.NH ) THEN +* +* Determine when to use unblocked instead of blocked code. +* + NX = MAX( NB, ILAENV( 3, 'SGGHD3', ' ', N, ILO, IHI, -1 ) ) + IF( NX.LT.NH ) THEN +* +* Determine if workspace is large enough for blocked code. +* + IF( LWORK.LT.LWKOPT ) THEN +* +* Not enough workspace to use optimal NB: determine the +* minimum value of NB, and reduce NB or force use of +* unblocked code. +* + NBMIN = MAX( 2, ILAENV( 2, 'SGGHD3', ' ', N, ILO, IHI, + $ -1 ) ) + IF( LWORK.GE.6*N*NBMIN ) THEN + NB = LWORK / ( 6*N ) + ELSE + NB = 1 + END IF + END IF + END IF + END IF +* + IF( NB.LT.NBMIN .OR. NB.GE.NH ) THEN +* +* Use unblocked code below +* + JCOL = ILO +* + ELSE +* +* Use blocked code +* + KACC22 = ILAENV( 16, 'SGGHD3', ' ', N, ILO, IHI, -1 ) + BLK22 = KACC22.EQ.2 + DO JCOL = ILO, IHI-2, NB + NNB = MIN( NB, IHI-JCOL-1 ) +* +* Initialize small orthogonal factors that will hold the +* accumulated Givens rotations in workspace. +* N2NB denotes the number of 2*NNB-by-2*NNB factors +* NBLST denotes the (possibly smaller) order of the last +* factor. +* + N2NB = ( IHI-JCOL-1 ) / NNB - 1 + NBLST = IHI - JCOL - N2NB*NNB + CALL SLASET( 'All', NBLST, NBLST, ZERO, ONE, WORK, NBLST ) + PW = NBLST * NBLST + 1 + DO I = 1, N2NB + CALL SLASET( 'All', 2*NNB, 2*NNB, ZERO, ONE, + $ WORK( PW ), 2*NNB ) + PW = PW + 4*NNB*NNB + END DO +* +* Reduce columns JCOL:JCOL+NNB-1 of A to Hessenberg form. +* + DO J = JCOL, JCOL+NNB-1 +* +* Reduce Jth column of A. Store cosines and sines in Jth +* column of A and B, respectively. +* + DO I = IHI, J+2, -1 + TEMP = A( I-1, J ) + CALL SLARTG( TEMP, A( I, J ), C, S, A( I-1, J ) ) + A( I, J ) = C + B( I, J ) = S + END DO +* +* Accumulate Givens rotations into workspace array. +* + PPW = ( NBLST + 1 )*( NBLST - 2 ) - J + JCOL + 1 + LEN = 2 + J - JCOL + JROW = J + N2NB*NNB + 2 + DO I = IHI, JROW, -1 + C = A( I, J ) + S = B( I, J ) + DO JJ = PPW, PPW+LEN-1 + TEMP = WORK( JJ + NBLST ) + WORK( JJ + NBLST ) = C*TEMP - S*WORK( JJ ) + WORK( JJ ) = S*TEMP + C*WORK( JJ ) + END DO + LEN = LEN + 1 + PPW = PPW - NBLST - 1 + END DO +* + PPWO = NBLST*NBLST + ( NNB+J-JCOL-1 )*2*NNB + NNB + J0 = JROW - NNB + DO JROW = J0, J+2, -NNB + PPW = PPWO + LEN = 2 + J - JCOL + DO I = JROW+NNB-1, JROW, -1 + C = A( I, J ) + S = B( I, J ) + DO JJ = PPW, PPW+LEN-1 + TEMP = WORK( JJ + 2*NNB ) + WORK( JJ + 2*NNB ) = C*TEMP - S*WORK( JJ ) + WORK( JJ ) = S*TEMP + C*WORK( JJ ) + END DO + LEN = LEN + 1 + PPW = PPW - 2*NNB - 1 + END DO + PPWO = PPWO + 4*NNB*NNB + END DO +* +* TOP denotes the number of top rows in A and B that will +* not be updated during the next steps. +* + IF( JCOL.LE.2 ) THEN + TOP = 0 + ELSE + TOP = JCOL + END IF +* +* Propagate transformations through B and replace stored +* left sines/cosines by right sines/cosines. +* + DO JJ = N, J+1, -1 +* +* Update JJth column of B. +* + DO I = MIN( JJ+1, IHI ), J+2, -1 + C = A( I, J ) + S = B( I, J ) + TEMP = B( I, JJ ) + B( I, JJ ) = C*TEMP - S*B( I-1, JJ ) + B( I-1, JJ ) = S*TEMP + C*B( I-1, JJ ) + END DO +* +* Annihilate B( JJ+1, JJ ). +* + IF( JJ.LT.IHI ) THEN + TEMP = B( JJ+1, JJ+1 ) + CALL SLARTG( TEMP, B( JJ+1, JJ ), C, S, + $ B( JJ+1, JJ+1 ) ) + B( JJ+1, JJ ) = ZERO + CALL SROT( JJ-TOP, B( TOP+1, JJ+1 ), 1, + $ B( TOP+1, JJ ), 1, C, S ) + A( JJ+1, J ) = C + B( JJ+1, J ) = -S + END IF + END DO +* +* Update A by transformations from right. +* Explicit loop unrolling provides better performance +* compared to SLASR. +* CALL SLASR( 'Right', 'Variable', 'Backward', IHI-TOP, +* $ IHI-J, A( J+2, J ), B( J+2, J ), +* $ A( TOP+1, J+1 ), LDA ) +* + JJ = MOD( IHI-J-1, 3 ) + DO I = IHI-J-3, JJ+1, -3 + C = A( J+1+I, J ) + S = -B( J+1+I, J ) + C1 = A( J+2+I, J ) + S1 = -B( J+2+I, J ) + C2 = A( J+3+I, J ) + S2 = -B( J+3+I, J ) +* + DO K = TOP+1, IHI + TEMP = A( K, J+I ) + TEMP1 = A( K, J+I+1 ) + TEMP2 = A( K, J+I+2 ) + TEMP3 = A( K, J+I+3 ) + A( K, J+I+3 ) = C2*TEMP3 + S2*TEMP2 + TEMP2 = -S2*TEMP3 + C2*TEMP2 + A( K, J+I+2 ) = C1*TEMP2 + S1*TEMP1 + TEMP1 = -S1*TEMP2 + C1*TEMP1 + A( K, J+I+1 ) = C*TEMP1 + S*TEMP + A( K, J+I ) = -S*TEMP1 + C*TEMP + END DO + END DO +* + IF( JJ.GT.0 ) THEN + DO I = JJ, 1, -1 + CALL SROT( IHI-TOP, A( TOP+1, J+I+1 ), 1, + $ A( TOP+1, J+I ), 1, A( J+1+I, J ), + $ -B( J+1+I, J ) ) + END DO + END IF +* +* Update (J+1)th column of A by transformations from left. +* + IF ( J .LT. JCOL + NNB - 1 ) THEN + LEN = 1 + J - JCOL +* +* Multiply with the trailing accumulated orthogonal +* matrix, which takes the form +* +* [ U11 U12 ] +* U = [ ], +* [ U21 U22 ] +* +* where U21 is a LEN-by-LEN matrix and U12 is lower +* triangular. +* + JROW = IHI - NBLST + 1 + CALL SGEMV( 'Transpose', NBLST, LEN, ONE, WORK, + $ NBLST, A( JROW, J+1 ), 1, ZERO, + $ WORK( PW ), 1 ) + PPW = PW + LEN + DO I = JROW, JROW+NBLST-LEN-1 + WORK( PPW ) = A( I, J+1 ) + PPW = PPW + 1 + END DO + CALL STRMV( 'Lower', 'Transpose', 'Non-unit', + $ NBLST-LEN, WORK( LEN*NBLST + 1 ), NBLST, + $ WORK( PW+LEN ), 1 ) + CALL SGEMV( 'Transpose', LEN, NBLST-LEN, ONE, + $ WORK( (LEN+1)*NBLST - LEN + 1 ), NBLST, + $ A( JROW+NBLST-LEN, J+1 ), 1, ONE, + $ WORK( PW+LEN ), 1 ) + PPW = PW + DO I = JROW, JROW+NBLST-1 + A( I, J+1 ) = WORK( PPW ) + PPW = PPW + 1 + END DO +* +* Multiply with the other accumulated orthogonal +* matrices, which take the form +* +* [ U11 U12 0 ] +* [ ] +* U = [ U21 U22 0 ], +* [ ] +* [ 0 0 I ] +* +* where I denotes the (NNB-LEN)-by-(NNB-LEN) identity +* matrix, U21 is a LEN-by-LEN upper triangular matrix +* and U12 is an NNB-by-NNB lower triangular matrix. +* + PPWO = 1 + NBLST*NBLST + J0 = JROW - NNB + DO JROW = J0, JCOL+1, -NNB + PPW = PW + LEN + DO I = JROW, JROW+NNB-1 + WORK( PPW ) = A( I, J+1 ) + PPW = PPW + 1 + END DO + PPW = PW + DO I = JROW+NNB, JROW+NNB+LEN-1 + WORK( PPW ) = A( I, J+1 ) + PPW = PPW + 1 + END DO + CALL STRMV( 'Upper', 'Transpose', 'Non-unit', LEN, + $ WORK( PPWO + NNB ), 2*NNB, WORK( PW ), + $ 1 ) + CALL STRMV( 'Lower', 'Transpose', 'Non-unit', NNB, + $ WORK( PPWO + 2*LEN*NNB ), + $ 2*NNB, WORK( PW + LEN ), 1 ) + CALL SGEMV( 'Transpose', NNB, LEN, ONE, + $ WORK( PPWO ), 2*NNB, A( JROW, J+1 ), 1, + $ ONE, WORK( PW ), 1 ) + CALL SGEMV( 'Transpose', LEN, NNB, ONE, + $ WORK( PPWO + 2*LEN*NNB + NNB ), 2*NNB, + $ A( JROW+NNB, J+1 ), 1, ONE, + $ WORK( PW+LEN ), 1 ) + PPW = PW + DO I = JROW, JROW+LEN+NNB-1 + A( I, J+1 ) = WORK( PPW ) + PPW = PPW + 1 + END DO + PPWO = PPWO + 4*NNB*NNB + END DO + END IF + END DO +* +* Apply accumulated orthogonal matrices to A. +* + COLA = N - JCOL - NNB + 1 + J = IHI - NBLST + 1 + CALL SGEMM( 'Transpose', 'No Transpose', NBLST, + $ COLA, NBLST, ONE, WORK, NBLST, + $ A( J, JCOL+NNB ), LDA, ZERO, WORK( PW ), + $ NBLST ) + CALL SLACPY( 'All', NBLST, COLA, WORK( PW ), NBLST, + $ A( J, JCOL+NNB ), LDA ) + PPWO = NBLST*NBLST + 1 + J0 = J - NNB + DO J = J0, JCOL+1, -NNB + IF ( BLK22 ) THEN +* +* Exploit the structure of +* +* [ U11 U12 ] +* U = [ ] +* [ U21 U22 ], +* +* where all blocks are NNB-by-NNB, U21 is upper +* triangular and U12 is lower triangular. +* + CALL SORM22( 'Left', 'Transpose', 2*NNB, COLA, NNB, + $ NNB, WORK( PPWO ), 2*NNB, + $ A( J, JCOL+NNB ), LDA, WORK( PW ), + $ LWORK-PW+1, IERR ) + ELSE +* +* Ignore the structure of U. +* + CALL SGEMM( 'Transpose', 'No Transpose', 2*NNB, + $ COLA, 2*NNB, ONE, WORK( PPWO ), 2*NNB, + $ A( J, JCOL+NNB ), LDA, ZERO, WORK( PW ), + $ 2*NNB ) + CALL SLACPY( 'All', 2*NNB, COLA, WORK( PW ), 2*NNB, + $ A( J, JCOL+NNB ), LDA ) + END IF + PPWO = PPWO + 4*NNB*NNB + END DO +* +* Apply accumulated orthogonal matrices to Q. +* + IF( WANTQ ) THEN + J = IHI - NBLST + 1 + IF ( INITQ ) THEN + TOPQ = MAX( 2, J - JCOL + 1 ) + NH = IHI - TOPQ + 1 + ELSE + TOPQ = 1 + NH = N + END IF + CALL SGEMM( 'No Transpose', 'No Transpose', NH, + $ NBLST, NBLST, ONE, Q( TOPQ, J ), LDQ, + $ WORK, NBLST, ZERO, WORK( PW ), NH ) + CALL SLACPY( 'All', NH, NBLST, WORK( PW ), NH, + $ Q( TOPQ, J ), LDQ ) + PPWO = NBLST*NBLST + 1 + J0 = J - NNB + DO J = J0, JCOL+1, -NNB + IF ( INITQ ) THEN + TOPQ = MAX( 2, J - JCOL + 1 ) + NH = IHI - TOPQ + 1 + END IF + IF ( BLK22 ) THEN +* +* Exploit the structure of U. +* + CALL SORM22( 'Right', 'No Transpose', NH, 2*NNB, + $ NNB, NNB, WORK( PPWO ), 2*NNB, + $ Q( TOPQ, J ), LDQ, WORK( PW ), + $ LWORK-PW+1, IERR ) + ELSE +* +* Ignore the structure of U. +* + CALL SGEMM( 'No Transpose', 'No Transpose', NH, + $ 2*NNB, 2*NNB, ONE, Q( TOPQ, J ), LDQ, + $ WORK( PPWO ), 2*NNB, ZERO, WORK( PW ), + $ NH ) + CALL SLACPY( 'All', NH, 2*NNB, WORK( PW ), NH, + $ Q( TOPQ, J ), LDQ ) + END IF + PPWO = PPWO + 4*NNB*NNB + END DO + END IF +* +* Accumulate right Givens rotations if required. +* + IF ( WANTZ .OR. TOP.GT.0 ) THEN +* +* Initialize small orthogonal factors that will hold the +* accumulated Givens rotations in workspace. +* + CALL SLASET( 'All', NBLST, NBLST, ZERO, ONE, WORK, + $ NBLST ) + PW = NBLST * NBLST + 1 + DO I = 1, N2NB + CALL SLASET( 'All', 2*NNB, 2*NNB, ZERO, ONE, + $ WORK( PW ), 2*NNB ) + PW = PW + 4*NNB*NNB + END DO +* +* Accumulate Givens rotations into workspace array. +* + DO J = JCOL, JCOL+NNB-1 + PPW = ( NBLST + 1 )*( NBLST - 2 ) - J + JCOL + 1 + LEN = 2 + J - JCOL + JROW = J + N2NB*NNB + 2 + DO I = IHI, JROW, -1 + C = A( I, J ) + A( I, J ) = ZERO + S = B( I, J ) + B( I, J ) = ZERO + DO JJ = PPW, PPW+LEN-1 + TEMP = WORK( JJ + NBLST ) + WORK( JJ + NBLST ) = C*TEMP - S*WORK( JJ ) + WORK( JJ ) = S*TEMP + C*WORK( JJ ) + END DO + LEN = LEN + 1 + PPW = PPW - NBLST - 1 + END DO +* + PPWO = NBLST*NBLST + ( NNB+J-JCOL-1 )*2*NNB + NNB + J0 = JROW - NNB + DO JROW = J0, J+2, -NNB + PPW = PPWO + LEN = 2 + J - JCOL + DO I = JROW+NNB-1, JROW, -1 + C = A( I, J ) + A( I, J ) = ZERO + S = B( I, J ) + B( I, J ) = ZERO + DO JJ = PPW, PPW+LEN-1 + TEMP = WORK( JJ + 2*NNB ) + WORK( JJ + 2*NNB ) = C*TEMP - S*WORK( JJ ) + WORK( JJ ) = S*TEMP + C*WORK( JJ ) + END DO + LEN = LEN + 1 + PPW = PPW - 2*NNB - 1 + END DO + PPWO = PPWO + 4*NNB*NNB + END DO + END DO + ELSE + + DO J = ILO, ILO+NNB + DO I = J+2, IHI + A( I, J ) = ZERO + B( I, J ) = ZERO + END DO + END DO + END IF +* +* Apply accumulated orthogonal matrices to A and B. +* + IF ( TOP.GT.0 ) THEN + J = IHI - NBLST + 1 + CALL SGEMM( 'No Transpose', 'No Transpose', TOP, + $ NBLST, NBLST, ONE, A( 1, J ), LDA, + $ WORK, NBLST, ZERO, WORK( PW ), TOP ) + CALL SLACPY( 'All', TOP, NBLST, WORK( PW ), TOP, + $ A( 1, J ), LDA ) + PPWO = NBLST*NBLST + 1 + J0 = J - NNB + DO J = J0, JCOL+1, -NNB + IF ( BLK22 ) THEN +* +* Exploit the structure of U. +* + CALL SORM22( 'Right', 'No Transpose', TOP, 2*NNB, + $ NNB, NNB, WORK( PPWO ), 2*NNB, + $ A( 1, J ), LDA, WORK( PW ), + $ LWORK-PW+1, IERR ) + ELSE +* +* Ignore the structure of U. +* + CALL SGEMM( 'No Transpose', 'No Transpose', TOP, + $ 2*NNB, 2*NNB, ONE, A( 1, J ), LDA, + $ WORK( PPWO ), 2*NNB, ZERO, + $ WORK( PW ), TOP ) + CALL SLACPY( 'All', TOP, 2*NNB, WORK( PW ), TOP, + $ A( 1, J ), LDA ) + END IF + PPWO = PPWO + 4*NNB*NNB + END DO +* + J = IHI - NBLST + 1 + CALL SGEMM( 'No Transpose', 'No Transpose', TOP, + $ NBLST, NBLST, ONE, B( 1, J ), LDB, + $ WORK, NBLST, ZERO, WORK( PW ), TOP ) + CALL SLACPY( 'All', TOP, NBLST, WORK( PW ), TOP, + $ B( 1, J ), LDB ) + PPWO = NBLST*NBLST + 1 + J0 = J - NNB + DO J = J0, JCOL+1, -NNB + IF ( BLK22 ) THEN +* +* Exploit the structure of U. +* + CALL SORM22( 'Right', 'No Transpose', TOP, 2*NNB, + $ NNB, NNB, WORK( PPWO ), 2*NNB, + $ B( 1, J ), LDA, WORK( PW ), + $ LWORK-PW+1, IERR ) + ELSE +* +* Ignore the structure of U. +* + CALL SGEMM( 'No Transpose', 'No Transpose', TOP, + $ 2*NNB, 2*NNB, ONE, B( 1, J ), LDB, + $ WORK( PPWO ), 2*NNB, ZERO, + $ WORK( PW ), TOP ) + CALL SLACPY( 'All', TOP, 2*NNB, WORK( PW ), TOP, + $ B( 1, J ), LDB ) + END IF + PPWO = PPWO + 4*NNB*NNB + END DO + END IF +* +* Apply accumulated orthogonal matrices to Z. +* + IF( WANTZ ) THEN + J = IHI - NBLST + 1 + IF ( INITQ ) THEN + TOPQ = MAX( 2, J - JCOL + 1 ) + NH = IHI - TOPQ + 1 + ELSE + TOPQ = 1 + NH = N + END IF + CALL SGEMM( 'No Transpose', 'No Transpose', NH, + $ NBLST, NBLST, ONE, Z( TOPQ, J ), LDZ, + $ WORK, NBLST, ZERO, WORK( PW ), NH ) + CALL SLACPY( 'All', NH, NBLST, WORK( PW ), NH, + $ Z( TOPQ, J ), LDZ ) + PPWO = NBLST*NBLST + 1 + J0 = J - NNB + DO J = J0, JCOL+1, -NNB + IF ( INITQ ) THEN + TOPQ = MAX( 2, J - JCOL + 1 ) + NH = IHI - TOPQ + 1 + END IF + IF ( BLK22 ) THEN +* +* Exploit the structure of U. +* + CALL SORM22( 'Right', 'No Transpose', NH, 2*NNB, + $ NNB, NNB, WORK( PPWO ), 2*NNB, + $ Z( TOPQ, J ), LDZ, WORK( PW ), + $ LWORK-PW+1, IERR ) + ELSE +* +* Ignore the structure of U. +* + CALL SGEMM( 'No Transpose', 'No Transpose', NH, + $ 2*NNB, 2*NNB, ONE, Z( TOPQ, J ), LDZ, + $ WORK( PPWO ), 2*NNB, ZERO, WORK( PW ), + $ NH ) + CALL SLACPY( 'All', NH, 2*NNB, WORK( PW ), NH, + $ Z( TOPQ, J ), LDZ ) + END IF + PPWO = PPWO + 4*NNB*NNB + END DO + END IF + END DO + END IF +* +* Use unblocked code to reduce the rest of the matrix +* Avoid re-initialization of modified Q and Z. +* + COMPQ2 = COMPQ + COMPZ2 = COMPZ + IF ( JCOL.NE.ILO ) THEN + IF ( WANTQ ) + $ COMPQ2 = 'V' + IF ( WANTZ ) + $ COMPZ2 = 'V' + END IF +* + IF ( JCOL.LT.IHI ) + $ CALL SGGHRD( COMPQ2, COMPZ2, N, JCOL, IHI, A, LDA, B, LDB, Q, + $ LDQ, Z, LDZ, IERR ) + WORK( 1 ) = REAL( LWKOPT ) +* + RETURN +* +* End of SGGHD3 +* + END diff --git a/SRC/sorm22.f b/SRC/sorm22.f new file mode 100644 index 00000000..fdb5cd8b --- /dev/null +++ b/SRC/sorm22.f @@ -0,0 +1,441 @@ +*> \brief \b SORM22 multiplies a general matrix by a banded orthogonal matrix. +* +* =========== DOCUMENTATION =========== +* +* Online html documentation available at +* http://www.netlib.org/lapack/explore-html/ +* +*> \htmlonly +*> Download SORM22 + dependencies +*> <a href="http://www.netlib.org/cgi-bin/netlibfiles.tgz?format=tgz&filename=/lapack/lapack_routine/sorm22.f"> +*> [TGZ]</a> +*> <a href="http://www.netlib.org/cgi-bin/netlibfiles.zip?format=zip&filename=/lapack/lapack_routine/sorm22.f"> +*> [ZIP]</a> +*> <a href="http://www.netlib.org/cgi-bin/netlibfiles.txt?format=txt&filename=/lapack/lapack_routine/sorm22.f"> +*> [TXT]</a> +*> \endhtmlonly +* +* Definition: +* =========== +* +* SUBROUTINE SORM22( SIDE, TRANS, M, N, N1, N2, Q, LDQ, C, LDC, +* $ WORK, LWORK, INFO ) +* +* .. Scalar Arguments .. +* CHARACTER SIDE, TRANS +* INTEGER M, N, N1, N2, LDQ, LDC, LWORK, INFO +* .. +* .. Array Arguments .. +* REAL Q( LDQ, * ), C( LDC, * ), WORK( * ) +* .. +* +*> \par Purpose +* ============ +*> +*> \verbatim +*> +*> +*> SORM22 overwrites the general real M-by-N matrix C with +*> +*> SIDE = 'L' SIDE = 'R' +*> TRANS = 'N': Q * C C * Q +*> TRANS = 'T': Q**T * C C * Q**T +*> +*> where Q is a real orthogonal matrix of order NQ, with NQ = M if +*> SIDE = 'L' and NQ = N if SIDE = 'R'. +*> The orthogonal matrix Q processes a 2-by-2 block structure +*> +*> [ Q11 Q12 ] +*> Q = [ ] +*> [ Q21 Q22 ], +*> +*> where Q12 is an N1-by-N1 lower triangular matrix and Q21 is an +*> N2-by-N2 upper triangular matrix. +*> \endverbatim +* +* Arguments +* ========= +* +*> \param[in] SIDE +*> \verbatim +*> SIDE is CHARACTER*1 +*> = 'L': apply Q or Q**T from the Left; +*> = 'R': apply Q or Q**T from the Right. +*> \endverbatim +*> +*> \param[in] TRANS +*> \verbatim +*> TRANS is CHARACTER*1 +*> = 'N': apply Q (No transpose); +*> = 'C': apply Q**T (Conjugate transpose). +*> \endverbatim +*> +*> \param[in] M +*> \verbatim +*> M is INTEGER +*> The number of rows of the matrix C. M >= 0. +*> \endverbatim +*> +*> \param[in] N +*> \verbatim +*> N is INTEGER +*> The number of columns of the matrix C. N >= 0. +*> \endverbatim +*> +*> \param[in] N1 +*> \param[in] N2 +*> \verbatim +*> N1 is INTEGER +*> N2 is INTEGER +*> The dimension of Q12 and Q21, respectively. N1, N2 >= 0. +*> The following requirement must be satisfied: +*> N1 + N2 = M if SIDE = 'L' and N1 + N2 = N if SIDE = 'R'. +*> \endverbatim +*> +*> \param[in] Q +*> \verbatim +*> Q is REAL array, dimension +*> (LDQ,M) if SIDE = 'L' +*> (LDQ,N) if SIDE = 'R' +*> \endverbatim +*> +*> \param[in] LDQ +*> \verbatim +*> LDQ is INTEGER +*> The leading dimension of the array Q. +*> LDQ >= max(1,M) if SIDE = 'L'; LDQ >= max(1,N) if SIDE = 'R'. +*> \endverbatim +*> +*> \param[in,out] C +*> \verbatim +*> C is REAL array, dimension (LDC,N) +*> On entry, the M-by-N matrix C. +*> On exit, C is overwritten by Q*C or Q**T*C or C*Q**T or C*Q. +*> \endverbatim +*> +*> \param[in] LDC +*> \verbatim +*> LDC is INTEGER +*> The leading dimension of the array C. LDC >= max(1,M). +*> \endverbatim +*> +*> \param[out] WORK +*> \verbatim +*> WORK is REAL array, dimension (MAX(1,LWORK)) +*> On exit, if INFO = 0, WORK(1) returns the optimal LWORK. +*> \endverbatim +*> +*> \param[in] LWORK +*> \verbatim +*> LWORK is INTEGER +*> The dimension of the array WORK. +*> If SIDE = 'L', LWORK >= max(1,N); +*> if SIDE = 'R', LWORK >= max(1,M). +*> For optimum performance LWORK >= M*N. +*> +*> If LWORK = -1, then a workspace query is assumed; the routine +*> only calculates the optimal size of the WORK array, returns +*> this value as the first entry of the WORK array, and no error +*> message related to LWORK is issued by XERBLA. +*> \endverbatim +*> +*> \param[out] INFO +*> \verbatim +*> INFO is INTEGER +*> = 0: successful exit +*> < 0: if INFO = -i, the i-th argument had an illegal value +*> \endverbatim +* +* +* Authors: +* ======== +* +*> \author Univ. of Tennessee +*> \author Univ. of California Berkeley +*> \author Univ. of Colorado Denver +*> \author NAG Ltd. +* +*> \date January 2015 +* +*> \ingroup complexOTHERcomputational +* +* ===================================================================== + SUBROUTINE SORM22( SIDE, TRANS, M, N, N1, N2, Q, LDQ, C, LDC, + $ WORK, LWORK, INFO ) +* +* -- LAPACK computational routine (version 3.6.0) -- +* -- LAPACK is a software package provided by Univ. of Tennessee, -- +* -- Univ. of California Berkeley, Univ. of Colorado Denver and NAG Ltd..-- +* January 2015 +* + IMPLICIT NONE +* +* .. Scalar Arguments .. + CHARACTER SIDE, TRANS + INTEGER M, N, N1, N2, LDQ, LDC, LWORK, INFO +* .. +* .. Array Arguments .. + REAL Q( LDQ, * ), C( LDC, * ), WORK( * ) +* .. +* +* ===================================================================== +* +* .. Parameters .. + REAL ONE + PARAMETER ( ONE = 1.0E+0 ) +* +* .. Local Scalars .. + LOGICAL LEFT, LQUERY, NOTRAN + INTEGER I, LDWORK, LEN, LWKOPT, NB, NQ, NW +* .. +* .. External Functions .. + LOGICAL LSAME + EXTERNAL LSAME +* .. +* .. External Subroutines .. + EXTERNAL SGEMM, SLACPY, STRMM, XERBLA +* .. +* .. Intrinsic Functions .. + INTRINSIC REAL, MAX, MIN +* .. +* .. Executable Statements .. +* +* Test the input arguments +* + INFO = 0 + LEFT = LSAME( SIDE, 'L' ) + NOTRAN = LSAME( TRANS, 'N' ) + LQUERY = ( LWORK.EQ.-1 ) +* +* NQ is the order of Q; +* NW is the minimum dimension of WORK. +* + IF( LEFT ) THEN + NQ = M + ELSE + NQ = N + END IF + NW = NQ + IF( N1.EQ.0 .OR. N2.EQ.0 ) NW = 1 + IF( .NOT.LEFT .AND. .NOT.LSAME( SIDE, 'R' ) ) THEN + INFO = -1 + ELSE IF( .NOT.LSAME( TRANS, 'N' ) .AND. .NOT.LSAME( TRANS, 'T' ) ) + $ THEN + INFO = -2 + ELSE IF( M.LT.0 ) THEN + INFO = -3 + ELSE IF( N.LT.0 ) THEN + INFO = -4 + ELSE IF( N1.LT.0 .OR. N1+N2.NE.NQ ) THEN + INFO = -5 + ELSE IF( N2.LT.0 ) THEN + INFO = -6 + ELSE IF( LDQ.LT.MAX( 1, NQ ) ) THEN + INFO = -8 + ELSE IF( LDC.LT.MAX( 1, M ) ) THEN + INFO = -10 + ELSE IF( LWORK.LT.NW .AND. .NOT.LQUERY ) THEN + INFO = -12 + END IF +* + IF( INFO.EQ.0 ) THEN + LWKOPT = M*N + WORK( 1 ) = REAL( LWKOPT ) + END IF +* + IF( INFO.NE.0 ) THEN + CALL XERBLA( 'SORM22', -INFO ) + RETURN + ELSE IF( LQUERY ) THEN + RETURN + END IF +* +* Quick return if possible +* + IF( M.EQ.0 .OR. N.EQ.0 ) THEN + WORK( 1 ) = 1 + RETURN + END IF +* +* Degenerate cases (N1 = 0 or N2 = 0) are handled using STRMM. +* + IF( N1.EQ.0 ) THEN + CALL STRMM( SIDE, 'Upper', TRANS, 'Non-Unit', M, N, ONE, + $ Q, LDQ, C, LDC ) + WORK( 1 ) = ONE + RETURN + ELSE IF( N2.EQ.0 ) THEN + CALL STRMM( SIDE, 'Lower', TRANS, 'Non-Unit', M, N, ONE, + $ Q, LDQ, C, LDC ) + WORK( 1 ) = ONE + RETURN + END IF +* +* Compute the largest chunk size available from the workspace. +* + NB = MAX( 1, MIN( LWORK, LWKOPT ) / NQ ) +* + IF( LEFT ) THEN + IF( NOTRAN ) THEN + DO I = 1, N, NB + LEN = MIN( NB, N-I+1 ) + LDWORK = M +* +* Multiply bottom part of C by Q12. +* + CALL SLACPY( 'All', N1, LEN, C( N2+1, I ), LDC, WORK, + $ LDWORK ) + CALL STRMM( 'Left', 'Lower', 'No Transpose', 'Non-Unit', + $ N1, LEN, ONE, Q( 1, N2+1 ), LDQ, WORK, + $ LDWORK ) +* +* Multiply top part of C by Q11. +* + CALL SGEMM( 'No Transpose', 'No Transpose', N1, LEN, N2, + $ ONE, Q, LDQ, C( 1, I ), LDC, ONE, WORK, + $ LDWORK ) +* +* Multiply top part of C by Q21. +* + CALL SLACPY( 'All', N2, LEN, C( 1, I ), LDC, + $ WORK( N1+1 ), LDWORK ) + CALL STRMM( 'Left', 'Upper', 'No Transpose', 'Non-Unit', + $ N2, LEN, ONE, Q( N1+1, 1 ), LDQ, + $ WORK( N1+1 ), LDWORK ) +* +* Multiply bottom part of C by Q22. +* + CALL SGEMM( 'No Transpose', 'No Transpose', N2, LEN, N1, + $ ONE, Q( N1+1, N2+1 ), LDQ, C( N2+1, I ), LDC, + $ ONE, WORK( N1+1 ), LDWORK ) +* +* Copy everything back. +* + CALL SLACPY( 'All', M, LEN, WORK, LDWORK, C( 1, I ), + $ LDC ) + END DO + ELSE + DO I = 1, N, NB + LEN = MIN( NB, N-I+1 ) + LDWORK = M +* +* Multiply bottom part of C by Q21**T. +* + CALL SLACPY( 'All', N2, LEN, C( N1+1, I ), LDC, WORK, + $ LDWORK ) + CALL STRMM( 'Left', 'Upper', 'Transpose', 'Non-Unit', + $ N2, LEN, ONE, Q( N1+1, 1 ), LDQ, WORK, + $ LDWORK ) +* +* Multiply top part of C by Q11**T. +* + CALL SGEMM( 'Transpose', 'No Transpose', N2, LEN, N1, + $ ONE, Q, LDQ, C( 1, I ), LDC, ONE, WORK, + $ LDWORK ) +* +* Multiply top part of C by Q12**T. +* + CALL SLACPY( 'All', N1, LEN, C( 1, I ), LDC, + $ WORK( N2+1 ), LDWORK ) + CALL STRMM( 'Left', 'Lower', 'Transpose', 'Non-Unit', + $ N1, LEN, ONE, Q( 1, N2+1 ), LDQ, + $ WORK( N2+1 ), LDWORK ) +* +* Multiply bottom part of C by Q22**T. +* + CALL SGEMM( 'Transpose', 'No Transpose', N1, LEN, N2, + $ ONE, Q( N1+1, N2+1 ), LDQ, C( N1+1, I ), LDC, + $ ONE, WORK( N2+1 ), LDWORK ) +* +* Copy everything back. +* + CALL SLACPY( 'All', M, LEN, WORK, LDWORK, C( 1, I ), + $ LDC ) + END DO + END IF + ELSE + IF( NOTRAN ) THEN + DO I = 1, M, NB + LEN = MIN( NB, M-I+1 ) + LDWORK = LEN +* +* Multiply right part of C by Q21. +* + CALL SLACPY( 'All', LEN, N2, C( I, N1+1 ), LDC, WORK, + $ LDWORK ) + CALL STRMM( 'Right', 'Upper', 'No Transpose', 'Non-Unit', + $ LEN, N2, ONE, Q( N1+1, 1 ), LDQ, WORK, + $ LDWORK ) +* +* Multiply left part of C by Q11. +* + CALL SGEMM( 'No Transpose', 'No Transpose', LEN, N2, N1, + $ ONE, C( I, 1 ), LDC, Q, LDQ, ONE, WORK, + $ LDWORK ) +* +* Multiply left part of C by Q12. +* + CALL SLACPY( 'All', LEN, N1, C( I, 1 ), LDC, + $ WORK( 1 + N2*LDWORK ), LDWORK ) + CALL STRMM( 'Right', 'Lower', 'No Transpose', 'Non-Unit', + $ LEN, N1, ONE, Q( 1, N2+1 ), LDQ, + $ WORK( 1 + N2*LDWORK ), LDWORK ) +* +* Multiply right part of C by Q22. +* + CALL SGEMM( 'No Transpose', 'No Transpose', LEN, N1, N2, + $ ONE, C( I, N1+1 ), LDC, Q( N1+1, N2+1 ), LDQ, + $ ONE, WORK( 1 + N2*LDWORK ), LDWORK ) +* +* Copy everything back. +* + CALL SLACPY( 'All', LEN, N, WORK, LDWORK, C( I, 1 ), + $ LDC ) + END DO + ELSE + DO I = 1, M, NB + LEN = MIN( NB, M-I+1 ) + LDWORK = LEN +* +* Multiply right part of C by Q12**T. +* + CALL SLACPY( 'All', LEN, N1, C( I, N2+1 ), LDC, WORK, + $ LDWORK ) + CALL STRMM( 'Right', 'Lower', 'Transpose', 'Non-Unit', + $ LEN, N1, ONE, Q( 1, N2+1 ), LDQ, WORK, + $ LDWORK ) +* +* Multiply left part of C by Q11**T. +* + CALL SGEMM( 'No Transpose', 'Transpose', LEN, N1, N2, + $ ONE, C( I, 1 ), LDC, Q, LDQ, ONE, WORK, + $ LDWORK ) +* +* Multiply left part of C by Q21**T. +* + CALL SLACPY( 'All', LEN, N2, C( I, 1 ), LDC, + $ WORK( 1 + N1*LDWORK ), LDWORK ) + CALL STRMM( 'Right', 'Upper', 'Transpose', 'Non-Unit', + $ LEN, N2, ONE, Q( N1+1, 1 ), LDQ, + $ WORK( 1 + N1*LDWORK ), LDWORK ) +* +* Multiply right part of C by Q22**T. +* + CALL SGEMM( 'No Transpose', 'Transpose', LEN, N2, N1, + $ ONE, C( I, N2+1 ), LDC, Q( N1+1, N2+1 ), LDQ, + $ ONE, WORK( 1 + N1*LDWORK ), LDWORK ) +* +* Copy everything back. +* + CALL SLACPY( 'All', LEN, N, WORK, LDWORK, C( I, 1 ), + $ LDC ) + END DO + END IF + END IF +* + WORK( 1 ) = REAL( LWKOPT ) + RETURN +* +* End of SORM22 +* + END diff --git a/SRC/zgges3.f b/SRC/zgges3.f new file mode 100644 index 00000000..d4455148 --- /dev/null +++ b/SRC/zgges3.f @@ -0,0 +1,595 @@ +*> \brief <b> ZGGES3 computes the eigenvalues, the Schur form, and, optionally, the matrix of Schur vectors for GE matrices (blocked algorithm)</b> +* +* =========== DOCUMENTATION =========== +* +* Online html documentation available at +* http://www.netlib.org/lapack/explore-html/ +* +*> \htmlonly +*> Download ZGGES3 + dependencies +*> <a href="http://www.netlib.org/cgi-bin/netlibfiles.tgz?format=tgz&filename=/lapack/lapack_routine/zgges3.f"> +*> [TGZ]</a> +*> <a href="http://www.netlib.org/cgi-bin/netlibfiles.zip?format=zip&filename=/lapack/lapack_routine/zgges3.f"> +*> [ZIP]</a> +*> <a href="http://www.netlib.org/cgi-bin/netlibfiles.txt?format=txt&filename=/lapack/lapack_routine/zgges3.f"> +*> [TXT]</a> +*> \endhtmlonly +* +* Definition: +* =========== +* +* SUBROUTINE ZGGES3( JOBVSL, JOBVSR, SORT, SELCTG, N, A, LDA, B, +* $ LDB, SDIM, ALPHA, BETA, VSL, LDVSL, VSR, LDVSR, +* $ WORK, LWORK, RWORK, BWORK, INFO ) +* +* .. Scalar Arguments .. +* CHARACTER JOBVSL, JOBVSR, SORT +* INTEGER INFO, LDA, LDB, LDVSL, LDVSR, LWORK, N, SDIM +* .. +* .. Array Arguments .. +* LOGICAL BWORK( * ) +* DOUBLE PRECISION RWORK( * ) +* COMPLEX*16 A( LDA, * ), ALPHA( * ), B( LDB, * ), +* $ BETA( * ), VSL( LDVSL, * ), VSR( LDVSR, * ), +* $ WORK( * ) +* .. +* .. Function Arguments .. +* LOGICAL SELCTG +* EXTERNAL SELCTG +* .. +* +* +*> \par Purpose: +* ============= +*> +*> \verbatim +*> +*> ZGGES3 computes for a pair of N-by-N complex nonsymmetric matrices +*> (A,B), the generalized eigenvalues, the generalized complex Schur +*> form (S, T), and optionally left and/or right Schur vectors (VSL +*> and VSR). This gives the generalized Schur factorization +*> +*> (A,B) = ( (VSL)*S*(VSR)**H, (VSL)*T*(VSR)**H ) +*> +*> where (VSR)**H is the conjugate-transpose of VSR. +*> +*> Optionally, it also orders the eigenvalues so that a selected cluster +*> of eigenvalues appears in the leading diagonal blocks of the upper +*> triangular matrix S and the upper triangular matrix T. The leading +*> columns of VSL and VSR then form an unitary basis for the +*> corresponding left and right eigenspaces (deflating subspaces). +*> +*> (If only the generalized eigenvalues are needed, use the driver +*> ZGGEV instead, which is faster.) +*> +*> A generalized eigenvalue for a pair of matrices (A,B) is a scalar w +*> or a ratio alpha/beta = w, such that A - w*B is singular. It is +*> usually represented as the pair (alpha,beta), as there is a +*> reasonable interpretation for beta=0, and even for both being zero. +*> +*> A pair of matrices (S,T) is in generalized complex Schur form if S +*> and T are upper triangular and, in addition, the diagonal elements +*> of T are non-negative real numbers. +*> \endverbatim +* +* Arguments: +* ========== +* +*> \param[in] JOBVSL +*> \verbatim +*> JOBVSL is CHARACTER*1 +*> = 'N': do not compute the left Schur vectors; +*> = 'V': compute the left Schur vectors. +*> \endverbatim +*> +*> \param[in] JOBVSR +*> \verbatim +*> JOBVSR is CHARACTER*1 +*> = 'N': do not compute the right Schur vectors; +*> = 'V': compute the right Schur vectors. +*> \endverbatim +*> +*> \param[in] SORT +*> \verbatim +*> SORT is CHARACTER*1 +*> Specifies whether or not to order the eigenvalues on the +*> diagonal of the generalized Schur form. +*> = 'N': Eigenvalues are not ordered; +*> = 'S': Eigenvalues are ordered (see SELCTG). +*> \endverbatim +*> +*> \param[in] SELCTG +*> \verbatim +*> SELCTG is a LOGICAL FUNCTION of two COMPLEX*16 arguments +*> SELCTG must be declared EXTERNAL in the calling subroutine. +*> If SORT = 'N', SELCTG is not referenced. +*> If SORT = 'S', SELCTG is used to select eigenvalues to sort +*> to the top left of the Schur form. +*> An eigenvalue ALPHA(j)/BETA(j) is selected if +*> SELCTG(ALPHA(j),BETA(j)) is true. +*> +*> Note that a selected complex eigenvalue may no longer satisfy +*> SELCTG(ALPHA(j),BETA(j)) = .TRUE. after ordering, since +*> ordering may change the value of complex eigenvalues +*> (especially if the eigenvalue is ill-conditioned), in this +*> case INFO is set to N+2 (See INFO below). +*> \endverbatim +*> +*> \param[in] N +*> \verbatim +*> N is INTEGER +*> The order of the matrices A, B, VSL, and VSR. N >= 0. +*> \endverbatim +*> +*> \param[in,out] A +*> \verbatim +*> A is COMPLEX*16 array, dimension (LDA, N) +*> On entry, the first of the pair of matrices. +*> On exit, A has been overwritten by its generalized Schur +*> form S. +*> \endverbatim +*> +*> \param[in] LDA +*> \verbatim +*> LDA is INTEGER +*> The leading dimension of A. LDA >= max(1,N). +*> \endverbatim +*> +*> \param[in,out] B +*> \verbatim +*> B is COMPLEX*16 array, dimension (LDB, N) +*> On entry, the second of the pair of matrices. +*> On exit, B has been overwritten by its generalized Schur +*> form T. +*> \endverbatim +*> +*> \param[in] LDB +*> \verbatim +*> LDB is INTEGER +*> The leading dimension of B. LDB >= max(1,N). +*> \endverbatim +*> +*> \param[out] SDIM +*> \verbatim +*> SDIM is INTEGER +*> If SORT = 'N', SDIM = 0. +*> If SORT = 'S', SDIM = number of eigenvalues (after sorting) +*> for which SELCTG is true. +*> \endverbatim +*> +*> \param[out] ALPHA +*> \verbatim +*> ALPHA is COMPLEX*16 array, dimension (N) +*> \endverbatim +*> +*> \param[out] BETA +*> \verbatim +*> BETA is COMPLEX*16 array, dimension (N) +*> On exit, ALPHA(j)/BETA(j), j=1,...,N, will be the +*> generalized eigenvalues. ALPHA(j), j=1,...,N and BETA(j), +*> j=1,...,N are the diagonals of the complex Schur form (A,B) +*> output by ZGGES3. The BETA(j) will be non-negative real. +*> +*> Note: the quotients ALPHA(j)/BETA(j) may easily over- or +*> underflow, and BETA(j) may even be zero. Thus, the user +*> should avoid naively computing the ratio alpha/beta. +*> However, ALPHA will be always less than and usually +*> comparable with norm(A) in magnitude, and BETA always less +*> than and usually comparable with norm(B). +*> \endverbatim +*> +*> \param[out] VSL +*> \verbatim +*> VSL is COMPLEX*16 array, dimension (LDVSL,N) +*> If JOBVSL = 'V', VSL will contain the left Schur vectors. +*> Not referenced if JOBVSL = 'N'. +*> \endverbatim +*> +*> \param[in] LDVSL +*> \verbatim +*> LDVSL is INTEGER +*> The leading dimension of the matrix VSL. LDVSL >= 1, and +*> if JOBVSL = 'V', LDVSL >= N. +*> \endverbatim +*> +*> \param[out] VSR +*> \verbatim +*> VSR is COMPLEX*16 array, dimension (LDVSR,N) +*> If JOBVSR = 'V', VSR will contain the right Schur vectors. +*> Not referenced if JOBVSR = 'N'. +*> \endverbatim +*> +*> \param[in] LDVSR +*> \verbatim +*> LDVSR is INTEGER +*> The leading dimension of the matrix VSR. LDVSR >= 1, and +*> if JOBVSR = 'V', LDVSR >= N. +*> \endverbatim +*> +*> \param[out] WORK +*> \verbatim +*> WORK is COMPLEX*16 array, dimension (MAX(1,LWORK)) +*> On exit, if INFO = 0, WORK(1) returns the optimal LWORK. +*> \endverbatim +*> +*> \param[in] LWORK +*> \verbatim +*> LWORK is INTEGER +*> The dimension of the array WORK. +*> +*> If LWORK = -1, then a workspace query is assumed; the routine +*> only calculates the optimal size of the WORK array, returns +*> this value as the first entry of the WORK array, and no error +*> message related to LWORK is issued by XERBLA. +*> \endverbatim +*> +*> \param[out] RWORK +*> \verbatim +*> RWORK is DOUBLE PRECISION array, dimension (8*N) +*> \endverbatim +*> +*> \param[out] BWORK +*> \verbatim +*> BWORK is LOGICAL array, dimension (N) +*> Not referenced if SORT = 'N'. +*> \endverbatim +*> +*> \param[out] INFO +*> \verbatim +*> INFO is INTEGER +*> = 0: successful exit +*> < 0: if INFO = -i, the i-th argument had an illegal value. +*> =1,...,N: +*> The QZ iteration failed. (A,B) are not in Schur +*> form, but ALPHA(j) and BETA(j) should be correct for +*> j=INFO+1,...,N. +*> > N: =N+1: other than QZ iteration failed in ZHGEQZ +*> =N+2: after reordering, roundoff changed values of +*> some complex eigenvalues so that leading +*> eigenvalues in the Generalized Schur form no +*> longer satisfy SELCTG=.TRUE. This could also +*> be caused due to scaling. +*> =N+3: reordering failed in ZTGSEN. +*> \endverbatim +* +* Authors: +* ======== +* +*> \author Univ. of Tennessee +*> \author Univ. of California Berkeley +*> \author Univ. of Colorado Denver +*> \author NAG Ltd. +* +*> \date January 2015 +* +*> \ingroup complex16GEeigen +* +* ===================================================================== + SUBROUTINE ZGGES3( JOBVSL, JOBVSR, SORT, SELCTG, N, A, LDA, B, + $ LDB, SDIM, ALPHA, BETA, VSL, LDVSL, VSR, LDVSR, + $ WORK, LWORK, RWORK, BWORK, INFO ) +* +* -- LAPACK driver routine (version 3.6.0) -- +* -- LAPACK is a software package provided by Univ. of Tennessee, -- +* -- Univ. of California Berkeley, Univ. of Colorado Denver and NAG Ltd..-- +* January 2015 +* +* .. Scalar Arguments .. + CHARACTER JOBVSL, JOBVSR, SORT + INTEGER INFO, LDA, LDB, LDVSL, LDVSR, LWORK, N, SDIM +* .. +* .. Array Arguments .. + LOGICAL BWORK( * ) + DOUBLE PRECISION RWORK( * ) + COMPLEX*16 A( LDA, * ), ALPHA( * ), B( LDB, * ), + $ BETA( * ), VSL( LDVSL, * ), VSR( LDVSR, * ), + $ WORK( * ) +* .. +* .. Function Arguments .. + LOGICAL SELCTG + EXTERNAL SELCTG +* .. +* +* ===================================================================== +* +* .. Parameters .. + DOUBLE PRECISION ZERO, ONE + PARAMETER ( ZERO = 0.0D0, ONE = 1.0D0 ) + COMPLEX*16 CZERO, CONE + PARAMETER ( CZERO = ( 0.0D0, 0.0D0 ), + $ CONE = ( 1.0D0, 0.0D0 ) ) +* .. +* .. Local Scalars .. + LOGICAL CURSL, ILASCL, ILBSCL, ILVSL, ILVSR, LASTSL, + $ LQUERY, WANTST + INTEGER I, ICOLS, IERR, IHI, IJOBVL, IJOBVR, ILEFT, + $ ILO, IRIGHT, IROWS, IRWRK, ITAU, IWRK, LWKOPT + DOUBLE PRECISION ANRM, ANRMTO, BIGNUM, BNRM, BNRMTO, EPS, PVSL, + $ PVSR, SMLNUM +* .. +* .. Local Arrays .. + INTEGER IDUM( 1 ) + DOUBLE PRECISION DIF( 2 ) +* .. +* .. External Subroutines .. + EXTERNAL DLABAD, XERBLA, ZGEQRF, ZGGBAK, ZGGBAL, ZGGHD3, + $ ZHGEQZ, ZLACPY, ZLASCL, ZLASET, ZTGSEN, ZUNGQR, + $ ZUNMQR +* .. +* .. External Functions .. + LOGICAL LSAME + DOUBLE PRECISION DLAMCH, ZLANGE + EXTERNAL LSAME, DLAMCH, ZLANGE +* .. +* .. Intrinsic Functions .. + INTRINSIC MAX, SQRT +* .. +* .. Executable Statements .. +* +* Decode the input arguments +* + IF( LSAME( JOBVSL, 'N' ) ) THEN + IJOBVL = 1 + ILVSL = .FALSE. + ELSE IF( LSAME( JOBVSL, 'V' ) ) THEN + IJOBVL = 2 + ILVSL = .TRUE. + ELSE + IJOBVL = -1 + ILVSL = .FALSE. + END IF +* + IF( LSAME( JOBVSR, 'N' ) ) THEN + IJOBVR = 1 + ILVSR = .FALSE. + ELSE IF( LSAME( JOBVSR, 'V' ) ) THEN + IJOBVR = 2 + ILVSR = .TRUE. + ELSE + IJOBVR = -1 + ILVSR = .FALSE. + END IF +* + WANTST = LSAME( SORT, 'S' ) +* +* Test the input arguments +* + INFO = 0 + LQUERY = ( LWORK.EQ.-1 ) + IF( IJOBVL.LE.0 ) THEN + INFO = -1 + ELSE IF( IJOBVR.LE.0 ) THEN + INFO = -2 + ELSE IF( ( .NOT.WANTST ) .AND. ( .NOT.LSAME( SORT, 'N' ) ) ) THEN + INFO = -3 + ELSE IF( N.LT.0 ) THEN + INFO = -5 + ELSE IF( LDA.LT.MAX( 1, N ) ) THEN + INFO = -7 + ELSE IF( LDB.LT.MAX( 1, N ) ) THEN + INFO = -9 + ELSE IF( LDVSL.LT.1 .OR. ( ILVSL .AND. LDVSL.LT.N ) ) THEN + INFO = -14 + ELSE IF( LDVSR.LT.1 .OR. ( ILVSR .AND. LDVSR.LT.N ) ) THEN + INFO = -16 + ELSE IF( LWORK.LT.MAX( 1, 2*N ) .AND. .NOT.LQUERY ) THEN + INFO = -18 + END IF +* +* Compute workspace +* + IF( INFO.EQ.0 ) THEN + CALL ZGEQRF( N, N, B, LDB, WORK, WORK, -1, IERR ) + LWKOPT = MAX( 1, N + INT ( WORK( 1 ) ) ) + CALL ZUNMQR( 'L', 'C', N, N, N, B, LDB, WORK, A, LDA, WORK, + $ -1, IERR ) + LWKOPT = MAX( LWKOPT, N + INT ( WORK( 1 ) ) ) + IF( ILVSL ) THEN + CALL ZUNGQR( N, N, N, VSL, LDVSL, WORK, WORK, -1, IERR ) + LWKOPT = MAX( LWKOPT, N + INT ( WORK( 1 ) ) ) + END IF + CALL ZGGHD3( JOBVSL, JOBVSR, N, 1, N, A, LDA, B, LDB, VSL, + $ LDVSL, VSR, LDVSR, WORK, -1, IERR ) + LWKOPT = MAX( LWKOPT, N + INT ( WORK( 1 ) ) ) + CALL ZHGEQZ( 'S', JOBVSL, JOBVSR, N, 1, N, A, LDA, B, LDB, + $ ALPHA, BETA, VSL, LDVSL, VSR, LDVSR, WORK, + $ -1, RWORK, IERR ) + LWKOPT = MAX( LWKOPT, INT ( WORK( 1 ) ) ) + IF( WANTST ) THEN + CALL ZTGSEN( 0, ILVSL, ILVSR, BWORK, N, A, LDA, B, LDB, + $ ALPHA, BETA, VSL, LDVSL, VSR, LDVSR, SDIM, + $ PVSL, PVSR, DIF, WORK, -1, IDUM, 1, IERR ) + LWKOPT = MAX( LWKOPT, INT ( WORK( 1 ) ) ) + END IF + WORK( 1 ) = DCMPLX( WKOPT ) + END IF +* + IF( INFO.NE.0 ) THEN + CALL XERBLA( 'ZGGES3 ', -INFO ) + RETURN + ELSE IF( LQUERY ) THEN + RETURN + END IF +* +* Quick return if possible +* + IF( N.EQ.0 ) THEN + SDIM = 0 + RETURN + END IF +* +* Get machine constants +* + EPS = DLAMCH( 'P' ) + SMLNUM = DLAMCH( 'S' ) + BIGNUM = ONE / SMLNUM + CALL DLABAD( SMLNUM, BIGNUM ) + SMLNUM = SQRT( SMLNUM ) / EPS + BIGNUM = ONE / SMLNUM +* +* Scale A if max element outside range [SMLNUM,BIGNUM] +* + ANRM = ZLANGE( 'M', N, N, A, LDA, RWORK ) + ILASCL = .FALSE. + IF( ANRM.GT.ZERO .AND. ANRM.LT.SMLNUM ) THEN + ANRMTO = SMLNUM + ILASCL = .TRUE. + ELSE IF( ANRM.GT.BIGNUM ) THEN + ANRMTO = BIGNUM + ILASCL = .TRUE. + END IF +* + IF( ILASCL ) + $ CALL ZLASCL( 'G', 0, 0, ANRM, ANRMTO, N, N, A, LDA, IERR ) +* +* Scale B if max element outside range [SMLNUM,BIGNUM] +* + BNRM = ZLANGE( 'M', N, N, B, LDB, RWORK ) + ILBSCL = .FALSE. + IF( BNRM.GT.ZERO .AND. BNRM.LT.SMLNUM ) THEN + BNRMTO = SMLNUM + ILBSCL = .TRUE. + ELSE IF( BNRM.GT.BIGNUM ) THEN + BNRMTO = BIGNUM + ILBSCL = .TRUE. + END IF +* + IF( ILBSCL ) + $ CALL ZLASCL( 'G', 0, 0, BNRM, BNRMTO, N, N, B, LDB, IERR ) +* +* Permute the matrix to make it more nearly triangular +* + ILEFT = 1 + IRIGHT = N + 1 + IRWRK = IRIGHT + N + CALL ZGGBAL( 'P', N, A, LDA, B, LDB, ILO, IHI, RWORK( ILEFT ), + $ RWORK( IRIGHT ), RWORK( IRWRK ), IERR ) +* +* Reduce B to triangular form (QR decomposition of B) +* + IROWS = IHI + 1 - ILO + ICOLS = N + 1 - ILO + ITAU = 1 + IWRK = ITAU + IROWS + CALL ZGEQRF( IROWS, ICOLS, B( ILO, ILO ), LDB, WORK( ITAU ), + $ WORK( IWRK ), LWORK+1-IWRK, IERR ) +* +* Apply the orthogonal transformation to matrix A +* + CALL ZUNMQR( 'L', 'C', IROWS, ICOLS, IROWS, B( ILO, ILO ), LDB, + $ WORK( ITAU ), A( ILO, ILO ), LDA, WORK( IWRK ), + $ LWORK+1-IWRK, IERR ) +* +* Initialize VSL +* + IF( ILVSL ) THEN + CALL ZLASET( 'Full', N, N, CZERO, CONE, VSL, LDVSL ) + IF( IROWS.GT.1 ) THEN + CALL ZLACPY( 'L', IROWS-1, IROWS-1, B( ILO+1, ILO ), LDB, + $ VSL( ILO+1, ILO ), LDVSL ) + END IF + CALL ZUNGQR( IROWS, IROWS, IROWS, VSL( ILO, ILO ), LDVSL, + $ WORK( ITAU ), WORK( IWRK ), LWORK+1-IWRK, IERR ) + END IF +* +* Initialize VSR +* + IF( ILVSR ) + $ CALL ZLASET( 'Full', N, N, CZERO, CONE, VSR, LDVSR ) +* +* Reduce to generalized Hessenberg form +* + CALL ZGGHD3( JOBVSL, JOBVSR, N, ILO, IHI, A, LDA, B, LDB, VSL, + $ LDVSL, VSR, LDVSR, WORK( IWRK ), LWORK+1-IWRK, IERR ) +* + SDIM = 0 +* +* Perform QZ algorithm, computing Schur vectors if desired +* + IWRK = ITAU + CALL ZHGEQZ( 'S', JOBVSL, JOBVSR, N, ILO, IHI, A, LDA, B, LDB, + $ ALPHA, BETA, VSL, LDVSL, VSR, LDVSR, WORK( IWRK ), + $ LWORK+1-IWRK, RWORK( IRWRK ), IERR ) + IF( IERR.NE.0 ) THEN + IF( IERR.GT.0 .AND. IERR.LE.N ) THEN + INFO = IERR + ELSE IF( IERR.GT.N .AND. IERR.LE.2*N ) THEN + INFO = IERR - N + ELSE + INFO = N + 1 + END IF + GO TO 30 + END IF +* +* Sort eigenvalues ALPHA/BETA if desired +* + IF( WANTST ) THEN +* +* Undo scaling on eigenvalues before selecting +* + IF( ILASCL ) + $ CALL ZLASCL( 'G', 0, 0, ANRM, ANRMTO, N, 1, ALPHA, N, IERR ) + IF( ILBSCL ) + $ CALL ZLASCL( 'G', 0, 0, BNRM, BNRMTO, N, 1, BETA, N, IERR ) +* +* Select eigenvalues +* + DO 10 I = 1, N + BWORK( I ) = SELCTG( ALPHA( I ), BETA( I ) ) + 10 CONTINUE +* + CALL ZTGSEN( 0, ILVSL, ILVSR, BWORK, N, A, LDA, B, LDB, ALPHA, + $ BETA, VSL, LDVSL, VSR, LDVSR, SDIM, PVSL, PVSR, + $ DIF, WORK( IWRK ), LWORK-IWRK+1, IDUM, 1, IERR ) + IF( IERR.EQ.1 ) + $ INFO = N + 3 +* + END IF +* +* Apply back-permutation to VSL and VSR +* + IF( ILVSL ) + $ CALL ZGGBAK( 'P', 'L', N, ILO, IHI, RWORK( ILEFT ), + $ RWORK( IRIGHT ), N, VSL, LDVSL, IERR ) + IF( ILVSR ) + $ CALL ZGGBAK( 'P', 'R', N, ILO, IHI, RWORK( ILEFT ), + $ RWORK( IRIGHT ), N, VSR, LDVSR, IERR ) +* +* Undo scaling +* + IF( ILASCL ) THEN + CALL ZLASCL( 'U', 0, 0, ANRMTO, ANRM, N, N, A, LDA, IERR ) + CALL ZLASCL( 'G', 0, 0, ANRMTO, ANRM, N, 1, ALPHA, N, IERR ) + END IF +* + IF( ILBSCL ) THEN + CALL ZLASCL( 'U', 0, 0, BNRMTO, BNRM, N, N, B, LDB, IERR ) + CALL ZLASCL( 'G', 0, 0, BNRMTO, BNRM, N, 1, BETA, N, IERR ) + END IF +* + IF( WANTST ) THEN +* +* Check if reordering is correct +* + LASTSL = .TRUE. + SDIM = 0 + DO 20 I = 1, N + CURSL = SELCTG( ALPHA( I ), BETA( I ) ) + IF( CURSL ) + $ SDIM = SDIM + 1 + IF( CURSL .AND. .NOT.LASTSL ) + $ INFO = N + 2 + LASTSL = CURSL + 20 CONTINUE +* + END IF +* + 30 CONTINUE +* + WORK( 1 ) = DCMPLX( LWKOPT ) +* + RETURN +* +* End of ZGGES3 +* + END diff --git a/SRC/zggev3.f b/SRC/zggev3.f new file mode 100644 index 00000000..1c4e832a --- /dev/null +++ b/SRC/zggev3.f @@ -0,0 +1,559 @@ +*> \brief <b> ZGGEV3 computes the eigenvalues and, optionally, the left and/or right eigenvectors for GE matrices (blocked algorithm)</b> +* +* =========== DOCUMENTATION =========== +* +* Online html documentation available at +* http://www.netlib.org/lapack/explore-html/ +* +*> \htmlonly +*> Download ZGGEV3 + dependencies +*> <a href="http://www.netlib.org/cgi-bin/netlibfiles.tgz?format=tgz&filename=/lapack/lapack_routine/zggev3.f"> +*> [TGZ]</a> +*> <a href="http://www.netlib.org/cgi-bin/netlibfiles.zip?format=zip&filename=/lapack/lapack_routine/zggev3.f"> +*> [ZIP]</a> +*> <a href="http://www.netlib.org/cgi-bin/netlibfiles.txt?format=txt&filename=/lapack/lapack_routine/zggev3.f"> +*> [TXT]</a> +*> \endhtmlonly +* +* Definition: +* =========== +* +* SUBROUTINE ZGGEV3( JOBVL, JOBVR, N, A, LDA, B, LDB, ALPHA, BETA, +* VL, LDVL, VR, LDVR, WORK, LWORK, RWORK, INFO ) +* +* .. Scalar Arguments .. +* CHARACTER JOBVL, JOBVR +* INTEGER INFO, LDA, LDB, LDVL, LDVR, LWORK, N +* .. +* .. Array Arguments .. +* DOUBLE PRECISION RWORK( * ) +* COMPLEX*16 A( LDA, * ), ALPHA( * ), B( LDB, * ), +* $ BETA( * ), VL( LDVL, * ), VR( LDVR, * ), +* $ WORK( * ) +* .. +* +* +*> \par Purpose: +* ============= +*> +*> \verbatim +*> +*> ZGGEV3 computes for a pair of N-by-N complex nonsymmetric matrices +*> (A,B), the generalized eigenvalues, and optionally, the left and/or +*> right generalized eigenvectors. +*> +*> A generalized eigenvalue for a pair of matrices (A,B) is a scalar +*> lambda or a ratio alpha/beta = lambda, such that A - lambda*B is +*> singular. It is usually represented as the pair (alpha,beta), as +*> there is a reasonable interpretation for beta=0, and even for both +*> being zero. +*> +*> The right generalized eigenvector v(j) corresponding to the +*> generalized eigenvalue lambda(j) of (A,B) satisfies +*> +*> A * v(j) = lambda(j) * B * v(j). +*> +*> The left generalized eigenvector u(j) corresponding to the +*> generalized eigenvalues lambda(j) of (A,B) satisfies +*> +*> u(j)**H * A = lambda(j) * u(j)**H * B +*> +*> where u(j)**H is the conjugate-transpose of u(j). +*> \endverbatim +* +* Arguments: +* ========== +* +*> \param[in] JOBVL +*> \verbatim +*> JOBVL is CHARACTER*1 +*> = 'N': do not compute the left generalized eigenvectors; +*> = 'V': compute the left generalized eigenvectors. +*> \endverbatim +*> +*> \param[in] JOBVR +*> \verbatim +*> JOBVR is CHARACTER*1 +*> = 'N': do not compute the right generalized eigenvectors; +*> = 'V': compute the right generalized eigenvectors. +*> \endverbatim +*> +*> \param[in] N +*> \verbatim +*> N is INTEGER +*> The order of the matrices A, B, VL, and VR. N >= 0. +*> \endverbatim +*> +*> \param[in,out] A +*> \verbatim +*> A is COMPLEX*16 array, dimension (LDA, N) +*> On entry, the matrix A in the pair (A,B). +*> On exit, A has been overwritten. +*> \endverbatim +*> +*> \param[in] LDA +*> \verbatim +*> LDA is INTEGER +*> The leading dimension of A. LDA >= max(1,N). +*> \endverbatim +*> +*> \param[in,out] B +*> \verbatim +*> B is COMPLEX*16 array, dimension (LDB, N) +*> On entry, the matrix B in the pair (A,B). +*> On exit, B has been overwritten. +*> \endverbatim +*> +*> \param[in] LDB +*> \verbatim +*> LDB is INTEGER +*> The leading dimension of B. LDB >= max(1,N). +*> \endverbatim +*> +*> \param[out] ALPHA +*> \verbatim +*> ALPHA is COMPLEX*16 array, dimension (N) +*> \endverbatim +*> +*> \param[out] BETA +*> \verbatim +*> BETA is COMPLEX*16 array, dimension (N) +*> On exit, ALPHA(j)/BETA(j), j=1,...,N, will be the +*> generalized eigenvalues. +*> +*> Note: the quotients ALPHA(j)/BETA(j) may easily over- or +*> underflow, and BETA(j) may even be zero. Thus, the user +*> should avoid naively computing the ratio alpha/beta. +*> However, ALPHA will be always less than and usually +*> comparable with norm(A) in magnitude, and BETA always less +*> than and usually comparable with norm(B). +*> \endverbatim +*> +*> \param[out] VL +*> \verbatim +*> VL is COMPLEX*16 array, dimension (LDVL,N) +*> If JOBVL = 'V', the left generalized eigenvectors u(j) are +*> stored one after another in the columns of VL, in the same +*> order as their eigenvalues. +*> Each eigenvector is scaled so the largest component has +*> abs(real part) + abs(imag. part) = 1. +*> Not referenced if JOBVL = 'N'. +*> \endverbatim +*> +*> \param[in] LDVL +*> \verbatim +*> LDVL is INTEGER +*> The leading dimension of the matrix VL. LDVL >= 1, and +*> if JOBVL = 'V', LDVL >= N. +*> \endverbatim +*> +*> \param[out] VR +*> \verbatim +*> VR is COMPLEX*16 array, dimension (LDVR,N) +*> If JOBVR = 'V', the right generalized eigenvectors v(j) are +*> stored one after another in the columns of VR, in the same +*> order as their eigenvalues. +*> Each eigenvector is scaled so the largest component has +*> abs(real part) + abs(imag. part) = 1. +*> Not referenced if JOBVR = 'N'. +*> \endverbatim +*> +*> \param[in] LDVR +*> \verbatim +*> LDVR is INTEGER +*> The leading dimension of the matrix VR. LDVR >= 1, and +*> if JOBVR = 'V', LDVR >= N. +*> \endverbatim +*> +*> \param[out] WORK +*> \verbatim +*> WORK is COMPLEX*16 array, dimension (MAX(1,LWORK)) +*> On exit, if INFO = 0, WORK(1) returns the optimal LWORK. +*> \endverbatim +*> +*> \param[in] LWORK +*> \verbatim +*> LWORK is INTEGER +*> The dimension of the array WORK. +*> +*> If LWORK = -1, then a workspace query is assumed; the routine +*> only calculates the optimal size of the WORK array, returns +*> this value as the first entry of the WORK array, and no error +*> message related to LWORK is issued by XERBLA. +*> \endverbatim +*> +*> \param[out] RWORK +*> \verbatim +*> RWORK is DOUBLE PRECISION array, dimension (8*N) +*> \endverbatim +*> +*> \param[out] INFO +*> \verbatim +*> INFO is INTEGER +*> = 0: successful exit +*> < 0: if INFO = -i, the i-th argument had an illegal value. +*> =1,...,N: +*> The QZ iteration failed. No eigenvectors have been +*> calculated, but ALPHA(j) and BETA(j) should be +*> correct for j=INFO+1,...,N. +*> > N: =N+1: other then QZ iteration failed in DHGEQZ, +*> =N+2: error return from DTGEVC. +*> \endverbatim +* +* Authors: +* ======== +* +*> \author Univ. of Tennessee +*> \author Univ. of California Berkeley +*> \author Univ. of Colorado Denver +*> \author NAG Ltd. +* +*> \date January 2015 +* +*> \ingroup complex16GEeigen +* +* ===================================================================== + SUBROUTINE ZGGEV3( JOBVL, JOBVR, N, A, LDA, B, LDB, ALPHA, BETA, + $ VL, LDVL, VR, LDVR, WORK, LWORK, RWORK, INFO ) +* +* -- LAPACK driver routine (version 3.6.0) -- +* -- LAPACK is a software package provided by Univ. of Tennessee, -- +* -- Univ. of California Berkeley, Univ. of Colorado Denver and NAG Ltd..-- +* January 2015 +* +* .. Scalar Arguments .. + CHARACTER JOBVL, JOBVR + INTEGER INFO, LDA, LDB, LDVL, LDVR, LWORK, N +* .. +* .. Array Arguments .. + DOUBLE PRECISION RWORK( * ) + COMPLEX*16 A( LDA, * ), ALPHA( * ), B( LDB, * ), + $ BETA( * ), VL( LDVL, * ), VR( LDVR, * ), + $ WORK( * ) +* .. +* +* ===================================================================== +* +* .. Parameters .. + DOUBLE PRECISION ZERO, ONE + PARAMETER ( ZERO = 0.0D0, ONE = 1.0D0 ) + COMPLEX*16 CZERO, CONE + PARAMETER ( CZERO = ( 0.0D0, 0.0D0 ), + $ CONE = ( 1.0D0, 0.0D0 ) ) +* .. +* .. Local Scalars .. + LOGICAL ILASCL, ILBSCL, ILV, ILVL, ILVR, LQUERY + CHARACTER CHTEMP + INTEGER ICOLS, IERR, IHI, IJOBVL, IJOBVR, ILEFT, ILO, + $ IN, IRIGHT, IROWS, IRWRK, ITAU, IWRK, JC, JR, + $ LWKOPT + DOUBLE PRECISION ANRM, ANRMTO, BIGNUM, BNRM, BNRMTO, EPS, + $ SMLNUM, TEMP + COMPLEX*16 X +* .. +* .. Local Arrays .. + LOGICAL LDUMMA( 1 ) +* .. +* .. External Subroutines .. + EXTERNAL DLABAD, XERBLA, ZGEQRF, ZGGBAK, ZGGBAL, ZGGHD3, + $ ZHGEQZ, ZLACPY, ZLASCL, ZLASET, ZTGEVC, ZUNGQR, + $ ZUNMQR +* .. +* .. External Functions .. + LOGICAL LSAME + DOUBLE PRECISION DLAMCH, ZLANGE + EXTERNAL LSAME, DLAMCH, ZLANGE +* .. +* .. Intrinsic Functions .. + INTRINSIC ABS, DBLE, DIMAG, MAX, SQRT +* .. +* .. Statement Functions .. + DOUBLE PRECISION ABS1 +* .. +* .. Statement Function definitions .. + ABS1( X ) = ABS( DBLE( X ) ) + ABS( DIMAG( X ) ) +* .. +* .. Executable Statements .. +* +* Decode the input arguments +* + IF( LSAME( JOBVL, 'N' ) ) THEN + IJOBVL = 1 + ILVL = .FALSE. + ELSE IF( LSAME( JOBVL, 'V' ) ) THEN + IJOBVL = 2 + ILVL = .TRUE. + ELSE + IJOBVL = -1 + ILVL = .FALSE. + END IF +* + IF( LSAME( JOBVR, 'N' ) ) THEN + IJOBVR = 1 + ILVR = .FALSE. + ELSE IF( LSAME( JOBVR, 'V' ) ) THEN + IJOBVR = 2 + ILVR = .TRUE. + ELSE + IJOBVR = -1 + ILVR = .FALSE. + END IF + ILV = ILVL .OR. ILVR +* +* Test the input arguments +* + INFO = 0 + LQUERY = ( LWORK.EQ.-1 ) + IF( IJOBVL.LE.0 ) THEN + INFO = -1 + ELSE IF( IJOBVR.LE.0 ) THEN + INFO = -2 + ELSE IF( N.LT.0 ) THEN + INFO = -3 + ELSE IF( LDA.LT.MAX( 1, N ) ) THEN + INFO = -5 + ELSE IF( LDB.LT.MAX( 1, N ) ) THEN + INFO = -7 + ELSE IF( LDVL.LT.1 .OR. ( ILVL .AND. LDVL.LT.N ) ) THEN + INFO = -11 + ELSE IF( LDVR.LT.1 .OR. ( ILVR .AND. LDVR.LT.N ) ) THEN + INFO = -13 + ELSE IF( LWORK.LT.MAX( 1, 2*N ) .AND. .NOT.LQUERY ) THEN + INFO = -15 + END IF +* +* Compute workspace +* + IF( INFO.EQ.0 ) THEN + CALL ZGEQRF( N, N, B, LDB, WORK, WORK, -1, IERR ) + LWKOPT = MAX( 1, N+INT( WORK( 1 ) ) ) + CALL ZUNMQR( 'L', 'C', N, N, N, B, LDB, WORK, A, LDA, WORK, + $ -1, IERR ) + LWKOPT = MAX( LWKOPT, N+INT( WORK( 1 ) ) ) + IF( ILVL ) THEN + CALL ZUNGQR( N, N, N, VL, LDVL, WORK, WORK, -1, IERR ) + LWKOPT = MAX( LWKOPT, N+INT( WORK( 1 ) ) ) + END IF + IF( ILV ) THEN + CALL ZGGHD3( JOBVL, JOBVR, N, 1, N, A, LDA, B, LDB, VL, + $ LDVL, VR, LDVR, WORK, -1, IERR ) + LWKOPT = MAX( LWKOPT, N+INT( WORK( 1 ) ) ) + CALL ZHGEQZ( 'S', JOBVL, JOBVR, N, 1, N, A, LDA, B, LDB, + $ ALPHA, BETA, VL, LDVL, VR, LDVR, WORK, -1, + $ WORK, IERR ) + LWKOPT = MAX( LWKOPT, N+INT( WORK( 1 ) ) ) + ELSE + CALL ZGGHD3( JOBVL, JOBVR, N, 1, N, A, LDA, B, LDB, VL, + $ LDVL, VR, LDVR, WORK, -1, IERR ) + LWKOPT = MAX( LWKOPT, N+INT( WORK( 1 ) ) ) + CALL ZHGEQZ( 'E', JOBVL, JOBVR, N, 1, N, A, LDA, B, LDB, + $ ALPHA, BETA, VL, LDVL, VR, LDVR, WORK, -1, + $ WORK, IERR ) + LWKOPT = MAX( LWKOPT, N+INT( WORK( 1 ) ) ) + END IF + WORK( 1 ) = DCMPLX( LWKOPT ) + END IF +* + IF( INFO.NE.0 ) THEN + CALL XERBLA( 'ZGGEV3 ', -INFO ) + RETURN + ELSE IF( LQUERY ) THEN + RETURN + END IF +* +* Quick return if possible +* + IF( N.EQ.0 ) + $ RETURN +* +* Get machine constants +* + EPS = DLAMCH( 'E' )*DLAMCH( 'B' ) + SMLNUM = DLAMCH( 'S' ) + BIGNUM = ONE / SMLNUM + CALL DLABAD( SMLNUM, BIGNUM ) + SMLNUM = SQRT( SMLNUM ) / EPS + BIGNUM = ONE / SMLNUM +* +* Scale A if max element outside range [SMLNUM,BIGNUM] +* + ANRM = ZLANGE( 'M', N, N, A, LDA, RWORK ) + ILASCL = .FALSE. + IF( ANRM.GT.ZERO .AND. ANRM.LT.SMLNUM ) THEN + ANRMTO = SMLNUM + ILASCL = .TRUE. + ELSE IF( ANRM.GT.BIGNUM ) THEN + ANRMTO = BIGNUM + ILASCL = .TRUE. + END IF + IF( ILASCL ) + $ CALL ZLASCL( 'G', 0, 0, ANRM, ANRMTO, N, N, A, LDA, IERR ) +* +* Scale B if max element outside range [SMLNUM,BIGNUM] +* + BNRM = ZLANGE( 'M', N, N, B, LDB, RWORK ) + ILBSCL = .FALSE. + IF( BNRM.GT.ZERO .AND. BNRM.LT.SMLNUM ) THEN + BNRMTO = SMLNUM + ILBSCL = .TRUE. + ELSE IF( BNRM.GT.BIGNUM ) THEN + BNRMTO = BIGNUM + ILBSCL = .TRUE. + END IF + IF( ILBSCL ) + $ CALL ZLASCL( 'G', 0, 0, BNRM, BNRMTO, N, N, B, LDB, IERR ) +* +* Permute the matrices A, B to isolate eigenvalues if possible +* + ILEFT = 1 + IRIGHT = N + 1 + IRWRK = IRIGHT + N + CALL ZGGBAL( 'P', N, A, LDA, B, LDB, ILO, IHI, RWORK( ILEFT ), + $ RWORK( IRIGHT ), RWORK( IRWRK ), IERR ) +* +* Reduce B to triangular form (QR decomposition of B) +* + IROWS = IHI + 1 - ILO + IF( ILV ) THEN + ICOLS = N + 1 - ILO + ELSE + ICOLS = IROWS + END IF + ITAU = 1 + IWRK = ITAU + IROWS + CALL ZGEQRF( IROWS, ICOLS, B( ILO, ILO ), LDB, WORK( ITAU ), + $ WORK( IWRK ), LWORK+1-IWRK, IERR ) +* +* Apply the orthogonal transformation to matrix A +* + CALL ZUNMQR( 'L', 'C', IROWS, ICOLS, IROWS, B( ILO, ILO ), LDB, + $ WORK( ITAU ), A( ILO, ILO ), LDA, WORK( IWRK ), + $ LWORK+1-IWRK, IERR ) +* +* Initialize VL +* + IF( ILVL ) THEN + CALL ZLASET( 'Full', N, N, CZERO, CONE, VL, LDVL ) + IF( IROWS.GT.1 ) THEN + CALL ZLACPY( 'L', IROWS-1, IROWS-1, B( ILO+1, ILO ), LDB, + $ VL( ILO+1, ILO ), LDVL ) + END IF + CALL ZUNGQR( IROWS, IROWS, IROWS, VL( ILO, ILO ), LDVL, + $ WORK( ITAU ), WORK( IWRK ), LWORK+1-IWRK, IERR ) + END IF +* +* Initialize VR +* + IF( ILVR ) + $ CALL ZLASET( 'Full', N, N, CZERO, CONE, VR, LDVR ) +* +* Reduce to generalized Hessenberg form +* + IF( ILV ) THEN +* +* Eigenvectors requested -- work on whole matrix. +* + CALL ZGGHD3( JOBVL, JOBVR, N, ILO, IHI, A, LDA, B, LDB, VL, + $ LDVL, VR, LDVR, WORK( IWRK ), LWORK+1-IWRK, IERR ) + ELSE + CALL ZGGHD3( 'N', 'N', IROWS, 1, IROWS, A( ILO, ILO ), LDA, + $ B( ILO, ILO ), LDB, VL, LDVL, VR, LDVR, + $ WORK( IWRK ), LWORK+1-IWRK, IERR ) + END IF +* +* Perform QZ algorithm (Compute eigenvalues, and optionally, the +* Schur form and Schur vectors) +* + IWRK = ITAU + IF( ILV ) THEN + CHTEMP = 'S' + ELSE + CHTEMP = 'E' + END IF + CALL ZHGEQZ( CHTEMP, JOBVL, JOBVR, N, ILO, IHI, A, LDA, B, LDB, + $ ALPHA, BETA, VL, LDVL, VR, LDVR, WORK( IWRK ), + $ LWORK+1-IWRK, RWORK( IRWRK ), IERR ) + IF( IERR.NE.0 ) THEN + IF( IERR.GT.0 .AND. IERR.LE.N ) THEN + INFO = IERR + ELSE IF( IERR.GT.N .AND. IERR.LE.2*N ) THEN + INFO = IERR - N + ELSE + INFO = N + 1 + END IF + GO TO 70 + END IF +* +* Compute Eigenvectors +* + IF( ILV ) THEN + IF( ILVL ) THEN + IF( ILVR ) THEN + CHTEMP = 'B' + ELSE + CHTEMP = 'L' + END IF + ELSE + CHTEMP = 'R' + END IF +* + CALL ZTGEVC( CHTEMP, 'B', LDUMMA, N, A, LDA, B, LDB, VL, LDVL, + $ VR, LDVR, N, IN, WORK( IWRK ), RWORK( IRWRK ), + $ IERR ) + IF( IERR.NE.0 ) THEN + INFO = N + 2 + GO TO 70 + END IF +* +* Undo balancing on VL and VR and normalization +* + IF( ILVL ) THEN + CALL ZGGBAK( 'P', 'L', N, ILO, IHI, RWORK( ILEFT ), + $ RWORK( IRIGHT ), N, VL, LDVL, IERR ) + DO 30 JC = 1, N + TEMP = ZERO + DO 10 JR = 1, N + TEMP = MAX( TEMP, ABS1( VL( JR, JC ) ) ) + 10 CONTINUE + IF( TEMP.LT.SMLNUM ) + $ GO TO 30 + TEMP = ONE / TEMP + DO 20 JR = 1, N + VL( JR, JC ) = VL( JR, JC )*TEMP + 20 CONTINUE + 30 CONTINUE + END IF + IF( ILVR ) THEN + CALL ZGGBAK( 'P', 'R', N, ILO, IHI, RWORK( ILEFT ), + $ RWORK( IRIGHT ), N, VR, LDVR, IERR ) + DO 60 JC = 1, N + TEMP = ZERO + DO 40 JR = 1, N + TEMP = MAX( TEMP, ABS1( VR( JR, JC ) ) ) + 40 CONTINUE + IF( TEMP.LT.SMLNUM ) + $ GO TO 60 + TEMP = ONE / TEMP + DO 50 JR = 1, N + VR( JR, JC ) = VR( JR, JC )*TEMP + 50 CONTINUE + 60 CONTINUE + END IF + END IF +* +* Undo scaling if necessary +* + 70 CONTINUE +* + IF( ILASCL ) + $ CALL ZLASCL( 'G', 0, 0, ANRMTO, ANRM, N, 1, ALPHA, N, IERR ) +* + IF( ILBSCL ) + $ CALL ZLASCL( 'G', 0, 0, BNRMTO, BNRM, N, 1, BETA, N, IERR ) +* + WORK( 1 ) = DCMPLX( LWKOPT ) + RETURN +* +* End of ZGGEV3 +* + END diff --git a/SRC/zgghd3.f b/SRC/zgghd3.f new file mode 100644 index 00000000..55952a4b --- /dev/null +++ b/SRC/zgghd3.f @@ -0,0 +1,896 @@ +*> \brief \b ZGGHD3 +* +* =========== DOCUMENTATION =========== +* +* Online html documentation available at +* http://www.netlib.org/lapack/explore-html/ +* +*> \htmlonly +*> Download ZGGHD3 + dependencies +*> <a href="http://www.netlib.org/cgi-bin/netlibfiles.tgz?format=tgz&filename=/lapack/lapack_routine/zgghd3.f"> +*> [TGZ]</a> +*> <a href="http://www.netlib.org/cgi-bin/netlibfiles.zip?format=zip&filename=/lapack/lapack_routine/zgghd3.f"> +*> [ZIP]</a> +*> <a href="http://www.netlib.org/cgi-bin/netlibfiles.txt?format=txt&filename=/lapack/lapack_routine/zgghd3.f"> +*> [TXT]</a> +*> \endhtmlonly +* +* Definition: +* =========== +* +* SUBROUTINE ZGGHD3( COMPQ, COMPZ, N, ILO, IHI, A, LDA, B, LDB, Q, +* LDQ, Z, LDZ, WORK, LWORK, INFO ) +* +* .. Scalar Arguments .. +* CHARACTER COMPQ, COMPZ +* INTEGER IHI, ILO, INFO, LDA, LDB, LDQ, LDZ, N, LWORK +* .. +* .. Array Arguments .. +* COMPLEX*16 A( LDA, * ), B( LDB, * ), Q( LDQ, * ), +* $ Z( LDZ, * ), WORK( * ) +* .. +* +* +*> \par Purpose: +* ============= +*> +*> \verbatim +*> +*> ZGGHD3 reduces a pair of complex matrices (A,B) to generalized upper +*> Hessenberg form using unitary transformations, where A is a +*> general matrix and B is upper triangular. The form of the +*> generalized eigenvalue problem is +*> A*x = lambda*B*x, +*> and B is typically made upper triangular by computing its QR +*> factorization and moving the unitary matrix Q to the left side +*> of the equation. +*> +*> This subroutine simultaneously reduces A to a Hessenberg matrix H: +*> Q**H*A*Z = H +*> and transforms B to another upper triangular matrix T: +*> Q**H*B*Z = T +*> in order to reduce the problem to its standard form +*> H*y = lambda*T*y +*> where y = Z**H*x. +*> +*> The unitary matrices Q and Z are determined as products of Givens +*> rotations. They may either be formed explicitly, or they may be +*> postmultiplied into input matrices Q1 and Z1, so that +*> Q1 * A * Z1**H = (Q1*Q) * H * (Z1*Z)**H +*> Q1 * B * Z1**H = (Q1*Q) * T * (Z1*Z)**H +*> If Q1 is the unitary matrix from the QR factorization of B in the +*> original equation A*x = lambda*B*x, then ZGGHD3 reduces the original +*> problem to generalized Hessenberg form. +*> +*> This is a blocked variant of CGGHRD, using matrix-matrix +*> multiplications for parts of the computation to enhance performance. +*> \endverbatim +* +* Arguments: +* ========== +* +*> \param[in] COMPQ +*> \verbatim +*> COMPQ is CHARACTER*1 +*> = 'N': do not compute Q; +*> = 'I': Q is initialized to the unit matrix, and the +*> unitary matrix Q is returned; +*> = 'V': Q must contain a unitary matrix Q1 on entry, +*> and the product Q1*Q is returned. +*> \endverbatim +*> +*> \param[in] COMPZ +*> \verbatim +*> COMPZ is CHARACTER*1 +*> = 'N': do not compute Z; +*> = 'I': Z is initialized to the unit matrix, and the +*> unitary matrix Z is returned; +*> = 'V': Z must contain a unitary matrix Z1 on entry, +*> and the product Z1*Z is returned. +*> \endverbatim +*> +*> \param[in] N +*> \verbatim +*> N is INTEGER +*> The order of the matrices A and B. N >= 0. +*> \endverbatim +*> +*> \param[in] ILO +*> \verbatim +*> ILO is INTEGER +*> \endverbatim +*> +*> \param[in] IHI +*> \verbatim +*> IHI is INTEGER +*> +*> ILO and IHI mark the rows and columns of A which are to be +*> reduced. It is assumed that A is already upper triangular +*> in rows and columns 1:ILO-1 and IHI+1:N. ILO and IHI are +*> normally set by a previous call to ZGGBAL; otherwise they +*> should be set to 1 and N respectively. +*> 1 <= ILO <= IHI <= N, if N > 0; ILO=1 and IHI=0, if N=0. +*> \endverbatim +*> +*> \param[in,out] A +*> \verbatim +*> A is COMPLEX*16 array, dimension (LDA, N) +*> On entry, the N-by-N general matrix to be reduced. +*> On exit, the upper triangle and the first subdiagonal of A +*> are overwritten with the upper Hessenberg matrix H, and the +*> rest is set to zero. +*> \endverbatim +*> +*> \param[in] LDA +*> \verbatim +*> LDA is INTEGER +*> The leading dimension of the array A. LDA >= max(1,N). +*> \endverbatim +*> +*> \param[in,out] B +*> \verbatim +*> B is COMPLEX*16 array, dimension (LDB, N) +*> On entry, the N-by-N upper triangular matrix B. +*> On exit, the upper triangular matrix T = Q**H B Z. The +*> elements below the diagonal are set to zero. +*> \endverbatim +*> +*> \param[in] LDB +*> \verbatim +*> LDB is INTEGER +*> The leading dimension of the array B. LDB >= max(1,N). +*> \endverbatim +*> +*> \param[in,out] Q +*> \verbatim +*> Q is COMPLEX*16 array, dimension (LDQ, N) +*> On entry, if COMPQ = 'V', the unitary matrix Q1, typically +*> from the QR factorization of B. +*> On exit, if COMPQ='I', the unitary matrix Q, and if +*> COMPQ = 'V', the product Q1*Q. +*> Not referenced if COMPQ='N'. +*> \endverbatim +*> +*> \param[in] LDQ +*> \verbatim +*> LDQ is INTEGER +*> The leading dimension of the array Q. +*> LDQ >= N if COMPQ='V' or 'I'; LDQ >= 1 otherwise. +*> \endverbatim +*> +*> \param[in,out] Z +*> \verbatim +*> Z is COMPLEX*16 array, dimension (LDZ, N) +*> On entry, if COMPZ = 'V', the unitary matrix Z1. +*> On exit, if COMPZ='I', the unitary matrix Z, and if +*> COMPZ = 'V', the product Z1*Z. +*> Not referenced if COMPZ='N'. +*> \endverbatim +*> +*> \param[in] LDZ +*> \verbatim +*> LDZ is INTEGER +*> The leading dimension of the array Z. +*> LDZ >= N if COMPZ='V' or 'I'; LDZ >= 1 otherwise. +*> \endverbatim +*> +*> \param[out] WORK +*> \verbatim +*> WORK is COMPLEX*16 array, dimension (LWORK) +*> On exit, if INFO = 0, WORK(1) returns the optimal LWORK. +*> \endverbatim +*> +*> \param[in] LWORK +*> \verbatim +*> LWORK is INTEGER +*> The length of the array WORK. LWORK >= 1. +*> For optimum performance LWORK >= 6*N*NB, where NB is the +*> optimal blocksize. +*> +*> If LWORK = -1, then a workspace query is assumed; the routine +*> only calculates the optimal size of the WORK array, returns +*> this value as the first entry of the WORK array, and no error +*> message related to LWORK is issued by XERBLA. +*> \endverbatim +*> +*> \param[out] INFO +*> \verbatim +*> INFO is INTEGER +*> = 0: successful exit. +*> < 0: if INFO = -i, the i-th argument had an illegal value. +*> \endverbatim +* +* Authors: +* ======== +* +*> \author Univ. of Tennessee +*> \author Univ. of California Berkeley +*> \author Univ. of Colorado Denver +*> \author NAG Ltd. +* +*> \date January 2015 +* +*> \ingroup complex16OTHERcomputational +* +*> \par Further Details: +* ===================== +*> +*> \verbatim +*> +*> This routine reduces A to Hessenberg form and maintains B in +*> using a blocked variant of Moler and Stewart's original algorithm, +*> as described by Kagstrom, Kressner, Quintana-Orti, and Quintana-Orti +*> (BIT 2008). +*> \endverbatim +*> +* ===================================================================== + SUBROUTINE ZGGHD3( COMPQ, COMPZ, N, ILO, IHI, A, LDA, B, LDB, Q, + $ LDQ, Z, LDZ, WORK, LWORK, INFO ) +* +* -- LAPACK computational routine (version 3.6.0) -- +* -- LAPACK is a software package provided by Univ. of Tennessee, -- +* -- Univ. of California Berkeley, Univ. of Colorado Denver and NAG Ltd..-- +* January 2015 +* + IMPLICIT NONE +* +* .. Scalar Arguments .. + CHARACTER COMPQ, COMPZ + INTEGER IHI, ILO, INFO, LDA, LDB, LDQ, LDZ, N, LWORK +* .. +* .. Array Arguments .. + COMPLEX*16 A( LDA, * ), B( LDB, * ), Q( LDQ, * ), + $ Z( LDZ, * ), WORK( * ) +* .. +* +* ===================================================================== +* +* .. Parameters .. + COMPLEX*16 CONE, CZERO + PARAMETER ( CONE = ( 1.0D+0, 0.0D+0 ), + $ CZERO = ( 0.0D+0, 0.0D+0 ) ) +* .. +* .. Local Scalars .. + LOGICAL BLK22, INITQ, INITZ, LQUERY, WANTQ, WANTZ + CHARACTER*1 COMPQ2, COMPZ2 + INTEGER COLA, I, IERR, J, J0, JCOL, JJ, JROW, K, + $ KACC22, LEN, LWKOPT, N2NB, NB, NBLST, NBMIN, + $ NH, NNB, NX, PPW, PPWO, PW, TOP, TOPQ + DOUBLE PRECISION C + COMPLEX*16 C1, C2, CTEMP, S, S1, S2, TEMP, TEMP1, TEMP2, + $ TEMP3 +* .. +* .. External Functions .. + LOGICAL LSAME + INTEGER ILAENV + EXTERNAL ILAENV, LSAME +* .. +* .. External Subroutines .. + EXTERNAL ZGGHRD, ZLARTG, ZLASET, ZUNM22, ZROT, XERBLA +* .. +* .. Intrinsic Functions .. + INTRINSIC DBLE, DCMPLX, DCONJG, MAX +* .. +* .. Executable Statements .. +* +* Decode and test the input parameters. +* + INFO = 0 + NB = ILAENV( 1, 'ZGGHD3', ' ', N, ILO, IHI, -1 ) + LWKOPT = 6*N*NB + WORK( 1 ) = DCMPLX( LWKOPT ) + INITQ = LSAME( COMPQ, 'I' ) + WANTQ = INITQ .OR. LSAME( COMPQ, 'V' ) + INITZ = LSAME( COMPZ, 'I' ) + WANTZ = INITZ .OR. LSAME( COMPZ, 'V' ) + LQUERY = ( LWORK.EQ.-1 ) +* + IF( .NOT.LSAME( COMPQ, 'N' ) .AND. .NOT.WANTQ ) THEN + INFO = -1 + ELSE IF( .NOT.LSAME( COMPZ, 'N' ) .AND. .NOT.WANTZ ) THEN + INFO = -2 + ELSE IF( N.LT.0 ) THEN + INFO = -3 + ELSE IF( ILO.LT.1 ) THEN + INFO = -4 + ELSE IF( IHI.GT.N .OR. IHI.LT.ILO-1 ) THEN + INFO = -5 + ELSE IF( LDA.LT.MAX( 1, N ) ) THEN + INFO = -7 + ELSE IF( LDB.LT.MAX( 1, N ) ) THEN + INFO = -9 + ELSE IF( ( WANTQ .AND. LDQ.LT.N ) .OR. LDQ.LT.1 ) THEN + INFO = -11 + ELSE IF( ( WANTZ .AND. LDZ.LT.N ) .OR. LDZ.LT.1 ) THEN + INFO = -13 + ELSE IF( LWORK.LT.1 .AND. .NOT.LQUERY ) THEN + INFO = -15 + END IF + IF( INFO.NE.0 ) THEN + CALL XERBLA( 'ZGGHD3', -INFO ) + RETURN + ELSE IF( LQUERY ) THEN + RETURN + END IF +* +* Initialize Q and Z if desired. +* + IF( INITQ ) + $ CALL ZLASET( 'All', N, N, CZERO, CONE, Q, LDQ ) + IF( INITZ ) + $ CALL ZLASET( 'All', N, N, CZERO, CONE, Z, LDZ ) +* +* Zero out lower triangle of B. +* + IF( N.GT.1 ) + $ CALL ZLASET( 'Lower', N-1, N-1, CZERO, CZERO, B(2, 1), LDB ) +* +* Quick return if possible +* + NH = IHI - ILO + 1 + IF( NH.LE.1 ) THEN + WORK( 1 ) = CONE + RETURN + END IF +* +* Determine the blocksize. +* + NBMIN = ILAENV( 2, 'ZGGHD3', ' ', N, ILO, IHI, -1 ) + IF( NB.GT.1 .AND. NB.LT.NH ) THEN +* +* Determine when to use unblocked instead of blocked code. +* + NX = MAX( NB, ILAENV( 3, 'ZGGHD3', ' ', N, ILO, IHI, -1 ) ) + IF( NX.LT.NH ) THEN +* +* Determine if workspace is large enough for blocked code. +* + IF( LWORK.LT.LWKOPT ) THEN +* +* Not enough workspace to use optimal NB: determine the +* minimum value of NB, and reduce NB or force use of +* unblocked code. +* + NBMIN = MAX( 2, ILAENV( 2, 'ZGGHD3', ' ', N, ILO, IHI, + $ -1 ) ) + IF( LWORK.GE.6*N*NBMIN ) THEN + NB = LWORK / ( 6*N ) + ELSE + NB = 1 + END IF + END IF + END IF + END IF +* + IF( NB.LT.NBMIN .OR. NB.GE.NH ) THEN +* +* Use unblocked code below +* + JCOL = ILO +* + ELSE +* +* Use blocked code +* + KACC22 = ILAENV( 16, 'ZGGHD3', ' ', N, ILO, IHI, -1 ) + BLK22 = KACC22.EQ.2 + DO JCOL = ILO, IHI-2, NB + NNB = MIN( NB, IHI-JCOL-1 ) +* +* Initialize small unitary factors that will hold the +* accumulated Givens rotations in workspace. +* N2NB denotes the number of 2*NNB-by-2*NNB factors +* NBLST denotes the (possibly smaller) order of the last +* factor. +* + N2NB = ( IHI-JCOL-1 ) / NNB - 1 + NBLST = IHI - JCOL - N2NB*NNB + CALL ZLASET( 'All', NBLST, NBLST, CZERO, CONE, WORK, NBLST ) + PW = NBLST * NBLST + 1 + DO I = 1, N2NB + CALL ZLASET( 'All', 2*NNB, 2*NNB, CZERO, CONE, + $ WORK( PW ), 2*NNB ) + PW = PW + 4*NNB*NNB + END DO +* +* Reduce columns JCOL:JCOL+NNB-1 of A to Hessenberg form. +* + DO J = JCOL, JCOL+NNB-1 +* +* Reduce Jth column of A. Store cosines and sines in Jth +* column of A and B, respectively. +* + DO I = IHI, J+2, -1 + TEMP = A( I-1, J ) + CALL ZLARTG( TEMP, A( I, J ), C, S, A( I-1, J ) ) + A( I, J ) = DCMPLX( C ) + B( I, J ) = S + END DO +* +* Accumulate Givens rotations into workspace array. +* + PPW = ( NBLST + 1 )*( NBLST - 2 ) - J + JCOL + 1 + LEN = 2 + J - JCOL + JROW = J + N2NB*NNB + 2 + DO I = IHI, JROW, -1 + CTEMP = A( I, J ) + S = B( I, J ) + DO JJ = PPW, PPW+LEN-1 + TEMP = WORK( JJ + NBLST ) + WORK( JJ + NBLST ) = CTEMP*TEMP - S*WORK( JJ ) + WORK( JJ ) = DCONJG( S )*TEMP + CTEMP*WORK( JJ ) + END DO + LEN = LEN + 1 + PPW = PPW - NBLST - 1 + END DO +* + PPWO = NBLST*NBLST + ( NNB+J-JCOL-1 )*2*NNB + NNB + J0 = JROW - NNB + DO JROW = J0, J+2, -NNB + PPW = PPWO + LEN = 2 + J - JCOL + DO I = JROW+NNB-1, JROW, -1 + CTEMP = A( I, J ) + S = B( I, J ) + DO JJ = PPW, PPW+LEN-1 + TEMP = WORK( JJ + 2*NNB ) + WORK( JJ + 2*NNB ) = CTEMP*TEMP - S*WORK( JJ ) + WORK( JJ ) = DCONJG( S )*TEMP + CTEMP*WORK( JJ ) + END DO + LEN = LEN + 1 + PPW = PPW - 2*NNB - 1 + END DO + PPWO = PPWO + 4*NNB*NNB + END DO +* +* TOP denotes the number of top rows in A and B that will +* not be updated during the next steps. +* + IF( JCOL.LE.2 ) THEN + TOP = 0 + ELSE + TOP = JCOL + END IF +* +* Propagate transformations through B and replace stored +* left sines/cosines by right sines/cosines. +* + DO JJ = N, J+1, -1 +* +* Update JJth column of B. +* + DO I = MIN( JJ+1, IHI ), J+2, -1 + CTEMP = A( I, J ) + S = B( I, J ) + TEMP = B( I, JJ ) + B( I, JJ ) = CTEMP*TEMP - DCONJG( S )*B( I-1, JJ ) + B( I-1, JJ ) = S*TEMP + CTEMP*B( I-1, JJ ) + END DO +* +* Annihilate B( JJ+1, JJ ). +* + IF( JJ.LT.IHI ) THEN + TEMP = B( JJ+1, JJ+1 ) + CALL ZLARTG( TEMP, B( JJ+1, JJ ), C, S, + $ B( JJ+1, JJ+1 ) ) + B( JJ+1, JJ ) = CZERO + CALL ZROT( JJ-TOP, B( TOP+1, JJ+1 ), 1, + $ B( TOP+1, JJ ), 1, C, S ) + A( JJ+1, J ) = DCMPLX( C ) + B( JJ+1, J ) = -DCONJG( S ) + END IF + END DO +* +* Update A by transformations from right. +* + JJ = MOD( IHI-J-1, 3 ) + DO I = IHI-J-3, JJ+1, -3 + CTEMP = A( J+1+I, J ) + S = -B( J+1+I, J ) + C1 = A( J+2+I, J ) + S1 = -B( J+2+I, J ) + C2 = A( J+3+I, J ) + S2 = -B( J+3+I, J ) +* + DO K = TOP+1, IHI + TEMP = A( K, J+I ) + TEMP1 = A( K, J+I+1 ) + TEMP2 = A( K, J+I+2 ) + TEMP3 = A( K, J+I+3 ) + A( K, J+I+3 ) = C2*TEMP3 + DCONJG( S2 )*TEMP2 + TEMP2 = -S2*TEMP3 + C2*TEMP2 + A( K, J+I+2 ) = C1*TEMP2 + DCONJG( S1 )*TEMP1 + TEMP1 = -S1*TEMP2 + C1*TEMP1 + A( K, J+I+1 ) = CTEMP*TEMP1 + DCONJG( S )*TEMP + A( K, J+I ) = -S*TEMP1 + CTEMP*TEMP + END DO + END DO +* + IF( JJ.GT.0 ) THEN + DO I = JJ, 1, -1 + C = DBLE( A( J+1+I, J ) ) + CALL ZROT( IHI-TOP, A( TOP+1, J+I+1 ), 1, + $ A( TOP+1, J+I ), 1, C, + $ -DCONJG( B( J+1+I, J ) ) ) + END DO + END IF +* +* Update (J+1)th column of A by transformations from left. +* + IF ( J .LT. JCOL + NNB - 1 ) THEN + LEN = 1 + J - JCOL +* +* Multiply with the trailing accumulated unitary +* matrix, which takes the form +* +* [ U11 U12 ] +* U = [ ], +* [ U21 U22 ] +* +* where U21 is a LEN-by-LEN matrix and U12 is lower +* triangular. +* + JROW = IHI - NBLST + 1 + CALL ZGEMV( 'Conjugate', NBLST, LEN, CONE, WORK, + $ NBLST, A( JROW, J+1 ), 1, CZERO, + $ WORK( PW ), 1 ) + PPW = PW + LEN + DO I = JROW, JROW+NBLST-LEN-1 + WORK( PPW ) = A( I, J+1 ) + PPW = PPW + 1 + END DO + CALL ZTRMV( 'Lower', 'Conjugate', 'Non-unit', + $ NBLST-LEN, WORK( LEN*NBLST + 1 ), NBLST, + $ WORK( PW+LEN ), 1 ) + CALL ZGEMV( 'Conjugate', LEN, NBLST-LEN, CONE, + $ WORK( (LEN+1)*NBLST - LEN + 1 ), NBLST, + $ A( JROW+NBLST-LEN, J+1 ), 1, CONE, + $ WORK( PW+LEN ), 1 ) + PPW = PW + DO I = JROW, JROW+NBLST-1 + A( I, J+1 ) = WORK( PPW ) + PPW = PPW + 1 + END DO +* +* Multiply with the other accumulated unitary +* matrices, which take the form +* +* [ U11 U12 0 ] +* [ ] +* U = [ U21 U22 0 ], +* [ ] +* [ 0 0 I ] +* +* where I denotes the (NNB-LEN)-by-(NNB-LEN) identity +* matrix, U21 is a LEN-by-LEN upper triangular matrix +* and U12 is an NNB-by-NNB lower triangular matrix. +* + PPWO = 1 + NBLST*NBLST + J0 = JROW - NNB + DO JROW = J0, JCOL+1, -NNB + PPW = PW + LEN + DO I = JROW, JROW+NNB-1 + WORK( PPW ) = A( I, J+1 ) + PPW = PPW + 1 + END DO + PPW = PW + DO I = JROW+NNB, JROW+NNB+LEN-1 + WORK( PPW ) = A( I, J+1 ) + PPW = PPW + 1 + END DO + CALL ZTRMV( 'Upper', 'Conjugate', 'Non-unit', LEN, + $ WORK( PPWO + NNB ), 2*NNB, WORK( PW ), + $ 1 ) + CALL ZTRMV( 'Lower', 'Conjugate', 'Non-unit', NNB, + $ WORK( PPWO + 2*LEN*NNB ), + $ 2*NNB, WORK( PW + LEN ), 1 ) + CALL ZGEMV( 'Conjugate', NNB, LEN, CONE, + $ WORK( PPWO ), 2*NNB, A( JROW, J+1 ), 1, + $ CONE, WORK( PW ), 1 ) + CALL ZGEMV( 'Conjugate', LEN, NNB, CONE, + $ WORK( PPWO + 2*LEN*NNB + NNB ), 2*NNB, + $ A( JROW+NNB, J+1 ), 1, CONE, + $ WORK( PW+LEN ), 1 ) + PPW = PW + DO I = JROW, JROW+LEN+NNB-1 + A( I, J+1 ) = WORK( PPW ) + PPW = PPW + 1 + END DO + PPWO = PPWO + 4*NNB*NNB + END DO + END IF + END DO +* +* Apply accumulated unitary matrices to A. +* + COLA = N - JCOL - NNB + 1 + J = IHI - NBLST + 1 + CALL ZGEMM( 'Conjugate', 'No Transpose', NBLST, + $ COLA, NBLST, CONE, WORK, NBLST, + $ A( J, JCOL+NNB ), LDA, CZERO, WORK( PW ), + $ NBLST ) + CALL ZLACPY( 'All', NBLST, COLA, WORK( PW ), NBLST, + $ A( J, JCOL+NNB ), LDA ) + PPWO = NBLST*NBLST + 1 + J0 = J - NNB + DO J = J0, JCOL+1, -NNB + IF ( BLK22 ) THEN +* +* Exploit the structure of +* +* [ U11 U12 ] +* U = [ ] +* [ U21 U22 ], +* +* where all blocks are NNB-by-NNB, U21 is upper +* triangular and U12 is lower triangular. +* + CALL ZUNM22( 'Left', 'Conjugate', 2*NNB, COLA, NNB, + $ NNB, WORK( PPWO ), 2*NNB, + $ A( J, JCOL+NNB ), LDA, WORK( PW ), + $ LWORK-PW+1, IERR ) + ELSE +* +* Ignore the structure of U. +* + CALL ZGEMM( 'Conjugate', 'No Transpose', 2*NNB, + $ COLA, 2*NNB, CONE, WORK( PPWO ), 2*NNB, + $ A( J, JCOL+NNB ), LDA, CZERO, WORK( PW ), + $ 2*NNB ) + CALL ZLACPY( 'All', 2*NNB, COLA, WORK( PW ), 2*NNB, + $ A( J, JCOL+NNB ), LDA ) + END IF + PPWO = PPWO + 4*NNB*NNB + END DO +* +* Apply accumulated unitary matrices to Q. +* + IF( WANTQ ) THEN + J = IHI - NBLST + 1 + IF ( INITQ ) THEN + TOPQ = MAX( 2, J - JCOL + 1 ) + NH = IHI - TOPQ + 1 + ELSE + TOPQ = 1 + NH = N + END IF + CALL ZGEMM( 'No Transpose', 'No Transpose', NH, + $ NBLST, NBLST, CONE, Q( TOPQ, J ), LDQ, + $ WORK, NBLST, CZERO, WORK( PW ), NH ) + CALL ZLACPY( 'All', NH, NBLST, WORK( PW ), NH, + $ Q( TOPQ, J ), LDQ ) + PPWO = NBLST*NBLST + 1 + J0 = J - NNB + DO J = J0, JCOL+1, -NNB + IF ( INITQ ) THEN + TOPQ = MAX( 2, J - JCOL + 1 ) + NH = IHI - TOPQ + 1 + END IF + IF ( BLK22 ) THEN +* +* Exploit the structure of U. +* + CALL ZUNM22( 'Right', 'No Transpose', NH, 2*NNB, + $ NNB, NNB, WORK( PPWO ), 2*NNB, + $ Q( TOPQ, J ), LDQ, WORK( PW ), + $ LWORK-PW+1, IERR ) + ELSE +* +* Ignore the structure of U. +* + CALL ZGEMM( 'No Transpose', 'No Transpose', NH, + $ 2*NNB, 2*NNB, CONE, Q( TOPQ, J ), LDQ, + $ WORK( PPWO ), 2*NNB, CZERO, WORK( PW ), + $ NH ) + CALL ZLACPY( 'All', NH, 2*NNB, WORK( PW ), NH, + $ Q( TOPQ, J ), LDQ ) + END IF + PPWO = PPWO + 4*NNB*NNB + END DO + END IF +* +* Accumulate right Givens rotations if required. +* + IF ( WANTZ .OR. TOP.GT.0 ) THEN +* +* Initialize small unitary factors that will hold the +* accumulated Givens rotations in workspace. +* + CALL ZLASET( 'All', NBLST, NBLST, CZERO, CONE, WORK, + $ NBLST ) + PW = NBLST * NBLST + 1 + DO I = 1, N2NB + CALL ZLASET( 'All', 2*NNB, 2*NNB, CZERO, CONE, + $ WORK( PW ), 2*NNB ) + PW = PW + 4*NNB*NNB + END DO +* +* Accumulate Givens rotations into workspace array. +* + DO J = JCOL, JCOL+NNB-1 + PPW = ( NBLST + 1 )*( NBLST - 2 ) - J + JCOL + 1 + LEN = 2 + J - JCOL + JROW = J + N2NB*NNB + 2 + DO I = IHI, JROW, -1 + CTEMP = A( I, J ) + A( I, J ) = CZERO + S = B( I, J ) + B( I, J ) = CZERO + DO JJ = PPW, PPW+LEN-1 + TEMP = WORK( JJ + NBLST ) + WORK( JJ + NBLST ) = CTEMP*TEMP - + $ DCONJG( S )*WORK( JJ ) + WORK( JJ ) = S*TEMP + CTEMP*WORK( JJ ) + END DO + LEN = LEN + 1 + PPW = PPW - NBLST - 1 + END DO +* + PPWO = NBLST*NBLST + ( NNB+J-JCOL-1 )*2*NNB + NNB + J0 = JROW - NNB + DO JROW = J0, J+2, -NNB + PPW = PPWO + LEN = 2 + J - JCOL + DO I = JROW+NNB-1, JROW, -1 + CTEMP = A( I, J ) + A( I, J ) = CZERO + S = B( I, J ) + B( I, J ) = CZERO + DO JJ = PPW, PPW+LEN-1 + TEMP = WORK( JJ + 2*NNB ) + WORK( JJ + 2*NNB ) = CTEMP*TEMP - + $ DCONJG( S )*WORK( JJ ) + WORK( JJ ) = S*TEMP + CTEMP*WORK( JJ ) + END DO + LEN = LEN + 1 + PPW = PPW - 2*NNB - 1 + END DO + PPWO = PPWO + 4*NNB*NNB + END DO + END DO + ELSE + + DO J = ILO, ILO+NNB + DO I = J+2, IHI + A( I, J ) = CZERO + B( I, J ) = CZERO + END DO + END DO + END IF +* +* Apply accumulated unitary matrices to A and B. +* + IF ( TOP.GT.0 ) THEN + J = IHI - NBLST + 1 + CALL ZGEMM( 'No Transpose', 'No Transpose', TOP, + $ NBLST, NBLST, CONE, A( 1, J ), LDA, + $ WORK, NBLST, CZERO, WORK( PW ), TOP ) + CALL ZLACPY( 'All', TOP, NBLST, WORK( PW ), TOP, + $ A( 1, J ), LDA ) + PPWO = NBLST*NBLST + 1 + J0 = J - NNB + DO J = J0, JCOL+1, -NNB + IF ( BLK22 ) THEN +* +* Exploit the structure of U. +* + CALL ZUNM22( 'Right', 'No Transpose', TOP, 2*NNB, + $ NNB, NNB, WORK( PPWO ), 2*NNB, + $ A( 1, J ), LDA, WORK( PW ), + $ LWORK-PW+1, IERR ) + ELSE +* +* Ignore the structure of U. +* + CALL ZGEMM( 'No Transpose', 'No Transpose', TOP, + $ 2*NNB, 2*NNB, CONE, A( 1, J ), LDA, + $ WORK( PPWO ), 2*NNB, CZERO, + $ WORK( PW ), TOP ) + CALL ZLACPY( 'All', TOP, 2*NNB, WORK( PW ), TOP, + $ A( 1, J ), LDA ) + END IF + PPWO = PPWO + 4*NNB*NNB + END DO +* + J = IHI - NBLST + 1 + CALL ZGEMM( 'No Transpose', 'No Transpose', TOP, + $ NBLST, NBLST, CONE, B( 1, J ), LDB, + $ WORK, NBLST, CZERO, WORK( PW ), TOP ) + CALL ZLACPY( 'All', TOP, NBLST, WORK( PW ), TOP, + $ B( 1, J ), LDB ) + PPWO = NBLST*NBLST + 1 + J0 = J - NNB + DO J = J0, JCOL+1, -NNB + IF ( BLK22 ) THEN +* +* Exploit the structure of U. +* + CALL ZUNM22( 'Right', 'No Transpose', TOP, 2*NNB, + $ NNB, NNB, WORK( PPWO ), 2*NNB, + $ B( 1, J ), LDA, WORK( PW ), + $ LWORK-PW+1, IERR ) + ELSE +* +* Ignore the structure of U. +* + CALL ZGEMM( 'No Transpose', 'No Transpose', TOP, + $ 2*NNB, 2*NNB, CONE, B( 1, J ), LDB, + $ WORK( PPWO ), 2*NNB, CZERO, + $ WORK( PW ), TOP ) + CALL ZLACPY( 'All', TOP, 2*NNB, WORK( PW ), TOP, + $ B( 1, J ), LDB ) + END IF + PPWO = PPWO + 4*NNB*NNB + END DO + END IF +* +* Apply accumulated unitary matrices to Z. +* + IF( WANTZ ) THEN + J = IHI - NBLST + 1 + IF ( INITQ ) THEN + TOPQ = MAX( 2, J - JCOL + 1 ) + NH = IHI - TOPQ + 1 + ELSE + TOPQ = 1 + NH = N + END IF + CALL ZGEMM( 'No Transpose', 'No Transpose', NH, + $ NBLST, NBLST, CONE, Z( TOPQ, J ), LDZ, + $ WORK, NBLST, CZERO, WORK( PW ), NH ) + CALL ZLACPY( 'All', NH, NBLST, WORK( PW ), NH, + $ Z( TOPQ, J ), LDZ ) + PPWO = NBLST*NBLST + 1 + J0 = J - NNB + DO J = J0, JCOL+1, -NNB + IF ( INITQ ) THEN + TOPQ = MAX( 2, J - JCOL + 1 ) + NH = IHI - TOPQ + 1 + END IF + IF ( BLK22 ) THEN +* +* Exploit the structure of U. +* + CALL ZUNM22( 'Right', 'No Transpose', NH, 2*NNB, + $ NNB, NNB, WORK( PPWO ), 2*NNB, + $ Z( TOPQ, J ), LDZ, WORK( PW ), + $ LWORK-PW+1, IERR ) + ELSE +* +* Ignore the structure of U. +* + CALL ZGEMM( 'No Transpose', 'No Transpose', NH, + $ 2*NNB, 2*NNB, CONE, Z( TOPQ, J ), LDZ, + $ WORK( PPWO ), 2*NNB, CZERO, WORK( PW ), + $ NH ) + CALL ZLACPY( 'All', NH, 2*NNB, WORK( PW ), NH, + $ Z( TOPQ, J ), LDZ ) + END IF + PPWO = PPWO + 4*NNB*NNB + END DO + END IF + END DO + END IF +* +* Use unblocked code to reduce the rest of the matrix +* Avoid re-initialization of modified Q and Z. +* + COMPQ2 = COMPQ + COMPZ2 = COMPZ + IF ( JCOL.NE.ILO ) THEN + IF ( WANTQ ) + $ COMPQ2 = 'V' + IF ( WANTZ ) + $ COMPZ2 = 'V' + END IF +* + IF ( JCOL.LT.IHI ) + $ CALL ZGGHRD( COMPQ2, COMPZ2, N, JCOL, IHI, A, LDA, B, LDB, Q, + $ LDQ, Z, LDZ, IERR ) + WORK( 1 ) = DCMPLX( LWKOPT ) +* + RETURN +* +* End of ZGGHD3 +* + END diff --git a/SRC/zunm22.f b/SRC/zunm22.f new file mode 100644 index 00000000..468d7d8c --- /dev/null +++ b/SRC/zunm22.f @@ -0,0 +1,440 @@ +*> \brief \b ZUNM22 multiplies a general matrix by a banded unitary matrix. +* +* =========== DOCUMENTATION =========== +* +* Online html documentation available at +* http://www.netlib.org/lapack/explore-html/ +* +*> \htmlonly +*> Download ZUNM22 + dependencies +*> <a href="http://www.netlib.org/cgi-bin/netlibfiles.tgz?format=tgz&filename=/lapack/lapack_routine/zunm22.f"> +*> [TGZ]</a> +*> <a href="http://www.netlib.org/cgi-bin/netlibfiles.zip?format=zip&filename=/lapack/lapack_routine/zunm22.f"> +*> [ZIP]</a> +*> <a href="http://www.netlib.org/cgi-bin/netlibfiles.txt?format=txt&filename=/lapack/lapack_routine/zunm22.f"> +*> [TXT]</a> +*> \endhtmlonly +* +* Definition: +* =========== +* +* SUBROUTINE ZUNM22( SIDE, TRANS, M, N, N1, N2, Q, LDQ, C, LDC, +* $ WORK, LWORK, INFO ) +* +* .. Scalar Arguments .. +* CHARACTER SIDE, TRANS +* INTEGER M, N, N1, N2, LDQ, LDC, LWORK, INFO +* .. +* .. Array Arguments .. +* COMPLEX*16 Q( LDQ, * ), C( LDC, * ), WORK( * ) +* .. +* +*> \par Purpose +* ============ +*> +*> \verbatim +*> +*> ZUNM22 overwrites the general complex M-by-N matrix C with +*> +*> SIDE = 'L' SIDE = 'R' +*> TRANS = 'N': Q * C C * Q +*> TRANS = 'C': Q**H * C C * Q**H +*> +*> where Q is a complex unitary matrix of order NQ, with NQ = M if +*> SIDE = 'L' and NQ = N if SIDE = 'R'. +*> The unitary matrix Q processes a 2-by-2 block structure +*> +*> [ Q11 Q12 ] +*> Q = [ ] +*> [ Q21 Q22 ], +*> +*> where Q12 is an N1-by-N1 lower triangular matrix and Q21 is an +*> N2-by-N2 upper triangular matrix. +*> \endverbatim +* +* Arguments +* ========= +* +*> \param[in] SIDE +*> \verbatim +*> SIDE is CHARACTER*1 +*> = 'L': apply Q or Q**H from the Left; +*> = 'R': apply Q or Q**H from the Right. +*> \endverbatim +*> +*> \param[in] TRANS +*> \verbatim +*> TRANS is CHARACTER*1 +*> = 'N': apply Q (No transpose); +*> = 'C': apply Q**H (Conjugate transpose). +*> \endverbatim +*> +*> \param[in] M +*> \verbatim +*> M is INTEGER +*> The number of rows of the matrix C. M >= 0. +*> \endverbatim +*> +*> \param[in] N +*> \verbatim +*> N is INTEGER +*> The number of columns of the matrix C. N >= 0. +*> \endverbatim +*> +*> \param[in] N1 +*> \param[in] N2 +*> \verbatim +*> N1 is INTEGER +*> N2 is INTEGER +*> The dimension of Q12 and Q21, respectively. N1, N2 >= 0. +*> The following requirement must be satisfied: +*> N1 + N2 = M if SIDE = 'L' and N1 + N2 = N if SIDE = 'R'. +*> \endverbatim +*> +*> \param[in] Q +*> \verbatim +*> Q is COMPLEX*16 array, dimension +*> (LDQ,M) if SIDE = 'L' +*> (LDQ,N) if SIDE = 'R' +*> \endverbatim +*> +*> \param[in] LDQ +*> \verbatim +*> LDQ is INTEGER +*> The leading dimension of the array Q. +*> LDQ >= max(1,M) if SIDE = 'L'; LDQ >= max(1,N) if SIDE = 'R'. +*> \endverbatim +*> +*> \param[in,out] C +*> \verbatim +*> C is COMPLEX*16 array, dimension (LDC,N) +*> On entry, the M-by-N matrix C. +*> On exit, C is overwritten by Q*C or Q**H*C or C*Q**H or C*Q. +*> \endverbatim +*> +*> \param[in] LDC +*> \verbatim +*> LDC is INTEGER +*> The leading dimension of the array C. LDC >= max(1,M). +*> \endverbatim +*> +*> \param[out] WORK +*> \verbatim +*> WORK is COMPLEX*16 array, dimension (MAX(1,LWORK)) +*> On exit, if INFO = 0, WORK(1) returns the optimal LWORK. +*> \endverbatim +*> +*> \param[in] LWORK +*> \verbatim +*> LWORK is INTEGER +*> The dimension of the array WORK. +*> If SIDE = 'L', LWORK >= max(1,N); +*> if SIDE = 'R', LWORK >= max(1,M). +*> For optimum performance LWORK >= M*N. +*> +*> If LWORK = -1, then a workspace query is assumed; the routine +*> only calculates the optimal size of the WORK array, returns +*> this value as the first entry of the WORK array, and no error +*> message related to LWORK is issued by XERBLA. +*> \endverbatim +*> +*> \param[out] INFO +*> \verbatim +*> INFO is INTEGER +*> = 0: successful exit +*> < 0: if INFO = -i, the i-th argument had an illegal value +*> \endverbatim +* +* +* Authors: +* ======== +* +*> \author Univ. of Tennessee +*> \author Univ. of California Berkeley +*> \author Univ. of Colorado Denver +*> \author NAG Ltd. +* +*> \date January 2015 +* +*> \ingroup complexOTHERcomputational +* +* ===================================================================== + SUBROUTINE ZUNM22( SIDE, TRANS, M, N, N1, N2, Q, LDQ, C, LDC, + $ WORK, LWORK, INFO ) +* +* -- LAPACK computational routine (version 3.6.0) -- +* -- LAPACK is a software package provided by Univ. of Tennessee, -- +* -- Univ. of California Berkeley, Univ. of Colorado Denver and NAG Ltd..-- +* January 2015 +* + IMPLICIT NONE +* +* .. Scalar Arguments .. + CHARACTER SIDE, TRANS + INTEGER M, N, N1, N2, LDQ, LDC, LWORK, INFO +* .. +* .. Array Arguments .. + COMPLEX*16 Q( LDQ, * ), C( LDC, * ), WORK( * ) +* .. +* +* ===================================================================== +* +* .. Parameters .. + COMPLEX*16 ONE + PARAMETER ( ONE = ( 1.0D+0, 0.0D+0 ) ) +* +* .. Local Scalars .. + LOGICAL LEFT, LQUERY, NOTRAN + INTEGER I, LDWORK, LEN, LWKOPT, NB, NQ, NW +* .. +* .. External Functions .. + LOGICAL LSAME + EXTERNAL LSAME +* .. +* .. External Subroutines .. + EXTERNAL ZGEMM, ZLACPY, ZTRMM, XERBLA +* .. +* .. Intrinsic Functions .. + INTRINSIC DCMPLX, MAX, MIN +* .. +* .. Executable Statements .. +* +* Test the input arguments +* + INFO = 0 + LEFT = LSAME( SIDE, 'L' ) + NOTRAN = LSAME( TRANS, 'N' ) + LQUERY = ( LWORK.EQ.-1 ) +* +* NQ is the order of Q; +* NW is the minimum dimension of WORK. +* + IF( LEFT ) THEN + NQ = M + ELSE + NQ = N + END IF + NW = NQ + IF( N1.EQ.0 .OR. N2.EQ.0 ) NW = 1 + IF( .NOT.LEFT .AND. .NOT.LSAME( SIDE, 'R' ) ) THEN + INFO = -1 + ELSE IF( .NOT.LSAME( TRANS, 'N' ) .AND. .NOT.LSAME( TRANS, 'C' ) ) + $ THEN + INFO = -2 + ELSE IF( M.LT.0 ) THEN + INFO = -3 + ELSE IF( N.LT.0 ) THEN + INFO = -4 + ELSE IF( N1.LT.0 .OR. N1+N2.NE.NQ ) THEN + INFO = -5 + ELSE IF( N2.LT.0 ) THEN + INFO = -6 + ELSE IF( LDQ.LT.MAX( 1, NQ ) ) THEN + INFO = -8 + ELSE IF( LDC.LT.MAX( 1, M ) ) THEN + INFO = -10 + ELSE IF( LWORK.LT.NW .AND. .NOT.LQUERY ) THEN + INFO = -12 + END IF +* + IF( INFO.EQ.0 ) THEN + LWKOPT = M*N + WORK( 1 ) = DCMPLX( LWKOPT ) + END IF +* + IF( INFO.NE.0 ) THEN + CALL XERBLA( 'ZUNM22', -INFO ) + RETURN + ELSE IF( LQUERY ) THEN + RETURN + END IF +* +* Quick return if possible +* + IF( M.EQ.0 .OR. N.EQ.0 ) THEN + WORK( 1 ) = 1 + RETURN + END IF +* +* Degenerate cases (N1 = 0 or N2 = 0) are handled using ZTRMM. +* + IF( N1.EQ.0 ) THEN + CALL ZTRMM( SIDE, 'Upper', TRANS, 'Non-Unit', M, N, ONE, + $ Q, LDQ, C, LDC ) + WORK( 1 ) = ONE + RETURN + ELSE IF( N2.EQ.0 ) THEN + CALL ZTRMM( SIDE, 'Lower', TRANS, 'Non-Unit', M, N, ONE, + $ Q, LDQ, C, LDC ) + WORK( 1 ) = ONE + RETURN + END IF +* +* Compute the largest chunk size available from the workspace. +* + NB = MAX( 1, MIN( LWORK, LWKOPT ) / NQ ) +* + IF( LEFT ) THEN + IF( NOTRAN ) THEN + DO I = 1, N, NB + LEN = MIN( NB, N-I+1 ) + LDWORK = M +* +* Multiply bottom part of C by Q12. +* + CALL ZLACPY( 'All', N1, LEN, C( N2+1, I ), LDC, WORK, + $ LDWORK ) + CALL ZTRMM( 'Left', 'Lower', 'No Transpose', 'Non-Unit', + $ N1, LEN, ONE, Q( 1, N2+1 ), LDQ, WORK, + $ LDWORK ) +* +* Multiply top part of C by Q11. +* + CALL ZGEMM( 'No Transpose', 'No Transpose', N1, LEN, N2, + $ ONE, Q, LDQ, C( 1, I ), LDC, ONE, WORK, + $ LDWORK ) +* +* Multiply top part of C by Q21. +* + CALL ZLACPY( 'All', N2, LEN, C( 1, I ), LDC, + $ WORK( N1+1 ), LDWORK ) + CALL ZTRMM( 'Left', 'Upper', 'No Transpose', 'Non-Unit', + $ N2, LEN, ONE, Q( N1+1, 1 ), LDQ, + $ WORK( N1+1 ), LDWORK ) +* +* Multiply bottom part of C by Q22. +* + CALL ZGEMM( 'No Transpose', 'No Transpose', N2, LEN, N1, + $ ONE, Q( N1+1, N2+1 ), LDQ, C( N2+1, I ), LDC, + $ ONE, WORK( N1+1 ), LDWORK ) +* +* Copy everything back. +* + CALL ZLACPY( 'All', M, LEN, WORK, LDWORK, C( 1, I ), + $ LDC ) + END DO + ELSE + DO I = 1, N, NB + LEN = MIN( NB, N-I+1 ) + LDWORK = M +* +* Multiply bottom part of C by Q21**H. +* + CALL ZLACPY( 'All', N2, LEN, C( N1+1, I ), LDC, WORK, + $ LDWORK ) + CALL ZTRMM( 'Left', 'Upper', 'Conjugate', 'Non-Unit', + $ N2, LEN, ONE, Q( N1+1, 1 ), LDQ, WORK, + $ LDWORK ) +* +* Multiply top part of C by Q11**H. +* + CALL ZGEMM( 'Conjugate', 'No Transpose', N2, LEN, N1, + $ ONE, Q, LDQ, C( 1, I ), LDC, ONE, WORK, + $ LDWORK ) +* +* Multiply top part of C by Q12**H. +* + CALL ZLACPY( 'All', N1, LEN, C( 1, I ), LDC, + $ WORK( N2+1 ), LDWORK ) + CALL ZTRMM( 'Left', 'Lower', 'Conjugate', 'Non-Unit', + $ N1, LEN, ONE, Q( 1, N2+1 ), LDQ, + $ WORK( N2+1 ), LDWORK ) +* +* Multiply bottom part of C by Q22**H. +* + CALL ZGEMM( 'Conjugate', 'No Transpose', N1, LEN, N2, + $ ONE, Q( N1+1, N2+1 ), LDQ, C( N1+1, I ), LDC, + $ ONE, WORK( N2+1 ), LDWORK ) +* +* Copy everything back. +* + CALL ZLACPY( 'All', M, LEN, WORK, LDWORK, C( 1, I ), + $ LDC ) + END DO + END IF + ELSE + IF( NOTRAN ) THEN + DO I = 1, M, NB + LEN = MIN( NB, M-I+1 ) + LDWORK = LEN +* +* Multiply right part of C by Q21. +* + CALL ZLACPY( 'All', LEN, N2, C( I, N1+1 ), LDC, WORK, + $ LDWORK ) + CALL ZTRMM( 'Right', 'Upper', 'No Transpose', 'Non-Unit', + $ LEN, N2, ONE, Q( N1+1, 1 ), LDQ, WORK, + $ LDWORK ) +* +* Multiply left part of C by Q11. +* + CALL ZGEMM( 'No Transpose', 'No Transpose', LEN, N2, N1, + $ ONE, C( I, 1 ), LDC, Q, LDQ, ONE, WORK, + $ LDWORK ) +* +* Multiply left part of C by Q12. +* + CALL ZLACPY( 'All', LEN, N1, C( I, 1 ), LDC, + $ WORK( 1 + N2*LDWORK ), LDWORK ) + CALL ZTRMM( 'Right', 'Lower', 'No Transpose', 'Non-Unit', + $ LEN, N1, ONE, Q( 1, N2+1 ), LDQ, + $ WORK( 1 + N2*LDWORK ), LDWORK ) +* +* Multiply right part of C by Q22. +* + CALL ZGEMM( 'No Transpose', 'No Transpose', LEN, N1, N2, + $ ONE, C( I, N1+1 ), LDC, Q( N1+1, N2+1 ), LDQ, + $ ONE, WORK( 1 + N2*LDWORK ), LDWORK ) +* +* Copy everything back. +* + CALL ZLACPY( 'All', LEN, N, WORK, LDWORK, C( I, 1 ), + $ LDC ) + END DO + ELSE + DO I = 1, M, NB + LEN = MIN( NB, M-I+1 ) + LDWORK = LEN +* +* Multiply right part of C by Q12**H. +* + CALL ZLACPY( 'All', LEN, N1, C( I, N2+1 ), LDC, WORK, + $ LDWORK ) + CALL ZTRMM( 'Right', 'Lower', 'Conjugate', 'Non-Unit', + $ LEN, N1, ONE, Q( 1, N2+1 ), LDQ, WORK, + $ LDWORK ) +* +* Multiply left part of C by Q11**H. +* + CALL ZGEMM( 'No Transpose', 'Conjugate', LEN, N1, N2, + $ ONE, C( I, 1 ), LDC, Q, LDQ, ONE, WORK, + $ LDWORK ) +* +* Multiply left part of C by Q21**H. +* + CALL ZLACPY( 'All', LEN, N2, C( I, 1 ), LDC, + $ WORK( 1 + N1*LDWORK ), LDWORK ) + CALL ZTRMM( 'Right', 'Upper', 'Conjugate', 'Non-Unit', + $ LEN, N2, ONE, Q( N1+1, 1 ), LDQ, + $ WORK( 1 + N1*LDWORK ), LDWORK ) +* +* Multiply right part of C by Q22**H. +* + CALL ZGEMM( 'No Transpose', 'Conjugate', LEN, N2, N1, + $ ONE, C( I, N2+1 ), LDC, Q( N1+1, N2+1 ), LDQ, + $ ONE, WORK( 1 + N1*LDWORK ), LDWORK ) +* +* Copy everything back. +* + CALL ZLACPY( 'All', LEN, N, WORK, LDWORK, C( I, 1 ), + $ LDC ) + END DO + END IF + END IF +* + WORK( 1 ) = DCMPLX( LWKOPT ) + RETURN +* +* End of ZUNM22 +* + END |