Back to home page

EIC code displayed by LXR

 
 

    


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

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 _MathRoot_Bisection_HeaderFile
0015 #define _MathRoot_Bisection_HeaderFile
0016 
0017 #include <MathUtils_Types.hxx>
0018 #include <MathUtils_Config.hxx>
0019 #include <MathUtils_Core.hxx>
0020 
0021 #include <cmath>
0022 #include <utility>
0023 
0024 namespace MathRoot
0025 {
0026 using namespace MathUtils;
0027 
0028 //! Bisection method for root finding.
0029 //! Simple and robust, guaranteed to converge if a valid bracket is provided.
0030 //! Converges linearly, halving the interval at each step.
0031 //!
0032 //! Algorithm:
0033 //! 1. Start with bracket [a, b] where f(a) * f(b) < 0
0034 //! 2. Compute midpoint m = (a + b) / 2
0035 //! 3. If f(m) has same sign as f(a), set a = m, else set b = m
0036 //! 4. Repeat until convergence
0037 //!
0038 //! @tparam Function type with Value(double theX, double& theF) method
0039 //! @param theFunc function to find root of
0040 //! @param theLower lower bound of bracket
0041 //! @param theUpper upper bound of bracket
0042 //! @param theConfig solver configuration
0043 //! @return result containing root location and convergence status
0044 template <typename Function>
0045 MathUtils::ScalarResult Bisection(Function&                theFunc,
0046                                   double                   theLower,
0047                                   double                   theUpper,
0048                                   const MathUtils::Config& theConfig = MathUtils::Config())
0049 {
0050   MathUtils::ScalarResult aResult;
0051 
0052   double aA  = theLower;
0053   double aB  = theUpper;
0054   double aFa = 0.0;
0055   double aFb = 0.0;
0056 
0057   // Evaluate at endpoints
0058   if (!theFunc.Value(aA, aFa))
0059   {
0060     aResult.Status = MathUtils::Status::NumericalError;
0061     return aResult;
0062   }
0063   if (!theFunc.Value(aB, aFb))
0064   {
0065     aResult.Status = MathUtils::Status::NumericalError;
0066     return aResult;
0067   }
0068 
0069   // Check that bracket is valid (sign change)
0070   if (aFa * aFb > 0.0)
0071   {
0072     aResult.Status = MathUtils::Status::InvalidInput;
0073     return aResult;
0074   }
0075 
0076   // Ensure a < b
0077   if (aA > aB)
0078   {
0079     std::swap(aA, aB);
0080     std::swap(aFa, aFb);
0081   }
0082 
0083   // Track which endpoint has the negative value
0084   // We use separate tracking instead of swapping to maintain aA < aB
0085   bool aIsNegAtA = (aFa < 0.0);
0086 
0087   for (int anIter = 0; anIter < theConfig.MaxIterations; ++anIter)
0088   {
0089     // Compute midpoint
0090     const double aM  = 0.5 * (aA + aB);
0091     double       aFm = 0.0;
0092 
0093     if (!theFunc.Value(aM, aFm))
0094     {
0095       aResult.Status       = MathUtils::Status::NumericalError;
0096       aResult.Root         = aM;
0097       aResult.NbIterations = anIter + 1;
0098       return aResult;
0099     }
0100 
0101     aResult.NbIterations = anIter + 1;
0102 
0103     // Check convergence on function value
0104     if (std::abs(aFm) < theConfig.FTolerance)
0105     {
0106       aResult.Status = MathUtils::Status::OK;
0107       aResult.Root   = aM;
0108       aResult.Value  = aFm;
0109       return aResult;
0110     }
0111 
0112     // Check convergence on interval size
0113     if ((aB - aA) < theConfig.XTolerance * std::max(1.0, std::abs(aM)))
0114     {
0115       aResult.Status = MathUtils::Status::OK;
0116       aResult.Root   = aM;
0117       aResult.Value  = aFm;
0118       return aResult;
0119     }
0120 
0121     // Update bracket based on sign tracking
0122     const bool aIsNegAtM = (aFm < 0.0);
0123     if (aIsNegAtM == aIsNegAtA)
0124     {
0125       // Same sign as endpoint A, replace A
0126       aA  = aM;
0127       aFa = aFm;
0128     }
0129     else
0130     {
0131       // Same sign as endpoint B, replace B
0132       aB  = aM;
0133       aFb = aFm;
0134     }
0135   }
0136 
0137   // Maximum iterations reached
0138   aResult.Status = MathUtils::Status::MaxIterations;
0139   aResult.Root   = 0.5 * (aA + aB);
0140   aResult.Value  = 0.0;
0141   theFunc.Value(*aResult.Root, *aResult.Value);
0142   return aResult;
0143 }
0144 
0145 //! Hybrid bisection-Newton method.
0146 //! Combines the robustness of bisection with the speed of Newton's method.
0147 //! Uses Newton step when it stays within bracket, otherwise bisects.
0148 //!
0149 //! @tparam Function type with Values(double theX, double& theF, double& theDf) method
0150 //! @param theFunc function with value and derivative
0151 //! @param theLower lower bound of bracket
0152 //! @param theUpper upper bound of bracket
0153 //! @param theConfig solver configuration
0154 //! @return result containing root location and convergence status
0155 template <typename Function>
0156 MathUtils::ScalarResult BisectionNewton(Function&                theFunc,
0157                                         double                   theLower,
0158                                         double                   theUpper,
0159                                         const MathUtils::Config& theConfig = MathUtils::Config())
0160 {
0161   MathUtils::ScalarResult aResult;
0162 
0163   double aA     = theLower;
0164   double aB     = theUpper;
0165   double aFa    = 0.0;
0166   double aFb    = 0.0;
0167   double aDummy = 0.0;
0168 
0169   // Evaluate at endpoints
0170   if (!theFunc.Values(aA, aFa, aDummy))
0171   {
0172     aResult.Status = MathUtils::Status::NumericalError;
0173     return aResult;
0174   }
0175   if (!theFunc.Values(aB, aFb, aDummy))
0176   {
0177     aResult.Status = MathUtils::Status::NumericalError;
0178     return aResult;
0179   }
0180 
0181   // Check that bracket is valid
0182   if (aFa * aFb > 0.0)
0183   {
0184     aResult.Status = MathUtils::Status::InvalidInput;
0185     return aResult;
0186   }
0187 
0188   // Ensure a < b
0189   if (aA > aB)
0190   {
0191     std::swap(aA, aB);
0192     std::swap(aFa, aFb);
0193   }
0194 
0195   // Track which endpoint has the negative value
0196   bool aIsNegAtA = (aFa < 0.0);
0197 
0198   // Start from midpoint
0199   double aX   = 0.5 * (aA + aB);
0200   double aFx  = 0.0;
0201   double aDfx = 0.0;
0202 
0203   for (int anIter = 0; anIter < theConfig.MaxIterations; ++anIter)
0204   {
0205     if (!theFunc.Values(aX, aFx, aDfx))
0206     {
0207       aResult.Status       = MathUtils::Status::NumericalError;
0208       aResult.Root         = aX;
0209       aResult.NbIterations = anIter + 1;
0210       return aResult;
0211     }
0212 
0213     aResult.NbIterations = anIter + 1;
0214 
0215     // Check convergence
0216     if (std::abs(aFx) < theConfig.FTolerance)
0217     {
0218       aResult.Status     = MathUtils::Status::OK;
0219       aResult.Root       = aX;
0220       aResult.Value      = aFx;
0221       aResult.Derivative = aDfx;
0222       return aResult;
0223     }
0224 
0225     if ((aB - aA) < theConfig.XTolerance * std::max(1.0, std::abs(aX)))
0226     {
0227       aResult.Status     = MathUtils::Status::OK;
0228       aResult.Root       = aX;
0229       aResult.Value      = aFx;
0230       aResult.Derivative = aDfx;
0231       return aResult;
0232     }
0233 
0234     // Try Newton step
0235     double aXNew = aX;
0236     if (!MathUtils::IsZero(aDfx))
0237     {
0238       aXNew = aX - aFx / aDfx;
0239     }
0240 
0241     // Check if Newton step stays within bracket
0242     if (aXNew <= aA || aXNew >= aB)
0243     {
0244       // Fall back to bisection
0245       aXNew = 0.5 * (aA + aB);
0246     }
0247 
0248     // Update bracket based on current point using sign tracking
0249     const bool aIsNegAtX = (aFx < 0.0);
0250     if (aIsNegAtX == aIsNegAtA)
0251     {
0252       // Same sign as endpoint A, replace A
0253       aA = aX;
0254     }
0255     else
0256     {
0257       // Same sign as endpoint B, replace B
0258       aB = aX;
0259     }
0260 
0261     aX = aXNew;
0262   }
0263 
0264   // Maximum iterations reached
0265   aResult.Status     = MathUtils::Status::MaxIterations;
0266   aResult.Root       = aX;
0267   aResult.Value      = 0.0;
0268   aResult.Derivative = 0.0;
0269   theFunc.Values(*aResult.Root, *aResult.Value, *aResult.Derivative);
0270   return aResult;
0271 }
0272 
0273 } // namespace MathRoot
0274 
0275 #endif // _MathRoot_Bisection_HeaderFile