Tomographer  v1.0a
Tomographer C++ Framework Documentation
solveclyap.h
Go to the documentation of this file.
1 /* This file is part of the Tomographer project, which is distributed under the
2  * terms of the MIT license.
3  *
4  * The MIT License (MIT)
5  *
6  * Copyright (c) 2015 ETH Zurich, Institute for Theoretical Physics, Philippe Faist
7  *
8  * Permission is hereby granted, free of charge, to any person obtaining a copy
9  * of this software and associated documentation files (the "Software"), to deal
10  * in the Software without restriction, including without limitation the rights
11  * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
12  * copies of the Software, and to permit persons to whom the Software is
13  * furnished to do so, subject to the following conditions:
14  *
15  * The above copyright notice and this permission notice shall be included in
16  * all copies or substantial portions of the Software.
17  *
18  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
19  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
20  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
21  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
22  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
23  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
24  * SOFTWARE.
25  */
26 
27 #ifndef SOLVECLYAP_H
28 #define SOLVECLYAP_H
29 
37 #include <string>
38 
39 #include <Eigen/Core>
40 #include <Eigen/Eigenvalues>
41 
42 
43 extern "C" void ztrsyl_(char *TRANA, char *TRANB, int *ISGN, int *M, int *N,
44  double *A, int *lda, double *B, int *ldb,
45  double *C, int *ldc, double *scale, int *info);
46 
47 namespace Tomographer
48 {
53 namespace SolveCLyap
54 {
55 
60 class SolveError : public std::exception {
61  std::string p_msg;
62 public:
63  SolveError(const std::string& msg)
64  : p_msg(msg)
65  {
66  }
67  virtual ~SolveError() throw() { }
68  virtual const char * what() const throw() {
69  return p_msg.c_str();
70  }
71 };
72 
73 namespace tomo_internal {
74 
75  static const std::string ztrsyl_argnames[] = {
76  "TRANA", "TRANB", "ISGN", "M", "N", "A", "lda", "B", "ldb", "C", "ldc", "scale", "info"
77  };
78 
79 
80  template<bool debug_perform_check>
81  struct solve_check_helper {
82  template<typename XT, typename AT, typename CT, typename Log>
83  static inline void check(const XT&, const AT&, const CT&, Log&)
84  {
85  // no-op.
86  }
87  };
88  // specialize for if we want debug checks
89  template<>
90  struct solve_check_helper<true> {
91  template<typename XT, typename AT, typename CT, typename Log>
92  static inline void check(const XT& X, const AT& A, const CT& C, Log & logger)
93  {
94  //logger.debug("LyapCSolve::solve/check", "checking solution ...");
95 
96  double n1 = (A.adjoint() * X + X * A - C).norm();
97  double n2 = A.norm() + C.norm();
98  double ok = n1 / n2;
99  logger.debug("LyapCSolve::solve/check",
100  "(A.adjoint() * X + X * A - C).norm()/(A.norm()+C.norm()) == %g/%g == %g ; norm(C)=%g",
101  n1, n2, ok, (double)C.norm());
102  if (ok > 0.1) {
103  logger.warning("LyapCSolve::solve/check", "Bad solution quality! rel norm error = %g", (double)ok);
104  }
105  }
106  };
107 
108 } // namespace tomo_internal
109 
110 
124 template<bool debug_perform_check = false, typename Log, typename DerX, typename DerA, typename DerC>
126  const Eigen::MatrixBase<DerC> & C, Log & logger, const double tol = 1e-8)
127 {
128  // do an eigenvalue decomposition of A
129 
130  const int d = A.rows(); // dimension of problem
131 
133 
134  if (eigA.info() == Eigen::NoConvergence) {
135  throw SolveError("Can't diagonalize matrix A: No Convergence");
136  }
137 
138  const typename Eigen::SelfAdjointEigenSolver<DerA>::RealVectorType& eigvalsA = eigA.eigenvalues();
140 
141  int M = 0;
142  for (int k = 0; k < d; ++k) {
143  if (eigvalsA(k) > tol) {
144  ++M;
145  }
146  }
147 
148  // cout << "there are M="<< M << " nonzero eigenvalues\n";
149 
151 
152  MatType D = MatType::Zero(M, M);
153  MatType W = MatType::Zero(d, M);
154 
155  D.setZero();
156 
157  int counter = 0;
158  for (int k = 0; k < d; ++k) {
159  if (eigvalsA(k) < tol) {
160  continue;
161  }
162  // include this eigenvector
163  W.col(counter) = eigU.col(k);
164  D(counter, counter) = eigvalsA(k);
165  ++counter;
166  }
167 
168  // cout << "W = \n" << W << "\nD = \n"<< D << "\n";
169 
170  // we have that A = W * D * W^{-1} , where D is full rank (Yes!)
171 
172  // Original equation: A^H*X + X*A == C (with A^H==A)
173  // replace eig of A: (W*D*W^{-1})*X + X*(W*D*W^{-1}) == C
174  // apply W^{-1} . W : D*(W^{-1}*X*W) + (W^{-1}*X*W)*D == W^{-1}*C*W
175  //
176  // we can now solve the Sylvester equation with ZTRSYL, for W^{-1}*X*W =: Z
177 
179 
180  Z = W.adjoint() * C * W;
181 
182  int plusone = +1;
183 
184  int info = 0;
185  double scale = 0.0;
186 
187  int lda = D.outerStride();
188  int ldb = lda;//D.outerStride();
189  int ldc = Z.outerStride();
190 
191  ztrsyl_((char*)"C", (char*)"N", &plusone, &M, &M, (double*)D.data(), &lda, (double*)D.data(), &ldb,
192  (double*)Z.data(), &ldc, &scale, &info);
193 
194  if (info >= 0) {
195  if (info == 1) {
196  logger.warning("SolveCLyap::solve()", "Warning: A and B have common or very close eigenvalues; "
197  "perturbed values were used to solve the equation");
198  }
199  // success
200  X.noalias() = W * Z * W.adjoint() / scale;
201 
202  // maybe do some debug consistency check
203  tomo_internal::solve_check_helper<debug_perform_check>::check(X, A, C, logger);
204  return;
205  }
206 
207  // error with argument # (-info)
208  throw SolveError("Argument " + tomo_internal::ztrsyl_argnames[(-info)-1] + " to ztrsyl_ was invalid.");
209 }
210 
211 } // namespace SolveCLyap
212 } // namespace Tomographer
213 
214 #endif
ComputationInfo info() const
const Scalar * data() const
Base namespace for the Tomographer project.
Definition: dmmhrw.h:51
Error while attempting to solve complex Lyapunov/Sylvester equation.
Definition: solveclyap.h:60
const RealVectorType & eigenvalues() const
void solve(Eigen::MatrixBase< DerX > &X, const Eigen::MatrixBase< DerA > &A, const Eigen::MatrixBase< DerC > &C, Log &logger, const double tol=1e-8)
Solve complex Lyapunov equation of the form A'*X + X*A == C
Definition: solveclyap.h:125
NoAlias< Derived, Eigen::MatrixBase > noalias()
STL class.
STL class.
internal::plain_col_type< MatrixType, RealScalar >::type RealVectorType
const MatrixType & eigenvectors() const
T c_str(T...args)
NoConvergence