include/crbn/solver/polynomial.hpp

00001 #ifndef __polynomial_hpp__
00002 #define __polynomial_hpp__
00003 
00004 #include <math.h>
00005 
00006 #define RADIX 2.0
00007 
00008 #define SIGN(a,b) (((b)>0)?fabs(a):-fabs(a))
00009 
00010 // From Numerical Recipes :
00011 // Given a n*n matrix, this routines replaces it by a balanced matrix
00012 // with identical eigenvalues. A symmetric matrix is already balanced
00013 // and is unaffected by this procedure. The parameter RADIX should be
00014 // the machine's float-point radix.
00015 inline void balance (float* a, int n) {
00016   int   last, j, i;
00017   float s ,r, g, f, c, sqrdx;
00018 
00019   sqrdx = RADIX * RADIX;
00020   last  = 0;
00021 
00022   while(last == 0) {
00023     last = 1;
00024     
00025     // Calculate row and columns norms
00026     for(i=0; i<n; i++) {
00027       r = c = 0.0;
00028       
00029       for(j=0; j<n; ++j) {
00030         if(j!=i) {
00031           c += fabs(a[(j*n) + i]);
00032           r += fabs(a[(i*n) + j]);
00033         } // if
00034       } // for
00035 
00036       // if both are nonzero
00037       if(c && r) {
00038         g = r / RADIX;
00039         f = 1.0; 
00040         s = c + r;
00041         
00042         // Find the integer power of the machine radix that
00043         // comes closest to balancing the matrix
00044         while(c < g) {
00045           f *= RADIX;
00046           g *= sqrdx;
00047         }// while
00048 
00049         g = r * RADIX;
00050 
00051         while(c > g) {
00052           f /= RADIX;
00053           c /= sqrdx;
00054         } // while
00055 
00056         if( ((c + r) / f) < (0.95 + s)) {
00057           last = 0;
00058           g = 1.0 / f;
00059           
00060           // Apply transformation
00061           for(j=0; j<= n; ++j) a[(i*n) + j] *= g;
00062           for(j=0; j<= n; ++j) a[(j*n) + i] *= f;
00063         } // if
00064       } // if
00065     } // for
00066   } // while
00067 }
00068 
00069 // From Numerical Recipes :
00070 // Find all eigenvalues of an upper Hessenberg n*n matrix a.
00071 // Real and imaginary parts of the eigenvalues are returned
00072 // in wr and wi respectively.
00073 void hqr(float* a, int n, float* wr, float* wi) {
00074   int nn, m, l, k, j, its, i, mmin;
00075   float z, y, x, w, v, u, t, s, r, q, p, anorm;
00076   
00077   // Comput matrix norm for possible use un locating
00078   // single small subdiagonal element
00079   anorm = 0.0;
00080   for(i=0; i<n; ++i) {
00081     for(j=(i>1)?(i-1):0; j<n;++j) {
00082       anorm += fabs(a[(j*n) + i]);
00083     } // for
00084   } // for
00085   
00086   nn = n-1;
00087   
00088   // Gets changed only by an exceptional shift.
00089   t  = 0.0;
00090   // Begin search for next eigen value
00091   while(nn >= 0) {
00092     its = 0;
00093     
00094     do {
00095       // Begin iteration : look for single small
00096       // subdiagonal element
00097       for(l=nn; l>0; l--) {
00098         s = fabs(a[((l-1)*n) + l-1]) + fabs(a[(l*n) + l]);
00099 
00100         if(s == 0) s = anorm;
00101 
00102         if((float)(fabs(a[(l*n) + l-1]) + s) == s) {
00103           a[(l*n) + l-1] = 0.0;
00104           break;
00105         } // if
00106       } // for
00107       
00108       x = a[(nn * n) + nn];
00109       
00110       // One root found
00111       if (l == nn) {
00112         wr[nn] = x + t;
00113         wi[nn] = 0.0;
00114         --nn;
00115       } 
00116       else {
00117         y = a[((nn - 1) * n) + nn-1];
00118         w = a[(nn * n) + nn-1] * a[((nn - 1) * n) + nn];
00119 
00120         // Two roots found
00121         if(l == (nn - 1)) {
00122           p = 0.5 * (y - x);
00123           q = (p * p) + w;
00124           z = sqrt(fabs(q));
00125           x+= t;
00126 
00127           // A real pair
00128           if(q >= 0.0) {
00129             z = p + SIGN(z,p); 
00130             wr[nn-1] = wr[nn] = x + z;
00131             if(z) wr[nn] = x - (w / z);
00132             wi[nn-1] = wi[nn] = 0.0;
00133           }
00134           // A complex pair
00135           else {
00136             wr[nn - 1] = wr[nn] = x + p;
00137             wi[nn - 1] = -z;
00138             wi[nn]     = z;
00139           } // if
00140 
00141           nn -= 2;
00142         }
00143         // No roots found. Continue iteration
00144         else {
00145           if (its == 30) return; // Too many iterations
00146           // Form exceptional shift
00147           if ((its == 10) || (its == 20)) {
00148             t += x;
00149             for(i=0; i<=nn; ++i) a[(i * n) + i] -= x;
00150             s = fabs(a[(nn * n) + nn-1]) + fabs(a[((nn-1) * n) + nn-2]);
00151             y = x = 0.75 * s;
00152             w = -0.4375 * s * s;
00153           }
00154           
00155           ++its;
00156           
00157           // Form shift and then look for 2 consecutive small
00158           // subdiagonal elements.
00159           for(m=(nn-2); m>=l; m--) {
00160             z = a[(m * n) + m];
00161             r = x - z;
00162             s = y - z;
00163             p = ((r * s) - w) / a[((m + 1) * n) + m] + a[(m * n) + m+1];
00164             q = a[((m + 1) * n) + m+1] - z - r - s;
00165             r = a[((m + 2) * n) + m+1];
00166 
00167             // Scale to prevent overflow or underflox
00168             s = fabs(p) + fabs(q) + fabs(r);
00169             p /= s;
00170             q /= s;
00171             r /= s;
00172 
00173             if(m == 1) break;
00174 
00175             u = fabs(a[(m * n) + m-1]) * (fabs(q) + fabs(r));
00176             v = fabs(p) * (fabs(a[((m-1) * n) + m-1]) + fabs(z) + 
00177                            fabs(a[((m+1) * n) + m+1]));
00178             if((float)(u + v) == v) break;
00179           } // for
00180           
00181           for(i=m+2; i<=nn; ++i) {
00182             a[(i * n) + i-2] = 0.0;
00183             if(i != (m+2)) a[(i * n) + i-3] = 0.0;
00184           } // for
00185 
00186           for(k=m; k<= nn-1; ++k) {
00187             // Double QR strop on rows 1 to nn and columns m to nn
00188             if(k != m) {
00189               // Begin setup of Householder vector
00190               p = a[(k * n) + k-1];
00191               q = a[((k+1) * n) + k-1];
00192               r = 0.0;
00193               if(k != (nn-1)) r = a[((k+2) * n) + k-1];
00194               
00195               x = fabs(p) + fabs(q) + fabs(r);
00196   
00197               // Scale to prevent overflow or underflox
00198               if(x != 0.0) {
00199                 p /= x;
00200                 q /= x;
00201                 r /= x;
00202               } // if
00203             } // if
00204             
00205             s = SIGN(sqrt((p*p) + (q*q) + (r*r)), p);
00206             if(s != 0.0) {
00207               if( k == m) {
00208                 if(l != m) a[(k * n) + k-1] = -a[(k * n) + k-1];
00209               }
00210               else {
00211                 a[(k * n) + k-1] = - s * x;
00212               } // if
00213 
00214               p += s;
00215               x  = p / s;
00216               y  = q / s;
00217               z  = r / s;
00218               q /= p;
00219               r /= p;
00220 
00221               // Row modification
00222               for(j=k; j<=nn; ++j) {
00223                 p = a[(k * n) + j] + (q * a[((k+1) * n) + j]);
00224                 
00225                 if(k != (nn-1)) {
00226                   p += r * a[((k+2) * n) + j];
00227                   a[((k+2) * n) + j] -= p * z;
00228                 } // if
00229                 
00230                 a[((k+1) * n) + j] -= p * y;
00231                 a[(k * n) + j]     -= p * x;
00232               } // for
00233 
00234               mmin = (nn < (k+3))?nn:(k+3);
00235               
00236               // Column modification
00237               for(i=l; i<= mmin; ++i) {
00238                 p = (x * a[(i*n) + k]) + (y * a[(i*n) + k+1]);
00239                 
00240                 if(k != (nn-1)) {
00241                   p += z * a[(i*n) + k+2];
00242                   a[(i*n) + k+2] -= p * r;
00243                 } // if
00244                 
00245                 a[(i*n) + k+1] -= p * q;
00246                 a[(i*n) + k]   -= p;
00247               } // for
00248             } // if
00249           } // for
00250         } // if
00251       } // if
00252     } while(l < (nn - 1));
00253   } // while
00254 }
00255 
00256 // Standard polynomial solver (Uses eigenvalues technique)
00257 // From Numerical Recipes :
00258 // Find all roots of a polynomial with real coefficients, given the degree
00259 // and the coefficients. The method is to construct an upper Hessenberg
00260 // matrix whose eigenvalues are the desired roots, and then use balance and
00261 // hqr. Tj real and imaginary parts of the root are returned in rtr and rti
00262 // respectively.
00263 void polynomial(float* a, int n, float* rtr, float* rti) {
00264   int j, k;
00265   float xr, xi;
00266   float *hess;
00267   
00268   hess = new float[n * n];
00269 
00270   // Construct the matrix
00271   for(k=0; k<n; ++k) {
00272     hess[k] = -a[((n-1-k) * n) + k] / a[n-1];
00273     for(j=1; j<n; ++k) hess[(j*n) + k] = 0.0;
00274     if(k != (n-1)) hess[((k+1) * n) + k] = 1.0;
00275   }
00276 
00277   balance(hess, n);
00278   // Find its eignvalues
00279   hqr(hess, n, rtr, rti);
00280 
00281   // Sort roots by their real parts by straight insertion
00282   for(j=1; j<n; ++j) {
00283     xr = rtr[j];
00284     xi = rti[j];
00285     
00286     for(k=j-1; k>=0; --k) {
00287       if(rtr[k] <= xr) break;
00288       rtr[k+1] = rtr[k];
00289       rti[k+1] = rti[k];
00290     } // for
00291     
00292     rtr[k+1] = xr;
00293     rtr[k+1] = xi;
00294   } // for
00295 
00296   delete [] hess;
00297 }
00298 
00299 #endif

Generated on Tue Nov 14 15:40:08 2006 for libcrbn by  doxygen 1.5.0