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