opennurbs_optimize.cpp
Go to the documentation of this file.
00001 /* $NoKeywords: $ */
00002 /*
00003 //
00004 // Copyright (c) 1993-2012 Robert McNeel & Associates. All rights reserved.
00005 // OpenNURBS, Rhinoceros, and Rhino3D are registered trademarks of Robert
00006 // McNeel & Associates.
00007 //
00008 // THIS SOFTWARE IS PROVIDED "AS IS" WITHOUT EXPRESS OR IMPLIED WARRANTY.
00009 // ALL IMPLIED WARRANTIES OF FITNESS FOR ANY PARTICULAR PURPOSE AND OF
00010 // MERCHANTABILITY ARE HEREBY DISCLAIMED.
00011 //                              
00012 // For complete openNURBS copyright information see <http://www.opennurbs.org>.
00013 //
00015 */
00016 
00017 #include "pcl/surface/3rdparty/opennurbs/opennurbs.h"
00018 
00019 
00020 
00021 int ON_FindLocalMinimum(
00022                 int (*f)(void*,double,double*,double*), void* farg,
00023                 double ax, double bx, double cx,
00024                 double rel_stepsize_tol, double abs_stepsize_tol, int max_it, 
00025                 double *t_addr
00026                 )
00027 /* Use Brent's algorithm (with derivative) to Find a (local) minimum of a function
00028  *
00029  * INPUT:
00030  *   ax, bx, cx a bracketed minimum satisfying conditions 1 and 2.
00031  *      1) either ax < bx < cx or cx < bx < ax.
00032  *      2) f(bx) < f(ax) and f(bx) < f(ax).
00033  *   farg
00034  *      pointer passed to function f()
00035  *   f
00036  *      evaluation function with prototype
00037  *              int f(void* farg,double t,double* ft,double* dft)
00038  *      f(farg,t,&ft,&dft) should compute ft = value of function at t
00039  *      and dft = value of derivative at t.
00040  *      -1: failure
00041  *       0: success
00042  *       1: |f(x)| is small enough - TL_NRdbrent() will return *t_addr = x
00043  *          and the return code 1.
00044  *   rel_stepsize_tol, abs_stepsize_tol  (0 < rel_stepsize_tol < 1 and 0 < abs_stepsize_tol)
00045  *      rel_stepsize_tol is a fractional tolerance and abs_stepsize_tol is an absolute tolerance
00046  *      that determine the minimum step size for a given iteration.
00047  *        minimum delta t = rel_stepsize_tol*|t| + abs_stepsize_tol.
00048  *      When in doubt, use 
00049  *         rel_stepsize_tol = ON_EPSILON 
00050  *         abs_stepsize_tol = 1/2*(desired absolute precision for *t_addr).
00051  *   max_it ( >= 2)
00052  *      maximum number of iterations to permit (when in doubt use 100)
00053  *      Closest Point to bezier minimizations typically take < 30
00054  *      iterations.
00055  *
00056  * OUTPUT:
00057  *   *t_addr abcissa of a local minimum between ax and cx.
00058  *       0: failure
00059  *       1: success
00060  *       2: After max_iteration_cnt iterations the tolerance restrictions
00061  *          where not satisfied.  Try increasing max_it, rel_stepsize_tol and/or abs_stepsize_tol
00062  *          or use the value of (*t_addr) with extreme caution.
00063  */
00064 {
00065   // See Numerical Recipes in C's dbrent() for a description of the basic algorithm
00066   int rc,ok1,ok2;
00067   double a,b,d,d1,d2,du,dv,dw,dx,e,fu,fv,fw,fx,olde,tol1,tol2,u,u1,u2,v,w,x,xm;
00068 
00069   d=e=0.0;
00070 
00071   if ( 0 == t_addr )
00072   {
00073     ON_ERROR("t_addr is NULL");
00074     return 0;
00075   }
00076 
00077   *t_addr = bx;
00078 
00079   if ( max_it < 2 )
00080   {
00081     ON_ERROR("max_it must be >= 2");
00082     return 0;
00083   }
00084   if ( !ON_IsValid(rel_stepsize_tol) || rel_stepsize_tol <= 0.0 || rel_stepsize_tol >= 1.0 )
00085   {
00086     ON_ERROR("rel_stepsize_tol must be strictly between 0.0 and 1.0");
00087     return 0;
00088   }
00089   if ( !ON_IsValid(abs_stepsize_tol) || abs_stepsize_tol <= 0.0 )
00090   {
00091     ON_ERROR("abs_stepsize_tol must be > 0");
00092     return 0;
00093   }
00094 
00095   a=(ax < cx ? ax : cx);
00096   b=(ax > cx ? ax : cx);
00097   x=w=v=bx;
00098   rc = f(farg,x,&fx,&dx);
00099   if (rc) {
00100      // f() returned nonzero return code which means we need to bailout
00101     if ( rc < 0 ) {
00102       ON_ERROR("ON_FindLocalMinimum() f() failed to evaluate.");
00103     }
00104     *t_addr = x;
00105     return rc>0 ? 1 : 0; // return 1 means f() said result is good enough, return = 0 means f() failed
00106   }
00107   fw=fv=fx;
00108   dw=dv=dx;
00109   while(max_it--) {
00110     xm=0.5*(a+b);
00111     tol1=rel_stepsize_tol*fabs(x)+abs_stepsize_tol;
00112     tol2=2.0*tol1;
00113     if (fabs(x-xm) <= (tol2-0.5*(b-a))) {
00114       // further adjustments to x are smaller than stepsize tolerance
00115       *t_addr=x;
00116       return 1;
00117     }
00118     if (fabs(e) > tol1) {
00119       d1=2.0*(b-a);
00120       d2=d1;
00121       if (dw != dx) d1=(w-x)*dx/(dx-dw);
00122       if (dv != dx) d2=(v-x)*dx/(dx-dv);
00123       u1=x+d1;
00124       u2=x+d2;
00125       ok1 = (a-u1)*(u1-b) > 0.0 && dx*d1 <= 0.0;
00126       ok2 = (a-u2)*(u2-b) > 0.0 && dx*d2 <= 0.0;
00127       olde=e;
00128       e=d;
00129       if (ok1 || ok2) {
00130         if (ok1 && ok2)
00131           d=(fabs(d1) < fabs(d2) ? d1 : d2);
00132         else if (ok1)
00133           d=d1;
00134         else
00135           d=d2;
00136         if (fabs(d) <= fabs(0.5*olde)) {
00137           u=x+d;
00138           if (u-a < tol2 || b-u < tol2)
00139             {d = (xm >= x) ? tol1 : -tol1;}
00140         } else {
00141           d=0.5*(e=(dx >= 0.0 ? a-x : b-x));
00142         }
00143       } else {
00144         d=0.5*(e=(dx >= 0.0 ? a-x : b-x));
00145       }
00146     } else {
00147       d=0.5*(e=(dx >= 0.0 ? a-x : b-x));
00148     }
00149     if (fabs(d) >= tol1) {
00150       u=x+d;
00151       rc = f(farg,u,&fu,&du);
00152     }
00153     else {
00154       u = (d >= 0.0) ? x+tol1 : x-tol1;
00155       rc = f(farg,u,&fu,&du);
00156       if (rc >= 0 && fu > fx) {
00157         // tweaking x any more increases function value - x is a numerical minimum
00158         *t_addr=x;
00159         return 1;
00160       }
00161     }
00162     if (rc) {
00163       // f() returned nonzero return code which means we need to bailout
00164       if ( rc < 0 ) {
00165         ON_ERROR("ON_FindLocalMinimum() f() failed to evaluate.");
00166       }
00167       else {
00168         *t_addr = (fu < fx) ? u : x;
00169       }
00170       return rc>0 ? 1 : 0;
00171     }
00172     if (fu <= fx) {
00173       if (u >= x) a=x; else b=x;
00174       v=w;fv=fw;dv=dw;
00175       w=x;fw=fx;dw=dx;
00176       x=u;fx=fu;dx=du;
00177     } else {
00178       if (u < x) a=u; else b=u;
00179       if (fu <= fw || w == x) {
00180         v=w;fv=fw;dv=dw;
00181         w=u;fw=fu;dw=du;
00182       } else if (fu < fv || v == x || v == w) {
00183         v=u;fv=fu;dv=du;
00184       }
00185     }
00186   }
00187   *t_addr = x; // best known answer
00188   ON_ERROR("ON_FindLocalMinimum() failed to converge");
00189   return 2; // 2 means we failed to converge
00190 }
00191 
00192 
00193 ON_LocalZero1::ON_LocalZero1() 
00194                : m_t0(ON_UNSET_VALUE), m_t1(ON_UNSET_VALUE),
00195                  m_f_tolerance(0.0), m_t_tolerance(0.0),
00196                  m_k(NULL), m_k_count(0)
00197 {}
00198 
00199 ON_LocalZero1::~ON_LocalZero1()
00200 {}
00201 
00202 ON_BOOL32
00203 ON_LocalZero1::BracketZero( double s0, double f0, 
00204                              double s1, double f1,
00205                              int level )
00206 {
00207   double s, f, d;
00208 
00209   // private helper for FindSearchDomain()
00210   if (    (f0 <= 0.0 && f1 >= 0.0) || (f0 >= 0.0 && f1 <= 0.0)
00211        || fabs(f0) <= m_f_tolerance || fabs(f1) <= m_f_tolerance ) {
00212     m_t0 = s0;
00213     m_t1 = s1;
00214     return true;
00215   }
00216 
00217   if ( level++ <= 8 ) {
00218     s = 0.5*s0+s1;
00219     if ( s0 < s && s < s1 && Evaluate(s,&f,&d,0) ) {
00220       if ( f*d >= 0.0 ) {
00221         // search left side first
00222         if ( BracketZero(s0,f0,s,f,level ) ) {
00223           m_s0 = s0;
00224           m_f0 = f0;
00225           m_s1 = s;
00226           m_f1 = f;
00227           return true;
00228         }
00229         if ( BracketZero(s,f,s1,f1,level ) ) {
00230           m_s0 = s;
00231           m_f0 = f;
00232           m_s1 = s1;
00233           m_f1 = f1;
00234           return true;
00235         }
00236       }
00237       else {
00238         // search right side first
00239         if ( BracketZero(s,f,s1,f1,level ) ) {
00240           m_s0 = s;
00241           m_f0 = f;
00242           m_s1 = s1;
00243           m_f1 = f1;
00244           return true;
00245         }
00246         if ( BracketZero(s0,f0,s,f,level ) ) {
00247           m_s0 = s0;
00248           m_f0 = f0;
00249           m_s1 = s;
00250           m_f1 = f;
00251           return true;
00252         }
00253       }
00254     }
00255   }
00256   return false;
00257 }
00258 
00259 ON_BOOL32
00260 ON_LocalZero1::BracketSpan( double s0, double f0, double s1, double f1 )
00261 {
00262   int i0, i1, i;
00263   double fm, fp;
00264   ON_BOOL32 rc = true;
00265   if ( m_k && m_k_count >= 3 ) {
00266     i0 = ON_SearchMonotoneArray(m_k,m_k_count,s0);
00267     if ( i0 < 0 )
00268       i0 = 0;
00269     i1 = ON_SearchMonotoneArray(m_k,m_k_count,s1);
00270     if ( i1 >= m_k_count )
00271       i1 = m_k_count-1;
00272     while ( i1 >= 0 && s1 == m_k[i1] ) {
00273       i1--;
00274     }
00275     i0++;
00276     while ( i0 < m_k_count-1 && m_k[i0] == m_k[i0+1] )
00277       i0++;
00278     if ( i0 <= i1 ) {
00279       // we have s0 < m_k[i0] <= ... <= m_k[i1] < s1
00280       Evaluate( m_k[i0], &fm, NULL,-1 ); // gaurd against C0 discontinuities
00281       Evaluate( m_k[i0], &fp, NULL, 1 );
00282       if ( (f0 <= 0.0 && fm >= 0.0) || (f0 >= 0.0 && fm <= 0.0) ) {
00283         m_s1 = m_k[i0];
00284         m_f1 = fm;
00285       }
00286       else if ( (f1 <= 0.0 && fp >= 0.0) || (f1 >= 0.0 && fp <= 0.0) ) {
00287         m_s0 = m_k[i0];
00288         m_f0 = fp;
00289         if ( i0 < i1 ) {
00290           Evaluate( m_k[i1], &fm, NULL, -1 );
00291           Evaluate( m_k[i1], &fp, NULL,  1 );
00292           if ( (f1 <= 0.0 && fp >= 0.0) || (f1 >= 0.0 && fp <= 0.0) ) {
00293             m_s0 = m_k[i1];
00294             m_f0 = fp;
00295           }
00296           else if ( (f0 <= 0.0 && fm >= 0.0) || (f0 >= 0.0 && fm <= 0.0) ) {
00297             m_s1 = m_k[i1];
00298             m_f1 = fm;
00299             // we have s0 = m_k[i0] < m_k[i1] = s1 and a bonafide sign change
00300             while (i0+1 < i1) {
00301               // bisect m_k[i0],...,m_k[i1] until we get a sign change between
00302               // m_k[i],m_k[i+1].  We need to do this in order to make sure
00303               // we are passing a C2 function to the repeated zero finders.
00304               i = (i0+i1)>>1;
00305               Evaluate( m_k[i], &fm, NULL, -1 );
00306               Evaluate( m_k[i], &fp, NULL,  1 );
00307               if ( (f0 <= 0.0 && fm >= 0.0) || (f0 >= 0.0 && fm <= 0.0) ) {
00308                 m_s1 = m_k[i];
00309                 m_f1 = fm;
00310                 i1 = i;
00311                 while ( i1>0 && m_k[i1-1]==m_k[i1])
00312                   i1--;
00313               }
00314               else if ( (f1 <= 0.0 && fp >= 0.0) || (f1 >= 0.0 && fp <= 0.0) ) {
00315                 m_s0 = m_k[i];
00316                 m_f0 = fp;
00317                 i0 = i;
00318                 while ( i0 < m_k_count-2 && m_k[i0] == m_k[i0+1] )
00319                   i0++;
00320               }
00321               else {
00322                 // discontinuous sign change across m_k[i]
00323                 rc = false;
00324                 break;
00325               }
00326             }
00327           }
00328           else {
00329             // discontinuous sign change across m_k[i1]
00330             rc = false;
00331           }
00332         }
00333       }
00334       else {
00335         // discontinuous sign change across m_k[i0]
00336         rc = false;
00337       }
00338     }
00339   }
00340   return rc;
00341 }
00342 
00343 ON_BOOL32 ON_LocalZero1::FindZero( double* t )
00344 {
00345   // Find values of m_t0 and m_t1 between t0 and t1 such that
00346   // f(m_t0) and f(m_t1) have different signs
00347   ON_BOOL32 rc = ( m_t0 == ON_UNSET_VALUE || m_t0 == ON_UNSET_VALUE ) ? true : false;
00348 
00349   if ( rc ) {
00350     if ( m_t0 < m_t0 ) {
00351       m_s0 = m_t0;
00352       m_s1 = m_t0;
00353     }
00354     else {
00355       m_s0 = m_t1;
00356       m_s1 = m_t0;
00357       if ( m_t0 == m_t1 ) {
00358         if ( Evaluate( m_t0, &m_f0, NULL, 1 ) ) {
00359           m_f1 = m_f0;
00360           if ( fabs(m_f0) <= m_f_tolerance ) {
00361             *t = m_t0;
00362             return true;
00363           }
00364         }
00365         ON_ERROR("Illegal input");
00366         return false;
00367       }
00368     }
00369   }
00370 
00371   if (rc)
00372     rc = Evaluate( m_s0, &m_f0, NULL, 1 );
00373   if (rc)
00374     rc = Evaluate( m_s1, &m_f1, NULL, -1 );
00375 
00376   if (rc)
00377     rc = BracketZero( m_s0, m_f0, m_s1, m_f1 );
00378   if ( rc ) {
00379     if ( fabs(m_f0) <= m_f_tolerance && fabs(m_f0) <= fabs(m_f1) ) {
00380       // |f(s0)| <= user specified stopping tolerance
00381       *t = m_s0;
00382     }
00383     else if ( fabs(m_f1) <= m_f_tolerance ) {
00384       // |f(s1)| <= user specified stopping tolerance
00385       *t = m_s1;
00386     }
00387     else {
00388       if (rc)
00389         rc = BracketSpan( m_s0, m_f0, m_s1, m_f1 );
00390       if (rc)
00391         rc = NewtonRaphson( m_s0, m_f0, m_s1, m_f1, 128, t );
00392     }
00393   }
00394   if (!rc) {
00395     ON_ERROR("ON_LocalZero1::FindZero() failed");
00396   }
00397 
00398   return rc;
00399 }
00400 
00401 ON_BOOL32 ON_LocalZero1::NewtonRaphson( double s0, double f0,
00402                                     double s1, double f1,
00403                                     int maxit, double* t )
00404 {
00405   // private function - input must satisfy
00406   //
00407   // 1) t is not NULL
00408   //
00409   // 2) maxit >= 2
00410   //
00411   // 3) s0 != s1, 
00412   //
00413   // 4) f0 = f(s0), f1 = f(s1), 
00414   //
00415   // 5) either f0 < 0.0 and f1 > 0.0, or f0 > 0.0 and f1 < 0.0
00416   //
00417   // 6) f() is C0 on [s0,s1] and C2 on (s0,s1)
00418   //
00419   // 7) ds_tolerance >= 0.0 - the search for a zero will stop
00420   //    if the zero in bracketed in an interval that is no
00421   //    larger than ds_tolerance.
00422   //
00423   //    When in doubt, ds_tolerance = (fabs(s0) + fabs(s1))*ON_EPSILON
00424   //    works well.
00425 
00426   double s, f, d, x, ds, prevds;
00427 
00428   if ( fabs(f0) <= m_f_tolerance && fabs(f0) <= fabs(f1) ) {
00429     // |f(s0)| <= user specified stopping tolerance
00430     *t = s0;
00431     return true;
00432   }
00433   if ( fabs(f1) <= m_f_tolerance ) {
00434     // |f(s1)| <= user specified stopping tolerance
00435     *t = s1;
00436     return true;
00437   }
00438 
00439   if ( f0 > 0.0 ) {
00440     x = s0; s0 = s1; s1 = x;
00441     x = f0; f0 = f1; f1 = x;
00442   }
00443 
00444   s = 0.5*(s0+s1);
00445   if ( !Evaluate( s, &f, &d, 0 ) ) {
00446     *t = (fabs(f0) <= fabs(f1)) ? s0 : s1;
00447     return false;
00448   }
00449 
00450   if ( fabs(f) <= m_f_tolerance ) {
00451     // |f(s)| <= user specified stopping tolerance
00452     *t = s;
00453     return true;
00454   }
00455 
00456   if ( f1 <= 0.0 ) {
00457     *t = (fabs(f0) <= fabs(f1)) ? s0 : s1;
00458     return false;
00459   }
00460 
00461   ds = fabs(s1-s0);
00462   prevds = 0.0;
00463 
00464   while (maxit--) {
00465     if ( (f+(s0-s)*d)*(f+(s1-s)*d) > 0.0  // true if NR's line segment doesn't cross zero inside interval
00466          || fabs(2.0*f) > fabs(prevds*d)  // true if expected decrease in function value from previous step didn't happen
00467          ) {
00468       // bisect
00469       prevds = ds;
00470       ds = 0.5*(s1-s0);
00471       s = s0+ds;
00472       if ( s == s0 ) {
00473         // interval is too small to be divided using double division
00474         if ( fabs(f1) < fabs(f0) ) {
00475           s = s1;
00476         }
00477         *t = s;
00478         return true;
00479       }      
00480     }
00481     else {
00482       // Newton iterate
00483       prevds = ds;
00484       ds = -f/d;
00485       x = s;
00486       s += ds;
00487       if ( s == x ) {
00488         // Newton step size < smallest double than can be added to s
00489         if ( fabs(f0) < fabs(f) ) {
00490           f = f0;
00491           s = s0;
00492         }
00493         if ( fabs(f1) < fabs(f) ) {
00494           s = s1;
00495         }
00496         *t = s;
00497         return true;
00498       } 
00499     }
00500 
00501     if ( !Evaluate( s, &f, &d, 0 ) ) {
00502       *t = (fabs(f0) <= fabs(f1)) ? s0 : s1; // emergency bailout
00503       return false;
00504     }
00505 
00506     if ( fabs(f) <= m_f_tolerance ) {
00507       // |f(s)| <= user specified stopping tolerance
00508       if ( fabs(f0) < fabs(f) ) {
00509         f = f0;
00510         *t = s0;
00511       }
00512       if ( fabs(f1) < fabs(f) ) {
00513         *t = s1;
00514       }
00515       return true;
00516     }
00517 
00518     if ( f < 0.0 ) {
00519       f0 = f; // f0 needed for emergency bailout
00520       s0 = s;
00521     }
00522     else { // f > 0.0
00523       f1 = f; // f1 needed for emergency bailout
00524       s1 = s;
00525     }
00526 
00527     if ( fabs(s1-s0) <= m_t_tolerance ) {
00528       // a root has been bracketed to an interval that is small enough
00529       // to satisify user.
00530       *t = (fabs(f0) <= fabs(f1)) ? s0 : s1;
00531       return true;
00532     }
00533   }
00534 
00535   *t = (fabs(f0) <= fabs(f1)) ? s0 : s1; // emergency bailout
00536   return false;
00537 }
00538 


pcl
Author(s): Open Perception
autogenerated on Wed Aug 26 2015 15:27:02