Back to home page

EIC code displayed by LXR

 
 

    


File indexing completed on 2026-09-28 09:20:54

0001 // Copyright (c) 2025 OPEN CASCADE SAS
0002 //
0003 // This file is part of Open CASCADE Technology software library.
0004 //
0005 // This library is free software; you can redistribute it and/or modify it under
0006 // the terms of the GNU Lesser General Public License version 2.1 as published
0007 // by the Free Software Foundation, with special exception defined in the file
0008 // OCCT_LGPL_EXCEPTION.txt. Consult the file LICENSE_LGPL_21.txt included in OCCT
0009 // distribution for complete text of the license and disclaimer of any warranty.
0010 //
0011 // Alternatively, this file may be used under the terms of Open CASCADE
0012 // commercial license or contractual agreement.
0013 
0014 #ifndef _MathUtils_LineSearch_HeaderFile
0015 #define _MathUtils_LineSearch_HeaderFile
0016 
0017 #include <math_Vector.hxx>
0018 #include <MathUtils_Core.hxx>
0019 
0020 #include <cmath>
0021 
0022 //! Core utilities for modern math solvers.
0023 namespace MathUtils
0024 {
0025 
0026 //! Result of line search operation.
0027 struct LineSearchResult
0028 {
0029   bool   IsValid = false; //!< True if line search succeeded
0030   double Alpha   = 0.0;   //!< Step size found
0031   double FNew    = 0.0;   //!< Function value at new point
0032   int    NbEvals = 0;     //!< Number of function evaluations
0033 };
0034 
0035 //! Backtracking line search with Armijo condition.
0036 //! Finds alpha such that f(x + alpha*d) <= f(x) + c1*alpha*grad(f).d
0037 //! (sufficient decrease condition)
0038 //!
0039 //! Algorithm:
0040 //! 1. Start with alpha = alpha_init
0041 //! 2. If Armijo condition satisfied, return alpha
0042 //! 3. Otherwise alpha = rho * alpha and repeat
0043 //!
0044 //! @tparam Function type with Value(const math_Vector&, double&) method
0045 //! @param theFunc objective function
0046 //! @param theX current point
0047 //! @param theDir search direction (should be descent direction)
0048 //! @param theGrad gradient at current point
0049 //! @param theFx function value at current point
0050 //! @param theAlphaInit initial step size (default 1.0)
0051 //! @param theC1 Armijo parameter (default 1e-4)
0052 //! @param theRho backtracking factor (default 0.5)
0053 //! @param theMaxIter maximum iterations (default 50)
0054 //! @return line search result with step size
0055 template <typename Function>
0056 LineSearchResult ArmijoBacktrack(Function&          theFunc,
0057                                  const math_Vector& theX,
0058                                  const math_Vector& theDir,
0059                                  const math_Vector& theGrad,
0060                                  double             theFx,
0061                                  double             theAlphaInit = 1.0,
0062                                  double             theC1        = 1.0e-4,
0063                                  double             theRho       = 0.5,
0064                                  int                theMaxIter   = 50)
0065 {
0066   LineSearchResult aResult;
0067   aResult.Alpha   = theAlphaInit;
0068   aResult.NbEvals = 0;
0069 
0070   const int aLower = theX.Lower();
0071   const int aUpper = theX.Upper();
0072 
0073   // Compute directional derivative: grad(f) . d
0074   double aDirDeriv = 0.0;
0075   for (int i = aLower; i <= aUpper; ++i)
0076   {
0077     aDirDeriv += theGrad(i) * theDir(i);
0078   }
0079 
0080   // Ensure descent direction
0081   if (aDirDeriv >= 0.0)
0082   {
0083     // Not a descent direction, return failure
0084     aResult.IsValid = false;
0085     return aResult;
0086   }
0087 
0088   // Temporary vector for new point
0089   math_Vector aXNew(aLower, aUpper);
0090 
0091   for (int k = 0; k < theMaxIter; ++k)
0092   {
0093     // Compute new point: x + alpha*d
0094     for (int i = aLower; i <= aUpper; ++i)
0095     {
0096       aXNew(i) = theX(i) + aResult.Alpha * theDir(i);
0097     }
0098 
0099     // Evaluate function at new point
0100     double aFNew = 0.0;
0101     if (!theFunc.Value(aXNew, aFNew))
0102     {
0103       // Function evaluation failed, reduce step
0104       aResult.Alpha *= theRho;
0105       ++aResult.NbEvals;
0106       continue;
0107     }
0108     ++aResult.NbEvals;
0109 
0110     // Check Armijo condition: f(x + alpha*d) <= f(x) + c1*alpha*phi'(0)
0111     if (aFNew <= theFx + theC1 * aResult.Alpha * aDirDeriv)
0112     {
0113       aResult.IsValid = true;
0114       aResult.FNew    = aFNew;
0115       return aResult;
0116     }
0117 
0118     // Reduce step size
0119     aResult.Alpha *= theRho;
0120 
0121     // Check for too small step
0122     if (aResult.Alpha < THE_EPSILON)
0123     {
0124       break;
0125     }
0126   }
0127 
0128   // Failed to satisfy Armijo condition
0129   aResult.IsValid = false;
0130   return aResult;
0131 }
0132 
0133 //! Strong Wolfe line search.
0134 //! Finds alpha satisfying both:
0135 //! 1. Armijo: f(x + alpha*d) <= f(x) + c1*alpha*grad(f).d
0136 //! 2. Curvature: |grad(f)(x + alpha*d).d| <= c2*|grad(f).d|
0137 //!
0138 //! Uses bracketing and zoom procedure for guaranteed convergence.
0139 //!
0140 //! @tparam Function type with:
0141 //!   - Value(const math_Vector&, double&) for function value
0142 //!   - Gradient(const math_Vector&, math_Vector&) for gradient
0143 //! @param theFunc objective function with gradient
0144 //! @param theX current point
0145 //! @param theDir search direction
0146 //! @param theGrad gradient at current point
0147 //! @param theFx function value at current point
0148 //! @param theAlphaInit initial step size
0149 //! @param theC1 Armijo parameter (default 1e-4)
0150 //! @param theC2 curvature parameter (default 0.9)
0151 //! @param theMaxIter maximum iterations
0152 //! @return line search result
0153 template <typename Function>
0154 LineSearchResult WolfeSearch(Function&          theFunc,
0155                              const math_Vector& theX,
0156                              const math_Vector& theDir,
0157                              const math_Vector& theGrad,
0158                              double             theFx,
0159                              double             theAlphaInit = 1.0,
0160                              double             theC1        = 1.0e-4,
0161                              double             theC2        = 0.9,
0162                              int                theMaxIter   = 20)
0163 {
0164   LineSearchResult aResult;
0165   aResult.NbEvals = 0;
0166 
0167   const int aLower = theX.Lower();
0168   const int aUpper = theX.Upper();
0169 
0170   // Compute initial directional derivative
0171   double aPhi0Prime = 0.0;
0172   for (int i = aLower; i <= aUpper; ++i)
0173   {
0174     aPhi0Prime += theGrad(i) * theDir(i);
0175   }
0176 
0177   if (aPhi0Prime >= 0.0)
0178   {
0179     aResult.IsValid = false;
0180     return aResult;
0181   }
0182 
0183   math_Vector aXNew(aLower, aUpper);
0184   math_Vector aGradNew(aLower, aUpper);
0185 
0186   double aAlphaLo = 0.0;
0187   double aAlphaHi = theAlphaInit * 2.0;
0188   double aAlpha   = theAlphaInit;
0189 
0190   double aPhiLo = theFx;
0191 
0192   // Phase 1: Bracket finding
0193   for (int k = 0; k < theMaxIter; ++k)
0194   {
0195     // Evaluate at current alpha
0196     for (int i = aLower; i <= aUpper; ++i)
0197     {
0198       aXNew(i) = theX(i) + aAlpha * theDir(i);
0199     }
0200 
0201     double aPhi = 0.0;
0202     if (!theFunc.Value(aXNew, aPhi))
0203     {
0204       // Shrink interval
0205       aAlphaHi = aAlpha;
0206       aAlpha   = 0.5 * (aAlphaLo + aAlphaHi);
0207       ++aResult.NbEvals;
0208       continue;
0209     }
0210     ++aResult.NbEvals;
0211 
0212     // Check Armijo
0213     if (aPhi > theFx + theC1 * aAlpha * aPhi0Prime || (k > 0 && aPhi >= aPhiLo))
0214     {
0215       // Found bracket [alpha_lo, alpha]
0216       aAlphaHi = aAlpha;
0217       break;
0218     }
0219 
0220     // Evaluate gradient at new point
0221     if (!theFunc.Gradient(aXNew, aGradNew))
0222     {
0223       aResult.IsValid = false;
0224       return aResult;
0225     }
0226 
0227     double aPhiPrime = 0.0;
0228     for (int i = aLower; i <= aUpper; ++i)
0229     {
0230       aPhiPrime += aGradNew(i) * theDir(i);
0231     }
0232 
0233     // Check curvature condition
0234     if (std::abs(aPhiPrime) <= -theC2 * aPhi0Prime)
0235     {
0236       aResult.IsValid = true;
0237       aResult.Alpha   = aAlpha;
0238       aResult.FNew    = aPhi;
0239       return aResult;
0240     }
0241 
0242     if (aPhiPrime >= 0.0)
0243     {
0244       // Found bracket [alpha, alpha_lo]
0245       aAlphaHi = aAlphaLo;
0246       aAlphaLo = aAlpha;
0247       aPhiLo   = aPhi;
0248       break;
0249     }
0250 
0251     // Increase alpha
0252     aAlphaLo = aAlpha;
0253     aPhiLo   = aPhi;
0254     aAlpha   = 0.5 * (aAlpha + aAlphaHi);
0255   }
0256 
0257   // Phase 2: Zoom
0258   for (int k = 0; k < theMaxIter; ++k)
0259   {
0260     // Bisection
0261     aAlpha = 0.5 * (aAlphaLo + aAlphaHi);
0262 
0263     for (int i = aLower; i <= aUpper; ++i)
0264     {
0265       aXNew(i) = theX(i) + aAlpha * theDir(i);
0266     }
0267 
0268     double aPhi = 0.0;
0269     if (!theFunc.Value(aXNew, aPhi))
0270     {
0271       aAlphaHi = aAlpha;
0272       ++aResult.NbEvals;
0273       continue;
0274     }
0275     ++aResult.NbEvals;
0276 
0277     if (aPhi > theFx + theC1 * aAlpha * aPhi0Prime || aPhi >= aPhiLo)
0278     {
0279       aAlphaHi = aAlpha;
0280     }
0281     else
0282     {
0283       if (!theFunc.Gradient(aXNew, aGradNew))
0284       {
0285         break;
0286       }
0287 
0288       double aPhiPrime = 0.0;
0289       for (int i = aLower; i <= aUpper; ++i)
0290       {
0291         aPhiPrime += aGradNew(i) * theDir(i);
0292       }
0293 
0294       if (std::abs(aPhiPrime) <= -theC2 * aPhi0Prime)
0295       {
0296         aResult.IsValid = true;
0297         aResult.Alpha   = aAlpha;
0298         aResult.FNew    = aPhi;
0299         return aResult;
0300       }
0301 
0302       if (aPhiPrime * (aAlphaHi - aAlphaLo) >= 0.0)
0303       {
0304         aAlphaHi = aAlphaLo;
0305       }
0306 
0307       aAlphaLo = aAlpha;
0308       aPhiLo   = aPhi;
0309     }
0310 
0311     // Check for convergence
0312     if (std::abs(aAlphaHi - aAlphaLo) < THE_EPSILON)
0313     {
0314       break;
0315     }
0316   }
0317 
0318   // Return best found
0319   aResult.IsValid = true;
0320   aResult.Alpha   = aAlpha;
0321   aResult.FNew    = aPhiLo;
0322   return aResult;
0323 }
0324 
0325 //! Exact line search using Brent's method.
0326 //! Minimizes f(x + alpha*d) over alpha in [-alpha_max, alpha_max].
0327 //! First brackets the minimum by exploring both directions.
0328 //! More expensive than inexact line search but can be more robust.
0329 //!
0330 //! @tparam Function type with Value(const math_Vector&, double&) method
0331 //! @param theFunc objective function
0332 //! @param theX current point
0333 //! @param theDir search direction
0334 //! @param theAlphaMax maximum step size (searches in both directions)
0335 //! @param theTolerance convergence tolerance
0336 //! @param theMaxIter maximum iterations
0337 //! @return line search result
0338 template <typename Function>
0339 LineSearchResult ExactLineSearch(Function&          theFunc,
0340                                  const math_Vector& theX,
0341                                  const math_Vector& theDir,
0342                                  double             theAlphaMax  = 10.0,
0343                                  double             theTolerance = 1.0e-6,
0344                                  int                theMaxIter   = 100)
0345 {
0346   LineSearchResult aResult;
0347   aResult.NbEvals = 0;
0348 
0349   const int aLower = theX.Lower();
0350   const int aUpper = theX.Upper();
0351 
0352   math_Vector aXNew(aLower, aUpper);
0353 
0354   // Lambda to evaluate phi(alpha) = f(x + alpha*d)
0355   auto aEvalPhi = [&](double theAlpha, double& thePhi) -> bool {
0356     for (int i = aLower; i <= aUpper; ++i)
0357     {
0358       aXNew(i) = theX(i) + theAlpha * theDir(i);
0359     }
0360     ++aResult.NbEvals;
0361     return theFunc.Value(aXNew, thePhi);
0362   };
0363 
0364   // Brent's method for 1D minimization
0365   // Search in [-theAlphaMax, theAlphaMax] to handle both directions
0366   double aA = -theAlphaMax;
0367   double aB = theAlphaMax;
0368   double aX = 0.0; // Start at the current point
0369   double aW = aX;
0370   double aV = aX;
0371 
0372   double aFx = 0.0;
0373   if (!aEvalPhi(aX, aFx))
0374   {
0375     aResult.IsValid = false;
0376     return aResult;
0377   }
0378   double aFw = aFx;
0379   double aFv = aFx;
0380 
0381   double aD = 0.0;
0382   double aE = 0.0;
0383 
0384   for (int anIter = 0; anIter < theMaxIter; ++anIter)
0385   {
0386     const double aXm   = 0.5 * (aA + aB);
0387     const double aTol1 = theTolerance * std::abs(aX) + THE_ZERO_TOL / 10.0;
0388     const double aTol2 = 2.0 * aTol1;
0389 
0390     // Check convergence
0391     if (std::abs(aX - aXm) <= (aTol2 - 0.5 * (aB - aA)))
0392     {
0393       aResult.IsValid = true;
0394       aResult.Alpha   = aX;
0395       aResult.FNew    = aFx;
0396       return aResult;
0397     }
0398 
0399     double aU            = 0.0;
0400     bool   aUseParabolic = false;
0401 
0402     // Try parabolic interpolation
0403     if (std::abs(aE) > aTol1)
0404     {
0405       const double aR = (aX - aW) * (aFx - aFv);
0406       double       aQ = (aX - aV) * (aFx - aFw);
0407       double       aP = (aX - aV) * aQ - (aX - aW) * aR;
0408       aQ              = 2.0 * (aQ - aR);
0409 
0410       if (aQ > 0.0)
0411       {
0412         aP = -aP;
0413       }
0414       else
0415       {
0416         aQ = -aQ;
0417       }
0418 
0419       const double aETmp = aE;
0420       aE                 = aD;
0421 
0422       if (std::abs(aP) < std::abs(0.5 * aQ * aETmp) && aP > aQ * (aA - aX) && aP < aQ * (aB - aX))
0423       {
0424         aD = aP / aQ;
0425         aU = aX + aD;
0426         if ((aU - aA) < aTol2 || (aB - aU) < aTol2)
0427         {
0428           aD = SignTransfer(aTol1, aXm - aX);
0429         }
0430         aUseParabolic = true;
0431       }
0432     }
0433 
0434     if (!aUseParabolic)
0435     {
0436       aE = (aX < aXm) ? (aB - aX) : (aA - aX);
0437       aD = THE_GOLDEN_SECTION * aE;
0438     }
0439 
0440     if (std::abs(aD) >= aTol1)
0441     {
0442       aU = aX + aD;
0443     }
0444     else
0445     {
0446       aU = aX + SignTransfer(aTol1, aD);
0447     }
0448 
0449     double aFu = 0.0;
0450     if (!aEvalPhi(aU, aFu))
0451     {
0452       aResult.IsValid = false;
0453       aResult.Alpha   = aX;
0454       aResult.FNew    = aFx;
0455       return aResult;
0456     }
0457 
0458     // Update bracket
0459     if (aFu <= aFx)
0460     {
0461       if (aU < aX)
0462       {
0463         aB = aX;
0464       }
0465       else
0466       {
0467         aA = aX;
0468       }
0469 
0470       aV  = aW;
0471       aW  = aX;
0472       aX  = aU;
0473       aFv = aFw;
0474       aFw = aFx;
0475       aFx = aFu;
0476     }
0477     else
0478     {
0479       if (aU < aX)
0480       {
0481         aA = aU;
0482       }
0483       else
0484       {
0485         aB = aU;
0486       }
0487 
0488       if (aFu <= aFw || aW == aX)
0489       {
0490         aV  = aW;
0491         aW  = aU;
0492         aFv = aFw;
0493         aFw = aFu;
0494       }
0495       else if (aFu <= aFv || aV == aX || aV == aW)
0496       {
0497         aV  = aU;
0498         aFv = aFu;
0499       }
0500     }
0501   }
0502 
0503   aResult.IsValid = true;
0504   aResult.Alpha   = aX;
0505   aResult.FNew    = aFx;
0506   return aResult;
0507 }
0508 
0509 //! Quadratic interpolation step for line search.
0510 //! Given phi(0), phi'(0), and phi(alpha1), finds minimum of quadratic fit.
0511 //!
0512 //! @param thePhi0 function value at 0
0513 //! @param thePhi0Prime directional derivative at 0
0514 //! @param theAlpha1 current step size
0515 //! @param thePhi1 function value at alpha1
0516 //! @return interpolated step size
0517 inline double QuadraticInterpolation(double thePhi0,
0518                                      double thePhi0Prime,
0519                                      double theAlpha1,
0520                                      double thePhi1)
0521 {
0522   // Quadratic: phi(alpha) = phi(0) + phi'(0)*alpha + c*alpha^2
0523   // where c = (phi(alpha1) - phi(0) - phi'(0)*alpha1) / alpha1^2
0524   // Minimum at alpha* = -phi'(0) / (2c)
0525   const double aNum   = thePhi0Prime * theAlpha1 * theAlpha1;
0526   const double aDenom = 2.0 * (thePhi1 - thePhi0 - thePhi0Prime * theAlpha1);
0527 
0528   if (std::abs(aDenom) < THE_ZERO_TOL)
0529   {
0530     return 0.5 * theAlpha1;
0531   }
0532 
0533   double aAlphaNew = -aNum / aDenom;
0534 
0535   // Safeguard: ensure we stay within reasonable bounds
0536   if (aAlphaNew < 0.1 * theAlpha1)
0537   {
0538     aAlphaNew = 0.1 * theAlpha1;
0539   }
0540   else if (aAlphaNew > 0.9 * theAlpha1)
0541   {
0542     aAlphaNew = 0.5 * theAlpha1;
0543   }
0544 
0545   return aAlphaNew;
0546 }
0547 
0548 //! Brent's method for 1D minimization along a single coordinate axis.
0549 //! Operates in-place on one coordinate of thePoint, avoiding vector allocations.
0550 //! Unlike ExactLineSearch which works with arbitrary direction vectors and allocates
0551 //! internal temporaries, this function modifies only thePoint(theDimIdx) during the
0552 //! search and restores it if no improvement is found.
0553 //!
0554 //! @tparam Function type with Value(const math_Vector&, double&) method
0555 //! @param theFunc function to minimize
0556 //! @param thePoint current point (the active coordinate is modified during search,
0557 //!                 restored if no improvement)
0558 //! @param theDimIdx index of the coordinate to optimize
0559 //! @param theLoBound lower bound for this coordinate
0560 //! @param theUpBound upper bound for this coordinate
0561 //! @param theFx current function value at thePoint (updated if improved)
0562 //! @param theTolerance convergence tolerance
0563 //! @param theMaxIter maximum iterations
0564 //! @param theEvalCount incremented by the number of function evaluations used
0565 //! @return true if an improvement was found; thePoint and theFx are updated accordingly
0566 template <typename Function>
0567 bool BrentAlongCoordinate(Function&    theFunc,
0568                           math_Vector& thePoint,
0569                           int          theDimIdx,
0570                           double       theLoBound,
0571                           double       theUpBound,
0572                           double&      theFx,
0573                           double       theTolerance,
0574                           int          theMaxIter,
0575                           int&         theEvalCount)
0576 {
0577   const double aOrigCoord = thePoint(theDimIdx);
0578 
0579   // Search interval [aA, aB] in coordinate space (not alpha space)
0580   double aA = theLoBound;
0581   double aB = theUpBound;
0582   double aX = aOrigCoord;
0583   double aW = aX;
0584   double aV = aX;
0585 
0586   double aFx = theFx;
0587   double aFw = aFx;
0588   double aFv = aFx;
0589 
0590   double aD = 0.0;
0591   double aE = 0.0;
0592 
0593   for (int anIter = 0; anIter < theMaxIter; ++anIter)
0594   {
0595     const double aXm   = 0.5 * (aA + aB);
0596     const double aTol1 = theTolerance * std::abs(aX) + THE_ZERO_TOL / 10.0;
0597     const double aTol2 = 2.0 * aTol1;
0598 
0599     // Check convergence
0600     if (std::abs(aX - aXm) <= (aTol2 - 0.5 * (aB - aA)))
0601     {
0602       break;
0603     }
0604 
0605     double aU            = 0.0;
0606     bool   aUseParabolic = false;
0607 
0608     // Try parabolic interpolation
0609     if (std::abs(aE) > aTol1)
0610     {
0611       const double aR = (aX - aW) * (aFx - aFv);
0612       double       aQ = (aX - aV) * (aFx - aFw);
0613       double       aP = (aX - aV) * aQ - (aX - aW) * aR;
0614       aQ              = 2.0 * (aQ - aR);
0615 
0616       if (aQ > 0.0)
0617       {
0618         aP = -aP;
0619       }
0620       else
0621       {
0622         aQ = -aQ;
0623       }
0624 
0625       const double aETmp = aE;
0626       aE                 = aD;
0627 
0628       if (std::abs(aP) < std::abs(0.5 * aQ * aETmp) && aP > aQ * (aA - aX) && aP < aQ * (aB - aX))
0629       {
0630         aD = aP / aQ;
0631         aU = aX + aD;
0632         if ((aU - aA) < aTol2 || (aB - aU) < aTol2)
0633         {
0634           aD = SignTransfer(aTol1, aXm - aX);
0635         }
0636         aUseParabolic = true;
0637       }
0638     }
0639 
0640     if (!aUseParabolic)
0641     {
0642       aE = (aX < aXm) ? (aB - aX) : (aA - aX);
0643       aD = THE_GOLDEN_SECTION * aE;
0644     }
0645 
0646     if (std::abs(aD) >= aTol1)
0647     {
0648       aU = aX + aD;
0649     }
0650     else
0651     {
0652       aU = aX + SignTransfer(aTol1, aD);
0653     }
0654 
0655     // Evaluate: only modify the single coordinate
0656     thePoint(theDimIdx) = aU;
0657     double aFu          = 0.0;
0658     if (!theFunc.Value(thePoint, aFu))
0659     {
0660       // Restore and abort
0661       thePoint(theDimIdx) = aOrigCoord;
0662       return false;
0663     }
0664     ++theEvalCount;
0665 
0666     // Update bracket
0667     if (aFu <= aFx)
0668     {
0669       if (aU < aX)
0670       {
0671         aB = aX;
0672       }
0673       else
0674       {
0675         aA = aX;
0676       }
0677       aV  = aW;
0678       aW  = aX;
0679       aX  = aU;
0680       aFv = aFw;
0681       aFw = aFx;
0682       aFx = aFu;
0683     }
0684     else
0685     {
0686       if (aU < aX)
0687       {
0688         aA = aU;
0689       }
0690       else
0691       {
0692         aB = aU;
0693       }
0694       if (aFu <= aFw || aW == aX)
0695       {
0696         aV  = aW;
0697         aW  = aU;
0698         aFv = aFw;
0699         aFw = aFu;
0700       }
0701       else if (aFu <= aFv || aV == aX || aV == aW)
0702       {
0703         aV  = aU;
0704         aFv = aFu;
0705       }
0706     }
0707   }
0708 
0709   // Accept if improved
0710   if (aFx < theFx)
0711   {
0712     thePoint(theDimIdx) = aX;
0713     theFx               = aFx;
0714     return true;
0715   }
0716 
0717   // Restore original coordinate
0718   thePoint(theDimIdx) = aOrigCoord;
0719   return false;
0720 }
0721 
0722 } // namespace MathUtils
0723 
0724 #endif // _MathUtils_LineSearch_HeaderFile