CHROMA
polynomial_op.h
Go to the documentation of this file.
1 // -*- C++ -*-
2 /*! \file
3  * \brief Polynomial filter for 4D Dirac operators. It creates an approximation
4  * to 1/Q^2 in the range \f$[\mu, \lambda_{max}]\f$ with \f$Q = \gamma5 M\f$
5  * where M is a dirac operator of some kind (EO preconditioned is accepted).
6  * Can handle any 4D operator but not 5D since gamma_5 hermiticity is more
7  * involved there.
8  *
9  * Initial version Feb. 6, 2006 (Kostas Orginos)
10  */
11 
12 #ifndef _LPOLY_H
13 #define _LPOLY_H
14 
15 #include "handle.h"
16 #include "linearop.h"
17 #include "polylinop.h"
18 
19 using namespace QDP::Hints;
20 
21 namespace Chroma
22 {
23  //! Polynomial operator
24  /*! \ingroup linop */
25  template<typename T, typename P, typename Q>
26  class lpoly: public PolyLinearOperator<T,P,Q>
27  {
28  private:
29  Handle< DiffLinearOperator<T,P,Q> > M; // this is the operator
30 
33 
34  int degree ;
35 
36  // The polynomium is: c_Zero * Prod_i ( Q^2 - root[i])*inv_c[i]
37 
38  multi1d<DComplex> root ;
39  multi1d<DComplex> inv_c ;
40 
41  Real c_Zero ; // the zeroth order approximation to the inverse ie. a constant
42 
43 
44  int bitrevers(int n,int maxBits){
45  // int bits[maxBits] ;
46  // int br_bits[maxBits] ;
47  multi1d<int> bits(maxBits);
48  multi1d<int> br_bits(maxBits);
49 
50  int br(0);
51  for(int i(0);i<maxBits;i++){
52  bits[i] = n % 2 ;
53  n /= 2;
54  int br_i = maxBits-i-1 ;
55  br_bits[br_i] = bits[i] ;
56  br = br + (br_bits[br_i]<<br_i) ;
57  }
58  return br ;
59  }
60 
61  void GetRoots(multi1d<DComplex>& r, multi1d<DComplex>& ic){
62 
63  Double eps = LowerBound/UpperBound ;
64  // 2PI/(N+1) ;
65  Double www = 6.28318530717958647696/(degree+1.0) ;
66 
67  // complex conjugate pairs are at i and N-1-i
68  for(int i(0);i<degree;i++){
69  //cout<<"i: "<<i<<std::endl ;
70  Real ii = i+1.0 ;
71  r[i] = UpperBound*cmplx(0.5*(1.0+eps)*(1.0-cos(www*ii)) , -sqrt(eps)*sin(www*ii));
72  ic[i] = 1.0/(UpperBound*0.5*(1.0+eps) - r[i]) ;
73  }
74  c_Zero = 2.0/(UpperBound+LowerBound) ;
75  // The c_Zero constant is computed here
76  // The polynomium is: c_Zero * Prod_i ( Q^2 - root[i])*inv_c[i]
77  }
78 
79 
80  public:
81  // constructor
82  // need to modify the contructor to pass down the roots
83  // this is doing my own stupid ordering...
85  int degree_,
86  const Real& lower_bound_,
87  const Real& upper_bound_,
88  int ord) : M(m_), LowerBound(lower_bound_), UpperBound(upper_bound_), degree(degree_)
89  { //This sets up Chebyshev Polynomials. But we should have a general
90  // class that allows us to use different types of polynomials
91 
92  //Qsq = new lmdagm<T>(*M) ;
93 
94  //QDPIO::cout<<"lpoly constructor\n" ;
95 
96  if(degree_%2 !=0 ){
97  QDPIO::cout<<"lpoly: Polynomium degree must be even.\n" ;
98  degree_++ ;
99  degree++ ;
100  QDPIO::cout<<"lpoly: Using degree:" << degree<<std::endl ;
101  }
102  //UpperBound = upper_bound_ ;
103  root.resize(degree_);
104  inv_c.resize(degree_);
105 
106  // get the natural order roots and constants
107  multi1d<DComplex> r(degree);
108  multi1d<DComplex> ic(degree);
109  GetRoots(r,ic);
110 
111  // Arrange the roots in complex conjugate pairs
112  int j(0) ;
113  // complex conjugate pairs are at i and N-1-i
114  for(int k(1);k<=ord;k++){
115  //QDPIO::cout<<"k: "<<k<<std::endl ;
116  for(int i(k-1);i<degree/2;i+=ord){
117  //QDPIO::cout<<"i: "<<i<<std::endl ;
118  root[j] = r[i] ;
119  inv_c[j] = ic[i] ;
120  root [degree-1-j] = conj(root[j] ) ;
121  inv_c[degree-1-j] = conj(inv_c[j]) ;
122  j++ ;
123  }
124  }
125  // The polynomium is: c_Zero * Prod_i ( Q^2 - root[i])*inv_c[i]
126 
127  }
128 
129  // This is doing the Jensen bit reversal ordering
131  int degree_,
132  const Real& lower_bound_,
133  const Real& upper_bound_) : M(m_), LowerBound(lower_bound_), UpperBound(upper_bound_), degree(degree_){
134  //This sets up Chebyshev Polynomials. But we should have a general
135  // class that allows us to use different types of polynomials
136 
137  //QDPIO::cout<<"tlpoly constructor\n" ;
138  //QDPIO::cout<<"tlpoly: degree: " << degree<<std::endl ;
139 
140  int maxBits = 0 ;
141  while(degree_%2==0)
142  {
143  degree_/=2 ;
144  maxBits ++ ;
145  }
146 
147  if(degree_ !=1 ){
148  QDPIO::cout<<"tlpoly: Polynomium degree must be power of two.\n" ;
149  while(degree_!=1){
150  degree_ = degree + 1 ;
151  degree = degree_ ;
152  QDPIO::cout<<"tlpoly: Using degree: " << degree<<std::endl ;
153  maxBits=0 ;
154  while(degree_%2==0){
155  degree_/=2 ;
156  maxBits++ ;
157  }
158  }
159 
160  QDPIO::cout<<"tlpoly: Using degree: " << degree<<std::endl ;
161  }
162 
163  //QDPIO::cout<<"tlpoly: maxBits: " << maxBits<<std::endl ;
164  //UpperBound = upper_bound_ ;
165  root.resize(degree);
166  inv_c.resize(degree);
167 
168  // get the natural order roots and constants
169  multi1d<DComplex> r(degree);
170  multi1d<DComplex> ic(degree);
171  GetRoots(r,ic);
172 
173  // complex conjugate pairs are at i and N-1-i
174  for(int i(0);i<degree;i++){
175  //cout<<"i: "<<i<<std::endl ;
176  int ii = bitrevers(i,maxBits) ;
177  root[i] = r[ii] ;
178  inv_c[i] = ic[ii] ;
179  }
180  // The polynomium is: c_Zero * Prod_i ( Q^2 - root[i])*inv_c[i]
181  }
182 
183  ~lpoly(){}
184 
185  //! Return the fermion BC object for this linear operator
186  const FermBC<T,P,Q>& getFermBC() const {return M->getFermBC();}
187 
188  //! Subset comes from underlying operator
189  inline const Subset& subset() const {return M->subset();}
190 
191  //! Returns the roots
192  multi1d<DComplex> Roots() const {
193  return root ;
194  }
195 
196  //! Returns the roots
197  multi1d<DComplex> MonomialNorm() const {
198  return inv_c ;
199  }
200 
201 
202  //! Apply the underlying (Mdagger M) operator
203  void Qsq(T& y, const T& x) const {
204  T t ; moveToFastMemoryHint(t);
205  (*M)(t,x,PLUS) ;
206  (*M)(y,t,MINUS);
207  }
208 
209  void applyChebInv(T& x, const T& b) const
210  {
211  //Chi has the initial guess as it comes in
212  //b is the right hand side
213  Real a = 2/(UpperBound-LowerBound) ;
214  Real d = (UpperBound+LowerBound)/(UpperBound-LowerBound) ;
215  T QsqX ;
216  //*Qsq(QsqX,x,PLUS) ;
217  Qsq(QsqX,x) ;
218  T r ;
219  r = b - QsqX ;
220  T y(x) ;
221  int m(1) ;
222  Real sigma_m(d) ;
223  Real sigma_m_minus_one(1.0) ;
224  Real sigma_m_plus_one ;
225 
226  x = x + a/d*r ;
227  //*Qsq(QsqX,x,PLUS) ;
228  Qsq(QsqX,x) ;
229  r = b - QsqX ;
230  T p ;
231  while (m<degree+1){
232  //QDPIO::cout<<"iter: "<<m<<" 1/sigma: "<<1.0/sigma_m<<std::endl ;
233  sigma_m_plus_one = 2.0*d*sigma_m - sigma_m_minus_one ;
234  p = 2.0*sigma_m/sigma_m_plus_one *(d*x+a*r) -
235  sigma_m_minus_one/sigma_m_plus_one*y ;
236  y = x; x= p ;
237  //*Qsq(QsqX,x,PLUS) ;
238  Qsq(QsqX,x) ;
239  r = b - QsqX ;
240 
241  m++;
242  sigma_m_minus_one = sigma_m ;
243  sigma_m = sigma_m_plus_one ;
244  }
245  QDPIO::cout<<"applyChebInv: 1/sigma("<<m<<"): "<<1.0/sigma_m<<std::endl ;
246 
247  }
248 
249  //! Here is your apply function
250  // use the Golub algorithm here
251  void operator()(T& chi, const T& b, PlusMinus isign) const
252  {
253  chi = zero ; // zero the initial guess. This way P(Qsq)*b is produced
254  applyChebInv(chi,b) ;
255  }
256 
257  //! Apply the A or A_dagger piece: P(Qsq) = A_dagger(Qsq) * A(Qsq)
258  // use the root representation here
259  void applyA(T& chi,const T& b, PlusMinus isign) const{
260 
261  int start, end ;
262  chi = b ;
263  switch (isign){
264  case PLUS:
265  start = 0 ;
266  end = degree/2 ;
267  break ;
268  case MINUS:
269  start = degree/2 ;
270  end = degree ;
271  break ;
272  }
273 
274  //QDPIO::cout<<start<<" "<<end<<std::endl ;
275 
276 
277  Real c0 = sqrt(c_Zero) ;
278 
279  T tt = c0*b ;
280 
281  for(int i(start);i<end;i++){
282  //QDPIO::cout<<" root, norm: "<<root[i]<<" "<<inv_c[i]<<std::endl ;
283  //*Qsq(chi, tt, PLUS);
284  Qsq(chi, tt);
285  chi -= root[i] * tt;
286  chi *= inv_c[i] ;
287  tt = chi ;
288  }
289 
290  }
291 
292  //! Apply the derivative of the linop
293  void deriv(P& ds_u, const T& chi, const T& psi, PlusMinus isign) const
294  {
295  // There's a problem here. The interface wants to do something like
296  // \f$\chi^\dag * \dot(Qsq) \psi\f$
297  // the derivs all expect to do the derivative on the Qsq leaving
298  // open some color indices. The chi^dag and psi multiplications will
299  // trace over the spin indices.
300  // However, there is no guarantee in the interface that chi = psi,
301  // so you **MUST** apply all the parts of the monomial each to psi
302  // and to chi. Sorry, current limitation. In the future, we could
303  // add another deriv function that takes only one fermion and you
304  // could optimize here.
305 
306  // So do something like
307  ds_u.resize(Nd);
308  ds_u = zero;
309  P ds_tmp;
310 
311  multi1d<T> chi_products(degree+1);
312  multi1d<T> psi_products(degree+1);
313 
314  multi1d<T> M_chi_products(degree+1);
315  multi1d<T> M_psi_products(degree+1);
316 
317  // Build up list of products of roots of poly against psi and chi each
318  // play some trick trying to pad boundaries
319 
320 
321  // Qsq is M^dag M
322  chi_products[degree] = sqrt(c_Zero)*chi;
323  for(int n(degree-1); n >= 0; --n){
324  Qsq(chi_products[n], chi_products[n+1]);
325  chi_products[n] -= conj(root [n]) * chi_products[n+1];
326  chi_products[n] *= conj(inv_c[n]) ;
327  }
328 
329  psi_products[0] = sqrt(c_Zero)*psi;
330  for(int n(0); n < degree; ++n){
331  Qsq(psi_products[n+1], psi_products[n]);
332  psi_products[n+1] -= root[n] * psi_products[n];
333  psi_products[n+1] *= inv_c[n] ;
334  }
335 
336  for(int n(0); n < degree+1; ++n)
337  {
338  (*M)(M_chi_products[n],chi_products[n],PLUS);
339  (*M)(M_psi_products[n],psi_products[n],PLUS);
340  }
341 
342  // Now do force computation
343  // be more careful than me about bouds
344  for(int n(0); n < degree; ++n)
345  {
346  M->deriv(ds_tmp, M_chi_products[n+1], psi_products[n], PLUS);
347  for(int d(0);d<Nd;d++)
348  ds_tmp[d] *= inv_c[n] ;
349  ds_u += ds_tmp; // build up force
350 
351  M->deriv(ds_tmp, chi_products[n+1], M_psi_products[n], MINUS);
352  for(int d(0);d<Nd;d++)
353  ds_tmp[d] *= inv_c[n] ;
354  ds_u += ds_tmp; // build up force
355  }
356 
357  //Check The force:
358  /**
359  QDPIO::cout<<"CheckProducts: ";
360  QDPIO::cout<< innerProduct(psi_products[0],psi_products[degree])<<std::endl ;
361  for(int n(0); n < degree+1; ++n){
362  QDPIO::cout<<"CheckProducts: "<<n<< ": " ;
363  QDPIO::cout<< innerProduct(chi_products[n],psi_products[n])<<std::endl ;
364 
365  }
366  **/
367  }
368 
369  };
370 
371 }// End Namespace Chroma
372 
373 #endif
Differentiable Linear Operator.
Definition: linearop.h:98
Base class for all fermion action boundary conditions.
Definition: fermbc.h:20
Class for counted reference semantics.
Definition: handle.h:33
Polynomial linear operator including derivatives.
Definition: polylinop.h:23
Polynomial operator.
Definition: polynomial_op.h:27
void deriv(P &ds_u, const T &chi, const T &psi, PlusMinus isign) const
Apply the derivative of the linop.
multi1d< DComplex > MonomialNorm() const
Returns the roots.
multi1d< DComplex > Roots() const
Returns the roots.
lpoly(Handle< DiffLinearOperator< T, P, Q > > m_, int degree_, const Real &lower_bound_, const Real &upper_bound_)
void operator()(T &chi, const T &b, PlusMinus isign) const
Here is your apply function.
void applyChebInv(T &x, const T &b) const
void applyA(T &chi, const T &b, PlusMinus isign) const
Apply the A or A_dagger piece: P(Qsq) = A_dagger(Qsq) * A(Qsq)
Double UpperBound
Definition: polynomial_op.h:32
Double LowerBound
Definition: polynomial_op.h:31
lpoly(Handle< DiffLinearOperator< T, P, Q > > m_, int degree_, const Real &lower_bound_, const Real &upper_bound_, int ord)
Definition: polynomial_op.h:84
multi1d< DComplex > root
Definition: polynomial_op.h:38
int bitrevers(int n, int maxBits)
Definition: polynomial_op.h:44
const Subset & subset() const
Subset comes from underlying operator.
multi1d< DComplex > inv_c
Definition: polynomial_op.h:39
void Qsq(T &y, const T &x) const
Apply the underlying (Mdagger M) operator.
const FermBC< T, P, Q > & getFermBC() const
Return the fermion BC object for this linear operator.
void GetRoots(multi1d< DComplex > &r, multi1d< DComplex > &ic)
Definition: polynomial_op.h:61
Handle< DiffLinearOperator< T, P, Q > > M
Definition: polynomial_op.h:29
Class for counted reference semantics.
unsigned j
Definition: ldumul_w.cc:35
unsigned i
Definition: ldumul_w.cc:34
unsigned n
Definition: ldumul_w.cc:36
Linear Operators.
static int m[4]
Definition: make_seeds.cc:16
int y
Definition: meslate.cc:35
int x
Definition: meslate.cc:34
int t
Definition: meslate.cc:37
Nd
Definition: meslate.cc:74
Asqtad Staggered-Dirac operator.
Definition: klein_gord.cc:10
LinOpSysSolverMGProtoClover::T T
@ MINUS
Definition: chromabase.h:45
@ PLUS
Definition: chromabase.h:45
Complex a
Definition: invbicg.cc:95
DComplex d
Definition: invbicg.cc:99
Complex b
Definition: invbicg.cc:96
Double zero
Definition: invbicg.cc:106
int k
Definition: invbicg.cc:119
FloatingPoint< double > Double
Definition: gtest.h:7351
multi1d< LatticeFermion > r(Ncb)
chi
Definition: pade_trln_w.cc:24
psi
Definition: pade_trln_w.cc:191
Polynomial Linear Operators.
multi1d< LatticeColorMatrix > P
Definition: t_clover.cc:13