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 
37 namespace remora{ namespace bindings {
38 
39 template <typename MatA, typename V>
40 void 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 
64 template <typename MatA, typename V>
65 void 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 }}
278 
279 #endif