SIRF  3.4.0
JacobiCG.h
1 /*
2 SyneRBI Synergistic Image Reconstruction Framework (SIRF)
3 Copyright 2020 Rutherford Appleton Laboratory STFC
4 
5 This is software developed for the Collaborative Computational
6 Project in Synergistic Reconstruction for Biomedical Imaging (formerly CCP PETMR)
7 (http://www.ccpsynerbi.ac.uk/).
8 
9 Licensed under the Apache License, Version 2.0 (the "License");
10 you may not use this file except in compliance with the License.
11 You may obtain a copy of the License at
12 http://www.apache.org/licenses/LICENSE-2.0
13 Unless required by applicable law or agreed to in writing, software
14 distributed under the License is distributed on an "AS IS" BASIS,
15 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16 See the License for the specific language governing permissions and
17 limitations under the License.
18 
19 */
20 
21 #pragma once
22 
23 #include <cmath>
24 #include <complex>
25 #include <iostream>
26 #include <memory>
27 
28 #include "sirf/common/Operator.h"
29 
30 /* A simplified implementation of Jacobi-Conjugated Gradients method
31  for computing extreme eigenvalues of real symmetric and Hermitian
32  operators by E. Ovtchinnikov [SIAM Numer. Anal. 46:2567-2619, 2008].
33 */
34 
35 namespace sirf {
36  template<class value_type>
37  class JacobiCG {
38  public:
39  /* This simplified version does fixed number of iterations. */
40  JacobiCG() : nit_(10) {}
41  void set_num_iterations(int nit)
42  {
43  nit_ = nit;
44  }
45 
46  /* Computes the largest eigenvalue of a positive semi-definite operator */
47  template<class vector_type>
48  float largest(Operator<vector_type>& A,
49  vector_type& x /*non-zero initial guess*/, int verb=0)
50  {
51  value_type lmd;
52  value_type a[] = { 0, 0, 0, 0 };
53  value_type mu[2];
54  value_type u[2];
55  value_type v[2];
56  value_type zero = 0;
57  value_type t;
58 
59  std::unique_ptr<vector_type> uptr_y = x.clone();
60  vector_type& y = *uptr_y;
61  std::unique_ptr<vector_type> uptr_z = x.clone();
62  vector_type& z = *uptr_z;
63  std::unique_ptr<vector_type> uptr_w = x.clone();
64  vector_type& w = *uptr_w;
65  std::unique_ptr<vector_type> uptr_Az = x.clone();
66  vector_type& Az = *uptr_Az;
67 
68  float s = sqrt(abs(x.dot(x)));
69  x.scale(s);
70  std::shared_ptr<vector_type> sptr_Ax = A(x);
71  vector_type& Ax = *sptr_Ax;
72 
73  for (int it = 0; it < nit_; it++) {
74  lmd = Ax.dot(x);
75  y.axpby(1.0, Ax, -lmd, x); // residual
76  if (verb > 0)
77  std::cout << "CG iteration " << it
78  << ": largest eigenvalue " << std::real(lmd) << '\n';
79  if (it) { // conjugate y to the previous search direction
80  t = Az.dot(z);
81  w.axpby(1.0, Az, -t, z);
82  t = w.dot(y) / (lmd - t);
83  y.axpby(1.0, y, t, z);
84  }
85  // normalize y
86  s = sqrt(abs(y.dot(y)));
87  if (s == 0.0)
88  break; // converged
89  y.scale(s);
90  // orthogonalize y to x
91  t = y.dot(x);
92  y.axpby(1.0, y, -t, x);
93  // normalize y again
94  s = sqrt(abs(y.dot(y)));
95  if (s == 0.0)
96  break; // converged
97  y.scale(s);
98  // perform Rayleigh-Ritz procedure in span{x,y}
99  std::shared_ptr<vector_type> sptr_Ay = A(y);
100  vector_type& Ay = *sptr_Ay;
101  a[0] = lmd;
102  a[1] = Ay.dot(x);
103  a[2] = x.dot(Ay);
104  a[3] = Ay.dot(y);
105  // compute eigenvalues and eigenvectors of 2x2 matrix a
106  eigh2_(a, mu, u, v);
107  z.axpby(u[0], x, u[1], y);
108  x.axpby(v[0], x, v[1], y);
109  // span{x,y} = span{x,z} => the new x is a linear combination
110  // of the old x and z => we use z as previous search direction
111  // on the next iteration
112  Az.axpby(u[0], Ax, u[1], Ay);
113  Ax.axpby(v[0], Ax, v[1], Ay);
114  lmd = mu[1];
115  s = sqrt(abs(x.dot(x)));
116  x.scale(s);
117  Ax.scale(s);
118  }
119  return std::real(lmd);
120  }
121  private:
122  int nit_;
123  /* computes eigenvalues and eigenvectors of a 2x2 real symmetric
124  or Hermitian matrix [ [a[0], a[1]], [a[2], a[3]] ]
125  */
126  void eigh2_(const value_type a[4], value_type lmd[2], value_type x[2],
127  value_type y[2]) const
128  {
129  value_type zero = 0;
130  value_type one = 1;
131  value_type c = abs(a[1]);
132  value_type d = abs(a[0] - a[3]);
133  d = d * d + (c + c) * (c + c);
134  value_type s = sqrt(d);
135  value_type t = a[0] + a[3];
136  if (s == zero && t == zero) {
137  lmd[0] = 0.0;
138  lmd[1] = 0.0;
139  x[0] = 1.0;
140  x[1] = 0.0;
141  x[2] = 0.0;
142  x[3] = 1.0;
143  return;
144  }
145  lmd[0] = (t - s) / (one + one);
146  lmd[1] = (t + s) / (one + one);
147  value_type p = a[1];
148  value_type q = lmd[0] - a[0];
149  value_type pc = (p == zero ? 0 : abs(p*p) / p);
150  value_type qc = (q == zero ? 0 : abs(q*q) / q);
151  x[0] = p;
152  x[1] = q;
153  s = sqrt(abs(pc*x[0] + qc*x[1]));
154  x[0] /= s;
155  x[1] /= s;
156  y[0] = -qc / s;
157  y[1] = pc / s;
158  return;
159  }
160  };
161 }
Definition: Operator.h:7
Definition: JacobiCG.h:37
Abstract data container.
Definition: GeometricalInfo.cpp:141