MAGMA  1.2.0
MatrixAlgebraonGPUandMulticoreArchitectures
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Macros Groups
dstedx.cpp
Go to the documentation of this file.
1 /* -- MAGMA (version 1.2.0) --
2  Univ. of Tennessee, Knoxville
3  Univ. of California, Berkeley
4  Univ. of Colorado, Denver
5  May 2012
6 
7  @author Raffaele Solca
8 
9  @precisions normal d -> s
10 */
11 #include "common_magma.h"
12 
13 #define Z(ix, iy) (z + (ix) + ldz * (iy))
14 
15 #define lapackf77_dlanst FORTRAN_NAME( dlanst, DLANST )
16 
17 extern "C"{
18  double lapackf77_dlanst( char* norm, magma_int_t* n, double* d, double* e);
19 
21  {
22  return 25;
23  }
24 }
25 
26 extern "C" magma_int_t
27 magma_dstedx(char range, magma_int_t n, double vl, double vu,
28  magma_int_t il, magma_int_t iu, double* d, double* e, double* z, magma_int_t ldz,
29  double* work, magma_int_t lwork, magma_int_t* iwork, magma_int_t liwork,
30  double* dwork, magma_int_t* info)
31 {
32 /*
33  -- MAGMA (version 1.2.0) --
34  Univ. of Tennessee, Knoxville
35  Univ. of California, Berkeley
36  Univ. of Colorado, Denver
37  May 2012
38 
39  .. Scalar Arguments ..
40  CHARACTER RANGE
41  INTEGER IL, IU, INFO, LDZ, LIWORK, LWORK, N
42  DOUBLE PRECISION VL, VU
43  ..
44  .. Array Arguments ..
45  INTEGER IWORK( * )
46  DOUBLE PRECISION D( * ), E( * ), WORK( * ), Z( LDZ, * ),
47  $ DWORK ( * )
48  ..
49 
50  Purpose
51  =======
52 
53  DSTEDX computes some eigenvalues and, optionally, eigenvectors of a
54  symmetric tridiagonal matrix using the divide and conquer method.
55 
56  This code makes very mild assumptions about floating point
57  arithmetic. It will work on machines with a guard digit in
58  add/subtract, or on those binary machines without guard digits
59  which subtract like the Cray X-MP, Cray Y-MP, Cray C-90, or Cray-2.
60  It could conceivably fail on hexadecimal or decimal machines
61  without guard digits, but we know of none. See DLAEX3 for details.
62 
63  Arguments
64  =========
65 
66  RANGE (input) CHARACTER*1
67  = 'A': all eigenvalues will be found.
68  = 'V': all eigenvalues in the half-open interval (VL,VU]
69  will be found.
70  = 'I': the IL-th through IU-th eigenvalues will be found.
71 
72  N (input) INTEGER
73  The dimension of the symmetric tridiagonal matrix. N >= 0.
74 
75  VL (input) DOUBLE PRECISION
76  VU (input) DOUBLE PRECISION
77  If RANGE='V', the lower and upper bounds of the interval to
78  be searched for eigenvalues. VL < VU.
79  Not referenced if RANGE = 'A' or 'I'.
80 
81  IL (input) INTEGER
82  IU (input) INTEGER
83  If RANGE='I', the indices (in ascending order) of the
84  smallest and largest eigenvalues to be returned.
85  1 <= IL <= IU <= N, if N > 0; IL = 1 and IU = 0 if N = 0.
86  Not referenced if RANGE = 'A' or 'V'.
87 
88  D (input/output) DOUBLE PRECISION array, dimension (N)
89  On entry, the diagonal elements of the tridiagonal matrix.
90  On exit, if INFO = 0, the eigenvalues in ascending order.
91 
92  E (input/output) DOUBLE PRECISION array, dimension (N-1)
93  On entry, the subdiagonal elements of the tridiagonal matrix.
94  On exit, E has been destroyed.
95 
96  Z (input/output) DOUBLE PRECISION array, dimension (LDZ,N)
97  On exit, if INFO = 0, Z contains the orthonormal eigenvectors
98  of the symmetric tridiagonal matrix.
99 
100  LDZ (input) INTEGER
101  The leading dimension of the array Z. LDZ >= max(1,N).
102 
103  WORK (workspace/output) DOUBLE PRECISION array,
104  dimension (LWORK)
105  On exit, if INFO = 0, WORK(1) returns the optimal LWORK.
106 
107  LWORK (input) INTEGER
108  The dimension of the array WORK.
109  If N > 1 then LWORK must be at least ( 1 + 4*N + N**2 ).
110  Note that if N is less than or
111  equal to the minimum divide size, usually 25, then LWORK need
112  only be max(1,2*(N-1)).
113 
114  If LWORK = -1, then a workspace query is assumed; the routine
115  only calculates the optimal size of the WORK array, returns
116  this value as the first entry of the WORK array, and no error
117  message related to LWORK is issued by XERBLA.
118 
119  IWORK (workspace/output) INTEGER array, dimension (MAX(1,LIWORK))
120  On exit, if INFO = 0, IWORK(1) returns the optimal LIWORK.
121 
122  LIWORK (input) INTEGER
123  The dimension of the array IWORK.
124  LIWORK must be at least ( 3 + 5*N ).
125  Note that if N is less than or
126  equal to the minimum divide size, usually 25, then LIWORK
127  need only be 1.
128 
129  If LIWORK = -1, then a workspace query is assumed; the
130  routine only calculates the optimal size of the IWORK array,
131  returns this value as the first entry of the IWORK array, and
132  no error message related to LIWORK is issued by XERBLA.
133 
134  DWORK (device workspace) DOUBLE PRECISION array, dimension (3*N*N/2+3*N)
135 
136  INFO (output) INTEGER
137  = 0: successful exit.
138  < 0: if INFO = -i, the i-th argument had an illegal value.
139  > 0: The algorithm failed to compute an eigenvalue while
140  working on the submatrix lying in rows and columns
141  INFO/(N+1) through mod(INFO,N+1).
142 
143  Further Details
144  ===============
145 
146  Based on contributions by
147  Jeff Rutter, Computer Science Division, University of California
148  at Berkeley, USA
149  Modified by Francoise Tisseur, University of Tennessee.
150 
151  =====================================================================
152 */
153  char range_[2] = {range, 0};
154 
155  double d_zero = 0.;
156  double d_one = 1.;
157  magma_int_t izero = 0;
158  magma_int_t ione = 1;
159 
160 
161  magma_int_t alleig, indeig, valeig, lquery;
162  magma_int_t i, j, k, m;
163  magma_int_t liwmin, lwmin;
164  magma_int_t start, end, smlsiz;
165  double eps, orgnrm, p, tiny;
166 
167  // Test the input parameters.
168 
169  alleig = lapackf77_lsame(range_, "A");
170  valeig = lapackf77_lsame(range_, "V");
171  indeig = lapackf77_lsame(range_, "I");
172  lquery = lwork == -1 || liwork == -1;
173 
174  *info = 0;
175 
176  if (! (alleig || valeig || indeig)) {
177  *info = -1;
178  } else if (n < 0) {
179  *info = -2;
180  } else if (ldz < max(1,n)) {
181  *info = -10;
182  } else {
183  if (valeig) {
184  if (n > 0 && vu <= vl) {
185  *info = -4;
186  }
187  } else if (indeig) {
188  if (il < 1 || il > max(1,n)) {
189  *info = -5;
190  } else if (iu < min(n,il) || iu > n) {
191  *info = -6;
192  }
193  }
194  }
195 
196  if (*info == 0) {
197  // Compute the workspace requirements
198 
199  smlsiz = get_dstedx_smlsize();
200  if( n <= 1 ){
201  lwmin = 1;
202  liwmin = 1;
203  } else {
204  lwmin = 1 + 4*n + n*n;
205  liwmin = 3 + 5*n;
206  }
207 
208  work[0] = lwmin;
209  iwork[0] = liwmin;
210 
211  if (lwork < lwmin && ! lquery) {
212  *info = -12;
213  } else if (liwork < liwmin && ! lquery) {
214  *info = -14;
215  }
216  }
217 
218  if (*info != 0) {
219  magma_xerbla( __func__, -(*info));
221  } else if (lquery) {
222  return MAGMA_SUCCESS;
223  }
224 
225  // Quick return if possible
226 
227  if(n==0)
228  return MAGMA_SUCCESS;
229  if(n==1){
230  *z = 1.;
231  return MAGMA_SUCCESS;
232  }
233 
234  // If N is smaller than the minimum divide size (SMLSIZ+1), then
235  // solve the problem with another solver.
236 
237  if (n < smlsiz){
238  char char_I[]= {'I', 0};
239  lapackf77_dsteqr(char_I, &n, d, e, z, &ldz, work, info);
240  } else {
241  char char_F[]= {'F', 0};
242  lapackf77_dlaset(char_F, &n, &n, &d_zero, &d_one, z, &ldz);
243 
244  //Scale.
245  char char_M[]= {'M', 0};
246 
247  orgnrm = lapackf77_dlanst(char_M, &n, d, e);
248 
249  if (orgnrm == 0){
250  work[0] = lwmin;
251  iwork[0] = liwmin;
252  return MAGMA_SUCCESS;
253  }
254 
255  eps = lapackf77_dlamch( "Epsilon" );
256 
257  if (alleig){
258  start = 0;
259  while ( start < n ){
260 
261  // Let FINISH be the position of the next subdiagonal entry
262  // such that E( END ) <= TINY or FINISH = N if no such
263  // subdiagonal exists. The matrix identified by the elements
264  // between START and END constitutes an independent
265  // sub-problem.
266 
267  for(end = start+1; end < n; ++end){
268  tiny = eps * sqrt( MAGMA_D_ABS(d[end-1]*d[end]));
269  if (MAGMA_D_ABS(e[end-1]) <= tiny)
270  break;
271  }
272 
273  // (Sub) Problem determined. Compute its size and solve it.
274 
275  m = end - start;
276  if (m==1){
277  start = end;
278  continue;
279  }
280  if (m > smlsiz){
281 
282  // Scale
283  char char_G[] = {'G', 0};
284  orgnrm = lapackf77_dlanst(char_M, &m, &d[start], &e[start]);
285  lapackf77_dlascl(char_G, &izero, &izero, &orgnrm, &d_one, &m, &ione, &d[start], &m, info);
286  magma_int_t mm = m-1;
287  lapackf77_dlascl(char_G, &izero, &izero, &orgnrm, &d_one, &mm, &ione, &e[start], &mm, info);
288 
289  magma_dlaex0( m, &d[start], &e[start], Z(start, start), ldz, work, iwork, dwork, 'A', vl, vu, il, iu, info);
290 
291  if( *info != 0) {
292  return MAGMA_SUCCESS;
293  }
294 
295  // Scale Back
296  lapackf77_dlascl(char_G, &izero, &izero, &d_one, &orgnrm, &m, &ione, &d[start], &m, info);
297 
298  } else {
299 
300  char char_I[]= {'I', 0};
301  lapackf77_dsteqr( char_I, &m, &d[start], &e[start], Z(start, start), &ldz, work, info);
302  if (*info != 0){
303  *info = (start+1) *(n+1) + end;
304  }
305  }
306 
307  start = end;
308  }
309 
310 
311  // If the problem split any number of times, then the eigenvalues
312  // will not be properly ordered. Here we permute the eigenvalues
313  // (and the associated eigenvectors) into ascending order.
314 
315  if (m < n){
316 
317  // Use Selection Sort to minimize swaps of eigenvectors
318  for (i = 1; i < n; ++i){
319  k = i-1;
320  p = d[i-1];
321  for (j = i; j < n; ++j){
322  if (d[j] < p){
323  k = j;
324  p = d[j];
325  }
326  }
327  if(k != i-1) {
328  d[k] = d[i-1];
329  d[i-1] = p;
330  blasf77_dswap(&n, Z(0,i-1), &ione, Z(0,k), &ione);
331  }
332  }
333  }
334 
335  } else {
336 
337  // Scale
338  char char_G[] = {'G', 0};
339  lapackf77_dlascl(char_G, &izero, &izero, &orgnrm, &d_one, &n, &ione, d, &n, info);
340  magma_int_t nm = n-1;
341  lapackf77_dlascl(char_G, &izero, &izero, &orgnrm, &d_one, &nm, &ione, e, &nm, info);
342 
343  magma_dlaex0( n, d, e, z, ldz, work, iwork, dwork, range, vl, vu, il, iu, info);
344 
345  if( *info != 0) {
346  return MAGMA_SUCCESS;
347  }
348 
349  // Scale Back
350  lapackf77_dlascl(char_G, &izero, &izero, &d_one, &orgnrm, &n, &ione, d, &n, info);
351 
352  }
353  }
354 
355  work[0] = lwmin;
356  iwork[0] = liwmin;
357 
358  return MAGMA_SUCCESS;
359 
360 } /* dstedx */