10
#include <libciomr/libciomr.h>
17
** david(): Computes the lowest few eigenvalues and eigenvectors of a
18
** symmetric matrix, A, using the Davidson-Liu algorithm. The matrix
19
** must be small enough to fit entirely in core. This algorithm is
20
** useful if one is interested in only a few roots of the matrix
21
** rather than the whole spectrum.
23
** NB: This implementation will keep up to eight guess vectors for each
24
** root desired before collapsing to one vector per root. In
25
** addition, if smart_guess=1 (the default), guess vectors are
26
** constructed by diagonalization of a sub-matrix of A; otherwise,
27
** unit vectors are used.
29
** TDC, July-August 2002
31
** \param A = matrix to diagonalize
32
** \param N = dimension of A
33
** \param M = number of roots desired
34
** \param eps = eigenvalues
35
** \param v = eigenvectors
36
** \param cutoff = tolerance for convergence of eigenvalues
37
** \param print = Boolean for printing additional information
39
** Returns: number of converged roots
43
int david(double **A, int N, int M, double *eps, double **v,
44
double cutoff, int print)
48
int min_pos, numf, iter, *conv, converged, maxdim, skip_check;
49
int *small2big, init_dim;
51
double *Adiag, **b, **bnew, **sigma, **G;
52
double *lambda, **alpha, **f, *lambda_old;
53
double norm, denom, diff;
57
b = block_matrix(maxdim, N); /* current set of guess vectors,
59
bnew = block_matrix(M, N); /* guess vectors formed from old vectors,
61
sigma = block_matrix(N, maxdim); /* sigma vectors, stored by column */
62
G = block_matrix(maxdim, maxdim); /* Davidson mini-Hamitonian */
63
f = block_matrix(maxdim, N); /* residual eigenvectors, stored by row */
64
alpha = block_matrix(maxdim, maxdim); /* eigenvectors of G */
65
lambda = init_array(maxdim); /* eigenvalues of G */
66
lambda_old = init_array(maxdim); /* approximate roots from previous
69
if(smart_guess) { /* Use eigenvectors of a sub-matrix as initial guesses */
71
if(N > 7*M) init_dim = 7*M;
73
Adiag = init_array(N);
74
small2big = init_int_array(7*M);
75
for(i=0; i < N; i++) { Adiag[i] = A[i][i]; }
76
for(i=0; i < init_dim; i++) {
80
if(Adiag[j] < minimum) {
86
Adiag[min_pos] = BIGNUM;
87
lambda_old[i] = minimum;
89
for(i=0; i < init_dim; i++) {
90
for(j=0; j < init_dim; j++)
91
G[i][j] = A[small2big[i]][small2big[j]];
94
sq_rsp(init_dim, init_dim, G, lambda, 1, alpha, 1e-12);
96
for(i=0; i < init_dim; i++) {
97
for(j=0; j < init_dim; j++)
98
b[i][small2big[j]] = alpha[j][i];
104
else { /* Use unit vectors as initial guesses */
105
Adiag = init_array(N);
106
for(i=0; i < N; i++) { Adiag[i] = A[i][i]; }
107
for(i=0; i < M; i++) {
111
if(Adiag[j] < minimum) { minimum = Adiag[j]; min_pos = j; }
114
Adiag[min_pos] = BIGNUM;
115
lambda_old[i] = minimum;
123
conv = init_int_array(M); /* boolean array for convergence of each
125
while(converged < M && iter < MAXIT) {
128
if(print) printf("\niter = %d\n", iter);
130
/* form mini-matrix */
131
C_DGEMM('n','t', N, L, N, 1.0, &(A[0][0]), N, &(b[0][0]), N,
132
0.0, &(sigma[0][0]), maxdim);
133
C_DGEMM('n','n', L, L, N, 1.0, &(b[0][0]), N,
134
&(sigma[0][0]), maxdim, 0.0, &(G[0][0]), maxdim);
136
/* diagonalize mini-matrix */
137
sq_rsp(L, L, G, lambda, 1, alpha, 1e-12);
139
/* form preconditioned residue vectors */
141
for(I=0; I < N; I++) {
143
for(i=0; i < L; i++) {
144
f[k][I] += alpha[i][k] * (sigma[I][i] - lambda[k] * b[i][I]);
146
denom = lambda[k] - A[I][I];
147
if(fabs(denom) > 1e-6) f[k][I] /= denom;
151
/* normalize each residual */
152
for(k=0; k < M; k++) {
154
for(I=0; I < N; I++) {
155
norm += f[k][I] * f[k][I];
158
for(I=0; I < N; I++) {
159
if(norm > 1e-6) f[k][I] /= norm;
164
/* schmidt orthogonalize the f[k] against the set of b[i] and add
166
for(k=0,numf=0; k < M; k++)
167
if(schmidt_add(b, L, N, f[k])) { L++; numf++; }
169
/* If L is close to maxdim, collapse to one guess per root */
172
printf("Subspace too large: maxdim = %d, L = %d\n", maxdim, L);
173
printf("Collapsing eigenvectors.\n");
175
for(i=0; i < M; i++) {
176
memset((void *) bnew[i], 0, N*sizeof(double));
177
for(j=0; j < L; j++) {
178
for(k=0; k < N; k++) {
179
bnew[i][k] += alpha[j][i] * b[j][k];
184
/* copy new vectors into place */
187
b[i][k] = bnew[i][k];
194
/* check convergence on all roots */
197
zero_int_array(conv, M);
199
printf("Root Eigenvalue Delta Converged?\n");
200
printf("---- -------------------- ------- ----------\n");
202
for(k=0; k < M; k++) {
203
diff = fabs(lambda[k] - lambda_old[k]);
208
lambda_old[k] = lambda[k];
210
printf("%3d %20.14f %4.3e %1s\n", k, lambda[k], diff,
211
conv[k] == 1 ? "Y" : "N");
219
/* generate final eigenvalues and eigenvectors */
221
for(i=0; i < M; i++) {
223
for(j=0; j < L; j++) {
224
for(I=0; I < N; I++) {
225
v[I][i] += alpha[j][i] * b[j][I];
229
if(print) printf("Davidson algorithm converged in %d iterations.\n", iter);