Jacobi.h
1//-*-C++-*-
2/***************************************************************************
3 *
4 * Copyright (C) 2004 by Willem van Straten
5 * Licensed under the Academic Free License version 2.1
6 *
7 ***************************************************************************/
8
9// epsic/src/util/Jacobi.h
10
11#ifndef __Jacobi_H
12#define __Jacobi_H
13
14#include "Matrix.h"
15#include "Quaternion.h"
16
17template <typename T>
18T norm (T x) { return fabs(x); }
19
20
22
24template <typename T, typename U>
25void calculate_Jacobi (const U& p, const U& q, const T& pq,
26 T& s, T& tau, U& correction)
27{
28 T h = q - p;
29 T g = 100.0 * fabs(pq);
30
31 T t;
32
33 if (fabs(h)+g == fabs(h))
34
35 t=pq/h;
36
37 else {
38
39 // Equation 11.1.8
40 T theta=0.5*h/(pq);
41
42 // Equation 11.1.10
43 t=1.0/(fabs(theta)+sqrt(1.0+theta*theta));
44 if (theta < 0.0) t = -t;
45
46 }
47
48 // Equation 11.1.11
49 T c=1.0/sqrt(1+t*t);
50
51 // Equation 11.1.12
52 s=t*c;
53
54 // Equation 11.1.18
55 tau=s/(1.0+c);
56
57 // last term of Equation 11.1.14 and 11.1.15
58 correction = t*pq;
59
60}
61
62// returns the correction factor for the diagonal
63template <typename T, typename U>
64void calculate_Jacobi (const U& p, const U& q, const std::complex<T>& pq,
65 std::complex<T>& s, std::complex<T>& tau, U& correction)
66{
67 T Sq = 0.5 * (p-q);
68 T Su = pq.real();
69 T Sv = -pq.imag();
70
71 Quaternion<T, Hermitian> herm (0, Sq, Su, Sv);
72 Quaternion<T, Unitary> rotation = eigen (herm);
73
74 T c = rotation.s0;
75 s = std::complex<T> (-rotation.s3, -rotation.s2);
76
77 // similar to Equation 11.1.18
78 tau = conj(s)/(1.0+c);
79
80 correction = 2.0 * ( c * ( s*conj(pq) ).real() + Sq * ( s*conj(s) ).real() );
81}
82
83
85
87template <unsigned RC, typename T>
88inline void rotate_Jacobi (Matrix<RC,RC,T>& x, T s, T tau,
89 unsigned i, unsigned j, unsigned k, unsigned l)
90{
91 T g=x[i][j];
92 T h=x[k][l];
93
94 // Equation 11.1.16
95 x[i][j] -= myconj(s)*(h+g*myconj(tau));
96 // Equation 11.1.17
97 x[k][l] += s*(g-h*tau);
98}
99
100// returns the correction factor for the diagonal
101template <unsigned RC, typename T, typename U>
102U JacobiRotation ( unsigned ip, unsigned iq,
104{
105 T s, tau;
106 U correction;
107
108 //cerr << "calculate_Jacobi ip=" << ip << " iq=" << iq << endl;
109 calculate_Jacobi (d[ip], d[iq], a[ip][iq], s, tau, correction);
110
111 d[ip] -= correction;
112 d[iq] += correction;
113
114 unsigned j;
115
116 //cerr << "in: pp=" << a[ip][ip] << " qq=" << a[iq][iq] << endl;
117
118#if 0
119
120 for (j=0; j<ip; j++)
121 rotate_Jacobi(a,s,tau,j,ip,j,iq);
122 for (j=ip+1; j<iq; j++)
123 rotate_Jacobi(a,s,tau,ip,j,j,iq);
124 for (j=iq+1; j<RC; j++)
125 rotate_Jacobi(a,s,tau,ip,j,iq,j);
126
127 a[ip][iq]=0.0;
128
129#else
130
131 // rotate the columns of the input matrix
132 for (j=0; j<RC; j++)
133 rotate_Jacobi(a,s,tau,j,ip,j,iq);
134
135 s = myconj(s);
136 tau = myconj(tau);
137
138 // rotate the rows of the input matrix
139 for (j=0; j<RC; j++)
140 rotate_Jacobi(a,s,tau,ip,j,iq,j);
141
142 //cerr << "out: pp=" << a[ip][ip] << " qq=" << a[iq][iq] << endl;
143 //cerr << "d: pp=" << d[ip] << " qq=" << d[iq] << endl;
144
145 a[iq][ip]=a[ip][iq]=0.0;
146
147#endif
148
149 // rotate the rows of the eigenvector matrix
150 for (j=0; j<RC; j++)
151 rotate_Jacobi (v,s,tau,ip,j,iq,j);
152
153 return correction;
154}
155
156
157
158template <typename T, typename U, unsigned RC>
159void Jacobi (Matrix<RC,RC,T>& a, Matrix<RC,RC,T>& evec, Vector<RC,U>& eval)
160{
161 Vector<RC,U> b, z;
162
163 // copy the diagonal elements into d and b
164 unsigned ip, iq;
165 for (ip=0; ip<RC; ip++)
166 b[ip] = eval[ip] = myreal( a[ip][ip] );
167
168 // start with the identity matrix
169 matrix_identity (evec);
170
171 for (unsigned iter=0; iter < 50; iter++) {
172
173 // sum off diagonal elements
174 U sum = 0.0;
175
176 for (ip=0; ip < RC; ip++)
177 for (iq=ip+1; iq < RC; iq++)
178 sum += norm(a[ip][iq]);
179
180 if (sum == 0.0)
181 return;
182
183 U thresh = 0;
184
185 if (iter < 4)
186 thresh=0.2*sum/(RC*RC);
187
188 for (ip=0; ip < RC; ip++)
189 for (iq=ip+1; iq < RC; iq++) {
190
191 // after four iterations, skip the rotation if the off-diagonal
192 // element is small
193
194 U g = 100.0 * norm(a[ip][iq]);
195
196 if ( iter > 4
197 && norm(eval[ip])+g == norm(eval[ip])
198 && norm(eval[iq])+g == norm(eval[iq]) )
199
200 a[ip][iq]=a[iq][ip]=0.0;
201
202
203 else if (norm(a[ip][iq]) > thresh) {
204
205 U correction = JacobiRotation ( ip, iq, a, evec, eval );
206
207 z[ip] -= correction;
208 z[iq] += correction;
209
210 }
211 }
212
213 for (ip=0; ip < RC; ip++) {
214 b[ip] += z[ip];
215 eval[ip]=b[ip];
216 z[ip]=0.0;
217 }
218
219
220 } // for each iteration
221
222}
223
224
225#endif

Generated using doxygen 1.14.0