syev.hpp
Go to the documentation of this file.
1//===========================================================================
2/*!
3 *
4 *
5 * \brief Contains the lapack bindings for the symmetric eigenvalue problem syev.
6 *
7 * \author O. Krause
8 * \date 2010
9 *
10 *
11 * \par Copyright 1995-2015 Shark Development Team
12 *
13 * <BR><HR>
14 * This file is part of Shark.
15 * <http://image.diku.dk/shark/>
16 *
17 * Shark is free software: you can redistribute it and/or modify
18 * it under the terms of the GNU Lesser General Public License as published
19 * by the Free Software Foundation, either version 3 of the License, or
20 * (at your option) any later version.
21 *
22 * Shark is distributed in the hope that it will be useful,
23 * but WITHOUT ANY WARRANTY; without even the implied warranty of
24 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
25 * GNU Lesser General Public License for more details.
26 *
27 * You should have received a copy of the GNU Lesser General Public License
28 * along with Shark. If not, see <http://www.gnu.org/licenses/>.
29 *
30 */
31//===========================================================================
32#ifndef REMORA_KERNELS_DEFAULT_SYEV_HPP
33#define REMORA_KERNELS_DEFAULT_SYEV_HPP
34
35#include "../../detail/traits.hpp"
36
37namespace remora{ namespace bindings {
38
39template <typename MatA, typename V>
40void eigensort
41(
42 matrix_expression<MatA, cpu_tag>& matA,
43 vector_expression<V, cpu_tag>& eigenValues
44){
45 REMORA_SIZE_CHECK(eigenValues().size() == matA().size1());
46 REMORA_SIZE_CHECK(matA().size1() == matA().size2());
47
48 std::size_t n = eigenValues().size();
49
50 for (std::size_t i = 0; i < n - 1; i++)
51 {
52 std::size_t l = arg_max(subrange(eigenValues,i,n))+i;
53 if (l != i) {
54 //switch position of i's eigenvalue and the largest remaining eigenvalue
55 std::swap(eigenValues()( l ),eigenValues()( i ));
56 //switch postions of corresponding eigenvectors
57 for (std::size_t j = 0; j < n; j++) {
58 std::swap(matA()( j , l ), matA()( j , i ));
59 }
60 }
61 }
62}
63
64template <typename MatA, typename V>
65void syev(
66 matrix_expression<MatA, cpu_tag>& vmatA,
67 vector_expression<V, cpu_tag>& dvecA
68) {
69 REMORA_SIZE_CHECK(vmatA().size1() == vmatA().size2());
70 REMORA_SIZE_CHECK(vmatA().size1() == dvecA().size());
71
72 const std::size_t maxIterC = 50;
73 std::size_t n = vmatA().size1();
74
75 vector<double> odvecA(n,0.0);
76
77 std::size_t j, k, l, m;
78 double b, c, f, g, h, hh, p, r, s, scale;
79
80
81 //
82 // reduction to tridiagonal form
83 //
84 for (std::size_t i = n; i-- > 1;) {
85 h = 0.0;
86 scale = 0.0;
87
88 if (i > 1) {
89 // scale row
90 for (std::size_t k = 0; k < i; k++) {
91 scale += std::fabs(vmatA()(i, k));
92 }
93 }
94
95 if (scale == 0.0) {
96 odvecA(i) = vmatA()(i, i-1);
97 }
98 else {
99 for (k = 0; k < i; k++) {
100 vmatA()(i, k) /= scale;
101 h += vmatA()(i, k) * vmatA()(i, k);
102 }
103
104 f = vmatA()(i, i-1);
105 g = f > 0 ? -std::sqrt(h) : std::sqrt(h);
106 odvecA(i) = scale * g;
107 h -= f * g;
108 vmatA()(i, i-1) = f - g;
109 f = 0.0;
110
111 for (j = 0; j < i; j++) {
112 vmatA()(j, i) = vmatA()(i, j) / (scale * h);
113 g = 0.0;
114
115 // form element of a*u
116 for (k = 0; k <= j; k++) {
117 g += vmatA()(j, k) * vmatA()(i, k);
118 }
119
120 for (k = j + 1; k < i; k++) {
121 g += vmatA()(k, j) * vmatA()(i, k);
122 }
123
124 // form element of p
125 f += (odvecA(j) = g / h) * vmatA()(i, j);
126 }
127
128 hh = f / (h + h);
129
130 // form reduced a
131 for (j = 0; j < i; j++) {
132 f = vmatA()(i, j);
133 g = odvecA(j) - hh * f;
134 odvecA(j) = g;
135
136 for (k = 0; k <= j; k++) {
137 vmatA()(j, k) -= f * odvecA(k) + g * vmatA()(i, k);
138 }
139 }
140
141 for (k = i; k--;) {
142 vmatA()(i, k) *= scale;
143 }
144 }
145
146 dvecA()(i) = h;
147 }
148
149 dvecA()(0) = odvecA(0) = 0.0;
150
151 // accumulation of transformation matrices
152 for (std::size_t i = 0; i < n; i++) {
153 if (dvecA()(i)) {
154 for (j = 0; j < i; j++) {
155 g = 0.0;
156
157 for (k = 0; k < i; k++) {
158 g += vmatA()(i, k) * vmatA()(k, j);
159 }
160
161 for (k = 0; k < i; k++) {
162 vmatA()(k, j) -= g * vmatA()(k, i);
163 }
164 }
165 }
166
167 dvecA()(i) = vmatA()(i, i);
168 vmatA()(i, i) = 1.0;
169
170 for (j = 0; j < i; j++) {
171 vmatA()(i, j) = vmatA()(j, i) = 0.0;
172 }
173 }
174
175 //
176 // eigenvalues from tridiagonal form
177 //
178 if (n <= 1) {
179 return;
180 }
181
182 for (std::size_t i = 1; i < n; i++) {
183 odvecA(i-1) = odvecA(i);
184 }
185
186 odvecA(n-1) = 0.0;
187
188 for (l = 0; l < n; l++) {
189 j = 0;
190
191 do {
192 // look for small sub-diagonal element
193 for (m = l; m < n - 1; m++) {
194 s = std::fabs(dvecA()(m)) + std::fabs(dvecA()(m+1));
195 if (std::fabs(odvecA(m)) + s == s) {
196 break;
197 }
198 }
199
200 p = dvecA()(l);
201
202 if (m != l) {
203 if (j++ == maxIterC)
204 throw std::invalid_argument("too many iterations in eigendecomposition");
205
206 // form shift
207 g = (dvecA()(l+1) - p) / (2.0 * odvecA(l));
208 r = std::sqrt(g * g + 1.0);
209 g = dvecA()(m) - p + odvecA(l) / (g + ((g) > 0 ? std::fabs(r) : -std::fabs(r)));
210 s = c = 1.0;
211 p = 0.0;
212
213 for (std::size_t i = m; i-- > l;) {
214 f = s * odvecA(i);
215 b = c * odvecA(i);
216
217 if (std::fabs(f) >= std::fabs(g)) {
218 c = g / f;
219 r = std::sqrt(c * c + 1.0);
220 odvecA(i+1) = f * r;
221 s = 1.0 / r;
222 c *= s;
223 }
224 else {
225 s = f / g;
226 r = std::sqrt(s * s + 1.0);
227 odvecA(i+1) = g * r;
228 c = 1.0 / r;
229 s *= c;
230 }
231
232 g = dvecA()(i+1) - p;
233 r = (dvecA()(i) - g) * s + 2.0 * c * b;
234 p = s * r;
235 dvecA()(i+1) = g + p;
236 g = c * r - b;
237
238 // form vector
239 for (k = 0; k < n; k++) {
240 f = vmatA()(k, i+1);
241 vmatA()(k, i+1) = s * vmatA()(k, i) + c * f;
242 vmatA()(k, i ) = c * vmatA()(k, i) - s * f;
243 }
244 }
245
246 dvecA()(l) -= p;
247 odvecA(l) = g;
248 odvecA(m) = 0.0;
249 }
250 }
251 while (m != l);
252 }
253
254 //
255 // sorting eigenvalues
256 //
257 eigensort(vmatA, dvecA);
258
259 //
260 // normalizing eigenvectors
261 //
262 for (std::size_t j = n; j--;) {
263 s = 0.0;
264 for (std::size_t i = n; i--;) {
265 s += vmatA()(i, j) * vmatA()(i, j);
266 }
267 s = std::sqrt(s);
268
269 for (std::size_t i = n; i--;) {
270 vmatA()(i, j) /= s;
271 }
272 }
273}
274
275}}
276
277#endif