roots.c

Go to the documentation of this file.
00001 /*                         R O O T S . C
00002  * BRL-CAD
00003  *
00004  * Copyright (c) 1985-2006 United States Government as represented by
00005  * the U.S. Army Research Laboratory.
00006  *
00007  * This library is free software; you can redistribute it and/or
00008  * modify it under the terms of the GNU Lesser General Public License
00009  * as published by the Free Software Foundation; either version 2 of
00010  * the License, or (at your option) any later version.
00011  *
00012  * This library is distributed in the hope that it will be useful, but
00013  * WITHOUT ANY WARRANTY; without even the implied warranty of
00014  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00015  * Library General Public License for more details.
00016  *
00017  * You should have received a copy of the GNU Lesser General Public
00018  * License along with this file; see the file named COPYING for more
00019  * information.
00020  */
00021 
00022 /** @addtogroup librt */
00023 /*@{*/
00024 /** @file roots.c
00025  * Find the roots of a polynomial
00026  *
00027  *  Author -
00028  *      Jeff Hanes
00029  *
00030  *  Source -
00031  *      SECAD/VLD Computing Consortium, Bldg 394
00032  *      The U. S. Army Ballistic Research Laboratory
00033  *      Aberdeen Proving Ground, Maryland  21005
00034  *
00035  */
00036 /*@}*/
00037 
00038 #ifndef lint
00039 static const char RCSroots[] = "@(#)$Header: /cvsroot/brlcad/brlcad/src/librt/roots.c,v 14.12 2006/09/16 02:04:26 lbutler Exp $ (BRL)";
00040 #endif
00041 
00042 #include "common.h"
00043 
00044 
00045 
00046 #include <stdio.h>
00047 #include <math.h>
00048 #include "machine.h"
00049 #include "bu.h"
00050 #include "vmath.h"
00051 #include "bn.h"
00052 #include "raytrace.h"
00053 
00054 int     rt_poly_roots(register bn_poly_t *eqn, register bn_complex_t *roots, const char *name);
00055 void    rt_poly_eval_w_2derivatives(register bn_complex_t *cZ, register bn_poly_t *eqn, register bn_complex_t *b, register bn_complex_t *c, register bn_complex_t *d);
00056 void    rt_poly_deflate(register bn_poly_t *oldP, register bn_complex_t *root);
00057 int     rt_poly_findroot(register bn_poly_t *eqn, register bn_complex_t *nxZ, const char *str);
00058 int     rt_poly_checkroots(register bn_poly_t *eqn, bn_complex_t *roots, register int nroots);
00059 
00060 /*
00061  *                      R T _ P O L Y _ R O O T S
00062  *
00063  *      WARNING:  The polynomial given as input is destroyed by this
00064  *              routine.  The caller must save it if it is important!
00065  *
00066  *      NOTE :  This routine is written for polynomials with real coef-
00067  *              ficients ONLY.  To use with complex coefficients, the
00068  *              Complex Math library should be used throughout.
00069  *              Some changes in the algorithm will also be required.
00070  */
00071 int
00072 rt_poly_roots(register bn_poly_t        *eqn,   /* equation to be solved */
00073               register bn_complex_t     roots[], /* space to put roots found */
00074               const char *name) /* name of the primitive being checked */
00075 {
00076         register int    n;              /* number of roots found        */
00077         LOCAL fastf_t   factor;         /* scaling factor for copy      */
00078 
00079         /* Remove leading coefficients which are too close to zero,
00080          * to prevent the polynomial factoring from blowing up, below.
00081          */
00082         while( NEAR_ZERO( eqn->cf[0], SMALL ) )  {
00083                 for ( n=0; n <= eqn->dgr; n++ ){
00084                         eqn->cf[n] = eqn->cf[n+1];
00085                 }
00086                 if ( --eqn->dgr <= 0 )
00087                         return 0;
00088         }
00089 
00090         /* Factor the polynomial so the first coefficient is one
00091          * for ease of handling.
00092          */
00093         factor = 1.0 / eqn->cf[0];
00094         (void) bn_poly_scale( eqn, factor );
00095         n = 0;          /* Number of roots found */
00096 
00097         /* A trailing coefficient of zero indicates that zero
00098          * is a root of the equation.
00099          */
00100         while( NEAR_ZERO( eqn->cf[eqn->dgr], SMALL ) )  {
00101                 roots[n].re = roots[n].im = 0.0;
00102                 --eqn->dgr;
00103                 ++n;
00104         }
00105 
00106         while ( eqn->dgr > 2 ){
00107                 if ( eqn->dgr == 4 )  {
00108                         if( rt_poly_quartic_roots( eqn, &roots[n] ) )  {
00109                                 if( rt_poly_checkroots( eqn, &roots[n], 4 ) == 0 )  {
00110                                         return( n+4 );
00111                                 }
00112                         }
00113                 } else if ( eqn->dgr == 3 )  {
00114                         if( rt_poly_cubic_roots( eqn, &roots[n] ) )  {
00115                                 if ( rt_poly_checkroots( eqn, &roots[n], 3 ) == 0 )  {
00116                                         return ( n+3 );
00117                                 }
00118                         }
00119                 }
00120 
00121                 /*
00122                  *  Set initial guess for root to almost zero.
00123                  *  This method requires a small nudge off the real axis.
00124                  */
00125                 bn_cx_cons( &roots[n], 0.0, SMALL );
00126                 if ( (rt_poly_findroot( eqn, &roots[n], name )) < 0 )
00127                         return(n);      /* return those we found, anyways */
00128 
00129                 if ( fabs(roots[n].im) > 1.0e-5* fabs(roots[n].re) ){
00130                         /* If root is complex, its complex conjugate is
00131                          * also a root since complex roots come in con-
00132                          * jugate pairs when all coefficients are real.
00133                          */
00134                         ++n;
00135                         roots[n] = roots[n-1];
00136                         bn_cx_conj(&roots[n]);
00137                 } else {
00138                         /* Change 'practically real' to real            */
00139                         roots[n].im = 0.0;
00140                 }
00141 
00142                 rt_poly_deflate( eqn, &roots[n] );
00143                 ++n;
00144         }
00145 
00146         /* For polynomials of lower degree, iterative techniques
00147          * are an inefficient way to find the roots.
00148          */
00149         if ( eqn->dgr == 1 ){
00150                 roots[n].re = -(eqn->cf[1]);
00151                 roots[n].im = 0.0;
00152                 ++n;
00153         } else if ( eqn->dgr == 2 ){
00154                 rt_poly_quadratic_roots( eqn, &roots[n] );
00155                 n += 2;
00156         }
00157         return n;
00158 }
00159 
00160 /*
00161  *                      R T _ P O L Y _ F I N D R O O T
00162  *
00163  *      Calculates one root of a polynomial ( p(Z) ) using Laguerre's
00164  *      method.  This is an iterative technique which has very good
00165  *      global convergence properties.  The formulas for this method
00166  *      are
00167  *
00168  *                                      n * p(Z)
00169  *              newZ  =  Z  -  -----------------------  ,
00170  *                              p'(Z) +- sqrt( H(Z) )
00171  *
00172  *      where
00173  *              H(Z) = (n-1) [ (n-1)(p'(Z))^2 - n p(Z)p''(Z) ],
00174  *
00175  *      where n is the degree of the polynomial.  The sign in the
00176  *      denominator is chosen so that  |newZ - Z|  is as small as
00177  *      possible.
00178  *
00179  */
00180 int
00181 rt_poly_findroot(register bn_poly_t *eqn, /* polynomial */
00182                  register bn_complex_t *nxZ, /* initial guess for root  */
00183                  const char *str)
00184 {
00185         LOCAL bn_complex_t  p0, p1, p2; /* evaluated polynomial+derivatives */
00186         LOCAL bn_complex_t  p1_H;               /* p1 - H, temporary */
00187         LOCAL bn_complex_t  cZ, cH;             /* 'Z' and H(Z) in comment      */
00188         LOCAL bn_complex_t  T;          /* temporary for making H */
00189         FAST fastf_t    diff=0.0;               /* test values for convergence  */
00190         FAST fastf_t    b=0.0;          /* floating temps */
00191         LOCAL int       n;
00192         register int    i;              /* iteration counter            */
00193 
00194         for( i=0; i < 20; i++ ) {
00195                 cZ = *nxZ;
00196                 rt_poly_eval_w_2derivatives( &cZ, eqn, &p0, &p1, &p2 );
00197 
00198                 /* Compute H for Laguerre's method. */
00199                 n = eqn->dgr-1;
00200                 bn_cx_mul2( &cH, &p1, &p1 );
00201                 bn_cx_scal( &cH, (double)(n*n) );
00202                 bn_cx_mul2( &T, &p2, &p0 );
00203                 bn_cx_scal( &T, (double)(eqn->dgr*n) );
00204                 bn_cx_sub( &cH, &T );
00205 
00206                 /* Calculate the next iteration for Laguerre's method.
00207                  * Test to see whether addition or subtraction gives the
00208                  * larger denominator for the next 'Z' , and use the
00209                  * appropriate value in the formula.
00210                  */
00211                 bn_cx_sqrt( &cH, &cH );
00212                 p1_H = p1;
00213                 bn_cx_sub( &p1_H, &cH );
00214                 bn_cx_add( &p1, &cH );          /* p1 <== p1+H */
00215                 bn_cx_scal( &p0, (double)(eqn->dgr) );
00216                 if ( bn_cx_amplsq( &p1_H ) > bn_cx_amplsq( &p1 ) ){
00217                         bn_cx_div( &p0, &p1_H);
00218                         bn_cx_sub( nxZ, &p0 );
00219                 } else {
00220                         bn_cx_div( &p0, &p1 );
00221                         bn_cx_sub( nxZ, &p0 );
00222                 }
00223 
00224                 /* Use proportional convergence test to allow very small
00225                  * roots and avoid wasting time on large roots.
00226                  * The original version used bn_cx_ampl(), which requires
00227                  * a square root.  Using bn_cx_amplsq() saves lots of cycles,
00228                  * but changes loop termination conditions somewhat.
00229                  *
00230                  * diff is |p0|**2.  nxZ = Z - p0.
00231                  *
00232                  * SGI XNS IRIS 3.5 compiler fails if following 2 assignments
00233                  * are imbedded in the IF statement, as before.
00234                  */
00235                 b = bn_cx_amplsq( nxZ );
00236                 diff = bn_cx_amplsq( &p0 );
00237                 if( b < diff )
00238                         continue;
00239                 if( (b-diff) == b )
00240                         return(i);              /* OK -- can't do better */
00241                 if( diff > (b - diff)*1.0e-5 )
00242                         continue;
00243                 return(i);                      /* OK */
00244         }
00245 
00246         /* If the thing hasn't converged yet, it probably won't. */
00247         bu_log("rt_poly_findroot: solving for %s didn't converge in %d iterations, b=%g, diff=%g\n",
00248                 str, i, b, diff);
00249         bu_log("nxZ=%gR+%gI, p0=%gR+%gI\n", nxZ->re, nxZ->im, p0.re, p0.im);
00250         return(-1);             /* ERROR */
00251 }
00252 
00253 
00254 /*
00255  *                      R T _ P O L Y _ E V A L _ W _ 2 D E R I V A T I V E S
00256  *
00257  *      Evaluates p(Z), p'(Z), and p''(Z) for any Z (real or complex).
00258  *      Given an equation of the form
00259  *
00260  *              p(Z) = a0*Z^n + a1*Z^(n-1) +... an != 0,
00261  *
00262  *      the function value and derivatives needed for the iterations
00263  *      can be computed by synthetic division using the formulas
00264  *
00265  *              p(Z) = bn,    p'(Z) = c(n-1),    p''(Z) = d(n-2),
00266  *
00267  *      where
00268  *
00269  *              b0 = a0,        bi = b(i-1)*Z + ai,     i = 1,2,...n
00270  *              c0 = b0,        ci = c(i-1)*Z + bi,     i = 1,2,...n-1
00271  *              d0 = c0,        di = d(i-1)*Z + ci,     i = 1,2,...n-2
00272  *
00273  */
00274 void
00275 rt_poly_eval_w_2derivatives(register bn_complex_t *cZ, register bn_poly_t *eqn, register bn_complex_t *b, register bn_complex_t *c, register bn_complex_t *d)
00276                                         /* input */
00277                                         /* input */
00278                                         /* outputs */
00279 {
00280         register int    n;
00281         register int    m;
00282 
00283         bn_cx_cons(b,eqn->cf[0],0.0);
00284         *c = *b;
00285         *d = *c;
00286 
00287         for ( n=1; ( m = eqn->dgr - n ) >= 0; ++n){
00288                 bn_cx_mul( b, cZ );
00289                 b->re += eqn->cf[n];
00290                 if ( m > 0 ){
00291                         bn_cx_mul( c, cZ );
00292                         bn_cx_add( c, b );
00293                 }
00294                 if ( m > 1 ){
00295                         bn_cx_mul( d, cZ );
00296                         bn_cx_add( d, c );
00297                 }
00298         }
00299 }
00300 
00301 
00302 /*
00303  *                      R T _ P O L Y _ C H E C K R O O T S
00304  *
00305  *      Evaluates p(Z) for any Z (real or complex).
00306  *      In this case, test all "nroots" entries of roots[] to ensure
00307  *      that they are roots (zeros) of this polynomial.
00308  *
00309  * Returns -
00310  *      0       all roots are true zeros
00311  *      1       at least one "root[]" entry is not a true zero
00312  *
00313  *      Given an equation of the form
00314  *
00315  *              p(Z) = a0*Z^n + a1*Z^(n-1) +... an != 0,
00316  *
00317  *      the function value can be computed using the formula
00318  *
00319  *              p(Z) = bn,      where
00320  *
00321  *              b0 = a0,        bi = b(i-1)*Z + ai,     i = 1,2,...n
00322  *
00323  */
00324 int
00325 rt_poly_checkroots(register bn_poly_t *eqn, bn_complex_t *roots, register int nroots)
00326 {
00327         register fastf_t        er, ei;         /* "epoly" */
00328         register fastf_t        zr, zi;         /* Z value to evaluate at */
00329         register int    n;
00330         int             m;
00331 
00332         for ( m=0; m < nroots; ++m ){
00333                 /* Select value of Z to evaluate at */
00334                 zr = bn_cx_real( &roots[m] );
00335                 zi = bn_cx_imag( &roots[m] );
00336 
00337                 /* Initialize */
00338                 er = eqn->cf[0];
00339                 /* ei = 0.0; */
00340 
00341                 /* n=1 step.  Broken out because ei = 0.0 */
00342                 ei = er*zi;             /* must come before er= */
00343                 er = er*zr + eqn->cf[1];
00344 
00345                 /* Remaining steps */
00346                 for ( n=2; n <= eqn->dgr; ++n)  {
00347                         register fastf_t        tr, ti; /* temps */
00348                         tr = er*zr - ei*zi + eqn->cf[n];
00349                         ti = er*zi + ei*zr;
00350                         er = tr;
00351                         ei = ti;
00352                 }
00353                 if ( fabs( er ) > 1.0e-5 || fabs( ei ) > 1.0e-5 )
00354                         return 1;       /* FAIL */
00355         }
00356         /* Evaluating polynomial for all Z values gives zero */
00357         return 0;                       /* OK */
00358 }
00359 
00360 
00361 /*
00362  *                      R T _ P O L Y _ D E F L A T E
00363  *
00364  *
00365  *      Deflates a polynomial by a given root.
00366  */
00367 void
00368 rt_poly_deflate(register bn_poly_t *oldP, register bn_complex_t *root)
00369 {
00370         LOCAL bn_poly_t div, rem;
00371 
00372         /* Make a polynomial out of the given root:  Linear for a real
00373          * root, Quadratic for a complex root (since they come in con-
00374          * jugate pairs).
00375          */
00376         if ( NEAR_ZERO( root->im, SMALL) ) {
00377                 /*  root is real                */
00378                 div.dgr = 1;
00379                 div.cf[0] = 1;
00380                 div.cf[1] = - root->re;
00381         } else {
00382                 /*  root is complex             */
00383                 div.dgr = 2;
00384                 div.cf[0] = 1;
00385                 div.cf[1] = -2 * root->re;
00386                 div.cf[2] = bn_cx_amplsq( root );
00387         }
00388 
00389         /* Use synthetic division to find the quotient (new polynomial)
00390          * and the remainder (should be zero if the root was really a
00391          * root).
00392          */
00393         rt_poly_synthetic_division( oldP, &div, oldP, &rem );
00394 }
00395 
00396 /*
00397  * Local Variables:
00398  * mode: C
00399  * tab-width: 8
00400  * c-basic-offset: 4
00401  * indent-tabs-mode: t
00402  * End:
00403  * ex: shiftwidth=4 tabstop=8
00404  */

Generated on Mon Sep 18 01:24:56 2006 for BRL-CAD by  doxygen 1.4.6