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_Brent_HeaderFile
0015 #define _MathRoot_Brent_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 //! Brent's method for root finding.
0029 //! Combines bisection, secant, and inverse quadratic interpolation.
0030 //! Guaranteed to converge if a valid bracket is provided.
0031 //!
0032 //! Algorithm:
0033 //! 1. Start with bracket [a, b] where f(a) * f(b) < 0
0034 //! 2. At each step, try inverse quadratic interpolation
0035 //! 3. If interpolation step is rejected, use bisection
0036 //! 4. Acceptance criteria ensure superlinear convergence when possible
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 (f(theLower) and f(theUpper) must have opposite signs)
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 Brent(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 |f(a)| >= |f(b)| (b is the better approximation)
0077   if (std::abs(aFa) < std::abs(aFb))
0078   {
0079     std::swap(aA, aB);
0080     std::swap(aFa, aFb);
0081   }
0082 
0083   double aC  = aA; // Previous iterate
0084   double aFc = aFa;
0085   double aD  = aB - aA; // Step size
0086   double aE  = aD;      // Previous step size
0087 
0088   for (int anIter = 0; anIter < theConfig.MaxIterations; ++anIter)
0089   {
0090     aResult.NbIterations = anIter + 1;
0091 
0092     const double aTol = 2.0 * MathUtils::THE_EPSILON * std::abs(aB) + 0.5 * theConfig.XTolerance;
0093     const double aM   = 0.5 * (aC - aB);
0094 
0095     if (std::abs(aFb) < theConfig.FTolerance || aFb == 0.0 || std::abs(aM) <= aTol)
0096     {
0097       aResult.Status = MathUtils::Status::OK;
0098       aResult.Root   = aB;
0099       aResult.Value  = aFb;
0100       return aResult;
0101     }
0102 
0103     double aS = 0.0; // New approximation
0104 
0105     // Try inverse quadratic interpolation if we have three distinct points
0106     if (std::abs(aFa - aFc) > MathUtils::THE_ZERO_TOL
0107         && std::abs(aFb - aFc) > MathUtils::THE_ZERO_TOL)
0108     {
0109       // Inverse quadratic interpolation
0110       aS = aA * aFb * aFc / ((aFa - aFb) * (aFa - aFc))
0111            + aB * aFa * aFc / ((aFb - aFa) * (aFb - aFc))
0112            + aC * aFa * aFb / ((aFc - aFa) * (aFc - aFb));
0113     }
0114     else
0115     {
0116       // Secant method
0117       aS = aB - aFb * (aB - aA) / (aFb - aFa);
0118     }
0119 
0120     // Decide whether to accept the interpolation step
0121     bool aUseInterp = false;
0122 
0123     // Check if s is between (3a+b)/4 and b
0124     const double aBound1 = (3.0 * aA + aB) / 4.0;
0125     if ((aS > std::min(aBound1, aB) && aS < std::max(aBound1, aB)))
0126     {
0127       // Accept interpolation if step is smaller than half the previous step
0128       // (ensures convergence rate). Minimum step is enforced later.
0129       if (std::abs(aS - aB) < std::abs(aE) / 2.0)
0130       {
0131         aUseInterp = true;
0132       }
0133     }
0134 
0135     if (!aUseInterp)
0136     {
0137       // Bisection step
0138       aS = aB + aM;
0139       aE = aM;
0140       aD = aM;
0141     }
0142     else
0143     {
0144       aE = aD;
0145       aD = aS - aB;
0146     }
0147 
0148     // Update previous values
0149     aA  = aB;
0150     aFa = aFb;
0151 
0152     // Compute new point, ensuring minimum step
0153     if (std::abs(aD) > aTol)
0154     {
0155       aB = aS;
0156     }
0157     else
0158     {
0159       aB += (aM > 0.0) ? aTol : -aTol;
0160     }
0161 
0162     // Evaluate function at new point
0163     if (!theFunc.Value(aB, aFb))
0164     {
0165       aResult.Status = MathUtils::Status::NumericalError;
0166       aResult.Root   = aB;
0167       return aResult;
0168     }
0169 
0170     // Update bracket
0171     if (aFb * aFc > 0.0)
0172     {
0173       aC  = aA;
0174       aFc = aFa;
0175       aD  = aB - aA;
0176       aE  = aD;
0177     }
0178     else if (std::abs(aFc) < std::abs(aFb))
0179     {
0180       // Swap b and c if c is better (use std::swap to avoid overwriting)
0181       aA  = aB;
0182       aFa = aFb;
0183       std::swap(aB, aC);
0184       std::swap(aFb, aFc);
0185     }
0186   }
0187 
0188   // Maximum iterations reached
0189   aResult.Status = MathUtils::Status::MaxIterations;
0190   aResult.Root   = aB;
0191   aResult.Value  = aFb;
0192   return aResult;
0193 }
0194 
0195 } // namespace MathRoot
0196 
0197 #endif // _MathRoot_Brent_HeaderFile