QUDA  v0.5.0
A library for QCD on GPUs
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Pages
svd_quda.h
Go to the documentation of this file.
1 #ifndef _SVD_QUDA_H_
2 #define _SVD_QUDA_H_
3 
4 #include "quda_matrix.h"
5 
6 #include <iostream>
7 #include <fstream>
8 #include <string>
9 #include <sstream>
10 
11 #define DEVICEHOST __device__ __host__
12 #define SVDPREC 1e-10
13 #define LOG2 0.69314718055994530942
14 
15 
16 namespace quda{
17 
18  template<class Cmplx>
19  inline DEVICEHOST
20  typename RealTypeId<Cmplx>::Type cabs(const Cmplx & z)
21  {
22  typename RealTypeId<Cmplx>::Type max, ratio, square;
23  if(fabs(z.x) > fabs(z.y)){ max = z.x; ratio = z.y/max; }else{ max=z.y; ratio = z.x/max; }
24  square = max*max*(1.0 + ratio*ratio);
25  return sqrt(square);
26  }
27 
28 
29 /*
30  template<class Cmplx>
31  inline DEVICEHOST
32  typename RealTypeId<Cmplx>::Type cabs(const Cmplx & z)
33  {
34  return sqrt(z.x*z.x + z.y*z.y);
35  }
36 */
37 
38  template<class T, class U>
39  inline DEVICEHOST typename PromoteTypeId<T,U>::Type quadSum(const T & a, const U & b){
40  typename PromoteTypeId<T,U>::Type ratio, square, max;
41  if(fabs(a) > fabs(b)){ max = a; ratio = b/a; }else{ max=b; ratio = a/b; }
42  square = max*max*(1.0 + ratio*ratio);
43  return sqrt(square);
44  }
45 
46 /*
47  template<class T, class U>
48  inline DEVICEHOST
49  T quadSum(const T & a, const U & b)
50  {
51  return sqrt(a*a + b*b);
52  }
53 */
54 
55 
56 
57  // In future iterations of the code, I would like to use templates to compute the norm
59  float getNorm(const Array<float2,3>& a){
60  float temp1, temp2, temp3;
61  temp1 = cabs(a[0]);
62  temp2 = cabs(a[1]);
63  temp3 = quadSum(temp1,temp2);
64  temp1 = cabs(a[2]);
65  return quadSum(temp1,temp3);
66  }
67 
68 
70  double getNorm(const Array<double2,3>& a){
71  double temp1, temp2, temp3;
72  temp1 = cabs(a[0]);
73  temp2 = cabs(a[1]);
74  temp3 = quadSum(temp1,temp2);
75  temp1 = cabs(a[2]);
76  return quadSum(temp1,temp3);
77  }
78 
79 
80  template<class T>
81  DEVICEHOST
82  void constructHHMat(const T & tau, const Array<T,3> & v, Matrix<T,3> & hh)
83  {
84  Matrix<T,3> temp1, temp2;
85  outerProd(v,v,&temp1);
86 
87  temp2 = conj(tau)*temp1;
88 
89  setIdentity(&temp1);
90 
91  hh = temp1 - temp2;
92  return;
93  }
94 
95 
96  template<class Real>
98  void getLambdaMax(const Matrix<Real,3> & b, Real & lambda_max){
99 
100  Real m11 = b(1,1)*b(1,1) + b(0,1)*b(0,1);
101  Real m22 = b(2,2)*b(2,2) + b(1,2)*b(1,2);
102  Real m12 = b(1,1)*b(1,2);
103  Real dm = (m11 - m22)/2.0;
104 
105  Real norm1 = quadSum(dm, m12);
106  if( dm >= 0.0 ){
107  lambda_max = m22 - (m12*m12)/(norm1 + dm);
108  }else{
109  lambda_max = m22 + (m12*m12)/(norm1 - dm);
110  }
111  return;
112  }
113 
114 
115  template<class Real>
116  DEVICEHOST
117  void getGivensRotation(const Real & alpha, const Real & beta, Real & c, Real & s)
118  {
119  Real ratio;
120  if( beta == 0.0 ){
121  c = 1.0; s = 0.0;
122  }else if( fabs(beta) > fabs(alpha) ){
123  ratio = -alpha/beta;
124  s = rsqrt(1.0 + ratio*ratio);
125  c = ratio*s;
126  }else{
127  ratio = -beta/alpha;
128  c = rsqrt(1.0 + ratio*ratio);
129  s = ratio*c;
130  }
131  return;
132  }
133 
134  template<class Real>
135  inline DEVICEHOST
136  void accumGivensRotation(int index, const Real & c, const Real & s, Matrix<Real,3> & m){
137  int index_p1 = index+1;
138  Real alpha, beta;
139  for(int i=0; i<3; ++i){
140  alpha = m(i,index);
141  beta = m(i,index_p1);
142  m(i,index) = alpha*c - beta*s;
143  m(i,index_p1) = alpha*s + beta*c;
144  }
145  return;
146  }
147 
148  template<class Real>
149  inline DEVICEHOST
150  void assignGivensRotation(const Real & c, const Real & s, Matrix<Real,2> & m){
151  m(0,0) = c;
152  m(0,1) = s;
153  m(1,0) = -s;
154  m(1,1) = c;
155  return;
156  }
157 
158  template<class Real>
159  inline DEVICEHOST
160  void swap(Real & a, Real & b){
161  Real temp = a;
162  a = b;
163  b = temp;
164  return;
165  }
166 
167  template<class Real>
168  inline DEVICEHOST
170  // set u and v to the 2x2 identity matrix
171  setIdentity(&u);
172  setIdentity(&v);
173 
174  Real c, s;
175 
176  if( m(0,0) == 0.0 ){
177 
178  getGivensRotation(m(0,1), m(1,1), c, s);
179 
180  m(0,0) = m(0,1)*c - m(1,1)*s;
181  m(0,1) = 0.0;
182  m(1,1) = 0.0;
183 
184  // exchange columns in v
185  v(0,0) = 0.0;
186  v(0,1) = 1.0;
187  v(1,0) = 1.0;
188  v(1,1) = 0.0;
189 
190  // u is a Givens rotation
191  assignGivensRotation(c, s, u);
192 
193  }else if( m(1,1) == 0.0 ){
194 
195  getGivensRotation(m(0,0), m(0,1), c, s);
196 
197  m(0,0) = m(0,0)*c - m(0,1)*s;
198  m(0,1) = 0.0;
199  m(1,1) = 0.0;
200 
201  assignGivensRotation(c,s,v);
202 
203  }else if( m(0,1) != 0.0 ){
204 
205  // need to calculate (m(1,1)**2 + m(0,1)**2 - m(0,0)**2)/(2*m(0,0)*m(0,1))
206  Real abs01 = fabs(m(0,1));
207  Real abs11 = fabs(m(1,1));
208  Real min, max;
209  if( abs01 > abs11 ){ min = abs11; max = abs01; }
210  else { min = abs01; max = abs11; }
211 
212 
213  Real ratio = min/max;
214  Real alpha = 2.0*log(max) + log(1.0 + ratio*ratio);
215 
216  Real abs00 = fabs(m(0,0));
217  Real beta = 2.0*log(abs00);
218 
219  int sign;
220  Real temp;
221 
222  if( alpha > beta ){
223  sign = 1;
224  temp = alpha + log(1.0 - exp(beta-alpha));
225  }else{
226  sign = -1;
227  temp = beta + log(1.0 - exp(alpha-beta));
228  }
229  temp -= LOG2 + log(abs00) + log(abs01);
230  temp = sign*exp(temp);
231 
232  if( m(0,0) < 0.0 ){ temp *= -1.0; }
233  if( m(0,1) < 0.0 ){ temp *= -1.0; }
234 
235  beta = quadSum(1.0, temp);
236 
237  if( temp >= 0.0 ){
238  temp = 1.0/(temp + beta);
239  }else{
240  temp = 1.0/(temp - beta);
241  }
242 
243  // Calculate beta = sqrt(1 + temp**2)
244  beta = quadSum(1.0, temp);
245 
246  c = 1.0/beta;
247  s = temp*c;
248 
249  Matrix<Real,2> p;
250 
251  p(0,0) = c*m(0,0) - s*m(0,1);
252  p(1,0) = - s*m(1,1);
253  p(0,1) = s*m(0,0) + c*m(0,1);
254  p(1,1) = c*m(1,1);
255 
256  assignGivensRotation(c, s, v);
257 
258  // Make the column with the largest norm the first column
259  alpha = quadSum(p(0,0),p(1,0));
260  beta = quadSum(p(0,1),p(1,1));
261 
262  if(alpha < beta){
263  swap(p(0,0),p(0,1));
264  swap(p(1,0),p(1,1));
265 
266  swap(v(0,0),v(0,1));
267  swap(v(1,0),v(1,1));
268  }
269 
270  getGivensRotation(p(0,0), p(1,0), c, s);
271 
272  m(0,0) = p(0,0)*c - s*p(1,0);
273  m(1,1) = p(0,1)*s + c*p(1,1);
274  m(0,1) = 0.0;
275 
276  assignGivensRotation(c,s,u);
277  }
278 
279  return;
280  }
281 
282  // Change this so that the bidiagonal terms are not returned
283  // as a complex matrix
284  template<class Cmplx>
285  DEVICEHOST
287  Matrix<Cmplx,3> & u,
288  Matrix<Cmplx,3> & v)
289  {
290  Matrix<Cmplx,3> p;
291  Array<Cmplx,3> vec;
292  Matrix<Cmplx,3> temp;
293 
294 
295  const Cmplx COMPLEX_UNITY = makeComplex<Cmplx>(1,0);
296  const Cmplx COMPLEX_ZERO = makeComplex<Cmplx>(0,0);
297  // Step 1: build the first left reflector v,
298  // calculate the first left rotation
299  // apply to the original matrix
300  typename RealTypeId<Cmplx>::Type x = cabs(mat(1,0));
301  typename RealTypeId<Cmplx>::Type y = cabs(mat(2,0));
302  typename RealTypeId<Cmplx>::Type norm1 = quadSum(x,y);
303  typename RealTypeId<Cmplx>::Type beta;
304  Cmplx w, tau, z;
305 
306  if(norm1 == 0 && mat(0,0).y == 0){
307  p = mat;
308  }else{
309  Array<Cmplx,3> temp_vec;
310  copyColumn(mat, 0, &temp_vec);
311 
312  beta = getNorm(temp_vec);
313 
314  if(mat(0,0).x > 0.0){ beta = -beta; }
315 
316  w = mat(0,0) - beta;
317  norm1 = cabs(w);
318  w = conj(w)/norm1;
319 
320  // Assign the vector elements
321  vec[0] = COMPLEX_UNITY;
322  vec[1] = mat(1,0)*w/norm1;
323  vec[2] = mat(2,0)*w/norm1;
324 
325  // Now compute tau
326  tau.x = (beta - mat(0,0).x)/beta;
327  tau.y = mat(0,0).y/beta;
328  // construct the Householder matrix
329  constructHHMat(tau, vec, temp);
330  p = conj(temp)*mat;
331  u = temp;
332  }
333 
334  // Step 2: build the first right reflector
335  typename RealTypeId<Cmplx>::Type norm2 = cabs(p(0,2));
336  if(norm2 != 0.0 || p(0,1).y != 0.0){
337  norm1 = cabs(p(0,1));
338  beta = quadSum(norm1,norm2);
339  vec[0] = COMPLEX_ZERO;
340  vec[1] = COMPLEX_UNITY;
341 
342  if( p(0,1).x > 0.0 ){ beta = -beta; }
343  w = p(0,1)-beta;
344  norm1 = cabs(w);
345  w = conj(w)/norm1;
346  z = conj(p(0,2))/norm1;
347  vec[2] = z*conj(w);
348 
349  tau = (beta - p(0,1))/beta;
350  // construct the Householder matrix
351  constructHHMat(tau, vec, temp);
352  p = p*temp;
353  v = temp;
354  }
355 
356  // Step 3: build the second left reflector
357  norm2 = cabs(p(2,1));
358  if(norm2 != 0.0 || p(1,1).y != 0.0){
359  norm1 = cabs(p(1,1));
360  beta = quadSum(norm1,norm2);
361 
362  // Set the first two elements of the vector
363  vec[0] = COMPLEX_ZERO;
364  vec[1] = COMPLEX_UNITY;
365 
366  if( p(1,1).x > 0 ){ beta = -beta; }
367  w = p(1,1) - beta;
368  norm1 = cabs(w);
369  w = conj(w)/norm1;
370  z = p(2,1)/norm1;
371  vec[2] = z*w;
372 
373  tau.x = (beta - p(1,1).x)/beta;
374  tau.y = p(1,1).y/beta;
375  // I could very easily change the definition of tau
376  // so that we wouldn't need to call conj(temp) below.
377  // I would have to be careful to make sure this change
378  // is consistent with the other parts of the code.
379  constructHHMat(tau, vec, temp);
380  p = conj(temp)*p;
381  u = u*temp;
382  }
383 
384  // Step 4: build the second right reflector
385  setIdentity(&temp);
386  if( p(1,2).y != 0.0 ){
387  beta = cabs(p(1,2));
388  if( p(1,2).x > 0.0 ){ beta = -beta; }
389  temp(2,2) = conj(p(1,2))/beta;
390  p(2,2) = p(2,2)*temp(2,2); // This is the only element of p needed below
391  v = v*temp;
392  }
393 
394 
395  // Step 5: build the third left reflector
396  if( p(2,2).y != 0.0 ){
397  beta = cabs(p(2,2));
398  if( p(2,2).x > 0.0 ){ beta = -beta; }
399  temp(2,2) = p(2,2)/beta;
400  u = u*temp;
401  }
402  return;
403  }
404 
405 
406 
407  template<class Real>
408  DEVICEHOST
409  void bdSVD(Matrix<Real,3>& u, Matrix<Real,3>& v, Matrix<Real,3>& b, int max_it)
410  {
411 
412  Real c,s;
413 
414  // set u and v matrices equal to the identity
415  setIdentity(&u);
416  setIdentity(&v);
417 
418  Real alpha, beta;
419 
420  int it=0;
421  do{
422 
423  if( fabs(b(0,1)) < SVDPREC*( fabs(b(0,0)) + fabs(b(1,1)) ) ){ b(0,1) = 0.0; }
424  if( fabs(b(1,2)) < SVDPREC*( fabs(b(0,0)) + fabs(b(2,2)) ) ){ b(1,2) = 0.0; }
425 
426  if( b(0,1) != 0.0 && b(1,2) != 0.0 ){
427  if( b(0,0) == 0.0 ){
428 
429  getGivensRotation(-b(0,1), b(1,1), s, c);
430 
431  for(int i=0; i<3; ++i){
432  alpha = u(i,0);
433  beta = u(i,1);
434  u(i,0) = alpha*c - beta*s;
435  u(i,1) = alpha*s + beta*c;
436  }
437 
438  b(1,1) = b(0,1)*s + b(1,1)*c;
439  b(0,1) = 0.0;
440 
441  b(0,2) = -b(1,2)*s;
442  b(1,2) *= c;
443 
444  getGivensRotation(-b(0,2), b(2,2), s, c);
445 
446  for(int i=0; i<3; ++i){
447  alpha = u(i,0);
448  beta = u(i,2);
449  u(i,0) = alpha*c - beta*s;
450  u(i,2) = alpha*s + beta*c;
451  }
452  b(2,2) = b(0,2)*s + b(2,2)*c;
453  b(0,2) = 0.0;
454 
455  }else if( b(1,1) == 0.0 ){
456  // same block
457  getGivensRotation(-b(1,2), b(2,2), s, c);
458  for(int i=0; i<3; ++i){
459  alpha = u(i,1);
460  beta = u(i,2);
461  u(i,1) = alpha*c - beta*s;
462  u(i,2) = alpha*s + beta*c;
463  }
464  b(2,2) = b(1,2)*s + b(2,2)*c;
465  b(1,2) = 0.0;
466  // end block
467  }else if( b(2,2) == 0.0 ){
468 
469  getGivensRotation(b(1,1), -b(1,2), c, s);
470  for(int i=0; i<3; ++i){
471  alpha = v(i,1);
472  beta = v(i,2);
473  v(i,1) = alpha*c + beta*s;
474  v(i,2) = -alpha*s + beta*c;
475  }
476  b(1,1) = b(1,1)*c + b(1,2)*s;
477  b(1,2) = 0.0;
478 
479  b(0,2) = -b(0,1)*s;
480  b(0,1) *= c;
481 
482  // apply second rotation, to remove b02
483  getGivensRotation(b(0,0), -b(0,2), c, s);
484  for(int i=0; i<3; ++i){
485  alpha = v(i,0);
486  beta = v(i,2);
487  v(i,0) = alpha*c + beta*s;
488  v(i,2) = -alpha*s + beta*c;
489  }
490  b(0,0) = b(0,0)*c + b(0,2)*s;
491  b(0,2) = 0.0;
492 
493  }else{ // Else entering normal QR iteration
494 
495  Real lambda_max;
496  getLambdaMax(b, lambda_max); // defined above
497 
498  alpha = b(0,0)*b(0,0) - lambda_max;
499  beta = b(0,0)*b(0,1);
500 
501  // c*beta + s*alpha = 0
502  getGivensRotation(alpha, beta, c, s);
503  // Multiply right v matrix
504  accumGivensRotation(0, c, s, v);
505  // apply to bidiagonal matrix, this generates b(1,0)
506  alpha = b(0,0);
507  beta = b(0,1);
508 
509  b(0,0) = alpha*c - beta*s;
510  b(0,1) = alpha*s + beta*c;
511  b(1,0) = -b(1,1)*s;
512  b(1,1) = b(1,1)*c;
513 
514  // Calculate the second Givens rotation (this time on the left)
515  getGivensRotation(b(0,0), b(1,0), c, s);
516 
517  // Multiply left u matrix
518  accumGivensRotation(0, c, s, u);
519 
520  b(0,0) = b(0,0)*c - b(1,0)*s;
521  alpha = b(0,1);
522  beta = b(1,1);
523  b(0,1) = alpha*c - beta*s;
524  b(1,1) = alpha*s + beta*c;
525  b(0,2) = -b(1,2)*s;
526  b(1,2) = b(1,2)*c;
527  // Following from the definition of the Givens rotation, b(1,0) should be equal to zero.
528  b(1,0) = 0.0;
529 
530  // Calculate the third Givens rotation (on the right)
531  getGivensRotation(b(0,1), b(0,2), c, s);
532 
533  // Multiply the right v matrix
534  accumGivensRotation(1, c, s, v);
535 
536  b(0,1) = b(0,1)*c - b(0,2)*s;
537  alpha = b(1,1);
538  beta = b(1,2);
539 
540  b(1,1) = alpha*c - beta*s;
541  b(1,2) = alpha*s + beta*c;
542  b(2,1) = -b(2,2)*s;
543  b(2,2) *= c;
544  b(0,2) = 0.0;
545 
546 
547  // Calculate the fourth Givens rotation (on the left)
548  getGivensRotation(b(1,1), b(2,1), c, s);
549  // Multiply left u matrix
550  accumGivensRotation(1, c, s, u);
551  // Eliminate b(2,1)
552  b(1,1) = b(1,1)*c - b(2,1)*s;
553  alpha = b(1,2);
554  beta = b(2,2);
555  b(1,2) = alpha*c - beta*s;
556  b(2,2) = alpha*s + beta*c;
557  b(2,1) = 0.0;
558 
559  } // end of normal QR iteration
560 
561 
562  }else if( b(0,1) == 0.0 ){
563  Matrix<Real,2> m_small, u_small, v_small;
564 
565  m_small(0,0) = b(1,1);
566  m_small(0,1) = b(1,2);
567  m_small(1,0) = b(2,1);
568  m_small(1,1) = b(2,2);
569 
570  smallSVD(u_small, v_small, m_small);
571 
572  b(1,1) = m_small(0,0);
573  b(1,2) = m_small(0,1);
574  b(2,1) = m_small(1,0);
575  b(2,2) = m_small(1,1);
576 
577 
578  for(int i=0; i<3; ++i){
579  alpha = u(i,1);
580  beta = u(i,2);
581  u(i,1) = alpha*u_small(0,0) + beta*u_small(1,0);
582  u(i,2) = alpha*u_small(0,1) + beta*u_small(1,1);
583 
584  alpha = v(i,1);
585  beta = v(i,2);
586  v(i,1) = alpha*v_small(0,0) + beta*v_small(1,0);
587  v(i,2) = alpha*v_small(0,1) + beta*v_small(1,1);
588  }
589 
590 
591  }else if( b(1,2) == 0.0 ){
592  Matrix<Real,2> m_small, u_small, v_small;
593 
594  m_small(0,0) = b(0,0);
595  m_small(0,1) = b(0,1);
596  m_small(1,0) = b(1,0);
597  m_small(1,1) = b(1,1);
598 
599  smallSVD(u_small, v_small, m_small);
600 
601  b(0,0) = m_small(0,0);
602  b(0,1) = m_small(0,1);
603  b(1,0) = m_small(1,0);
604  b(1,1) = m_small(1,1);
605 
606  for(int i=0; i<3; ++i){
607  alpha = u(i,0);
608  beta = u(i,1);
609  u(i,0) = alpha*u_small(0,0) + beta*u_small(1,0);
610  u(i,1) = alpha*u_small(0,1) + beta*u_small(1,1);
611 
612  alpha = v(i,0);
613  beta = v(i,1);
614  v(i,0) = alpha*v_small(0,0) + beta*v_small(1,0);
615  v(i,1) = alpha*v_small(0,1) + beta*v_small(1,1);
616  }
617 
618  } // end if b(1,2) == 0
619  } while( (b(0,1) != 0.0 || b(1,2) != 0.0) && it < max_it);
620 
621 
622  for(int i=0; i<3; ++i){
623  if( b(i,i) < 0.0) {
624  b(i,i) *= -1;
625  for(int j=0; j<3; ++j){
626  v(j,i) *= -1;
627  }
628  }
629  }
630  return;
631  }
632 
633 
634 
635  template<class Cmplx>
636  DEVICEHOST
637  void computeSVD(const Matrix<Cmplx,3> & m,
638  Matrix<Cmplx,3>& u,
639  Matrix<Cmplx,3>& v,
640  typename RealTypeId<Cmplx>::Type singular_values[3])
641  {
642 
643  getRealBidiagMatrix<Cmplx>(m, u, v);
644  Matrix<typename RealTypeId<Cmplx>::Type,3> bd, u_real, v_real;
645  // Make real
646  for(int i=0; i<3; ++i){
647  for(int j=0; j<3; ++j){
648  bd(i,j) = (conj(u)*m*(v))(i,j).x;
649  }
650  }
651 
652  bdSVD(u_real, v_real, bd, 40);
653  for(int i=0; i<3; ++i){
654  singular_values[i] = bd(i,i);
655  }
656 
657  u = u*u_real;
658  v = v*v_real;
659 
660  return;
661  }
662 
663 } // end namespace quda
664 
665 
666 #endif // _SVD_QUDA_H