MAGMA  1.2.0
MatrixAlgebraonGPUandMulticoreArchitectures
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Macros Groups
dsyevd.cpp
Go to the documentation of this file.
1 /*
2  -- MAGMA (version 1.2.0) --
3  Univ. of Tennessee, Knoxville
4  Univ. of California, Berkeley
5  Univ. of Colorado, Denver
6  May 2012
7 
8  @author Stan Tomov
9 
10  @precisions normal d -> s
11 
12 */
13 #include "common_magma.h"
14 
15 extern "C" magma_int_t
16 magma_dsyevd(char jobz, char uplo,
17  magma_int_t n,
18  double *a, magma_int_t lda,
19  double *w,
20  double *work, magma_int_t lwork,
21  magma_int_t *iwork, magma_int_t liwork,
22  magma_int_t *info)
23 {
24 /* -- MAGMA (version 1.2.0) --
25  Univ. of Tennessee, Knoxville
26  Univ. of California, Berkeley
27  Univ. of Colorado, Denver
28  May 2012
29 
30  Purpose
31  =======
32  DSYEVD computes all eigenvalues and, optionally, eigenvectors of a
33  real symmetric matrix A. If eigenvectors are desired, it uses a
34  divide and conquer algorithm.
35 
36  The divide and conquer algorithm makes very mild assumptions about
37  floating point arithmetic. It will work on machines with a guard
38  digit in add/subtract, or on those binary machines without guard
39  digits which subtract like the Cray X-MP, Cray Y-MP, Cray C-90, or
40  Cray-2. It could conceivably fail on hexadecimal or decimal machines
41  without guard digits, but we know of none.
42 
43  Arguments
44  =========
45  JOBZ (input) CHARACTER*1
46  = 'N': Compute eigenvalues only;
47  = 'V': Compute eigenvalues and eigenvectors.
48 
49  UPLO (input) CHARACTER*1
50  = 'U': Upper triangle of A is stored;
51  = 'L': Lower triangle of A is stored.
52 
53  N (input) INTEGER
54  The order of the matrix A. N >= 0.
55 
56  A (input/output) DOUBLE_PRECISION array, dimension (LDA, N)
57  On entry, the symmetric matrix A. If UPLO = 'U', the
58  leading N-by-N upper triangular part of A contains the
59  upper triangular part of the matrix A. If UPLO = 'L',
60  the leading N-by-N lower triangular part of A contains
61  the lower triangular part of the matrix A.
62  On exit, if JOBZ = 'V', then if INFO = 0, A contains the
63  orthonormal eigenvectors of the matrix A.
64  If JOBZ = 'N', then on exit the lower triangle (if UPLO='L')
65  or the upper triangle (if UPLO='U') of A, including the
66  diagonal, is destroyed.
67 
68  LDA (input) INTEGER
69  The leading dimension of the array A. LDA >= max(1,N).
70 
71  W (output) DOUBLE PRECISION array, dimension (N)
72  If INFO = 0, the eigenvalues in ascending order.
73 
74  WORK (workspace/output) DOUBLE_PRECISION array, dimension (MAX(1,LWORK))
75  On exit, if INFO = 0, WORK(1) returns the optimal LWORK.
76 
77  LWORK (input) INTEGER
78  The length of the array WORK.
79  If N <= 1, LWORK must be at least 1.
80  If JOBZ = 'N' and N > 1, LWORK must be at least 2*N + 1.
81  If JOBZ = 'V' and N > 1, LWORK must be at least 1 + 6*N + 2*N**2.
82 
83  If LWORK = -1, then a workspace query is assumed; the routine
84  only calculates the optimal sizes of the WORK and IWORK
85  arrays, returns these values as the first entries of the WORK
86  and IWORK arrays, and no error message related to LWORK or
87  LIWORK is issued by XERBLA.
88 
89  IWORK (workspace/output) INTEGER array, dimension (MAX(1,LIWORK))
90  On exit, if INFO = 0, IWORK(1) returns the optimal LIWORK.
91 
92  LIWORK (input) INTEGER
93  The dimension of the array IWORK.
94  If N <= 1, LIWORK must be at least 1.
95  If JOBZ = 'N' and N > 1, LIWORK must be at least 1.
96  If JOBZ = 'V' and N > 1, LIWORK must be at least 3 + 5*N.
97 
98  If LIWORK = -1, then a workspace query is assumed; the
99  routine only calculates the optimal sizes of the WORK and
100  IWORK arrays, returns these values as the first entries of
101  the WORK and IWORK arrays, and no error message related to
102  LWORK or LIWORK is issued by XERBLA.
103 
104  INFO (output) INTEGER
105  = 0: successful exit
106  < 0: if INFO = -i, the i-th argument had an illegal value
107  > 0: if INFO = i and JOBZ = 'N', then the algorithm failed
108  to converge; i off-diagonal elements of an intermediate
109  tridiagonal form did not converge to zero;
110  if INFO = i and JOBZ = 'V', then the algorithm failed
111  to compute an eigenvalue while working on the submatrix
112  lying in rows and columns INFO/(N+1) through
113  mod(INFO,N+1).
114 
115  Further Details
116  ===============
117  Based on contributions by
118  Jeff Rutter, Computer Science Division, University of California
119  at Berkeley, USA
120 
121  Modified description of INFO. Sven, 16 Feb 05.
122  ===================================================================== */
123 
124  char uplo_[2] = {uplo, 0};
125  char jobz_[2] = {jobz, 0};
126  static magma_int_t c__1 = 1;
127  static magma_int_t c_n1 = -1;
128  static magma_int_t c__0 = 0;
129  static double c_b18 = 1.;
130 
131  magma_int_t a_dim1, a_offset;
132  double d__1;
133 
134  static double eps;
135  static magma_int_t inde;
136  static double anrm;
137  static double rmin, rmax;
138  static magma_int_t lopt;
139  static double sigma;
140  static magma_int_t iinfo, lwmin, liopt;
141  static magma_int_t lower;
142  static magma_int_t wantz;
143  static magma_int_t indwk2, llwrk2;
144  static magma_int_t iscale;
145  static double safmin;
146  static double bignum;
147  static magma_int_t indtau;
148  static magma_int_t indwrk, liwmin;
149  static magma_int_t llwork;
150  static double smlnum;
151  static magma_int_t lquery;
152 
153  double* dwork;
154 
155  wantz = lapackf77_lsame(jobz_, MagmaVectorsStr);
156  lower = lapackf77_lsame(uplo_, MagmaLowerStr);
157  lquery = lwork == -1 || liwork == -1;
158 
159  *info = 0;
160  if (! (wantz || lapackf77_lsame(jobz_, MagmaNoVectorsStr))) {
161  *info = -1;
162  } else if (! (lower || lapackf77_lsame(uplo_, MagmaUpperStr))) {
163  *info = -2;
164  } else if (n < 0) {
165  *info = -3;
166  } else if (lda < max(1,n)) {
167  *info = -5;
168  }
169 
170  lapackf77_dsyevd(jobz_, uplo_, &n,
171  a, &lda, w, work, &c_n1,
172  iwork, &c_n1, info);
173 
174  lwmin = (magma_int_t)work[0];
175  liwmin = (magma_int_t)iwork[0];
176 
177  if ((lwork < lwmin) && !lquery) {
178  *info = -8;
179  } else if ((liwork < liwmin) && ! lquery) {
180  *info = -10;
181  }
182 
183  if (*info != 0) {
184  magma_xerbla( __func__, -(*info) );
186  }
187  else if (lquery) {
188  return MAGMA_SUCCESS;
189  }
190 
191  /* Quick return if possible */
192  if (n == 0) {
193  return MAGMA_SUCCESS;
194  }
195 
196  if (n == 1) {
197  w[0] = a[0];
198  if (wantz) {
199  a[0] = 1.;
200  }
201  return MAGMA_SUCCESS;
202  }
203 
204  a_dim1 = lda;
205  a_offset = 1 + a_dim1;
206  a -= a_offset;
207  --w;
208  --work;
209  --iwork;
210 
211  /* Get machine constants. */
212  safmin = lapackf77_dlamch("Safe minimum");
213  eps = lapackf77_dlamch("Precision");
214  smlnum = safmin / eps;
215  bignum = 1. / smlnum;
216  rmin = magma_dsqrt(smlnum);
217  rmax = magma_dsqrt(bignum);
218 
219  /* Scale matrix to allowable range, if necessary. */
220  anrm = lapackf77_dlansy("M", uplo_, &n, &a[a_offset], &lda, &work[1]);
221  iscale = 0;
222  if (anrm > 0. && anrm < rmin) {
223  iscale = 1;
224  sigma = rmin / anrm;
225  } else if (anrm > rmax) {
226  iscale = 1;
227  sigma = rmax / anrm;
228  }
229  if (iscale == 1) {
230  lapackf77_dlascl(uplo_, &c__0, &c__0, &c_b18, &sigma, &n, &n, &a[a_offset],
231  &lda, info);
232  }
233 
234  /* Call DSYTRD to reduce symmetric matrix to tridiagonal form. */
235  inde = 1;
236  indtau = inde + n;
237  indwrk = indtau + n;
238  llwork = lwork - indwrk + 1;
239  indwk2 = indwrk + n * n;
240  llwrk2 = lwork - indwk2 + 1;
241 
242 //#define ENABLE_TIMER
243 #ifdef ENABLE_TIMER
244  magma_timestr_t start, end;
245 
246  start = get_current_time();
247 #endif
248 
249  magma_dsytrd(uplo_[0], n, &a[a_offset], lda, &w[1], &work[inde],
250  &work[indtau], &work[indwrk], llwork, &iinfo);
251 
252 #ifdef ENABLE_TIMER
253  end = get_current_time();
254 
255  printf("time dsytrd = %6.2f\n", GetTimerValue(start,end)/1000.);
256 #endif
257 
258  /* For eigenvalues only, call DSTERF. For eigenvectors, first call
259  ZSTEDC to generate the eigenvector matrix, WORK(INDWRK), of the
260  tridiagonal matrix, then call DORMTR to multiply it to the Householder
261  transformations represented as Householder vectors in A. */
262  if (! wantz) {
263  lapackf77_dsterf(&n, &w[1], &work[inde], info);
264  } else {
265 
266 #ifdef ENABLE_TIMER
267  start = get_current_time();
268 #endif
269 
270  if (MAGMA_SUCCESS != magma_dmalloc( &dwork, 3*n*(n/2 + 1) )) {
271  *info = -14;
272  return MAGMA_ERR_DEVICE_ALLOC;
273  }
274 
275  magma_dstedx('A', n, 0., 0., 0, 0, &w[1], &work[inde],
276  &work[indwrk], n, &work[indwk2],
277  llwrk2, &iwork[1], liwork, dwork, info);
278 
279  magma_free( dwork );
280 
281 #ifdef ENABLE_TIMER
282  end = get_current_time();
283 
284  printf("time dstedx = %6.2f\n", GetTimerValue(start,end)/1000.);
285 
286  start = get_current_time();
287 #endif
288 
289  magma_dormtr(MagmaLeft, uplo, MagmaNoTrans, n, n, &a[a_offset], lda, &work[indtau],
290  &work[indwrk], n, &work[indwk2], llwrk2, &iinfo);
291 
292  lapackf77_dlacpy("A", &n, &n, &work[indwrk], &n, &a[a_offset], &lda);
293 
294 #ifdef ENABLE_TIMER
295  end = get_current_time();
296 
297  printf("time dormtr + copy = %6.2f\n", GetTimerValue(start,end)/1000.);
298 #endif
299 
300  }
301 
302  /* If matrix was scaled, then rescale eigenvalues appropriately. */
303  if (iscale == 1) {
304  d__1 = 1. / sigma;
305  blasf77_dscal(&n, &d__1, &w[1], &c__1);
306  }
307 
308  MAGMA_D_SET2REAL(work[1], (double) lopt);
309  iwork[1] = liopt;
310 
311  return MAGMA_SUCCESS;
312 } /* magma_dsyevd */