b2api
B2000++ API Reference Manual, VERSION 4.6
 
Loading...
Searching...
No Matches
b2eigen_decomposition.H
1//---------------------------------------------------------------------------
2// b2eigen_decomposition.H --
3//
4// Symmetric matrix A => eigenvectors in columns of V, corresponding
5// eigenvalues in d.
6//
7// void eigen_decomposition(double A[3][3], double V[3][3], double d[3]);
8//
9// written by Mathias Doreille, based on work by others (see below)
10//
11// Copyright (c) 2007-2012 SMR Engineering & Development SA
12// 2502 Bienne, Switzerland
13//
14// All Rights Reserved. Proprietary source code. The contents of
15// this file may not be disclosed to third parties, copied or
16// duplicated in any form, in whole or in part, without the prior
17// written permission of SMR.
18//---------------------------------------------------------------------------
19
20#ifndef __B2_EIGEN_DECOMPOSITION_H__
21#define __B2_EIGEN_DECOMPOSITION_H__
22
23#include <algorithm>
24#include <cmath>
25
26namespace {
27
28static double hypot2(double x, double y) { return std::sqrt(x * x + y * y); }
29
30// Symmetric Householder reduction to tridiagonal form.
31template <int n>
32static void tred2(double V[n][n], double d[n], double e[n]) {
33 // This is derived from the Algol procedures tred2 by
34 // Bowdler, Martin, Reinsch, and Wilkinson, Handbook for
35 // Auto. Comp., Vol.ii-Linear Algebra, and the corresponding
36 // Fortran subroutine in EISPACK.
37
38 for (int j = 0; j < n; ++j) { d[j] = V[n - 1][j]; }
39
40 // Householder reduction to tridiagonal form.
41
42 for (int i = n - 1; i > 0; --i) {
43 // Scale to avoid under/overflow.
44
45 double scale = 0.0;
46 double h = 0.0;
47 for (int k = 0; k < i; ++k) { scale = scale + std::abs(d[k]); }
48 if (scale == 0.0) {
49 e[i] = d[i - 1];
50 for (int j = 0; j < i; ++j) {
51 d[j] = V[i - 1][j];
52 V[i][j] = 0.0;
53 V[j][i] = 0.0;
54 }
55 } else {
56 // Generate Householder vector.
57
58 for (int k = 0; k < i; ++k) {
59 d[k] /= scale;
60 h += d[k] * d[k];
61 }
62 double f = d[i - 1];
63 double g = std::sqrt(h);
64 if (f > 0) { g = -g; }
65 e[i] = scale * g;
66 h = h - f * g;
67 d[i - 1] = f - g;
68 for (int j = 0; j < i; ++j) { e[j] = 0.0; }
69
70 // Apply similarity transformation to remaining columns.
71
72 for (int j = 0; j < i; ++j) {
73 f = d[j];
74 V[j][i] = f;
75 g = e[j] + V[j][j] * f;
76 for (int k = j + 1; k <= i - 1; ++k) {
77 g += V[k][j] * d[k];
78 e[k] += V[k][j] * f;
79 }
80 e[j] = g;
81 }
82 f = 0.0;
83 for (int j = 0; j < i; ++j) {
84 e[j] /= h;
85 f += e[j] * d[j];
86 }
87 double hh = f / (h + h);
88 for (int j = 0; j < i; ++j) { e[j] -= hh * d[j]; }
89 for (int j = 0; j < i; ++j) {
90 f = d[j];
91 g = e[j];
92 for (int k = j; k <= i - 1; ++k) { V[k][j] -= (f * e[k] + g * d[k]); }
93 d[j] = V[i - 1][j];
94 V[i][j] = 0.0;
95 }
96 }
97 d[i] = h;
98 }
99
100 // Accumulate transformations.
101
102 for (int i = 0; i < n - 1; ++i) {
103 V[n - 1][i] = V[i][i];
104 V[i][i] = 1.0;
105 double h = d[i + 1];
106 if (h != 0.0) {
107 for (int k = 0; k <= i; ++k) { d[k] = V[k][i + 1] / h; }
108 for (int j = 0; j <= i; ++j) {
109 double g = 0.0;
110 for (int k = 0; k <= i; ++k) { g += V[k][i + 1] * V[k][j]; }
111 for (int k = 0; k <= i; ++k) { V[k][j] -= g * d[k]; }
112 }
113 }
114 for (int k = 0; k <= i; ++k) { V[k][i + 1] = 0.0; }
115 }
116 for (int j = 0; j < n; ++j) {
117 d[j] = V[n - 1][j];
118 V[n - 1][j] = 0.0;
119 }
120 V[n - 1][n - 1] = 1.0;
121 e[0] = 0.0;
122}
123
124// Symmetric tridiagonal QL algorithm.
125template <int n>
126static void tql2(double V[n][n], double d[n], double e[n]) {
127 // This is derived from the Algol procedures tql2, by
128 // Bowdler, Martin, Reinsch, and Wilkinson, Handbook for
129 // Auto. Comp., Vol.ii-Linear Algebra, and the corresponding
130 // Fortran subroutine in EISPACK.
131
132 for (int i = 1; i < n; ++i) { e[i - 1] = e[i]; }
133 e[n - 1] = 0.0;
134
135 double f = 0.0;
136 double tst1 = 0.0;
137 double eps = pow(2.0, -52.0);
138 for (int l = 0; l < n; l++) {
139 // Find small subdiagonal element
140
141 tst1 = std::max(tst1, std::abs(d[l]) + std::abs(e[l]));
142 int m = l;
143 while (m < n) {
144 if (std::abs(e[m]) <= eps * tst1) { break; }
145 m++;
146 }
147
148 // If m == l, d[l] is an eigenvalue,
149 // otherwise, iterate.
150
151 if (m > l) {
152 int iter = 0;
153 do {
154 iter = iter + 1; // (Could check iteration count here.)
155
156 // Compute implicit shift
157
158 double g = d[l];
159 double p = (d[l + 1] - g) / (2.0 * e[l]);
160 double r = hypot2(p, 1.0);
161 if (p < 0) { r = -r; }
162 d[l] = e[l] / (p + r);
163 d[l + 1] = e[l] * (p + r);
164 double dl1 = d[l + 1];
165 double h = g - d[l];
166 for (int i = l + 2; i < n; ++i) { d[i] -= h; }
167 f = f + h;
168
169 // Implicit QL transformation.
170
171 p = d[m];
172 double c = 1.0;
173 double c2 = c;
174 double c3 = c;
175 double el1 = e[l + 1];
176 double s = 0.0;
177 double s2 = 0.0;
178 for (int i = m - 1; i >= l; i--) {
179 c3 = c2;
180 c2 = c;
181 s2 = s;
182 g = c * e[i];
183 h = c * p;
184 r = hypot2(p, e[i]);
185 e[i + 1] = s * r;
186 s = e[i] / r;
187 c = p / r;
188 p = c * d[i] - s * g;
189 d[i + 1] = h + s * (c * g + s * d[i]);
190
191 // Accumulate transformation.
192
193 for (int k = 0; k < n; ++k) {
194 h = V[k][i + 1];
195 V[k][i + 1] = s * V[k][i] + c * h;
196 V[k][i] = c * V[k][i] - s * h;
197 }
198 }
199 p = -s * s2 * c3 * el1 * e[l] / dl1;
200 e[l] = s * p;
201 d[l] = c * p;
202
203 // Check for convergence.
204
205 } while (std::abs(e[l]) > eps * tst1);
206 }
207 d[l] = d[l] + f;
208 e[l] = 0.0;
209 }
210
211 // Sort eigenvalues and corresponding vectors.
212
213 for (int i = 0; i < n - 1; ++i) {
214 int k = i;
215 double p = d[i];
216 for (int j = i + 1; j < n; ++j) {
217 if (d[j] > p) {
218 k = j;
219 p = d[j];
220 }
221 }
222 if (k != i) {
223 d[k] = d[i];
224 d[i] = p;
225 for (int j = 0; j < n; ++j) {
226 p = V[j][i];
227 V[j][i] = V[j][k];
228 V[j][k] = p;
229 }
230 }
231 }
232}
233} // namespace
234
235namespace b2000 {
236
237// Symmetric matrix A => eigenvectors in columns of V,
238// corresponding eigenvalues in d. The eigenvalues are sorted in
239// descending order.
240template <int n>
241void eigen_decomposition(const double A[n][n], double V[n][n], double d[n]) {
242 double e[n];
243 std::copy(A[0], A[n], V[0]);
244 tred2(V, d, e);
245 tql2(V, d, e);
246}
247
248// Symmetric matrix A => eigenvectors in columns of V,
249// corresponding eigenvalues in d. The eigenvalues are sorted in
250// descending order.
251template <int n>
252void eigen_decomposition(double A_and_V[n][n], double d[n]) {
253 double e[n];
254 tred2(A_and_V, d, e);
255 tql2(A_and_V, d, e);
256}
257
258} // namespace b2000
259
260#endif /* __B2_EIGEN_DECOMPOSITION_H__ */
csda< T > pow(const csda< T > &a, const csda< T > &b)
Power from two csda numbers.
Definition b2csda.H:378
Contains the base classes for implementing Finite Elements.
Definition b2boundary_condition.H:32