You can not select more than 25 topics Topics must start with a chinese character,a letter or number, can include dashes ('-') and can be up to 35 characters long.

zgedmd.f90 44 kB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996
  1. SUBROUTINE ZGEDMD( JOBS, JOBZ, JOBR, JOBF, WHTSVD, &
  2. M, N, X, LDX, Y, LDY, NRNK, TOL, &
  3. K, EIGS, Z, LDZ, RES, B, LDB, &
  4. W, LDW, S, LDS, ZWORK, LZWORK, &
  5. RWORK, LRWORK, IWORK, LIWORK, INFO )
  6. ! March 2023
  7. !.....
  8. USE iso_fortran_env
  9. IMPLICIT NONE
  10. INTEGER, PARAMETER :: WP = real64
  11. !.....
  12. ! Scalar arguments
  13. CHARACTER, INTENT(IN) :: JOBS, JOBZ, JOBR, JOBF
  14. INTEGER, INTENT(IN) :: WHTSVD, M, N, LDX, LDY, &
  15. NRNK, LDZ, LDB, LDW, LDS, &
  16. LIWORK, LRWORK, LZWORK
  17. INTEGER, INTENT(OUT) :: K, INFO
  18. REAL(KIND=WP), INTENT(IN) :: TOL
  19. ! Array arguments
  20. COMPLEX(KIND=WP), INTENT(INOUT) :: X(LDX,*), Y(LDY,*)
  21. COMPLEX(KIND=WP), INTENT(OUT) :: Z(LDZ,*), B(LDB,*), &
  22. W(LDW,*), S(LDS,*)
  23. COMPLEX(KIND=WP), INTENT(OUT) :: EIGS(*)
  24. COMPLEX(KIND=WP), INTENT(OUT) :: ZWORK(*)
  25. REAL(KIND=WP), INTENT(OUT) :: RES(*)
  26. REAL(KIND=WP), INTENT(OUT) :: RWORK(*)
  27. INTEGER, INTENT(OUT) :: IWORK(*)
  28. !............................................................
  29. ! Purpose
  30. ! =======
  31. ! ZGEDMD computes the Dynamic Mode Decomposition (DMD) for
  32. ! a pair of data snapshot matrices. For the input matrices
  33. ! X and Y such that Y = A*X with an unaccessible matrix
  34. ! A, ZGEDMD computes a certain number of Ritz pairs of A using
  35. ! the standard Rayleigh-Ritz extraction from a subspace of
  36. ! range(X) that is determined using the leading left singular
  37. ! vectors of X. Optionally, ZGEDMD returns the residuals
  38. ! of the computed Ritz pairs, the information needed for
  39. ! a refinement of the Ritz vectors, or the eigenvectors of
  40. ! the Exact DMD.
  41. ! For further details see the references listed
  42. ! below. For more details of the implementation see [3].
  43. !
  44. ! References
  45. ! ==========
  46. ! [1] P. Schmid: Dynamic mode decomposition of numerical
  47. ! and experimental data,
  48. ! Journal of Fluid Mechanics 656, 5-28, 2010.
  49. ! [2] Z. Drmac, I. Mezic, R. Mohr: Data driven modal
  50. ! decompositions: analysis and enhancements,
  51. ! SIAM J. on Sci. Comp. 40 (4), A2253-A2285, 2018.
  52. ! [3] Z. Drmac: A LAPACK implementation of the Dynamic
  53. ! Mode Decomposition I. Technical report. AIMDyn Inc.
  54. ! and LAPACK Working Note 298.
  55. ! [4] J. Tu, C. W. Rowley, D. M. Luchtenburg, S. L.
  56. ! Brunton, N. Kutz: On Dynamic Mode Decomposition:
  57. ! Theory and Applications, Journal of Computational
  58. ! Dynamics 1(2), 391 -421, 2014.
  59. !
  60. !......................................................................
  61. ! Developed and supported by:
  62. ! ===========================
  63. ! Developed and coded by Zlatko Drmac, Faculty of Science,
  64. ! University of Zagreb; drmac@math.hr
  65. ! In cooperation with
  66. ! AIMdyn Inc., Santa Barbara, CA.
  67. ! and supported by
  68. ! - DARPA SBIR project "Koopman Operator-Based Forecasting
  69. ! for Nonstationary Processes from Near-Term, Limited
  70. ! Observational Data" Contract No: W31P4Q-21-C-0007
  71. ! - DARPA PAI project "Physics-Informed Machine Learning
  72. ! Methodologies" Contract No: HR0011-18-9-0033
  73. ! - DARPA MoDyL project "A Data-Driven, Operator-Theoretic
  74. ! Framework for Space-Time Analysis of Process Dynamics"
  75. ! Contract No: HR0011-16-C-0116
  76. ! Any opinions, findings and conclusions or recommendations
  77. ! expressed in this material are those of the author and
  78. ! do not necessarily reflect the views of the DARPA SBIR
  79. ! Program Office
  80. !============================================================
  81. ! Distribution Statement A:
  82. ! Approved for Public Release, Distribution Unlimited.
  83. ! Cleared by DARPA on September 29, 2022
  84. !============================================================
  85. !............................................................
  86. ! Arguments
  87. ! =========
  88. ! JOBS (input) CHARACTER*1
  89. ! Determines whether the initial data snapshots are scaled
  90. ! by a diagonal matrix.
  91. ! 'S' :: The data snapshots matrices X and Y are multiplied
  92. ! with a diagonal matrix D so that X*D has unit
  93. ! nonzero columns (in the Euclidean 2-norm)
  94. ! 'C' :: The snapshots are scaled as with the 'S' option.
  95. ! If it is found that an i-th column of X is zero
  96. ! vector and the corresponding i-th column of Y is
  97. ! non-zero, then the i-th column of Y is set to
  98. ! zero and a warning flag is raised.
  99. ! 'Y' :: The data snapshots matrices X and Y are multiplied
  100. ! by a diagonal matrix D so that Y*D has unit
  101. ! nonzero columns (in the Euclidean 2-norm)
  102. ! 'N' :: No data scaling.
  103. !.....
  104. ! JOBZ (input) CHARACTER*1
  105. ! Determines whether the eigenvectors (Koopman modes) will
  106. ! be computed.
  107. ! 'V' :: The eigenvectors (Koopman modes) will be computed
  108. ! and returned in the matrix Z.
  109. ! See the description of Z.
  110. ! 'F' :: The eigenvectors (Koopman modes) will be returned
  111. ! in factored form as the product X(:,1:K)*W, where X
  112. ! contains a POD basis (leading left singular vectors
  113. ! of the data matrix X) and W contains the eigenvectors
  114. ! of the corresponding Rayleigh quotient.
  115. ! See the descriptions of K, X, W, Z.
  116. ! 'N' :: The eigenvectors are not computed.
  117. !.....
  118. ! JOBR (input) CHARACTER*1
  119. ! Determines whether to compute the residuals.
  120. ! 'R' :: The residuals for the computed eigenpairs will be
  121. ! computed and stored in the array RES.
  122. ! See the description of RES.
  123. ! For this option to be legal, JOBZ must be 'V'.
  124. ! 'N' :: The residuals are not computed.
  125. !.....
  126. ! JOBF (input) CHARACTER*1
  127. ! Specifies whether to store information needed for post-
  128. ! processing (e.g. computing refined Ritz vectors)
  129. ! 'R' :: The matrix needed for the refinement of the Ritz
  130. ! vectors is computed and stored in the array B.
  131. ! See the description of B.
  132. ! 'E' :: The unscaled eigenvectors of the Exact DMD are
  133. ! computed and returned in the array B. See the
  134. ! description of B.
  135. ! 'N' :: No eigenvector refinement data is computed.
  136. !.....
  137. ! WHTSVD (input) INTEGER, WHSTVD in { 1, 2, 3, 4 }
  138. ! Allows for a selection of the SVD algorithm from the
  139. ! LAPACK library.
  140. ! 1 :: ZGESVD (the QR SVD algorithm)
  141. ! 2 :: ZGESDD (the Divide and Conquer algorithm; if enough
  142. ! workspace available, this is the fastest option)
  143. ! 3 :: ZGESVDQ (the preconditioned QR SVD ; this and 4
  144. ! are the most accurate options)
  145. ! 4 :: ZGEJSV (the preconditioned Jacobi SVD; this and 3
  146. ! are the most accurate options)
  147. ! For the four methods above, a significant difference in
  148. ! the accuracy of small singular values is possible if
  149. ! the snapshots vary in norm so that X is severely
  150. ! ill-conditioned. If small (smaller than EPS*||X||)
  151. ! singular values are of interest and JOBS=='N', then
  152. ! the options (3, 4) give the most accurate results, where
  153. ! the option 4 is slightly better and with stronger
  154. ! theoretical background.
  155. ! If JOBS=='S', i.e. the columns of X will be normalized,
  156. ! then all methods give nearly equally accurate results.
  157. !.....
  158. ! M (input) INTEGER, M>= 0
  159. ! The state space dimension (the row dimension of X, Y).
  160. !.....
  161. ! N (input) INTEGER, 0 <= N <= M
  162. ! The number of data snapshot pairs
  163. ! (the number of columns of X and Y).
  164. !.....
  165. ! X (input/output) COMPLEX(KIND=WP) M-by-N array
  166. ! > On entry, X contains the data snapshot matrix X. It is
  167. ! assumed that the column norms of X are in the range of
  168. ! the normalized floating point numbers.
  169. ! < On exit, the leading K columns of X contain a POD basis,
  170. ! i.e. the leading K left singular vectors of the input
  171. ! data matrix X, U(:,1:K). All N columns of X contain all
  172. ! left singular vectors of the input matrix X.
  173. ! See the descriptions of K, Z and W.
  174. !.....
  175. ! LDX (input) INTEGER, LDX >= M
  176. ! The leading dimension of the array X.
  177. !.....
  178. ! Y (input/workspace/output) COMPLEX(KIND=WP) M-by-N array
  179. ! > On entry, Y contains the data snapshot matrix Y
  180. ! < On exit,
  181. ! If JOBR == 'R', the leading K columns of Y contain
  182. ! the residual vectors for the computed Ritz pairs.
  183. ! See the description of RES.
  184. ! If JOBR == 'N', Y contains the original input data,
  185. ! scaled according to the value of JOBS.
  186. !.....
  187. ! LDY (input) INTEGER , LDY >= M
  188. ! The leading dimension of the array Y.
  189. !.....
  190. ! NRNK (input) INTEGER
  191. ! Determines the mode how to compute the numerical rank,
  192. ! i.e. how to truncate small singular values of the input
  193. ! matrix X. On input, if
  194. ! NRNK = -1 :: i-th singular value sigma(i) is truncated
  195. ! if sigma(i) <= TOL*sigma(1)
  196. ! This option is recommended.
  197. ! NRNK = -2 :: i-th singular value sigma(i) is truncated
  198. ! if sigma(i) <= TOL*sigma(i-1)
  199. ! This option is included for R&D purposes.
  200. ! It requires highly accurate SVD, which
  201. ! may not be feasible.
  202. ! The numerical rank can be enforced by using positive
  203. ! value of NRNK as follows:
  204. ! 0 < NRNK <= N :: at most NRNK largest singular values
  205. ! will be used. If the number of the computed nonzero
  206. ! singular values is less than NRNK, then only those
  207. ! nonzero values will be used and the actually used
  208. ! dimension is less than NRNK. The actual number of
  209. ! the nonzero singular values is returned in the variable
  210. ! K. See the descriptions of TOL and K.
  211. !.....
  212. ! TOL (input) REAL(KIND=WP), 0 <= TOL < 1
  213. ! The tolerance for truncating small singular values.
  214. ! See the description of NRNK.
  215. !.....
  216. ! K (output) INTEGER, 0 <= K <= N
  217. ! The dimension of the POD basis for the data snapshot
  218. ! matrix X and the number of the computed Ritz pairs.
  219. ! The value of K is determined according to the rule set
  220. ! by the parameters NRNK and TOL.
  221. ! See the descriptions of NRNK and TOL.
  222. !.....
  223. ! EIGS (output) COMPLEX(KIND=WP) N-by-1 array
  224. ! The leading K (K<=N) entries of EIGS contain
  225. ! the computed eigenvalues (Ritz values).
  226. ! See the descriptions of K, and Z.
  227. !.....
  228. ! Z (workspace/output) COMPLEX(KIND=WP) M-by-N array
  229. ! If JOBZ =='V' then Z contains the Ritz vectors. Z(:,i)
  230. ! is an eigenvector of the i-th Ritz value; ||Z(:,i)||_2=1.
  231. ! If JOBZ == 'F', then the Z(:,i)'s are given implicitly as
  232. ! the columns of X(:,1:K)*W(1:K,1:K), i.e. X(:,1:K)*W(:,i)
  233. ! is an eigenvector corresponding to EIGS(i). The columns
  234. ! of W(1:k,1:K) are the computed eigenvectors of the
  235. ! K-by-K Rayleigh quotient.
  236. ! See the descriptions of EIGS, X and W.
  237. !.....
  238. ! LDZ (input) INTEGER , LDZ >= M
  239. ! The leading dimension of the array Z.
  240. !.....
  241. ! RES (output) REAL(KIND=WP) N-by-1 array
  242. ! RES(1:K) contains the residuals for the K computed
  243. ! Ritz pairs,
  244. ! RES(i) = || A * Z(:,i) - EIGS(i)*Z(:,i))||_2.
  245. ! See the description of EIGS and Z.
  246. !.....
  247. ! B (output) COMPLEX(KIND=WP) M-by-N array.
  248. ! IF JOBF =='R', B(1:M,1:K) contains A*U(:,1:K), and can
  249. ! be used for computing the refined vectors; see further
  250. ! details in the provided references.
  251. ! If JOBF == 'E', B(1:M,1:K) contains
  252. ! A*U(:,1:K)*W(1:K,1:K), which are the vectors from the
  253. ! Exact DMD, up to scaling by the inverse eigenvalues.
  254. ! If JOBF =='N', then B is not referenced.
  255. ! See the descriptions of X, W, K.
  256. !.....
  257. ! LDB (input) INTEGER, LDB >= M
  258. ! The leading dimension of the array B.
  259. !.....
  260. ! W (workspace/output) COMPLEX(KIND=WP) N-by-N array
  261. ! On exit, W(1:K,1:K) contains the K computed
  262. ! eigenvectors of the matrix Rayleigh quotient.
  263. ! The Ritz vectors (returned in Z) are the
  264. ! product of X (containing a POD basis for the input
  265. ! matrix X) and W. See the descriptions of K, S, X and Z.
  266. ! W is also used as a workspace to temporarily store the
  267. ! right singular vectors of X.
  268. !.....
  269. ! LDW (input) INTEGER, LDW >= N
  270. ! The leading dimension of the array W.
  271. !.....
  272. ! S (workspace/output) COMPLEX(KIND=WP) N-by-N array
  273. ! The array S(1:K,1:K) is used for the matrix Rayleigh
  274. ! quotient. This content is overwritten during
  275. ! the eigenvalue decomposition by ZGEEV.
  276. ! See the description of K.
  277. !.....
  278. ! LDS (input) INTEGER, LDS >= N
  279. ! The leading dimension of the array S.
  280. !.....
  281. ! ZWORK (workspace/output) COMPLEX(KIND=WP) LZWORK-by-1 array
  282. ! ZWORK is used as complex workspace in the complex SVD, as
  283. ! specified by WHTSVD (1,2, 3 or 4) and for ZGEEV for computing
  284. ! the eigenvalues of a Rayleigh quotient.
  285. ! If the call to ZGEDMD is only workspace query, then
  286. ! ZWORK(1) contains the minimal complex workspace length and
  287. ! ZWORK(2) is the optimal complex workspace length.
  288. ! Hence, the length of work is at least 2.
  289. ! See the description of LZWORK.
  290. !.....
  291. ! LZWORK (input) INTEGER
  292. ! The minimal length of the workspace vector ZWORK.
  293. ! LZWORK is calculated as MAX(LZWORK_SVD, LZWORK_ZGEEV),
  294. ! where LZWORK_ZGEEV = MAX( 1, 2*N ) and the minimal
  295. ! LZWORK_SVD is calculated as follows
  296. ! If WHTSVD == 1 :: ZGESVD ::
  297. ! LZWORK_SVD = MAX(1,2*MIN(M,N)+MAX(M,N))
  298. ! If WHTSVD == 2 :: ZGESDD ::
  299. ! LZWORK_SVD = 2*MIN(M,N)*MIN(M,N)+2*MIN(M,N)+MAX(M,N)
  300. ! If WHTSVD == 3 :: ZGESVDQ ::
  301. ! LZWORK_SVD = obtainable by a query
  302. ! If WHTSVD == 4 :: ZGEJSV ::
  303. ! LZWORK_SVD = obtainable by a query
  304. ! If on entry LZWORK = -1, then a workspace query is
  305. ! assumed and the procedure only computes the minimal
  306. ! and the optimal workspace lengths and returns them in
  307. ! LZWORK(1) and LZWORK(2), respectively.
  308. !.....
  309. ! RWORK (workspace/output) REAL(KIND=WP) LRWORK-by-1 array
  310. ! On exit, RWORK(1:N) contains the singular values of
  311. ! X (for JOBS=='N') or column scaled X (JOBS=='S', 'C').
  312. ! If WHTSVD==4, then RWORK(N+1) and RWORK(N+2) contain
  313. ! scaling factor RWORK(N+2)/RWORK(N+1) used to scale X
  314. ! and Y to avoid overflow in the SVD of X.
  315. ! This may be of interest if the scaling option is off
  316. ! and as many as possible smallest eigenvalues are
  317. ! desired to the highest feasible accuracy.
  318. ! If the call to ZGEDMD is only workspace query, then
  319. ! RWORK(1) contains the minimal workspace length.
  320. ! See the description of LRWORK.
  321. !.....
  322. ! LRWORK (input) INTEGER
  323. ! The minimal length of the workspace vector RWORK.
  324. ! LRWORK is calculated as follows:
  325. ! LRWORK = MAX(1, N+LRWORK_SVD,N+LRWORK_ZGEEV), where
  326. ! LRWORK_ZGEEV = MAX(1,2*N) and RWORK_SVD is the real workspace
  327. ! for the SVD subroutine determined by the input parameter
  328. ! WHTSVD.
  329. ! If WHTSVD == 1 :: ZGESVD ::
  330. ! LRWORK_SVD = 5*MIN(M,N)
  331. ! If WHTSVD == 2 :: ZGESDD ::
  332. ! LRWORK_SVD = MAX(5*MIN(M,N)*MIN(M,N)+7*MIN(M,N),
  333. ! 2*MAX(M,N)*MIN(M,N)+2*MIN(M,N)*MIN(M,N)+MIN(M,N) ) )
  334. ! If WHTSVD == 3 :: ZGESVDQ ::
  335. ! LRWORK_SVD = obtainable by a query
  336. ! If WHTSVD == 4 :: ZGEJSV ::
  337. ! LRWORK_SVD = obtainable by a query
  338. ! If on entry LRWORK = -1, then a workspace query is
  339. ! assumed and the procedure only computes the minimal
  340. ! real workspace length and returns it in RWORK(1).
  341. !.....
  342. ! IWORK (workspace/output) INTEGER LIWORK-by-1 array
  343. ! Workspace that is required only if WHTSVD equals
  344. ! 2 , 3 or 4. (See the description of WHTSVD).
  345. ! If on entry LWORK =-1 or LIWORK=-1, then the
  346. ! minimal length of IWORK is computed and returned in
  347. ! IWORK(1). See the description of LIWORK.
  348. !.....
  349. ! LIWORK (input) INTEGER
  350. ! The minimal length of the workspace vector IWORK.
  351. ! If WHTSVD == 1, then only IWORK(1) is used; LIWORK >=1
  352. ! If WHTSVD == 2, then LIWORK >= MAX(1,8*MIN(M,N))
  353. ! If WHTSVD == 3, then LIWORK >= MAX(1,M+N-1)
  354. ! If WHTSVD == 4, then LIWORK >= MAX(3,M+3*N)
  355. ! If on entry LIWORK = -1, then a workspace query is
  356. ! assumed and the procedure only computes the minimal
  357. ! and the optimal workspace lengths for ZWORK, RWORK and
  358. ! IWORK. See the descriptions of ZWORK, RWORK and IWORK.
  359. !.....
  360. ! INFO (output) INTEGER
  361. ! -i < 0 :: On entry, the i-th argument had an
  362. ! illegal value
  363. ! = 0 :: Successful return.
  364. ! = 1 :: Void input. Quick exit (M=0 or N=0).
  365. ! = 2 :: The SVD computation of X did not converge.
  366. ! Suggestion: Check the input data and/or
  367. ! repeat with different WHTSVD.
  368. ! = 3 :: The computation of the eigenvalues did not
  369. ! converge.
  370. ! = 4 :: If data scaling was requested on input and
  371. ! the procedure found inconsistency in the data
  372. ! such that for some column index i,
  373. ! X(:,i) = 0 but Y(:,i) /= 0, then Y(:,i) is set
  374. ! to zero if JOBS=='C'. The computation proceeds
  375. ! with original or modified data and warning
  376. ! flag is set with INFO=4.
  377. !.............................................................
  378. !.............................................................
  379. ! Parameters
  380. ! ~~~~~~~~~~
  381. REAL(KIND=WP), PARAMETER :: ONE = 1.0_WP
  382. REAL(KIND=WP), PARAMETER :: ZERO = 0.0_WP
  383. COMPLEX(KIND=WP), PARAMETER :: ZONE = ( 1.0_WP, 0.0_WP )
  384. COMPLEX(KIND=WP), PARAMETER :: ZZERO = ( 0.0_WP, 0.0_WP )
  385. ! Local scalars
  386. ! ~~~~~~~~~~~~~
  387. REAL(KIND=WP) :: OFL, ROOTSC, SCALE, SMALL, &
  388. SSUM, XSCL1, XSCL2
  389. INTEGER :: i, j, IMINWR, INFO1, INFO2, &
  390. LWRKEV, LWRSDD, LWRSVD, LWRSVJ, &
  391. LWRSVQ, MLWORK, MWRKEV, MWRSDD, &
  392. MWRSVD, MWRSVJ, MWRSVQ, NUMRNK, &
  393. OLWORK, MLRWRK
  394. LOGICAL :: BADXY, LQUERY, SCCOLX, SCCOLY, &
  395. WNTEX, WNTREF, WNTRES, WNTVEC
  396. CHARACTER :: JOBZL, T_OR_N
  397. CHARACTER :: JSVOPT
  398. !
  399. ! Local arrays
  400. ! ~~~~~~~~~~~~
  401. REAL(KIND=WP) :: RDUMMY(2)
  402. ! External functions (BLAS and LAPACK)
  403. ! ~~~~~~~~~~~~~~~~~
  404. REAL(KIND=WP) ZLANGE, DLAMCH, DZNRM2
  405. EXTERNAL ZLANGE, DLAMCH, DZNRM2, IZAMAX
  406. INTEGER IZAMAX
  407. LOGICAL DISNAN, LSAME
  408. EXTERNAL DISNAN, LSAME
  409. ! External subroutines (BLAS and LAPACK)
  410. ! ~~~~~~~~~~~~~~~~~~~~
  411. EXTERNAL ZAXPY, ZGEMM, ZDSCAL
  412. EXTERNAL ZGEEV, ZGEJSV, ZGESDD, ZGESVD, ZGESVDQ, &
  413. ZLACPY, ZLASCL, ZLASSQ, XERBLA
  414. ! Intrinsic functions
  415. ! ~~~~~~~~~~~~~~~~~~~
  416. INTRINSIC DBLE, INT, MAX, SQRT
  417. !............................................................
  418. !
  419. ! Test the input arguments
  420. !
  421. WNTRES = LSAME(JOBR,'R')
  422. SCCOLX = LSAME(JOBS,'S') .OR. LSAME(JOBS,'C')
  423. SCCOLY = LSAME(JOBS,'Y')
  424. WNTVEC = LSAME(JOBZ,'V')
  425. WNTREF = LSAME(JOBF,'R')
  426. WNTEX = LSAME(JOBF,'E')
  427. INFO = 0
  428. LQUERY = ( ( LZWORK == -1 ) .OR. ( LIWORK == -1 ) &
  429. .OR. ( LRWORK == -1 ) )
  430. !
  431. IF ( .NOT. (SCCOLX .OR. SCCOLY .OR. &
  432. LSAME(JOBS,'N')) ) THEN
  433. INFO = -1
  434. ELSE IF ( .NOT. (WNTVEC .OR. LSAME(JOBZ,'N') &
  435. .OR. LSAME(JOBZ,'F')) ) THEN
  436. INFO = -2
  437. ELSE IF ( .NOT. (WNTRES .OR. LSAME(JOBR,'N')) .OR. &
  438. ( WNTRES .AND. (.NOT.WNTVEC) ) ) THEN
  439. INFO = -3
  440. ELSE IF ( .NOT. (WNTREF .OR. WNTEX .OR. &
  441. LSAME(JOBF,'N') ) ) THEN
  442. INFO = -4
  443. ELSE IF ( .NOT.((WHTSVD == 1) .OR. (WHTSVD == 2) .OR. &
  444. (WHTSVD == 3) .OR. (WHTSVD == 4) )) THEN
  445. INFO = -5
  446. ELSE IF ( M < 0 ) THEN
  447. INFO = -6
  448. ELSE IF ( ( N < 0 ) .OR. ( N > M ) ) THEN
  449. INFO = -7
  450. ELSE IF ( LDX < M ) THEN
  451. INFO = -9
  452. ELSE IF ( LDY < M ) THEN
  453. INFO = -11
  454. ELSE IF ( .NOT. (( NRNK == -2).OR.(NRNK == -1).OR. &
  455. ((NRNK >= 1).AND.(NRNK <=N ))) ) THEN
  456. INFO = -12
  457. ELSE IF ( ( TOL < ZERO ) .OR. ( TOL >= ONE ) ) THEN
  458. INFO = -13
  459. ELSE IF ( LDZ < M ) THEN
  460. INFO = -17
  461. ELSE IF ( (WNTREF .OR. WNTEX ) .AND. ( LDB < M ) ) THEN
  462. INFO = -20
  463. ELSE IF ( LDW < N ) THEN
  464. INFO = -22
  465. ELSE IF ( LDS < N ) THEN
  466. INFO = -24
  467. END IF
  468. !
  469. IF ( INFO == 0 ) THEN
  470. ! Compute the minimal and the optimal workspace
  471. ! requirements. Simulate running the code and
  472. ! determine minimal and optimal sizes of the
  473. ! workspace at any moment of the run.
  474. IF ( N == 0 ) THEN
  475. ! Quick return. All output except K is void.
  476. ! INFO=1 signals the void input.
  477. ! In case of a workspace query, the default
  478. ! minimal workspace lengths are returned.
  479. IF ( LQUERY ) THEN
  480. IWORK(1) = 1
  481. RWORK(1) = 1
  482. ZWORK(1) = 2
  483. ZWORK(2) = 2
  484. ELSE
  485. K = 0
  486. END IF
  487. INFO = 1
  488. RETURN
  489. END IF
  490. IMINWR = 1
  491. MLRWRK = MAX(1,N)
  492. MLWORK = 2
  493. OLWORK = 2
  494. SELECT CASE ( WHTSVD )
  495. CASE (1)
  496. ! The following is specified as the minimal
  497. ! length of WORK in the definition of ZGESVD:
  498. ! MWRSVD = MAX(1,2*MIN(M,N)+MAX(M,N))
  499. MWRSVD = MAX(1,2*MIN(M,N)+MAX(M,N))
  500. MLWORK = MAX(MLWORK,MWRSVD)
  501. MLRWRK = MAX(MLRWRK,N + 5*MIN(M,N))
  502. IF ( LQUERY ) THEN
  503. CALL ZGESVD( 'O', 'S', M, N, X, LDX, RWORK, &
  504. B, LDB, W, LDW, ZWORK, -1, RDUMMY, INFO1 )
  505. LWRSVD = INT( ZWORK(1) )
  506. OLWORK = MAX(OLWORK,LWRSVD)
  507. END IF
  508. CASE (2)
  509. ! The following is specified as the minimal
  510. ! length of WORK in the definition of ZGESDD:
  511. ! MWRSDD = 2*min(M,N)*min(M,N)+2*min(M,N)+max(M,N).
  512. ! RWORK length: 5*MIN(M,N)*MIN(M,N)+7*MIN(M,N)
  513. ! In LAPACK 3.10.1 RWORK is defined differently.
  514. ! Below we take max over the two versions.
  515. ! IMINWR = 8*MIN(M,N)
  516. MWRSDD = 2*MIN(M,N)*MIN(M,N)+2*MIN(M,N)+MAX(M,N)
  517. MLWORK = MAX(MLWORK,MWRSDD)
  518. IMINWR = 8*MIN(M,N)
  519. MLRWRK = MAX( MLRWRK, N + &
  520. MAX( 5*MIN(M,N)*MIN(M,N)+7*MIN(M,N), &
  521. 5*MIN(M,N)*MIN(M,N)+5*MIN(M,N), &
  522. 2*MAX(M,N)*MIN(M,N)+ &
  523. 2*MIN(M,N)*MIN(M,N)+MIN(M,N) ) )
  524. IF ( LQUERY ) THEN
  525. CALL ZGESDD( 'O', M, N, X, LDX, RWORK, B,LDB,&
  526. W, LDW, ZWORK, -1, RDUMMY, IWORK, INFO1 )
  527. LWRSDD = MAX( MWRSDD,INT( ZWORK(1) ))
  528. ! Possible bug in ZGESDD optimal workspace size.
  529. OLWORK = MAX(OLWORK,LWRSDD)
  530. END IF
  531. CASE (3)
  532. CALL ZGESVDQ( 'H', 'P', 'N', 'R', 'R', M, N, &
  533. X, LDX, RWORK, Z, LDZ, W, LDW, NUMRNK, &
  534. IWORK, -1, ZWORK, -1, RDUMMY, -1, INFO1 )
  535. IMINWR = IWORK(1)
  536. MWRSVQ = INT(ZWORK(2))
  537. MLWORK = MAX(MLWORK,MWRSVQ)
  538. MLRWRK = MAX(MLRWRK,N + INT(RDUMMY(1)))
  539. IF ( LQUERY ) THEN
  540. LWRSVQ = INT(ZWORK(1))
  541. OLWORK = MAX(OLWORK,LWRSVQ)
  542. END IF
  543. CASE (4)
  544. JSVOPT = 'J'
  545. CALL ZGEJSV( 'F', 'U', JSVOPT, 'R', 'N', 'P', M, &
  546. N, X, LDX, RWORK, Z, LDZ, W, LDW, &
  547. ZWORK, -1, RDUMMY, -1, IWORK, INFO1 )
  548. IMINWR = IWORK(1)
  549. MWRSVJ = INT(ZWORK(2))
  550. MLWORK = MAX(MLWORK,MWRSVJ)
  551. MLRWRK = MAX(MLRWRK,N + MAX(7,INT(RDUMMY(1))))
  552. IF ( LQUERY ) THEN
  553. LWRSVJ = INT(ZWORK(1))
  554. OLWORK = MAX(OLWORK,LWRSVJ)
  555. END IF
  556. END SELECT
  557. IF ( WNTVEC .OR. WNTEX .OR. LSAME(JOBZ,'F') ) THEN
  558. JOBZL = 'V'
  559. ELSE
  560. JOBZL = 'N'
  561. END IF
  562. ! Workspace calculation to the ZGEEV call
  563. MWRKEV = MAX( 1, 2*N )
  564. MLWORK = MAX(MLWORK,MWRKEV)
  565. MLRWRK = MAX(MLRWRK,N+2*N)
  566. IF ( LQUERY ) THEN
  567. CALL ZGEEV( 'N', JOBZL, N, S, LDS, EIGS, &
  568. W, LDW, W, LDW, ZWORK, -1, RWORK, INFO1 )
  569. LWRKEV = INT(ZWORK(1))
  570. OLWORK = MAX( OLWORK, LWRKEV )
  571. END IF
  572. !
  573. IF ( LIWORK < IMINWR .AND. (.NOT.LQUERY) ) INFO = -30
  574. IF ( LRWORK < MLRWRK .AND. (.NOT.LQUERY) ) INFO = -28
  575. IF ( LZWORK < MLWORK .AND. (.NOT.LQUERY) ) INFO = -26
  576. END IF
  577. !
  578. IF( INFO /= 0 ) THEN
  579. CALL XERBLA( 'ZGEDMD', -INFO )
  580. RETURN
  581. ELSE IF ( LQUERY ) THEN
  582. ! Return minimal and optimal workspace sizes
  583. IWORK(1) = IMINWR
  584. RWORK(1) = MLRWRK
  585. ZWORK(1) = MLWORK
  586. ZWORK(2) = OLWORK
  587. RETURN
  588. END IF
  589. !............................................................
  590. !
  591. OFL = DLAMCH('O')
  592. SMALL = DLAMCH('S')
  593. BADXY = .FALSE.
  594. !
  595. ! <1> Optional scaling of the snapshots (columns of X, Y)
  596. ! ==========================================================
  597. IF ( SCCOLX ) THEN
  598. ! The columns of X will be normalized.
  599. ! To prevent overflows, the column norms of X are
  600. ! carefully computed using ZLASSQ.
  601. K = 0
  602. DO i = 1, N
  603. !WORK(i) = DZNRM2( M, X(1,i), 1 )
  604. SCALE = ZERO
  605. CALL ZLASSQ( M, X(1,i), 1, SCALE, SSUM )
  606. IF ( DISNAN(SCALE) .OR. DISNAN(SSUM) ) THEN
  607. K = 0
  608. INFO = -8
  609. CALL XERBLA('ZGEDMD',-INFO)
  610. END IF
  611. IF ( (SCALE /= ZERO) .AND. (SSUM /= ZERO) ) THEN
  612. ROOTSC = SQRT(SSUM)
  613. IF ( SCALE .GE. (OFL / ROOTSC) ) THEN
  614. ! Norm of X(:,i) overflows. First, X(:,i)
  615. ! is scaled by
  616. ! ( ONE / ROOTSC ) / SCALE = 1/||X(:,i)||_2.
  617. ! Next, the norm of X(:,i) is stored without
  618. ! overflow as RWORK(i) = - SCALE * (ROOTSC/M),
  619. ! the minus sign indicating the 1/M factor.
  620. ! Scaling is performed without overflow, and
  621. ! underflow may occur in the smallest entries
  622. ! of X(:,i). The relative backward and forward
  623. ! errors are small in the ell_2 norm.
  624. CALL ZLASCL( 'G', 0, 0, SCALE, ONE/ROOTSC, &
  625. M, 1, X(1,i), LDX, INFO2 )
  626. RWORK(i) = - SCALE * ( ROOTSC / DBLE(M) )
  627. ELSE
  628. ! X(:,i) will be scaled to unit 2-norm
  629. RWORK(i) = SCALE * ROOTSC
  630. CALL ZLASCL( 'G',0, 0, RWORK(i), ONE, M, 1, &
  631. X(1,i), LDX, INFO2 ) ! LAPACK CALL
  632. ! X(1:M,i) = (ONE/RWORK(i)) * X(1:M,i) ! INTRINSIC
  633. END IF
  634. ELSE
  635. RWORK(i) = ZERO
  636. K = K + 1
  637. END IF
  638. END DO
  639. IF ( K == N ) THEN
  640. ! All columns of X are zero. Return error code -8.
  641. ! (the 8th input variable had an illegal value)
  642. K = 0
  643. INFO = -8
  644. CALL XERBLA('ZGEDMD',-INFO)
  645. RETURN
  646. END IF
  647. DO i = 1, N
  648. ! Now, apply the same scaling to the columns of Y.
  649. IF ( RWORK(i) > ZERO ) THEN
  650. CALL ZDSCAL( M, ONE/RWORK(i), Y(1,i), 1 ) ! BLAS CALL
  651. ! Y(1:M,i) = (ONE/RWORK(i)) * Y(1:M,i) ! INTRINSIC
  652. ELSE IF ( RWORK(i) < ZERO ) THEN
  653. CALL ZLASCL( 'G', 0, 0, -RWORK(i), &
  654. ONE/DBLE(M), M, 1, Y(1,i), LDY, INFO2 ) ! LAPACK CALL
  655. ELSE IF ( ABS(Y(IZAMAX(M, Y(1,i),1),i )) &
  656. /= ZERO ) THEN
  657. ! X(:,i) is zero vector. For consistency,
  658. ! Y(:,i) should also be zero. If Y(:,i) is not
  659. ! zero, then the data might be inconsistent or
  660. ! corrupted. If JOBS == 'C', Y(:,i) is set to
  661. ! zero and a warning flag is raised.
  662. ! The computation continues but the
  663. ! situation will be reported in the output.
  664. BADXY = .TRUE.
  665. IF ( LSAME(JOBS,'C')) &
  666. CALL ZDSCAL( M, ZERO, Y(1,i), 1 ) ! BLAS CALL
  667. END IF
  668. END DO
  669. END IF
  670. !
  671. IF ( SCCOLY ) THEN
  672. ! The columns of Y will be normalized.
  673. ! To prevent overflows, the column norms of Y are
  674. ! carefully computed using ZLASSQ.
  675. DO i = 1, N
  676. !RWORK(i) = DZNRM2( M, Y(1,i), 1 )
  677. SCALE = ZERO
  678. CALL ZLASSQ( M, Y(1,i), 1, SCALE, SSUM )
  679. IF ( DISNAN(SCALE) .OR. DISNAN(SSUM) ) THEN
  680. K = 0
  681. INFO = -10
  682. CALL XERBLA('ZGEDMD',-INFO)
  683. END IF
  684. IF ( SCALE /= ZERO .AND. (SSUM /= ZERO) ) THEN
  685. ROOTSC = SQRT(SSUM)
  686. IF ( SCALE .GE. (OFL / ROOTSC) ) THEN
  687. ! Norm of Y(:,i) overflows. First, Y(:,i)
  688. ! is scaled by
  689. ! ( ONE / ROOTSC ) / SCALE = 1/||Y(:,i)||_2.
  690. ! Next, the norm of Y(:,i) is stored without
  691. ! overflow as RWORK(i) = - SCALE * (ROOTSC/M),
  692. ! the minus sign indicating the 1/M factor.
  693. ! Scaling is performed without overflow, and
  694. ! underflow may occur in the smallest entries
  695. ! of Y(:,i). The relative backward and forward
  696. ! errors are small in the ell_2 norm.
  697. CALL ZLASCL( 'G', 0, 0, SCALE, ONE/ROOTSC, &
  698. M, 1, Y(1,i), LDY, INFO2 )
  699. RWORK(i) = - SCALE * ( ROOTSC / DBLE(M) )
  700. ELSE
  701. ! Y(:,i) will be scaled to unit 2-norm
  702. RWORK(i) = SCALE * ROOTSC
  703. CALL ZLASCL( 'G',0, 0, RWORK(i), ONE, M, 1, &
  704. Y(1,i), LDY, INFO2 ) ! LAPACK CALL
  705. ! Y(1:M,i) = (ONE/RWORK(i)) * Y(1:M,i) ! INTRINSIC
  706. END IF
  707. ELSE
  708. RWORK(i) = ZERO
  709. END IF
  710. END DO
  711. DO i = 1, N
  712. ! Now, apply the same scaling to the columns of X.
  713. IF ( RWORK(i) > ZERO ) THEN
  714. CALL ZDSCAL( M, ONE/RWORK(i), X(1,i), 1 ) ! BLAS CALL
  715. ! X(1:M,i) = (ONE/RWORK(i)) * X(1:M,i) ! INTRINSIC
  716. ELSE IF ( RWORK(i) < ZERO ) THEN
  717. CALL ZLASCL( 'G', 0, 0, -RWORK(i), &
  718. ONE/DBLE(M), M, 1, X(1,i), LDX, INFO2 ) ! LAPACK CALL
  719. ELSE IF ( ABS(X(IZAMAX(M, X(1,i),1),i )) &
  720. /= ZERO ) THEN
  721. ! Y(:,i) is zero vector. If X(:,i) is not
  722. ! zero, then a warning flag is raised.
  723. ! The computation continues but the
  724. ! situation will be reported in the output.
  725. BADXY = .TRUE.
  726. END IF
  727. END DO
  728. END IF
  729. !
  730. ! <2> SVD of the data snapshot matrix X.
  731. ! =====================================
  732. ! The left singular vectors are stored in the array X.
  733. ! The right singular vectors are in the array W.
  734. ! The array W will later on contain the eigenvectors
  735. ! of a Rayleigh quotient.
  736. NUMRNK = N
  737. SELECT CASE ( WHTSVD )
  738. CASE (1)
  739. CALL ZGESVD( 'O', 'S', M, N, X, LDX, RWORK, B, &
  740. LDB, W, LDW, ZWORK, LZWORK, RWORK(N+1), INFO1 ) ! LAPACK CALL
  741. T_OR_N = 'C'
  742. CASE (2)
  743. CALL ZGESDD( 'O', M, N, X, LDX, RWORK, B, LDB, W, &
  744. LDW, ZWORK, LZWORK, RWORK(N+1), IWORK, INFO1 ) ! LAPACK CALL
  745. T_OR_N = 'C'
  746. CASE (3)
  747. CALL ZGESVDQ( 'H', 'P', 'N', 'R', 'R', M, N, &
  748. X, LDX, RWORK, Z, LDZ, W, LDW, &
  749. NUMRNK, IWORK, LIWORK, ZWORK, &
  750. LZWORK, RWORK(N+1), LRWORK-N, INFO1) ! LAPACK CALL
  751. CALL ZLACPY( 'A', M, NUMRNK, Z, LDZ, X, LDX ) ! LAPACK CALL
  752. T_OR_N = 'C'
  753. CASE (4)
  754. CALL ZGEJSV( 'F', 'U', JSVOPT, 'R', 'N', 'P', M, &
  755. N, X, LDX, RWORK, Z, LDZ, W, LDW, &
  756. ZWORK, LZWORK, RWORK(N+1), LRWORK-N, IWORK, INFO1 ) ! LAPACK CALL
  757. CALL ZLACPY( 'A', M, N, Z, LDZ, X, LDX ) ! LAPACK CALL
  758. T_OR_N = 'N'
  759. XSCL1 = RWORK(N+1)
  760. XSCL2 = RWORK(N+2)
  761. IF ( XSCL1 /= XSCL2 ) THEN
  762. ! This is an exceptional situation. If the
  763. ! data matrices are not scaled and the
  764. ! largest singular value of X overflows.
  765. ! In that case ZGEJSV can return the SVD
  766. ! in scaled form. The scaling factor can be used
  767. ! to rescale the data (X and Y).
  768. CALL ZLASCL( 'G', 0, 0, XSCL1, XSCL2, M, N, Y, LDY, INFO2 )
  769. END IF
  770. END SELECT
  771. !
  772. IF ( INFO1 > 0 ) THEN
  773. ! The SVD selected subroutine did not converge.
  774. ! Return with an error code.
  775. INFO = 2
  776. RETURN
  777. END IF
  778. !
  779. IF ( RWORK(1) == ZERO ) THEN
  780. ! The largest computed singular value of (scaled)
  781. ! X is zero. Return error code -8
  782. ! (the 8th input variable had an illegal value).
  783. K = 0
  784. INFO = -8
  785. CALL XERBLA('ZGEDMD',-INFO)
  786. RETURN
  787. END IF
  788. !
  789. !<3> Determine the numerical rank of the data
  790. ! snapshots matrix X. This depends on the
  791. ! parameters NRNK and TOL.
  792. SELECT CASE ( NRNK )
  793. CASE ( -1 )
  794. K = 1
  795. DO i = 2, NUMRNK
  796. IF ( ( RWORK(i) <= RWORK(1)*TOL ) .OR. &
  797. ( RWORK(i) <= SMALL ) ) EXIT
  798. K = K + 1
  799. END DO
  800. CASE ( -2 )
  801. K = 1
  802. DO i = 1, NUMRNK-1
  803. IF ( ( RWORK(i+1) <= RWORK(i)*TOL ) .OR. &
  804. ( RWORK(i) <= SMALL ) ) EXIT
  805. K = K + 1
  806. END DO
  807. CASE DEFAULT
  808. K = 1
  809. DO i = 2, NRNK
  810. IF ( RWORK(i) <= SMALL ) EXIT
  811. K = K + 1
  812. END DO
  813. END SELECT
  814. ! Now, U = X(1:M,1:K) is the SVD/POD basis for the
  815. ! snapshot data in the input matrix X.
  816. !<4> Compute the Rayleigh quotient S = U^H * A * U.
  817. ! Depending on the requested outputs, the computation
  818. ! is organized to compute additional auxiliary
  819. ! matrices (for the residuals and refinements).
  820. !
  821. ! In all formulas below, we need V_k*Sigma_k^(-1)
  822. ! where either V_k is in W(1:N,1:K), or V_k^H is in
  823. ! W(1:K,1:N). Here Sigma_k=diag(WORK(1:K)).
  824. IF ( LSAME(T_OR_N, 'N') ) THEN
  825. DO i = 1, K
  826. CALL ZDSCAL( N, ONE/RWORK(i), W(1,i), 1 ) ! BLAS CALL
  827. ! W(1:N,i) = (ONE/RWORK(i)) * W(1:N,i) ! INTRINSIC
  828. END DO
  829. ELSE
  830. ! This non-unit stride access is due to the fact
  831. ! that ZGESVD, ZGESVDQ and ZGESDD return the
  832. ! adjoint matrix of the right singular vectors.
  833. !DO i = 1, K
  834. ! CALL ZDSCAL( N, ONE/RWORK(i), W(i,1), LDW ) ! BLAS CALL
  835. ! ! W(i,1:N) = (ONE/RWORK(i)) * W(i,1:N) ! INTRINSIC
  836. !END DO
  837. DO i = 1, K
  838. RWORK(N+i) = ONE/RWORK(i)
  839. END DO
  840. DO j = 1, N
  841. DO i = 1, K
  842. W(i,j) = CMPLX(RWORK(N+i),ZERO,KIND=WP)*W(i,j)
  843. END DO
  844. END DO
  845. END IF
  846. !
  847. IF ( WNTREF ) THEN
  848. !
  849. ! Need A*U(:,1:K)=Y*V_k*inv(diag(WORK(1:K)))
  850. ! for computing the refined Ritz vectors
  851. ! (optionally, outside ZGEDMD).
  852. CALL ZGEMM( 'N', T_OR_N, M, K, N, ZONE, Y, LDY, W, &
  853. LDW, ZZERO, Z, LDZ ) ! BLAS CALL
  854. ! Z(1:M,1:K)=MATMUL(Y(1:M,1:N),TRANSPOSE(CONJG(W(1:K,1:N)))) ! INTRINSIC, for T_OR_N=='C'
  855. ! Z(1:M,1:K)=MATMUL(Y(1:M,1:N),W(1:N,1:K)) ! INTRINSIC, for T_OR_N=='N'
  856. !
  857. ! At this point Z contains
  858. ! A * U(:,1:K) = Y * V_k * Sigma_k^(-1), and
  859. ! this is needed for computing the residuals.
  860. ! This matrix is returned in the array B and
  861. ! it can be used to compute refined Ritz vectors.
  862. CALL ZLACPY( 'A', M, K, Z, LDZ, B, LDB ) ! BLAS CALL
  863. ! B(1:M,1:K) = Z(1:M,1:K) ! INTRINSIC
  864. CALL ZGEMM( 'C', 'N', K, K, M, ZONE, X, LDX, Z, &
  865. LDZ, ZZERO, S, LDS ) ! BLAS CALL
  866. ! S(1:K,1:K) = MATMUL(TRANSPOSE(CONJG(X(1:M,1:K))),Z(1:M,1:K)) ! INTRINSIC
  867. ! At this point S = U^H * A * U is the Rayleigh quotient.
  868. ELSE
  869. ! A * U(:,1:K) is not explicitly needed and the
  870. ! computation is organized differently. The Rayleigh
  871. ! quotient is computed more efficiently.
  872. CALL ZGEMM( 'C', 'N', K, N, M, ZONE, X, LDX, Y, LDY, &
  873. ZZERO, Z, LDZ ) ! BLAS CALL
  874. ! Z(1:K,1:N) = MATMUL( TRANSPOSE(CONJG(X(1:M,1:K))), Y(1:M,1:N) ) ! INTRINSIC
  875. !
  876. CALL ZGEMM( 'N', T_OR_N, K, K, N, ZONE, Z, LDZ, W, &
  877. LDW, ZZERO, S, LDS ) ! BLAS CALL
  878. ! S(1:K,1:K) = MATMUL(Z(1:K,1:N),TRANSPOSE(CONJG(W(1:K,1:N)))) ! INTRINSIC, for T_OR_N=='T'
  879. ! S(1:K,1:K) = MATMUL(Z(1:K,1:N),(W(1:N,1:K))) ! INTRINSIC, for T_OR_N=='N'
  880. ! At this point S = U^H * A * U is the Rayleigh quotient.
  881. ! If the residuals are requested, save scaled V_k into Z.
  882. ! Recall that V_k or V_k^H is stored in W.
  883. IF ( WNTRES .OR. WNTEX ) THEN
  884. IF ( LSAME(T_OR_N, 'N') ) THEN
  885. CALL ZLACPY( 'A', N, K, W, LDW, Z, LDZ )
  886. ELSE
  887. CALL ZLACPY( 'A', K, N, W, LDW, Z, LDZ )
  888. END IF
  889. END IF
  890. END IF
  891. !
  892. !<5> Compute the Ritz values and (if requested) the
  893. ! right eigenvectors of the Rayleigh quotient.
  894. !
  895. CALL ZGEEV( 'N', JOBZL, K, S, LDS, EIGS, W, LDW, &
  896. W, LDW, ZWORK, LZWORK, RWORK(N+1), INFO1 ) ! LAPACK CALL
  897. !
  898. ! W(1:K,1:K) contains the eigenvectors of the Rayleigh
  899. ! quotient. See the description of Z.
  900. ! Also, see the description of ZGEEV.
  901. IF ( INFO1 > 0 ) THEN
  902. ! ZGEEV failed to compute the eigenvalues and
  903. ! eigenvectors of the Rayleigh quotient.
  904. INFO = 3
  905. RETURN
  906. END IF
  907. !
  908. ! <6> Compute the eigenvectors (if requested) and,
  909. ! the residuals (if requested).
  910. !
  911. IF ( WNTVEC .OR. WNTEX ) THEN
  912. IF ( WNTRES ) THEN
  913. IF ( WNTREF ) THEN
  914. ! Here, if the refinement is requested, we have
  915. ! A*U(:,1:K) already computed and stored in Z.
  916. ! For the residuals, need Y = A * U(:,1;K) * W.
  917. CALL ZGEMM( 'N', 'N', M, K, K, ZONE, Z, LDZ, W, &
  918. LDW, ZZERO, Y, LDY ) ! BLAS CALL
  919. ! Y(1:M,1:K) = Z(1:M,1:K) * W(1:K,1:K) ! INTRINSIC
  920. ! This frees Z; Y contains A * U(:,1:K) * W.
  921. ELSE
  922. ! Compute S = V_k * Sigma_k^(-1) * W, where
  923. ! V_k * Sigma_k^(-1) (or its adjoint) is stored in Z
  924. CALL ZGEMM( T_OR_N, 'N', N, K, K, ZONE, Z, LDZ, &
  925. W, LDW, ZZERO, S, LDS )
  926. ! Then, compute Z = Y * S =
  927. ! = Y * V_k * Sigma_k^(-1) * W(1:K,1:K) =
  928. ! = A * U(:,1:K) * W(1:K,1:K)
  929. CALL ZGEMM( 'N', 'N', M, K, N, ZONE, Y, LDY, S, &
  930. LDS, ZZERO, Z, LDZ )
  931. ! Save a copy of Z into Y and free Z for holding
  932. ! the Ritz vectors.
  933. CALL ZLACPY( 'A', M, K, Z, LDZ, Y, LDY )
  934. IF ( WNTEX ) CALL ZLACPY( 'A', M, K, Z, LDZ, B, LDB )
  935. END IF
  936. ELSE IF ( WNTEX ) THEN
  937. ! Compute S = V_k * Sigma_k^(-1) * W, where
  938. ! V_k * Sigma_k^(-1) is stored in Z
  939. CALL ZGEMM( T_OR_N, 'N', N, K, K, ZONE, Z, LDZ, &
  940. W, LDW, ZZERO, S, LDS )
  941. ! Then, compute Z = Y * S =
  942. ! = Y * V_k * Sigma_k^(-1) * W(1:K,1:K) =
  943. ! = A * U(:,1:K) * W(1:K,1:K)
  944. CALL ZGEMM( 'N', 'N', M, K, N, ZONE, Y, LDY, S, &
  945. LDS, ZZERO, B, LDB )
  946. ! The above call replaces the following two calls
  947. ! that were used in the developing-testing phase.
  948. ! CALL ZGEMM( 'N', 'N', M, K, N, ZONE, Y, LDY, S, &
  949. ! LDS, ZZERO, Z, LDZ)
  950. ! Save a copy of Z into B and free Z for holding
  951. ! the Ritz vectors.
  952. ! CALL ZLACPY( 'A', M, K, Z, LDZ, B, LDB )
  953. END IF
  954. !
  955. ! Compute the Ritz vectors
  956. IF ( WNTVEC ) CALL ZGEMM( 'N', 'N', M, K, K, ZONE, X, LDX, W, LDW, &
  957. ZZERO, Z, LDZ ) ! BLAS CALL
  958. ! Z(1:M,1:K) = MATMUL(X(1:M,1:K), W(1:K,1:K)) ! INTRINSIC
  959. !
  960. IF ( WNTRES ) THEN
  961. DO i = 1, K
  962. CALL ZAXPY( M, -EIGS(i), Z(1,i), 1, Y(1,i), 1 ) ! BLAS CALL
  963. ! Y(1:M,i) = Y(1:M,i) - EIGS(i) * Z(1:M,i) ! INTRINSIC
  964. RES(i) = DZNRM2( M, Y(1,i), 1 ) ! BLAS CALL
  965. END DO
  966. END IF
  967. END IF
  968. !
  969. IF ( WHTSVD == 4 ) THEN
  970. RWORK(N+1) = XSCL1
  971. RWORK(N+2) = XSCL2
  972. END IF
  973. !
  974. ! Successful exit.
  975. IF ( .NOT. BADXY ) THEN
  976. INFO = 0
  977. ELSE
  978. ! A warning on possible data inconsistency.
  979. ! This should be a rare event.
  980. INFO = 4
  981. END IF
  982. !............................................................
  983. RETURN
  984. ! ......
  985. END SUBROUTINE ZGEDMD