CppNoddy  0.92
Loading...
Searching...
No Matches
DenseLinearEigenSystem.cpp
Go to the documentation of this file.
1/// \file DenseLinearEigenSystem.cpp
2/// Implementation for the DenseLinearEigenSystem class
3/// This class links to LAPACK to perform the solver phase.
4
5#include <vector>
6#include <set>
7
8#include <FortranLAPACK.h>
9#include <FortranData.h>
11#include <Exceptions.h>
12#include <Types.h>
13
14namespace CppNoddy {
15
16 template <typename _Type>
19 m_pA = Aptr;
20 m_pB = Bptr;
21 }
22
23 template <typename _Type>
25 {}
26
27 template <typename _Type>
29 // reset any tagged eigenvalues
30 m_tagged_indices.clear();
31 //
32 if(m_calc_eigenvectors) {
33 eigensolve_lapack_with_vectors();
34 } else {
35 eigensolve_lapack_without_vectors();
36 }
37 }
38
39
40 template <>
42#ifndef LAPACK
43 std::string problem;
44 problem = "The DenseLinearEigenSystem::eigensolve_lapack_without_vectors method has been called\n";
45 problem += "but the compiler option -DLAPACK was not provided when\n";
46 problem += "the library was built.";
47 throw ExceptionExternal(problem);
48#else
49
50 std::size_t N = m_pA -> nrows();
51 // Cache issues of varying significance plague problems of size 2^j + 2^k + ...
52 // when LDA = N, so this is my shameless 'tweak' to maintain predictable
53 // performance, at least for N <=1024 or so.
54 int padding(0);
55 if((N % 2 == 0) && (N > 127)) {
56 padding = 1;
57 }
58#ifdef PARANOID
59 if((m_pA -> nrows() != m_pB -> nrows()) ||
60 (m_pA -> ncols() != m_pB -> ncols())) {
61 std::string problem("The DenseLinearEigenSystem::eigensolve_lapack_without_vectors method has detected a failure \n");
62 throw ExceptionGeom(problem, m_pA -> nrows(), m_pA -> ncols(),
63 m_pB -> nrows(), m_pB -> ncols());
64 }
65#endif
66 FortranData Af(*m_pA, true, padding);
67 FortranData Bf(*m_pB, true, padding);
68 // eigenvalue storage
69 DenseVector<double> alpha_r(N, 0.0);
70 DenseVector<double> alpha_i(N, 0.0);
71 DenseVector<double> beta(N, 0.0);
72 // eigenvector storage
73 DenseVector<double> vec_left(1, 0.0);
74 DenseVector<double> vec_right(1, 0.0);
75 // some workspace for the LAPACK routine
76 DenseVector<double> work(1, 0.0);
77 int info(0);
78 // Call FORTRAN LAPACK to get the required workspace
79 LAPACK_DGGEV((char*) "N", (char*) "N", N, Af.base(), N + padding, Bf.base(), N + padding, &alpha_r[ 0 ], &alpha_i[ 0 ], &beta[ 0 ], &vec_left[ 0 ], 1, &vec_right[ 0 ], 1, &work[ 0 ], -1, info);
80 int required_workspace = (int) work[ 0 ];
81#ifdef DEBUG
82
83 std::cout << " [DEBUG] DenseLinearEigenSystem::eigensolve_lapack_without_vectors is requesting \n";
84 std::cout << " [DEBUG] a workspace vector of size " << required_workspace << "\n";
85#endif
86
87 work.resize(required_workspace);
88 // call FORTRAN LAPACK again with the optimum workspace
89 LAPACK_DGGEV((char*) "N", (char*) "N", N, Af.base(), N + padding, Bf.base(), N + padding, &alpha_r[ 0 ], &alpha_i[ 0 ], &beta[ 0 ], &vec_left[ 0 ], 1, &vec_right[ 0 ], 1, &work[ 0 ], required_workspace, info);
90 if(0 != info) {
91 std::string problem("The DenseLinearEigenSystem::eigensolve_lapack_without_vectors method has detected a failure. \n");
92 throw ExceptionExternal(problem, info);
93 }
94 // create a complex eigenvalue vector
95 m_eigenvalues_alpha = DenseVector<D_complex>(N, 0.0);
96 for(std::size_t i = 0; i < N; ++i) {
97 const D_complex eye(0.0, 1.0);
98 m_eigenvalues_alpha[ i ] = alpha_r[ i ] + alpha_i[ i ] * eye;
99 }
100 // set the eigenvalue member data
101 m_eigenvalues_beta = beta;
102#endif
103
104 }
105
106 template <>
107 void DenseLinearEigenSystem< std::complex<double> >::eigensolve_lapack_without_vectors() {
108#ifndef LAPACK
109 std::string problem;
110 problem = "The DenseLinearEigenSystem::eigensolve_lapack_without_vectors method has been called\n";
111 problem += "but the compiler option -DLAPACK was not provided when\n";
112 problem += "the library was built.";
113 throw ExceptionExternal(problem);
114#else
115
116 std::size_t N = m_pA -> nrows();
117 // Cache issues of varying significance plague problems of size 2^j + 2^k + ...
118 // when LDA = N, so this is my shameless 'tweak' to maintain predictable
119 // performance, at least for N <=1024 or so.
120 int padding(0);
121 if((N % 2 == 0) && (N > 127)) {
122 padding = 1;
123 }
124#ifdef PARANOID
125 if((m_pA -> nrows() != m_pB -> nrows()) ||
126 (m_pA -> ncols() != m_pB -> ncols())) {
127 std::string problem("The DenseLinearEigenSystem::eigensolve_lapack_without_vectors method has detected a failure. \n");
128 throw ExceptionGeom(problem, m_pA -> nrows(), m_pA -> ncols(),
129 m_pB -> nrows(), m_pB -> ncols());
130 }
131#endif
132 // transpose the input matrices so they are in column_major format
133 FortranData Af(*m_pA, true, padding);
134 FortranData Bf(*m_pB, true, padding);
135 // eigenvalue storage
136 DenseVector<double> alpha(2 * N, 0.0);
137 DenseVector<double> beta(2 * N, 0.0);
138 // eigenvector storage
139 DenseVector<double> vec_left(2, 0.0);
140 DenseVector<double> vec_right(2, 0.0);
141 // new the eigenvector storage
142 // some workspace for the LAPACK routine
143 DenseVector<double> work(2, 0.0);
144 DenseVector<double> rwork(8 * N, 0.0);
145 int info(0);
146 // Call FORTRAN LAPACK to get the required workspace
147 LAPACK_ZGGEV((char*) "N", (char*) "N", N, Af.base(), N + padding, Bf.base(), N + padding, &alpha[ 0 ], &beta[ 0 ], &vec_left[ 0 ], 1, &vec_right[ 0 ], 1, &work[ 0 ], -1, &rwork[ 0 ], info);
148 int required_workspace = int(work[ 0 ]);
149#ifdef DEBUG
150
151 std::cout << "[DEBUG] DenseLinearEigenSystem::eigensolve_lapack_without_vectors is requesting \n";
152 std::cout << "[DEBUG] a workspace vector of size " << required_workspace << "\n";
153#endif
154
155 work.resize(2 * required_workspace);
156 // call FORTRAN LAPACK again with the optimum workspace
157 LAPACK_ZGGEV((char*) "N", (char*) "N", N, Af.base(), N + padding, Bf.base(), N + padding, &alpha[ 0 ], &beta[ 0 ], &vec_left[ 0 ], 1, &vec_right[ 0 ], 1, &work[ 0 ], required_workspace, &rwork[ 0 ], info);
158 // error reporting
159 if(0 != info) {
160 std::string problem("The DenseLinearEigenSystem::eigensolve_lapack_without_vectors method has detected a failure \n");
161 throw ExceptionExternal(problem, info);
162 }
163 // create a complex eigenvalue vector for returning
164 m_eigenvalues_alpha = DenseVector<D_complex>(N, 0.0);
165 m_eigenvalues_beta = DenseVector<D_complex>(N, 0.0);
166 {
167 const D_complex eye(0.0, 1.0);
168 for(std::size_t i = 0; i < N; ++i) {
169 m_eigenvalues_alpha[ i ] = alpha[ 2 * i ] + alpha[ 2 * i + 1 ] * eye;
170 m_eigenvalues_beta[ i ] = beta[ 2 * i ] + beta[ 2 * i + 1 ] * eye;
171 }
172 }
173#endif
174
175 }
176
177 template <>
178 void DenseLinearEigenSystem< double >::eigensolve_lapack_with_vectors() {
179#ifndef LAPACK
180 std::string problem;
181 problem = "The DenseLinearEigenSystem::eigensolve_lapack_with_vectors method has been called\n";
182 problem += "but the compiler option -DLAPACK was not provided when\n";
183 problem += "the library was built.";
184 throw ExceptionExternal(problem);
185#else
186
187 std::size_t N = m_pA -> nrows();
188 // Cache contention issues of varying significance plague problems of size 2^j + 2^k + ...
189 // when LDA = N, so this is my shameless 'tweak' to maintain predictable
190 // performance, at least for N <=1024 or so.
191 int padding(0);
192 if((N % 2 == 0) && (N > 127)) {
193 padding = 1;
194 }
195#ifdef PARANOID
196 if((m_pA -> nrows() != m_pB -> nrows()) ||
197 (m_pA -> ncols() != m_pB -> ncols())) {
198 std::string problem("The DenseLinearEigenSystem::eigensolve_lapack_with_vectors method has detected a failure. \n");
199 throw ExceptionGeom(problem, m_pA -> nrows(), m_pA -> ncols(),
200 m_pB -> nrows(), m_pB -> ncols());
201 }
202#endif
203 // Convert to fortran data incl. a transpose of the
204 // input matrices so they are in column_major format then include padding
205 FortranData Af(*m_pA, true, padding);
206 FortranData Bf(*m_pB, true, padding);
207 // eigenvalue storage
208 DenseVector<double> alpha_r(N, 0.0);
209 DenseVector<double> alpha_i(N, 0.0);
210 DenseVector<double> beta(N, 0.0);
211 // new the right eigenvector storage
212 DenseVector<double> vec_left(1, 0.0);
213 DenseVector<double> vec_right(N * N, 0.0);
214 // some workspace for the LAPACK routine
215 DenseVector<double> work(1, 0.0);
216 // return integer for LAPACK
217 int info(0);
218 // Call FORTRAN LAPACK to get the required workspace
219 LAPACK_DGGEV((char*) "N", (char*) "V", N, Af.base(), N + padding, Bf.base(), N + padding, &alpha_r[ 0 ], &alpha_i[ 0 ], &beta[ 0 ], &vec_left[ 0 ], 1, &vec_right[ 0 ], N, &work[ 0 ], -1, info);
220 int required_workspace = 4 * int(work[ 0 ]);
221#ifdef DEBUG
222
223 std::cout << "[DEBUG] DenseLinearEigenSystem::eigensolve_lapack_with_vectors is requesting \n";
224 std::cout << "[DEBUG] a workspace vector of size " << required_workspace << "\n";
225#endif
226
227 work.resize(required_workspace);
228 // call FORTRAN LAPACK again with the optimum workspace
229 LAPACK_DGGEV((char*) "N", (char*) "V", N, Af.base(), N + padding, Bf.base(), N + padding, &alpha_r[ 0 ], &alpha_i[ 0 ], &beta[ 0 ], &vec_left[ 0 ], 1, &vec_right[ 0 ], N, &work[ 0 ], required_workspace, info);
230 if(0 != info) {
231 std::string problem("The DenseLinearEigenSystem::eigensolve_lapack_with_vectors method has detected a failure.\n");
232 throw ExceptionExternal(problem, info);
233 }
234 // create a complex eigenvalue vector
235 m_eigenvalues_alpha = DenseVector<D_complex>(N, 0.0);
236 // complex eigenvector matrix
237 m_all_eigenvectors = DenseMatrix<D_complex>(N, N, 0.0);
238 // step through the eigenvalues
239 for(std::size_t i = 0; i < N; ++i) {
240 const D_complex eye(0.0, 1.0);
241 // make the complex vector of alpha
242 m_eigenvalues_alpha[ i ] = alpha_r[ i ] + alpha_i[ i ] * eye;
243 if(std::abs(alpha_i[ i ]) > 0.0) {
244 // eigenvector is complex
245 for(std::size_t k = 0; k < N; ++k) {
246 m_all_eigenvectors[ i ][ k ] = vec_right[ i * N + k ] + eye * vec_right[(i + 1) * N + k ];
247 // store the conjugate too for completeness
248 m_all_eigenvectors[ i + 1 ][ k ] = vec_right[ i * N + k ] - eye * vec_right[(i + 1) * N + k ];
249 }
250 ++i;
251 } else { // eigenvector is real
252 for(std::size_t k = 0; k < N; ++k) {
253 m_all_eigenvectors(i, k) = vec_right[ i * N + k ];
254 }
255 }
256 }
257 // set the eigenvalue member data
258 m_eigenvalues_beta = beta;
259#endif
260
261 }
262
263 template <>
264 void DenseLinearEigenSystem< std::complex<double> >::eigensolve_lapack_with_vectors() {
265#ifndef LAPACK
266 std::string problem;
267 problem = "The DenseLinearEigenSystem::eigensolve_lapack_with_vectors method has been called\n";
268 problem += "but the compiler option -DLAPACK was not provided when\n";
269 problem += "the library was built.";
270 throw ExceptionExternal(problem);
271#else
272
273 std::size_t N = m_pA -> nrows();
274 // Cache issues of varying significance plague problems of size 2^j + 2^k + ...
275 // when LDA = N, so this is my shameless 'tweak' to maintain predictable
276 // performance, at least for N <=1024 or so.
277 int padding(0);
278 if((N % 2 == 0) && (N > 127)) {
279 padding = 1;
280 }
281#ifdef PARANOID
282 if((m_pA -> nrows() != m_pB -> nrows()) ||
283 (m_pA -> ncols() != m_pB -> ncols())) {
284 std::string problem("The DenseLinearEigenSystem::eigensolve_lapack_with_vectors method has detected a failure. \n");
285 throw ExceptionGeom(problem, m_pA -> nrows(), m_pA -> ncols(),
286 m_pB -> nrows(), m_pB -> ncols());
287 }
288#endif
289 // transpose the input matrices so they are in column_major format
290 FortranData Af(*m_pA, true, padding);
291 FortranData Bf(*m_pB, true, padding);
292 // eigenvalue storage
293 DenseVector<double> alpha(2 * N, 0.0);
294 DenseVector<double> beta(2 * N, 0.0);
295 // eigenvector storage
296 DenseVector<double> vec_left(2, 0.0);
297 DenseVector<double> vec_right(2 * N * N, 0.0);
298 // some workspace for the LAPACK routine
299 DenseVector<double> work(2, 0.0);
300 DenseVector<double> rwork(8 * N, 0.0);
301 int info(0);
302 // Call FORTRAN LAPACK to get the required workspace
303 LAPACK_ZGGEV((char*) "N", (char*) "V", N, Af.base(), N + padding, Bf.base(), N + padding, &alpha[ 0 ], &beta[ 0 ], &vec_left[ 0 ], 1, &vec_right[ 0 ], N, &work[ 0 ], -1, &rwork[ 0 ], info);
304 int required_workspace = int(work[ 0 ]);
305#ifdef DEBUG
306
307 std::cout << "[DEBUG] DenseLinearEigenSystem::eigensolve_lapack_with_vectors is requesting \n";
308 std::cout << "[DEBUG] a workspace vector of size " << required_workspace << "\n";
309#endif
310
311 work.resize(2 * required_workspace);
312 // call FORTRAN LAPACK again with the optimum workspace
313 LAPACK_ZGGEV((char*) "N", (char*) "V", N, Af.base(), N + padding, Bf.base(), N + padding, &alpha[ 0 ], &beta[ 0 ], &vec_left[ 0 ], 1, &vec_right[ 0 ], N, &work[ 0 ], required_workspace, &rwork[ 0 ], info);
314 if(0 != info) {
315 std::string problem("The DenseLinearEigenSystem::eigensolve_lapack_with_vectors method has detected a failure.\n");
316 throw ExceptionExternal(problem, info);
317 }
318 // create a complex eigenvalue vector
319 m_eigenvalues_alpha = DenseVector<D_complex>(N, 0.0);
320 m_eigenvalues_beta = DenseVector<D_complex>(N, 0.0);
321 // complex eigenvector matrix
322 m_all_eigenvectors = DenseMatrix<D_complex>(N, N, 0.0);
323 // step through the eigenvalues
324 for(std::size_t i = 0; i < N; ++i) {
325 const D_complex eye(0.0, 1.0);
326 m_eigenvalues_alpha[ i ] = alpha[ 2 * i ] + alpha[ 2 * i + 1 ] * eye;
327 m_eigenvalues_beta[ i ] = beta[ 2 * i ] + beta[ 2 * i + 1 ] * eye;
328 for(std::size_t j = 0; j < N; ++j) {
329 m_all_eigenvectors(i, j) = vec_right[ 2 * i * N + 2 * j ] + vec_right[ 2 * i * N + 2 * j + 1 ] * eye;
330 }
331 }
332#endif
333
334 }
335
336 template <typename _Type>
338 if(m_tagged_indices.size() == 0) {
339 std::string problem;
340 problem = "In DenseLinearEigenSystem.get_tagged_eigenvalues() : there are\n";
341 problem += "no eigenvalues that have been tagged. This set is empty.\n";
342 throw ExceptionRuntime(problem);
343 }
344 // storage for the eigenvalues
346 // loop through the tagged set
347 for(iter p = m_tagged_indices.begin(); p != m_tagged_indices.end(); ++p) {
348 // get the index of the relevant eigenvalue from the set
349 std::size_t j = *p;
350 // work out the complex eigenvalue associated with this index
351 // and add it to the vector
352 evals.push_back(m_eigenvalues_alpha[ j ] / m_eigenvalues_beta[ j ]);
353 }
354 // return the complex vector of eigenvalues
355 return evals;
356 }
357
358 template <typename _Type>
360 if(m_tagged_indices.size() == 0) {
361 std::string problem;
362 problem = "In DenseLinearEigenSystem.get_tagged_eigenvectors() : there are\n";
363 problem += "no eigenvalues that have been tagged. This set is empty.\n";
364 throw ExceptionRuntime(problem);
365 }
366 // order of the problem
367 std::size_t N = m_eigenvalues_alpha.size();
368 // eigenvector storage : size() eigenvectors each of length N
369 DenseMatrix<D_complex> evecs(m_tagged_indices.size(), N, 0.0);
370 std::size_t row = 0;
371 // loop through the tagged set
372 for(iter p = m_tagged_indices.begin(); p != m_tagged_indices.end(); ++p) {
373 // get the index of the relevant eigenvalue from the set
374 std::size_t j = *p;
375 // put the eigenvector in the matrix
376 evecs[ row ] = m_all_eigenvectors[ j ];
377 // next row/eigenvector
378 ++row;
379 }
380 return evecs;
381 }
382
383 // EIGENVALUE/VECTOR TAGGING
384
385 template <typename _Type>
386 void DenseLinearEigenSystem<_Type>::tag_eigenvalues_disc(const int &val, const double& radius) {
387 // loop through all the eigenvalues
388 for(std::size_t i = 0; i < m_eigenvalues_alpha.size(); ++i) {
389 // if the eigenvalue is in the disc centred at m_shift then include it
390 if(std::abs(m_eigenvalues_alpha[ i ] - m_shift * m_eigenvalues_beta[ i ]) <
391 std::abs(radius * m_eigenvalues_beta[ i ])) {
392 if(val > 0) {
393 // add it to our set of tagged eigenvalues
394 m_tagged_indices.insert(m_tagged_indices.end(), i);
395 } else {
396 // remove it from the set if it exists
397 m_tagged_indices.erase(i);
398 }
399 }
400 }
401 }
402
403 template <typename _Type>
405 double real_value = m_shift.real();
406 // loop through all the eigenvalues
407 for(std::size_t i = 0; i < m_eigenvalues_alpha.size(); ++i) {
408 // if the eigenvalue is to the right of the shift position
409 if((m_eigenvalues_alpha[ i ] * std::conj(m_eigenvalues_beta[ i ])).real() >
410 std::pow(std::abs(m_eigenvalues_beta[ i ]), 2) * real_value) {
411 if(val > 0) {
412 // add it to our set of tagged eigenvalues
413 m_tagged_indices.insert(m_tagged_indices.end(), i);
414 } else {
415 // remove it from the set if it exists
416 m_tagged_indices.erase(i);
417 }
418 }
419 }
420 }
421
422 template <typename _Type>
424 double real_value = m_shift.real();
425 // loop through all the eigenvalues
426 for(std::size_t i = 0; i < m_eigenvalues_alpha.size(); ++i) {
427 // if the eigenvalue is to the left of the shift position
428 if((m_eigenvalues_alpha[ i ] * std::conj(m_eigenvalues_beta[ i ])).real() <
429 std::pow(std::abs(m_eigenvalues_beta[ i ]), 2) * real_value) {
430 if(val > 0) {
431 // add it to our set of tagged eigenvalues
432 m_tagged_indices.insert(m_tagged_indices.end(), i);
433 } else {
434 // remove it from the set if it exists
435 m_tagged_indices.erase(i);
436 }
437 }
438 }
439 }
440
441 template <typename _Type>
443 double imag_value = m_shift.imag();
444 // loop through all the eigenvalues
445 for(std::size_t i = 0; i < m_eigenvalues_alpha.size(); ++i) {
446 // if the eigenvalue is in the half plane then include it
447 if((m_eigenvalues_alpha[ i ] * std::conj(m_eigenvalues_beta[ i ])).imag() >
448 std::pow(std::abs(m_eigenvalues_beta[ i ]), 2) * imag_value) {
449 if(val > 0) {
450 // add it to our set of tagged eigenvalues
451 m_tagged_indices.insert(m_tagged_indices.end(), i);
452 } else {
453 // remove it from the set if it exists
454 m_tagged_indices.erase(i);
455 }
456 }
457 }
458 }
459
460 template <typename _Type>
462 double imag_value = m_shift.imag();
463 // loop through all the eigenvalues
464 for(std::size_t i = 0; i < m_eigenvalues_alpha.size(); ++i) {
465 // if the eigenvalue is in the half plane then include it
466 if((m_eigenvalues_alpha[ i ] * std::conj(m_eigenvalues_beta[ i ])).imag() <
467 std::pow(std::abs(m_eigenvalues_beta[ i ]), 2) * imag_value) {
468 if(val > 0) {
469 // add it to our set of tagged eigenvalues
470 m_tagged_indices.insert(m_tagged_indices.end(), i);
471 } else {
472 // remove it from the set if it exists
473 m_tagged_indices.erase(i);
474 }
475 }
476 }
477 }
478
479
481 ;
482 template class DenseLinearEigenSystem<double>
483 ;
484
485} // end namespace
Specification of the dense linear eigensystem class.
The collection of CppNoddy exceptions.
Some interface definitions for calling external fortran routines in LAPACK.
A linear Nth-order generalised eigensystem class.
void tag_eigenvalues_right(const int &val)
Tag those eigenvalues that are to the right of a specified point.
void tag_eigenvalues_disc(const int &val, const double &radius)
Tag those eigenvalues that are within a disc centred at a point in the complex plane.
DenseVector< D_complex > get_tagged_eigenvalues() const
Get the the tagged eigenvalues.
void eigensolve()
Solve the matrix linear eigensystem.
void tag_eigenvalues_lower(const int &val)
Tag those eigenvalues that are in the lower half-plane below a specified point.
void tag_eigenvalues_left(const int &val)
Tag those eigenvalues that are to the left of a specified point.
DenseLinearEigenSystem(DenseMatrix< _Type > *Aptr, DenseMatrix< _Type > *Bptr)
Constructor for a linear system object.
void tag_eigenvalues_upper(const int &val)
Tag those eigenvalues that are in the upper half-plane above a specified point.
DenseMatrix< D_complex > get_tagged_eigenvectors() const
Get the the tagged eigenvectors.
~DenseLinearEigenSystem()
Destructor for a linear system object.
A matrix class that constructs a DENSE matrix as a row major std::vector of DenseVectors.
Definition: DenseMatrix.h:25
An DenseVector class – a dense vector object.
Definition: DenseVector.h:34
void push_back(const _Type &fill)
A pass-thru definition of push_back.
Definition: DenseVector.h:310
An exception to indicate that an error has been detected in an external (LAPACK) routine.
Definition: Exceptions.h:20
A generic runtime exception.
Definition: Exceptions.h:158
A linear Nth-order generalised eigensystem base class.
std::set< unsigned, std::less< unsigned > >::iterator iter
const D_complex eye(0., 1.0)
A collection of OO numerical routines aimed at simple (typical) applied problems in continuum mechani...
std::complex< double > D_complex
A complex double precision number using std::complex.
Definition: Types.h:98

© 2012

R.E. Hewitt