SheafSystem  0.0.0.0
svd.cc
Go to the documentation of this file.
1 
2 //
3 // Copyright (c) 2014 Limit Point Systems, Inc.
4 //
5 // Licensed under the Apache License, Version 2.0 (the "License");
6 // you may not use this file except in compliance with the License.
7 // You may obtain a copy of the License at
8 //
9 // http://www.apache.org/licenses/LICENSE-2.0
10 //
11 // Unless required by applicable law or agreed to in writing, software
12 // distributed under the License is distributed on an "AS IS" BASIS,
13 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 // See the License for the specific language governing permissions and
15 // limitations under the License.
16 //
17 
20 
21 
22 //$$TODO: See svd.h.
23 
24 #include "SheafSystem/svd.h"
25 
26 #include "SheafSystem/std_algorithm.h"
27 #include "SheafSystem/std_cmath.h"
28 #include "SheafSystem/std_iostream.h"
29 
30 using namespace std;
31 
32 // SVD-decomposition A = U*S*V', where U,V orthogonal and S diagonal.
33 // On completion A is replaced by U. Also, V and not V' (the transpose)
34 // is returned.
35 
36 bool
38 svd_decompose(double* xa, double* xw, double* xv, int nrows, int ncols)
39 {
40  // Preconditions:
41 
42  // Body:
43 
44  // Return true on success; otherwise return false;
45  //bool result = false;
46 
47  double* ltemp = new double[ncols];
48  double lnorm;
49 
50  // Householder reduction to bidiagonal form.
51 
52  svd_reduce(xa, xw, xv, nrows, ncols, ltemp, lnorm);
53 
54  // Diagonalization of the bidiagonal form.
55 
56  svd_diagonalize(xa, xw, xv, nrows, ncols, ltemp, lnorm);
57 
58  delete [] ltemp;
59 
60  //============================================================================
61 
62 // // Sort the results into decending order.
63 
64 // for(int i=0; i<n-1; ++i)
65 // {
66 // double largest = xw[i];
67 // int index = i;
68 // for(int j=i+1; j<n; ++j)
69 // {
70 // if(xw[j] > largest)
71 // {
72 // largest = xw[j];
73 // index = j;
74 // }
75 // }
76 
77 // if(index != i) // Need to swap rows and columns.
78 // {
79 // swap_cols(xa, i, index); // Swap columns in a.
80 // swap_rows(xv, i, index); // Swap rows in v.
81 // swap (xw, i, index); // Swap elements in w.
82 // }
83 // }
84 
85  //============================================================================
86 
87  // Postconditions:
88 
89  //ensure(???);
90 
91  // Exit:
92 
93  return true;
94 }
95 
96 bool
98 svd_reduce(double* xa, double* xw, double* xv, int nrows, int ncols,
99  double* ltemp, double& lnorm)
100 {
101  // Preconditions:
102 
103  // Body:
104 
105  // Householder reduction to bidiagonal form.
106 
107  lnorm = 0.0;
108 
109  double g = 0.0;
110  double lscale = 0.0;
111 
112  int l;
113  for(int i=0; i<ncols; ++i)
114  {
115  l = i+2; // l = i+1 ???
116  ltemp[i] = lscale*g;
117 
118  g = 0.0;
119  lscale = 0.0;
120 
121  double s = 0.0;
122 
123  if(i<nrows)
124  {
125  for(int k=i; k<nrows; ++k)
126  {
127  lscale += abs(xa[k*ncols+i]);
128  }
129 
130  if(lscale != 0.0)
131  {
132  for(int k=i; k<nrows; ++k)
133  {
134  int ki = k*ncols+i;
135  xa[ki] /= lscale;
136  s += xa[ki]*xa[ki];
137  }
138 
139  double f = xa[i*ncols+i];
140 
141  g = -same_sign(sqrt(s), f);
142  double h = f*g-s;
143  xa[i*ncols+i] = f-g;
144 
145  for(int j=l-1; j<ncols; ++j)
146  {
147  s = 0.0;
148  for(int k=i; k<nrows; ++k)
149  {
150  s += xa[k*ncols+i] * xa[k*ncols+j];
151  }
152 
153  f = s/h;
154 
155  for(int k=i; k<nrows; ++k)
156  {
157  xa[k*ncols+j] += f*xa[k*ncols+i];
158  }
159  }
160 
161  for(int k=i; k<nrows; ++k)
162  {
163  xa[k*ncols+i] *= lscale;
164  }
165  }
166  }
167 
168  xw[i] = lscale*g;
169  g = 0.0;
170  s = 0.0;
171  lscale = 0.0;
172 
173  if(((i+1)<=nrows) && ((i+1)!=ncols))
174  {
175  for(int k=l-1; k<ncols; ++k)
176  {
177  lscale += abs(xa[i*ncols+k]);
178  }
179 
180  if(lscale != 0.0)
181  {
182  for(int k=l-1; k<ncols; ++k)
183  {
184  xa[i*ncols+k] /= lscale;
185  s += xa[i*ncols+k]*xa[i*ncols+k];
186  }
187 
188  double f = xa[i*ncols+l-1];
189  g = -same_sign(sqrt(s),f);
190  double h = f*g-s;
191  xa[i*ncols+l-1] = f-g;
192 
193  for(int k=l-1; k<ncols; ++k)
194  {
195  ltemp[k] = xa[i*ncols+k]/h;
196  }
197 
198  for(int j=l-1; j<nrows; ++j)
199  {
200  s = 0.0;
201  for(int k=l-1; k<ncols; ++k)
202  {
203  s += xa[j*ncols+k]*xa[i*ncols+k];
204  }
205 
206  for(int k=l-1; k<ncols; ++k)
207  {
208  xa[j*ncols+k] += s*ltemp[k];
209  }
210  }
211 
212  for(int k=l-1; k<ncols; ++k)
213  {
214  xa[i*ncols+k] *= lscale;
215  }
216  }
217  }
218 
219  lnorm = max(lnorm, (abs(xw[i]) + abs(ltemp[i])));
220  }
221 
222  // Accumulation of right-hand transformations.
223 
224  for(int i=ncols-1; i>=0; i--)
225  {
226  if(i<ncols-1)
227  {
228  if(g != 0.0)
229  {
230  for(int j=l; j<ncols; ++j)
231  {
232  xv[j*ncols+i] = (xa[i*ncols+j]/xa[i*ncols+l])/g;
233  }
234 
235  for(int j=l; j<ncols; ++j)
236  {
237  double s = 0.0;
238  for(int k=l; k<ncols; ++k)
239  {
240  s += xa[i*ncols+k]*xv[k*ncols+j];
241  }
242 
243  for(int k=l; k<ncols; ++k)
244  {
245  xv[k*ncols+j] += s*xv[k*ncols+i];
246  }
247  }
248  }
249 
250  for(int j=l; j<ncols; ++j)
251  {
252  xv[i*ncols+j] = xv[j*ncols+i] = 0.0;
253  }
254  }
255 
256  xv[i*ncols+i] = 1.0;
257  g = ltemp[i];
258  l = i;
259  }
260 
261  // Accumulation of left-hand transformations.
262 
263  for(int i=min(nrows, ncols)-1; i>=0; i--)
264  {
265  l = i+1;
266  g = xw[i];
267 
268  for(int j=l; j<ncols; ++j)
269  {
270  xa[i*ncols+j] = 0.0;
271  }
272 
273  if(g != 0.0)
274  {
275  g = 1.0/g;
276 
277  for(int j=l; j<ncols; ++j)
278  {
279  double s = 0.0;
280  for(int k=l; k<nrows; ++k)
281  {
282  s += xa[k*ncols+i]*xa[k*ncols+j];
283  }
284 
285  double f = (s/xa[i*ncols+i])*g;
286 
287  for(int k=i; k<nrows; ++k)
288  {
289  xa[k*ncols+j] += f*xa[k*ncols+i];
290  }
291  }
292 
293  for(int j=i; j<nrows; ++j)
294  {
295  xa[j*ncols+i] *= g;
296  }
297  }
298 
299  else
300  {
301  for(int j=i; j<nrows; ++j)
302  {
303  xa[j*ncols+i] = 0.0;
304  }
305  }
306 
307  ++xa[i*ncols+i];
308  }
309 
310  // Postconditions:
311 
312  //ensure(???);
313 
314  // Exit:
315 
316  return true;
317 }
318 
319 bool
321 svd_diagonalize(double* xa, double* xw, double* xv, int nrows, int ncols,
322  double* ltemp, double lnorm)
323 {
324  // Preconditions:
325 
326  // Body:
327 
328  // Diagonalization of the bidiagonal form.
329 
330  static const int MAX_ITERATIONS = 30;
331 
332  // Loop over singular values.
333 
334  for(int k=ncols-1; k>=0; k--)
335  {
336  //int nm;
337 
338  // Loop over allowed iterations.
339 
340  for(int iterations=0; iterations<MAX_ITERATIONS; ++iterations)
341  {
342  bool ltest = true;
343 
344  int l;
345  int p;
346  for(l=k; l>=0; l--)
347  {
348  p = l-1;
349 
350  if(abs(ltemp[l])+lnorm == lnorm)
351  {
352  ltest = false;
353  break;
354  }
355 
356  if(abs(xw[p])+lnorm == lnorm)
357  {
358  break;
359  }
360  }
361 
362  // Cancellation of ltemp[l] if l>0.
363 
364  if(ltest)
365  {
366  double c = 0.0;
367  double s = 1.0;
368 
369  for(int i=l; i<k+1; ++i)
370  {
371  double f = s*ltemp[i];
372  ltemp[i] = c*ltemp[i];
373 
374  if((abs(f) + lnorm) == lnorm)
375  {
376  break;
377  }
378 
379  double g = xw[i];
380  double h = svd_pythag(f, g);
381  xw[i] = h;
382  h = 1.0/h;
383 
384  c = g*h;
385  s = -f*h;
386 
387  for(int j=0; j<nrows; ++j)
388  {
389  int jp = j*ncols+p;
390  int ji = j*ncols+i;
391  double y = xa[jp];
392  double z = xa[ji];
393  xa[jp] = y*c+z*s;
394  xa[ji] = z*c-y*s;
395  }
396  }
397  }
398 
399  double z = xw[k];
400 
401  // Convergence.
402 
403  // Make singular values nonnegative.
404 
405  if(l==k)
406  {
407  if(z<0.0)
408  {
409  xw[k] = -z;
410 
411  for(int j=0; j<ncols; ++j)
412  {
413  int jk = j*ncols+k;
414  xv[jk] = -xv[jk];
415  }
416  }
417 
418  break;
419  }
420 
421  if(iterations >= MAX_ITERATIONS)
422  {
423  //$$TODO: Use error_message here.
424  cerr << "Error: No convergence in " << MAX_ITERATIONS
425  << " svd iterations." << endl;
426  return false;
427  }
428 
429  // Shift from bottom 2-by-2 minor.
430 
431  int kk = k-1;
432 
433  double x = xw[l];
434  double y = xw[kk];
435  double g = ltemp[kk];
436  double h = ltemp[k];
437 
438  double f = ((y-z)*(y+z) + (g-h)*(g+h))/(2.0*h*y);
439  g = svd_pythag(f, 1.0);
440  f = ((x-z)*(x+z)+h*((y/(f+same_sign(g,f)))-h))/x;
441 
442  // Next QR transformation.
443 
444  double lsin = 1.0;
445  double lcos = 1.0;
446 
447  for(int j=l; j<=kk; ++j)
448  {
449  int i = j+1;
450 
451  g = ltemp[i];
452  h = g*lsin;
453  g = g*lcos;
454 
455  z = svd_pythag(f, h);
456  ltemp[j] = z;
457 
458  lcos = f/z;
459  lsin = h/z;
460 
461  f = x*lcos + g*lsin;
462  g = g*lcos - x*lsin;
463 
464  y = xw[i];
465  h = y*lsin;
466  y = y*lcos;
467 
468  for(int n=0; n<ncols; ++n)
469  {
470  int ni = n*ncols+i;
471  int nj = n*ncols+j;
472  x = xv[nj];
473  z = xv[ni];
474  xv[nj] = x*lcos + z*lsin;
475  xv[ni] = z*lcos - x*lsin;
476  }
477 
478  z = svd_pythag(f, h);
479  xw[j] = z;
480 
481  // Rotation can be arbitrary if z = 0;
482 
483  if(z != 0.0)
484  {
485  z = 1.0/z;
486  lcos = f*z;
487  lsin = h*z;
488  }
489 
490  f = g*lcos + y*lsin;
491  x = y*lcos - g*lsin;
492 
493  for(int m=0; m<nrows; ++m)
494  {
495  int mi = m*ncols+i;
496  int mj = m*ncols+j;
497  y = xa[mj];
498  z = xa[mi];
499  xa[mj] = y*lcos + z*lsin;
500  xa[mi] = z*lcos - y*lsin;
501  }
502  }
503 
504  ltemp[l] = 0.0;
505  ltemp[k] = f;
506  xw[k] = x;
507  }
508  }
509 
510  // Postconditions:
511 
512  //ensure(???);
513 
514  // Exit:
515 
516  return true;
517 }
518 
519 double
521 same_sign(double xa, double xb)
522 {
523  // Preconditions:
524 
525  // Body:
526 
527  double result;
528  if(xb >= 0.0)
529  {
530  result = abs(xa);
531  }
532  else
533  {
534  result = -abs(xa);
535  }
536 
537  // Postconditions:
538 
539  //ensure(???);
540 
541  // Exit:
542 
543  return result;
544 }
545 
546 double
548 svd_pythag(double xa, double xb)
549 {
550  // Preconditions:
551 
552  // Body:
553 
554  double labs_a = abs(xa);
555  double labs_b = abs(xb);
556 
557  double result;
558 
559  if(labs_a > labs_b)
560  {
561  double lratio = labs_b/labs_a;
562  double lratio2 = lratio*lratio;
563  result = labs_a*sqrt(1.0+lratio2);
564  }
565  else if(labs_b > 0.0)
566  {
567  double lratio = labs_a/labs_b;
568  double lratio2 = lratio*lratio;
569  result = labs_b*sqrt(1.0+lratio2);
570  }
571  else
572  {
573  result = 0.0;
574  }
575 
576  // Postconditions:
577 
578  //ensure(???);
579 
580  // Exit:
581 
582  return result;
583 }
SHEAF_DLL_SPEC void sqrt(const sec_at0 &x0, sec_at0 &xresult, bool xauto_access)
Compute sqrt of x0 (sqrt(x0)) (pre-allocated version).
Definition: sec_at0.cc:1556
STL namespace.
SHEAF_DLL_SPEC bool svd_diagonalize(double *xa, double *xw, double *xv, int nrows, int ncols, double *xtemp, double lnorm)
Diagonalization of the bidiagonal form. Convenience function.
Definition: svd.cc:321
SHEAF_DLL_SPEC double svd_pythag(double xa, double xb)
Pthagorean theorem calculation.
Definition: svd.cc:548
SHEAF_DLL_SPEC bool svd_reduce(double *xa, double *xw, double *xv, int nrows, int ncols, double *ltemp, double &lnorm)
Householder reduction to bidiagonal form. Convenience function.
Definition: svd.cc:98
SHEAF_DLL_SPEC bool svd_decompose(double *xa, double *xs, double *xv, int xnrows, int xncols)
Perform single value decomposition.
Definition: svd.cc:38
SHEAF_DLL_SPEC double same_sign(double xa, double xb)
Convert xa to have the same sign as xb.
Definition: svd.cc:521
SHEAF_DLL_SPEC void max(const vd &x0, vd_value_type &xresult, bool xauto_access)
Maximum component of x0, pre-allocated version.
Definition: vd.cc:2097
SHEAF_DLL_SPEC void min(const vd &x0, vd_value_type &xresult, bool xauto_access)
Minimum component of x0, pre-allocated version.
Definition: vd.cc:2161