Free Electron
BiconjugateGradient.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_BiconjugateGradient_h__
8 #define __solve_BiconjugateGradient_h__
9 
10 #define BCG_DEBUG FALSE
11 #define BCG_TRACE FALSE
12 #define BCG_VERIFY (FE_CODEGEN<=FE_DEBUG)
13 
14 namespace fe
15 {
16 namespace ext
17 {
18 
19 /**************************************************************************//**
20  @brief solve Ax=b for x
21 
22  @ingroup solve
23 
24  Uses Biconjugate-Gradient. The matrix must be positive-definite,
25  but not necessarily symmetric. For symmetric matrices, a regular
26  Conjugate-Gradient can be about twice as fast.
27 
28  The arguments are templated, so any argument types should work,
29  given that they have the appropriate methods and operators.
30 
31  TODO try seeding with previous x instead of clearing to zero.
32 *//***************************************************************************/
33 template <typename MATRIX, typename VECTOR>
35 {
36  public:
37  /// @brief Choice to alter matrix during solve
39  {
40  e_none,
41  e_diagonal_A
42  };
43 
44  BiconjugateGradient(void):
45  m_threshold(1e-6f),
46  m_preconditioning(e_none) {}
47 
48  void solve(VECTOR& x, const MATRIX& A, const VECTOR& b);
49 
50  void setThreshold(F64 threshold) { m_threshold=threshold; }
51 
52  void setPreconditioning(Preconditioning preconditioning)
53  { m_preconditioning=preconditioning; }
54 
55  private:
56  VECTOR r,r_1,r_2; //* residual (at k, k-1, k-2)
57  VECTOR rbar,rbar_1,rbar_2; //* second residual (r bar)
58  VECTOR p,p_1; //* direction
59  VECTOR pbar,pbar_1; //* second direction
60  VECTOR temp; //* persistent temporary
61  VECTOR Ap;
62  VECTOR m_preconditionedVector;
63  MATRIX m_preconditionedMatrix;
64  F64 m_threshold;
65  Preconditioning m_preconditioning;
66 };
67 
68 template <typename MATRIX, typename VECTOR>
69 inline void BiconjugateGradient<MATRIX,VECTOR>::solve(VECTOR& x,
70  const MATRIX& A, const VECTOR& b)
71 {
72  U32 N=size(b);
73  if(size(x)!=N)
74  {
75  x=b; // adopt size
76  }
77  if(size(Ap)!=N)
78  {
79  Ap=b; // adopt size
80  }
81  set(x);
82 
83  F64 dot_r_1;
84 
85 #if BCG_DEBUG
86  feLog("\nA\n%s\nb=<%s>\n",fe::print(A).c_str(),fe::print(b).c_str());
87 #endif
88 
89  if(magnitudeSquared(b)<m_threshold)
90  {
91 #if BCG_DEBUG
92  feLog("BiconjugateGradient::solve has trivial solution\n");
93 #endif
94  return;
95  }
96 
97  const MATRIX* pA=&A;
98  const VECTOR* pb=&b;
99  if(m_preconditioning==e_diagonal_A)
100  {
101  const MATRIX& preconditioner=A;
102  premultiplyInverseDiagonal(m_preconditionedMatrix,preconditioner,A);
103  pA=&m_preconditionedMatrix;
104 
105  premultiplyInverseDiagonal(m_preconditionedVector,preconditioner,b);
106  pb=&m_preconditionedVector;
107 
108 #if BCG_DEBUG
109  feLog("A'\n%s\nb`=<%s>\n",fe::print(*pA).c_str(),fe::print(*pb).c_str());
110 #endif
111  }
112 
113  for(U32 k=1;k<=N;k++)
114  {
115  if(k==1)
116  {
117  p= *pb;
118  r_1=p;
119 
120  pbar= *pb;
121  rbar_1=pbar;
122 
123  dot_r_1=dot(rbar_1,r_1);
124  }
125  else
126  {
127  r_2=r_1;
128  r_1=r;
129  p_1=p;
130 
131  rbar_2=rbar_1;
132  rbar_1=rbar;
133  pbar_1=pbar;
134 
135  dot_r_1=dot(rbar_1,r_1);
136 
137  F64 beta=dot_r_1/dot(rbar_2,r_2);
138 
139 // p=r_1+beta*p_1;
140  temp=p_1;
141  temp*=beta;
142  p=r_1;
143  p+=temp;
144 
145 // pbar=rbar_1+beta*pbar_1;
146  temp=pbar_1;
147  temp*=beta;
148  pbar=rbar_1;
149  pbar+=temp;
150  }
151 
152  transformVector(*pA,p,Ap);
153  F64 alpha=dot_r_1/dot(pbar,Ap);
154 
155 #if BCG_TRACE==FALSE
156  if(magnitudeSquared(p)==0.0f)
157 #endif
158  {
159  feLog("\n%d alpha=%.6G x<%s>\n",k,alpha,fe::print(x).c_str());
160  feLog("r<%s>\nr_1<%s>\nr_2<%s>\n",
161  fe::print(r).c_str(),fe::print(r_1).c_str(),fe::print(r_2).c_str());
162  feLog("rbar<%s>\nrbar_1<%s>\nrbar_2<%s>\n",
163  fe::print(rbar).c_str(),fe::print(rbar_1).c_str(),
164  fe::print(rbar_2).c_str());
165  feLog("p<%s>\np_1<%s>\nA*p<%s>\n",
166  fe::print(p).c_str(),fe::print(p_1).c_str(),fe::print(*pA*p).c_str());
167  feLog("pbar<%s>\npbar_1<%s>\n",
168  fe::print(pbar).c_str(),fe::print(pbar_1).c_str());
169  }
170 
171  if(magnitudeSquared(p)==0.0f)
172  {
173  feX("BiconjugateGradient::solve","direction lost its magnitude");
174  }
175 
176 // x+=alpha*p;
177  temp=p;
178  temp*=alpha;
179  x+=temp;
180 
181 // r=r_1-alpha*(Ap);
182  temp=Ap;
183  temp*=alpha;
184  r=r_1;
185  r-=temp;
186 
187 // rbar=rbar_1-alpha*transposeMultiply(*pA,pbar);
188  transposeTransformVector(*pA,pbar,temp);
189  temp*=alpha;
190  rbar=rbar_1;
191  rbar-=temp;
192 
193  if(magnitudeSquared(r)<m_threshold)
194  {
195 #if BCG_DEBUG
196  feLog("BiconjugateGradient::solve early solve %d/%d\n",k,N);
197 #endif
198  break;
199  }
200 
201  if(k==N)
202  {
203  feLog("BiconjugateGradient::solve ran %d/%d\n",k,N);
204  }
205  }
206 
207 
208 #if BCG_DEBUG
209  feLog("\nx=<%s>\nA*x=<%s>\n",fe::print(x).c_str(),fe::print(A*x).c_str());
210 #endif
211 
212 #if BCG_VERIFY
213  BWORD invalid=FALSE;
214  for(U32 k=0;k<N;k++)
215  {
216  if(FE_INVALID_SCALAR(x[k]))
217  {
218  invalid=TRUE;
219  }
220  }
221  VECTOR Ax=A*x;
222  F64 distance=magnitude(Ax-b);
223  if(invalid || distance>1.0f)
224  {
225  feLog("BiconjugateGradient::solve failed to converge (dist=%.6G)\n",
226  distance);
227  if(size(x)<100)
228  {
229  feLog(" collecting state ...\n");
230  feLog("A=\n%s\nx=<%s>\nA*x=<%s>\nb=<%s>\n",
231  fe::print(A).c_str(),fe::print(x).c_str(),
232  fe::print(Ax).c_str(),fe::print(b).c_str());
233  }
234 // feX("BiconjugateGradient::solve","failed to converge");
235  }
236 #endif
237 }
238 
239 /** @brief solve Ax=b (4x4 version)
240 
241  @relates Matrix
242  */
243 template <typename T>
244 void solve(Vector<4,T> x, const Matrix<4,4,T>& A, const Vector<4,T> &b)
245 {
246  Matrix<4,4,T> iA;
247  invert(iA,A);
248 
249  x=iA*b;
250 }
251 
252 } /* namespace ext */
253 } /* namespace fe */
254 
255 #endif /* __solve_BiconjugateGradient_h__ */
solve Ax=b for x
Definition: BiconjugateGradient.h:34
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
Matrix< 4, 4, T > & invert(Matrix< 4, 4, T > &a_inverted, const Matrix< 4, 4, T > &a_matrix)
4x4 full matrix inversion
Definition: Matrix.h:1033
General template for fixed size numeric matrices.
Definition: Matrix.h:42
Preconditioning
Choice to alter matrix during solve.
Definition: BiconjugateGradient.h:38
Partially specialized 4-component vector intended for spatial use.
Definition: Vector4.h:21