Free Electron
ConjugateGradient2.h
Go to the documentation of this file.
1 /* Copyright (C) 2003-2021 Free Electron Organization
2  Any use of this software requires a license. If a valid license
3  was not distributed with this file, visit freeelectron.org. */
4 
5 /** @file */
6 
7 #ifndef __solve_ConjugateGradient2_h__
8 #define __solve_ConjugateGradient2_h__
9 
10 #define CG_NORMALIZE TRUE
11 #define CG_DEBUG TRUE
12 #define CG_TRACE TRUE
13 
14 #define CG_CHECK_SYMMETRY (FE_CODEGEN<=FE_DEBUG)
15 #define CG_CHECK_POSITIVE (FE_CODEGEN<=FE_DEBUG)
16 #define CG_CHECK_PEAK (FE_CODEGEN<=FE_DEBUG)
17 #define CG_VERIFY (FE_CODEGEN<=FE_DEBUG)
18 
19 namespace fe
20 {
21 namespace ext
22 {
23 
24 /**************************************************************************//**
25  @brief solve Ax=b for x
26 
27  @ingroup solve
28 
29  Uses Conjugate-Gradient. The matrix must be positive-definite
30  and symmetric.
31 
32  The arguments are templated, so any argument types should work,
33  given that they have the appropriate methods and operators.
34 
35  TODO try seeding with previous x instead of clearing to zero.
36 *//***************************************************************************/
37 template <typename MATRIX, typename VECTOR>
39 {
40  public:
41  /// @brief Choice to alter matrix during solve
43  {
44  e_none,
45  e_diagonal_A
46  };
47 
48  ConjugateGradient2(void):
49  m_threshold(1e-6f),
50  m_preconditioning(e_none) {}
51 
52  void solve(VECTOR& x, const MATRIX& A, const VECTOR& b);
53 
54  void setThreshold(F64 threshold) { m_threshold=threshold; }
55 
56  void setPreconditioning(Preconditioning preconditioning)
57  { m_preconditioning=preconditioning; }
58 
59  private:
60  VECTOR r,r_1,r_2; //* residual (at k, k-1, k-2)
61  VECTOR p,p_1; //* direction
62  VECTOR temp; //* persistent temporary
63  VECTOR Ap;
64  VECTOR y;
65  VECTOR m_AT_b;
66  VECTOR m_preconditionedVector;
67  MATRIX m_tempMatrix;
68  MATRIX m_AT;
69  MATRIX m_AT_A;
70  MATRIX m_preconditionedMatrix;
71  F64 m_threshold;
72  Preconditioning m_preconditioning;
73 };
74 
75 template <typename MATRIX, typename VECTOR>
76 inline void ConjugateGradient2<MATRIX,VECTOR>::solve(VECTOR& x,
77  const MATRIX& A, const VECTOR& b)
78 {
79  U32 N=size(b);
80  if(size(y)!=N)
81  {
82  y=b; // adopt size
83  }
84  if(size(Ap)!=N)
85  {
86  Ap=b; // adopt size
87  }
88  set(y);
89 
90  F64 dot_r_1;
91 
92 #if CG_CHECK_SYMMETRY
93  for(U32 iy=0;iy<N;iy++)
94  {
95  for(U32 ix=iy;ix<N;ix++)
96  {
97  if(fabs(A(ix,iy)-A(iy,ix))>1e-9f)
98  {
99  feLog("ConjugateGradient2 symmetry error %d,%d %.6G %.6G\n",
100  ix,iy,A(ix,iy),A(iy,ix));
101  }
102  }
103  }
104 #endif
105 
106 #if CG_DEBUG
107  feLog("\nA\n%s\nb=<%s>\n",print(A).c_str(),print(b).c_str());
108 #endif
109 
110  if(magnitudeSquared(b)<m_threshold)
111  {
112 #if CG_DEBUG
113  feLog("ConjugateGradient2::solve has trivial solution\n");
114 #endif
115  x=y;
116  return;
117  }
118 
119  const MATRIX* pA=&A;
120  const VECTOR* pb=&b;
121  if(CG_NORMALIZE)
122  {
123  m_AT.setTranspose(A);
124  m_AT_A.setProduct(m_AT,A);
125  pA=&m_AT_A;
126 
127  m_AT_b=m_AT*b;
128  pb=&m_AT_b;
129 
130 #if CG_DEBUG
131  feLog("AT\n%s\n",print(m_AT).c_str());
132  feLog("AT*A\n%s\n",print(m_AT_A).c_str());
133  feLog("AT*b=<%s>\n",print(m_AT_b).c_str());
134 #endif
135  // HACK sloppy cut&paste
136  if(m_preconditioning==e_diagonal_A)
137  {
138  premultiplyInverseDiagonal(m_tempMatrix,m_AT_A,m_AT_A);
139  postmultiplyInverseDiagonal(m_preconditionedMatrix,
140  m_tempMatrix,m_AT_A);
141  pA=&m_preconditionedMatrix;
142 
143  premultiplyInverseDiagonal(m_preconditionedVector,m_AT_A,m_AT_b);
144  pb=&m_preconditionedVector;
145 
146 #if CG_DEBUG
147  feLog("mid\n%s\n",print(m_tempMatrix).c_str());
148  feLog("A'\n%s\nb`=<%s>\n",print(*pA).c_str(),print(*pb).c_str());
149 #endif
150  }
151  }
152  else if(m_preconditioning==e_diagonal_A)
153  {
154  premultiplyInverseDiagonal(m_tempMatrix,A,A);
155  postmultiplyInverseDiagonal(m_preconditionedMatrix,
156  m_tempMatrix,A);
157  pA=&m_preconditionedMatrix;
158 
159  premultiplyInverseDiagonal(m_preconditionedVector,A,b);
160  pb=&m_preconditionedVector;
161 
162 #if CG_DEBUG
163  feLog("mid\n%s\n",print(m_tempMatrix).c_str());
164  feLog("A'\n%s\nb`=<%s>\n",print(*pA).c_str(),print(*pb).c_str());
165 #endif
166  }
167 #if CG_CHECK_POSITIVE
168  for(U32 k=0;k<N;k++)
169  {
170  if((*pA)(k,k)<=0.0)
171  {
172  feLog("ConjugateGradient2::solve"
173  " non-positive diagonal %d,%d %.6G\n",
174  k,k,(*pA)(k,k));
175  }
176  }
177 #endif
178 #if CG_CHECK_POSITIVE
179  F64 peak=0.0;
180  U32 peak_i;
181  U32 peak_j;
182  for(U32 i=0;i<N;i++)
183  {
184  for(U32 j=0;j<N;j++)
185  {
186  F64 value=fabs((*pA)(i,j));
187  if(peak<value)
188  {
189  peak=value;
190  peak_i=i;
191  peak_j=j;
192  }
193  }
194  }
195  if(peak_i!=peak_j)
196  {
197  feLog("ConjugateGradient2::solve non-diagonal peak %d,%d %.6G\n",
198  peak_i,peak_j,peak);
199  }
200 #endif
201 
202  for(U32 k=1;k<=N;k++)
203  {
204  if(k==1)
205  {
206  p= *pb;
207  r_1=p;
208 
209  dot_r_1=dot(r_1,r_1);
210  }
211  else
212  {
213  r_2=r_1;
214  r_1=r;
215  p_1=p;
216 
217  dot_r_1=dot(r_1,r_1);
218 
219  F64 beta=dot_r_1/dot(r_2,r_2);
220 
221 // p=r_1+beta*p_1;
222  temp=p_1;
223  temp*=beta;
224  p=r_1;
225  p+=temp;
226  }
227 
228  transformVector(*pA,p,Ap);
229  F64 alpha=dot_r_1/dot(p,Ap);
230 
231 #if CG_TRACE==FALSE
232  if(magnitudeSquared(p)==0.0f)
233 #endif
234  {
235  feLog("\n%d alpha=%.6G r_1*r_1 %.6G y<%s>\n",
236  k,alpha,dot_r_1,print(y).c_str());
237  feLog("r_1<%s>\nr_2<%s>\n",
238  print(r_1).c_str(),print(r_2).c_str());
239  feLog("p<%s>\np_1<%s>\nA*p<%s>\n",
240  print(p).c_str(),print(p_1).c_str(),print(*pA*p).c_str());
241  }
242 
243  if(magnitudeSquared(p)==0.0f)
244  {
245  feX("ConjugateGradient2::solve","direction lost its magnitude");
246  }
247 
248 // y+=alpha*p;
249  temp=p;
250  temp*=alpha;
251  y+=temp;
252 
253 // r=r_1-alpha*(Ap);
254  temp=Ap;
255  temp*=alpha;
256  r=r_1;
257  r-=temp;
258 
259 #if CG_TRACE
260  feLog("r<%s>\n",print(r).c_str());
261 #endif
262 
263  feLog("ConjugateGradient2::solve ran %d/%d alpha %.6G r %.6G p %.6G\n",
264  k,N,alpha,magnitude(r),magnitude(p));
265 
266 // if(magnitudeSquared(r)<m_threshold)
267  if(magnitudeSquared(r)<1e-4)
268  {
269 #if CG_DEBUG
270 #endif
271  feLog("ConjugateGradient2::solve early solve %d/%d\n",k,N);
272  break;
273  }
274 
275  if(k==N)
276  {
277  feLog("ConjugateGradient2::solve ran %d/%d\n",k,N);
278  }
279  }
280 
281  if(m_preconditioning==e_diagonal_A)
282  {
283  premultiplyInverseDiagonal(x,A,y);
284  }
285  else
286  {
287  // TODO use x all along to save copy
288  x=y;
289  }
290 
291 #if CG_DEBUG
292  feLog("\ny=<%s>\nA'*y=<%s>\n",print(y).c_str(),print(*pA*y).c_str());
293  feLog("\nx=<%s>\nA*x=<%s>\n",print(x).c_str(),print(A*x).c_str());
294 #endif
295 
296 #if CG_VERIFY
297  BWORD invalid=FALSE;
298  for(U32 k=0;k<N;k++)
299  {
300  if(FE_INVALID_SCALAR(x[k]))
301  {
302  invalid=TRUE;
303  }
304  }
305  VECTOR Ax=A*x;
306  F64 distance=magnitude(Ax-b);
307  if(invalid || distance>1.0f)
308  {
309  feLog("ConjugateGradient2::solve failed to converge (dist=%.6G)\n",
310  distance);
311  if(size(x)<100)
312  {
313  feLog(" collecting state ...\n");
314  feLog("A=\n%s\nx=<%s>\nA*x=<%s>\nb=<%s>\n",
315  print(A).c_str(),print(x).c_str(),
316  print(Ax).c_str(),print(b).c_str());
317  }
318 // feX("ConjugateGradient2::solve","failed to converge");
319  }
320 #endif
321 }
322 
323 } /* namespace ext */
324 } /* namespace fe */
325 
326 #endif /* __solve_ConjugateGradient2_h__ */
solve Ax=b for x
Definition: ConjugateGradient2.h:38
const FESTRING_I8 * c_str(void) const
Return the contents of the 8-bit buffer cast as signed bytes.
Definition: String.h:352
kernel
Definition: namespace.dox:3
Preconditioning
Choice to alter matrix during solve.
Definition: ConjugateGradient2.h:42