PSturm.h

Go to the documentation of this file.
00001 /*
00002     LoopTK: Protein Loop Kinematic Toolkit
00003     Copyright (C) 2007 Stanford University
00004 
00005     This program is free software; you can redistribute it and/or modify
00006     it under the terms of the GNU General Public License as published by
00007     the Free Software Foundation; either version 2 of the License, or
00008     (at your option) any later version.
00009 
00010     This program is distributed in the hope that it will be useful,
00011     but WITHOUT ANY WARRANTY; without even the implied warranty of
00012     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00013     GNU General Public License for more details.
00014 
00015     You should have received a copy of the GNU General Public License along
00016     with this program; if not, write to the Free Software Foundation, Inc.,
00017     51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
00018 */
00019 
00020 /* Modified version of the code downloaded 
00021  * from http://www.acm.org/pubs/tog/GraphicsGems/gems/Sturm/ 
00022  * Modified by Chaok Seok 2003.
00023  *
00024  * Using Sturm Sequences to Bracket Real Roots of Polynomial Equations
00025  * by D.G. Hook and P.R. McAree
00026  * from "Graphics Gems", Academic Press, 1990
00027 */
00028 
00029 #define PRINT_LEVEL 0
00030 #define MAX_ORDER 16
00031 #define MAXPOW 32               
00032 #define SMALL_ENOUGH 1.0e-18
00033 
00034 /*
00035  * structure type for representing a polynomial
00036  */
00037 typedef         struct  p {
00038   int   ord;
00039   double        coef[MAX_ORDER+1];
00040 } poly;
00041 
00042 double RELERROR;
00043 int MAXIT, MAX_ITER_SECANT;
00044 
00045 void initialize_sturm(double *tol_secant, int *max_iter_sturm, int *max_iter_secant);
00046 void solve_sturm(int *p_order, int *n_root, double *poly_coeffs, double *roots);
00047 double hyper_tan(double a, double x);
00048 static int modp(poly *u, poly *v, poly *r);
00049 int buildsturm(int ord, poly *sseq);
00050 int numroots(int np, poly *sseq, int *atneg, int *atpos);
00051 int numchanges(int np, poly *sseq, double a);
00052 void sbisect(int np, poly *sseq, double min, double max, int atmin, int atmax, double *roots);
00053 double evalpoly(int ord, double *coef, double x);
00054 int modrf(int ord, double *coef, double a, double b, double *val);
00055         
00056 /* set termination criteria for polynomial solver */
00057 void initialize_sturm(double *tol_secant, int *max_iter_sturm, int *max_iter_secant)
00058 {
00059   RELERROR = *tol_secant;
00060   MAXIT = *max_iter_sturm;
00061   MAX_ITER_SECANT = *max_iter_secant;
00062 }
00063 
00064 void solve_sturm(int *p_order, int *n_root, double *poly_coeffs, double *roots)
00065 {
00066   poly sseq[MAX_ORDER*2];
00067   double min, max;
00068   int order, i, j, nroots, nchanges, np, atmin, atmax;
00069 
00070   order = *p_order;
00071 
00072   for (i = order; i >= 0; i--) 
00073     {
00074       sseq[0].coef[i] = poly_coeffs[i];
00075     }
00076 
00077   if (PRINT_LEVEL > 0) 
00078     {
00079       for (i = order; i >= 0; i--) 
00080         {
00081           printf("coefficients in Sturm solver\n");
00082           printf("%d %lf\n", i, sseq[0].coef[i]);
00083         }
00084     }
00085 
00086   /*
00087    * build the Sturm sequence
00088    */
00089   np = buildsturm(order, sseq);
00090   
00091   if (PRINT_LEVEL > 0) 
00092     {
00093       printf("Sturm sequence for:\n");
00094       for (i = order; i >= 0; i--)
00095         printf("%lf ", sseq[0].coef[i]);
00096       printf("\n\n");
00097       for (i = 0; i <= np; i++) 
00098         {
00099           for (j = sseq[i].ord; j >= 0; j--)
00100             printf("%lf ", sseq[i].coef[j]);
00101           printf("\n");
00102         }
00103       printf("\n");
00104     }
00105 
00106   /* 
00107    * get the number of real roots
00108    */
00109 
00110   nroots = numroots(np, sseq, &atmin, &atmax);
00111 
00112 
00113   if (nroots == 0) 
00114     {
00115       // printf("solve: no real roots\n");
00116       *n_root = nroots;
00117       return;
00118     }
00119 
00120   if (PRINT_LEVEL > 0) 
00121     printf("Number of real roots: %d\n", nroots);
00122  
00123   /*
00124    * calculate the bracket that the roots live in
00125    */
00126   min = -1.0;
00127   nchanges = numchanges(np, sseq, min);
00128 
00129   for (i = 0; nchanges != atmin && i != MAXPOW; i++) { 
00130     min *= 10.0;
00131     nchanges = numchanges(np, sseq, min);
00132   }
00133 
00134   if (nchanges != atmin) {
00135     printf("solve: unable to bracket all negative roots\n");
00136     atmin = nchanges;
00137   }
00138 
00139   max = 1.0;
00140   nchanges = numchanges(np, sseq, max);
00141   for (i = 0; nchanges != atmax && i != MAXPOW; i++) { 
00142     max *= 10.0;
00143     nchanges = numchanges(np, sseq, max);
00144   }
00145   
00146   if (nchanges != atmax) {
00147     printf("solve: unable to bracket all positive roots\n");
00148     atmax = nchanges;
00149   }
00150 
00151   nroots = atmin - atmax;
00152 
00153   /*
00154    * perform the bisection.
00155    */
00156 
00157   sbisect(np, sseq, min, max, atmin, atmax, roots);
00158 
00159   *n_root = nroots;
00160 
00161   /*
00162    * write out the roots...
00163    */
00164   if (PRINT_LEVEL > 0) 
00165     {
00166       if (nroots == 1) {
00167         printf("\n1 distinct real root at x = %f\n", roots[0]);
00168       } else {
00169         printf("\n%d distinct real roots for x: \n", nroots);
00170         
00171         for (i = 0; i != nroots; i++)
00172           {
00173             printf("%f\n", roots[i]);
00174           }
00175       }
00176     }
00177 
00178   return;
00179 }
00180 
00181 /*
00182  * modp
00183  *
00184  *      calculates the modulus of u(x) / v(x) leaving it in r, it
00185  *  returns 0 if r(x) is a constant.
00186  *  note: this function assumes the leading coefficient of v 
00187  *      is 1 or -1
00188  */
00189 
00190 double hyper_tan(double a, double x)
00191 {
00192   double exp_x1, exp_x2, ax;
00193 
00194   ax = a*x;
00195   if (ax > 100.0)
00196     {
00197       return(1.0);
00198     }
00199   else if (ax < -100.0)
00200     {
00201       return(-1.0);
00202     }
00203   else    
00204     {
00205       exp_x1 = exp(ax);
00206       exp_x2 = exp(-ax);
00207       return (exp_x1 - exp_x2)/(exp_x1 + exp_x2);
00208     }
00209 }
00210 
00211 static int modp(poly *u, poly *v, poly *r)
00212 //      poly *u, *v, *z;
00213 {
00214         int             k, j;
00215         double  *nr, *end, *uc;
00216 
00217         nr = r->coef;
00218         end = &u->coef[u->ord];
00219 
00220         uc = u->coef;
00221         while (uc <= end)
00222                         *nr++ = *uc++;
00223 
00224         if (v->coef[v->ord] < 0.0) {
00225 
00226 
00227                         for (k = u->ord - v->ord - 1; k >= 0; k -= 2)
00228                                 r->coef[k] = -r->coef[k];
00229 
00230                         for (k = u->ord - v->ord; k >= 0; k--)
00231                                 for (j = v->ord + k - 1; j >= k; j--)
00232                                         r->coef[j] = -r->coef[j] - r->coef[v->ord + k]
00233                                         * v->coef[j - k];
00234         } else {
00235                         for (k = u->ord - v->ord; k >= 0; k--)
00236                                 for (j = v->ord + k - 1; j >= k; j--)
00237                                 r->coef[j] -= r->coef[v->ord + k] * v->coef[j - k];
00238         }
00239 
00240         k = v->ord - 1;
00241         while (k >= 0 && fabs(r->coef[k]) < SMALL_ENOUGH) {
00242                 r->coef[k] = 0.0;
00243                 k--;
00244         }
00245 
00246         r->ord = (k < 0) ? 0 : k;
00247 
00248         return(r->ord);
00249 }
00250 
00251 /*
00252  * buildsturm
00253  *
00254  *      build up a sturm sequence for a polynomial in smat, returning
00255  * the number of polynomials in the sequence
00256  */
00257 int buildsturm(int ord, poly *sseq)
00258 //      int     ord;
00259 //      poly    *sseq;
00260 {
00261         int             i;
00262         double  f, *fp, *fc;
00263         poly    *sp;
00264 
00265         sseq[0].ord = ord;
00266         sseq[1].ord = ord - 1;
00267 
00268         /*
00269          * calculate the derivative and normalise the leading
00270          * coefficient.
00271          */
00272         f = fabs(sseq[0].coef[ord]*ord);
00273 
00274         fp = sseq[1].coef;
00275         fc = sseq[0].coef + 1;
00276 
00277         for (i = 1; i <= ord; i++)
00278           {
00279             *fp++ = *fc++ * i / f;
00280           }
00281 
00282 
00283         /*
00284          * construct the rest of the Sturm sequence
00285          */
00286         //      for (sp = sseq + 2; modp(sp - 2, sp - 1, sp); sp++) {
00287         for (sp = sseq + 2; modp(sp - 2, sp - 1, sp); sp++) {
00288 
00289           /*
00290            * reverse the sign and normalise
00291            */
00292 
00293           f = -fabs(sp->coef[sp->ord]);
00294           for (fp = &sp->coef[sp->ord]; fp >= sp->coef; fp--)
00295                   *fp /= f;
00296         }
00297 
00298         sp->coef[0] = -sp->coef[0];     /* reverse the sign */
00299 
00300         return(sp - sseq);
00301 }
00302 
00303 /*
00304  * numroots
00305  *
00306  *      return the number of distinct real roots of the polynomial
00307  * described in sseq.
00308  */
00309 int numroots(int np, poly *sseq, int *atneg, int *atpos)
00310 //              int             np;
00311 //              poly    *sseq;
00312 //              int             *atneg, *atpos;
00313 {
00314                 int             atposinf, atneginf;
00315                 poly    *s;
00316                 double  f, lf;
00317 
00318                 atposinf = atneginf = 0;
00319 
00320 
00321         /*
00322          * changes at positive infinity
00323          */
00324         lf = sseq[0].coef[sseq[0].ord];
00325 
00326         for (s = sseq + 1; s <= sseq + np; s++) {
00327                         f = s->coef[s->ord];
00328                         if (lf == 0.0 || lf * f < 0)
00329                                 atposinf++;
00330                 lf = f;
00331         }
00332 
00333         /*
00334          * changes at negative infinity
00335          */
00336         if (sseq[0].ord & 1)
00337                         lf = -sseq[0].coef[sseq[0].ord];
00338         else
00339                         lf = sseq[0].coef[sseq[0].ord];
00340 
00341         for (s = sseq + 1; s <= sseq + np; s++) {
00342                         if (s->ord & 1)
00343                                 f = -s->coef[s->ord];
00344                         else
00345                                 f = s->coef[s->ord];
00346                         if (lf == 0.0 || lf * f < 0)
00347                                 atneginf++;
00348                         lf = f;
00349         }
00350 
00351         *atneg = atneginf;
00352         *atpos = atposinf;
00353         //      printf("atneginf, atposinf = %d %d\n", atneginf, atposinf);
00354         return(atneginf - atposinf);
00355 }
00356 
00357 /*
00358  * numchanges
00359  *
00360  *      return the number of sign changes in the Sturm sequence in
00361  * sseq at the value a.
00362  */
00363 int numchanges(int np, poly *sseq, double a)
00364 //      int             np;
00365 //      poly    *sseq;
00366 //      double  a;
00367 
00368 {
00369         int             changes;
00370         double  f, lf;
00371         poly    *s;
00372 
00373         changes = 0;
00374 
00375         lf = evalpoly(sseq[0].ord, sseq[0].coef, a);
00376 
00377         for (s = sseq + 1; s <= sseq + np; s++) {
00378                         f = evalpoly(s->ord, s->coef, a);
00379                         if (lf == 0.0 || lf * f < 0)
00380                                 changes++;
00381                         lf = f;
00382 //                      printf("lf %lf %d \n", f, changes);
00383         }
00384 
00385         //      printf("%d \n", changes);
00386         return(changes);
00387 }
00388 
00389 /*
00390  * sbisect
00391  *
00392  *      uses a bisection based on the sturm sequence for the polynomial
00393  * described in sseq to isolate intervals in which roots occur,
00394  * the roots are returned in the roots array in order of magnitude.
00395  */
00396 void sbisect(int np, poly *sseq, double min, double max, int atmin, int atmax, double *roots)
00397 //      int     np;
00398 //      poly    *sseq;
00399 //      double  min, max;
00400 //      int             atmin, atmax;
00401 //      double  *roots;
00402 {
00403   double        mid=0.;
00404   int           n1 = 0, n2 = 0, its, atmid, nroot;
00405 
00406   if ((nroot = atmin - atmax) == 1) 
00407     {
00408 
00409       /*
00410        * first try a less expensive technique.
00411        */
00412       //      printf("min max %lf, %lf \n", min, max);
00413       if (modrf(sseq->ord, sseq->coef, min, max, &roots[0]))
00414         return;
00415 
00416       //      printf("try hard way\n");
00417       /*
00418        * if we get here we have to evaluate the root the hard
00419        * way by using the Sturm sequence.
00420        */
00421       for (its = 0; its < MAXIT; its++) 
00422         {
00423           mid = (min + max) / 2;
00424           
00425           atmid = numchanges(np, sseq, mid);
00426           
00427           if (fabs(mid) > RELERROR) 
00428             {
00429               if (fabs((max - min) / mid) < RELERROR) 
00430                 {
00431                   roots[0] = mid;
00432                   return;
00433                 }
00434             } 
00435           else if (fabs(max - min) < RELERROR) 
00436             {
00437               roots[0] = mid;
00438               return;
00439             }
00440           
00441           if ((atmin - atmid) == 0)
00442             min = mid;
00443           else
00444             max = mid;
00445         }
00446       
00447       if (its == MAXIT) 
00448         {
00449           fprintf(stderr, "sbisect: overflow min %f max %f\
00450                                         diff %e nroot %d n1 %d n2 %d\n",
00451                   min, max, max - min, nroot, n1, n2);
00452           roots[0] = mid;
00453         }
00454       return;
00455     }
00456 
00457   /*
00458    * more than one root in the interval, we have to bisect...
00459    */
00460 
00461   for (its = 0; its < MAXIT; its++) 
00462     {
00463 
00464       mid = (min + max) / 2;
00465       
00466       atmid = numchanges(np, sseq, mid);
00467 
00468       n1 = atmin - atmid;
00469       n2 = atmid - atmax;
00470 
00471       if (n1 != 0 && n2 != 0) 
00472         {
00473           sbisect(np, sseq, min, mid, atmin, atmid, roots);
00474           sbisect(np, sseq, mid, max, atmid, atmax, &roots[n1]);
00475           break;
00476         }
00477       
00478       if (n1 == 0)
00479         min = mid;
00480       else
00481         max = mid;
00482     }
00483 
00484   if (its == MAXIT) 
00485     {
00486       /*
00487       fprintf(stderr, "sbisect: roots too close together\n");
00488       fprintf(stderr, "sbisect: overflow min %f max %f diff %e\
00489                                 nroot %d n1 %d n2 %d\n",
00490               min, max, max - min, nroot, n1, n2);
00491       */
00492       for (n1 = atmax; n1 < atmin; n1++)
00493         roots[n1 - atmax] = mid;
00494     }
00495 }
00496 
00497 /*
00498  * evalpoly
00499  *
00500  *      evaluate polynomial defined in coef returning its value.
00501  */
00502 double evalpoly(int ord, double *coef, double x)
00503 //      int             ord;
00504 //      double  *coef, x;
00505 {
00506         double  *fp, f;
00507 
00508         fp = &coef[ord];
00509         f = *fp;
00510 
00511         for (fp--; fp >= coef; fp--)
00512         f = x * f + *fp;
00513 
00514         return(f);
00515 }
00516 
00517 
00518 /*
00519  * modrf
00520  *
00521  *      uses the modified regula-falsi method to evaluate the root
00522  * in interval [a,b] of the polynomial described in coef. The
00523  * root is returned is returned in *val. The routine returns zero
00524  * if it can't converge.
00525  */
00526 int modrf(int ord, double *coef, double a, double b, double *val)
00527 {
00528   int its;
00529   double fa, fb, x, fx, lfx;
00530   double *fp, *scoef, *ecoef;
00531 
00532   scoef = coef;
00533   ecoef = &coef[ord];
00534 
00535   fb = fa = *ecoef;
00536   for (fp = ecoef - 1; fp >= scoef; fp--) {
00537     fa = a * fa + *fp;
00538     fb = b * fb + *fp;
00539   }
00540 
00541   /*
00542    * if there is no sign difference the method won't work
00543    */
00544   if (fa * fb > 0.0)
00545     return(0);
00546 
00547   /*  commented out to avoid duplicate solutions when the bounds are close to the roots
00548 
00549   if (fabs(fa) < RELERROR) 
00550     {
00551       *val = a;
00552       return(1);
00553     }
00554   
00555   if (fabs(fb) < RELERROR) 
00556     {
00557       *val = b;
00558       return(1);
00559     }
00560   */
00561 
00562   lfx = fa;
00563 
00564   for (its = 0; its < MAX_ITER_SECANT; its++) 
00565     {
00566       x = (fb * a - fa * b) / (fb - fa);
00567 
00568       // constrain that x stays in the bounds
00569       if (x < a || x > b)
00570         x = 0.5 * (a+b);
00571 
00572       fx = *ecoef;
00573       for (fp = ecoef - 1; fp >= scoef; fp--)
00574         fx = x * fx + *fp;
00575 
00576       if (fabs(x) > RELERROR) 
00577         {
00578           if (fabs(fx / x) < RELERROR) 
00579             {
00580               *val = x;
00581               //              printf(" x, fx %lf %lf\n", x, fx);
00582               return(1);
00583             }
00584         } 
00585       else if (fabs(fx) < RELERROR) 
00586         {
00587           *val = x;
00588           //      printf(" x, fx %lf %lf\n", x, fx);
00589           return(1);
00590         }
00591 
00592       if ((fa * fx) < 0) 
00593         {
00594           b = x;
00595           fb = fx;
00596           if ((lfx * fx) > 0)
00597             fa /= 2;
00598         } 
00599       else 
00600         {
00601           a = x;
00602           fa = fx;
00603           if ((lfx * fx) > 0)
00604             fb /= 2;
00605         }
00606 
00607       lfx = fx;
00608     }
00609 
00610   //fprintf(stderr, "modrf overflow %f %f %f\n", a, b, fx);
00611 
00612   return(0);
00613 }
00614 

Generated on Wed May 16 20:22:08 2007 for LoopTK: Protein Loop Kinematic Toolkit by  doxygen 1.5.1