ARPACK-Armadillo
UpperHessenbergEigen.h
1 // Copyright (C) 2015 Yixuan Qiu
2 //
3 // This Source Code Form is subject to the terms of the Mozilla Public
4 // License, v. 2.0. If a copy of the MPL was not distributed with this
5 // file, You can obtain one at http://mozilla.org/MPL/2.0/.
6 
7 #ifndef UPPER_HESSENBERG_EIGEN_H
8 #define UPPER_HESSENBERG_EIGEN_H
9 
10 #include <armadillo>
11 #include <cmath>
12 #include <complex>
13 #include <stdexcept>
14 #include "LapackWrapperExtra.h"
15 
26 template <typename Scalar = double>
28 {
29 private:
30  typedef arma::Mat<Scalar> Matrix;
31  typedef arma::Col<Scalar> Vector;
32  typedef arma::Row<Scalar> RowVector;
33 
34  typedef std::complex<Scalar> Complex;
35  typedef arma::Mat<Complex> ComplexMatrix;
36  typedef arma::Col<Complex> ComplexVector;
37 
38  int n;
39  Matrix mat_Z; // In the first stage, H = ZTZ', Z is an orthogonal matrix
40  // In the second stage, Z will be overwritten by the eigenvectors of H
41  Matrix mat_T; // H = ZTZ', T is a Schur form matrix
42  ComplexVector evals; // eigenvalues of H
43 
44  bool computed;
45 
46  static bool is_real(Complex v, Scalar eps)
47  {
48  return std::abs(v.imag()) <= eps;
49  }
50 
51 public:
57  n(0), computed(false)
58  {}
59 
69  UpperHessenbergEigen(const Matrix &mat) :
70  n(mat.n_rows), computed(false)
71  {
72  compute(mat);
73  }
74 
83  void compute(const Matrix &mat)
84  {
85  if(!mat.is_square())
86  throw std::invalid_argument("UpperHessenbergEigen: matrix must be square");
87 
88  n = mat.n_rows;
89  mat_Z.set_size(n, n);
90  mat_T.set_size(n, n);
91  evals.set_size(n);
92 
93  mat_Z.eye();
94  mat_T = mat;
95 
96  int want_T = 1, want_Z = 1;
97  int ilo = 1, ihi = n, iloz = 1, ihiz = n;
98  Scalar *wr = new Scalar[n];
99  Scalar *wi = new Scalar[n];
100  int info;
101  arma::lapack::lahqr(&want_T, &want_Z, &n, &ilo, &ihi,
102  mat_T.memptr(), &n, wr, wi, &iloz, &ihiz,
103  mat_Z.memptr(), &n, &info);
104 
105  for(int i = 0; i < n; i++)
106  {
107  evals[i] = Complex(wr[i], wi[i]);
108  }
109  delete [] wr;
110  delete [] wi;
111 
112  if(info < 0)
113  throw std::logic_error("Lapack lahqr: failed to compute all the eigenvalues");
114 
115  char side = 'R', howmny = 'B';
116  Scalar *work = new Scalar[3 * n];
117  int m;
118 
119  arma::lapack::trevc(&side, &howmny, (int*) NULL, &n, mat_T.memptr(), &n,
120  (Scalar*) NULL, &n, mat_Z.memptr(), &n, &n, &m, work, &info);
121  delete [] work;
122 
123  if(info < 0)
124  throw std::invalid_argument("Lapack trevc: illegal value");
125 
126  computed = true;
127  }
128 
135  ComplexVector eigenvalues()
136  {
137  if(!computed)
138  throw std::logic_error("UpperHessenbergEigen: need to call compute() first");
139 
140  return evals;
141  }
142 
149  ComplexMatrix eigenvectors()
150  {
151  if(!computed)
152  throw std::logic_error("UpperHessenbergEigen: need to call compute() first");
153 
154  Scalar prec = std::pow(std::numeric_limits<Scalar>::epsilon(), Scalar(2.0) / 3);
155  ComplexMatrix evecs(n, n);
156  Complex *col_ptr = evecs.memptr();
157  for(int i = 0; i < n; i++)
158  {
159  if(is_real(evals[i], prec))
160  {
161  // For real eigenvector, normalize and copy
162  Scalar z_norm = arma::norm(mat_Z.col(i));
163  for(int j = 0; j < n; j++)
164  {
165  col_ptr[j] = Complex(mat_Z(j, i) / z_norm, 0);
166  }
167 
168  col_ptr += n;
169  } else {
170  // Complex eigenvectors are stored in consecutive columns
171  Scalar r2 = arma::dot(mat_Z.col(i), mat_Z.col(i));
172  Scalar i2 = arma::dot(mat_Z.col(i + 1), mat_Z.col(i + 1));
173  Scalar z_norm = std::sqrt(r2 + i2);
174  Scalar *z_ptr = mat_Z.colptr(i);
175  for(int j = 0; j < n; j++)
176  {
177  col_ptr[j] = Complex(z_ptr[j] / z_norm, z_ptr[j + n] / z_norm);
178  col_ptr[j + n] = std::conj(col_ptr[j]);
179  }
180 
181  i++;
182  col_ptr += 2 * n;
183  }
184  }
185 
186  return evecs;
187  }
188 };
189 
190 
191 
192 #endif // UPPER_HESSENBERG_EIGEN_H
UpperHessenbergEigen(const Matrix &mat)
void compute(const Matrix &mat)
ComplexMatrix eigenvectors()