Back to home page

EIC code displayed by LXR

 
 

    


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

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 _MathOpt_Brent_HeaderFile
0015 #define _MathOpt_Brent_HeaderFile
0016 
0017 #include <MathUtils_Types.hxx>
0018 #include <MathUtils_Config.hxx>
0019 #include <MathUtils_Core.hxx>
0020 #include <MathUtils_Bracket.hxx>
0021 
0022 #include <cmath>
0023 
0024 //! Optimization algorithms for scalar and vector functions.
0025 namespace MathOpt
0026 {
0027 using namespace MathUtils;
0028 
0029 //! Brent's method for 1D minimization.
0030 //! Combines golden section search with parabolic interpolation.
0031 //! Guaranteed to converge for unimodal functions within the given interval.
0032 //!
0033 //! Algorithm:
0034 //! 1. Maintain bracket [a, b] with interior point x where f(x) < f(a), f(x) < f(b)
0035 //! 2. Try parabolic interpolation using three points
0036 //! 3. If parabolic step is rejected, use golden section step
0037 //! 4. Update bracket and repeat until convergence
0038 //!
0039 //! @tparam Function type with Value(double theX, double& theF) method
0040 //! @param theFunc function to minimize
0041 //! @param theLower lower bound of search interval
0042 //! @param theUpper upper bound of search interval
0043 //! @param theConfig solver configuration
0044 //! @return result containing minimum location and value
0045 template <typename Function>
0046 ScalarResult Brent(Function&     theFunc,
0047                    double        theLower,
0048                    double        theUpper,
0049                    const Config& theConfig = Config())
0050 {
0051   ScalarResult aResult;
0052 
0053   double aA = theLower;
0054   double aB = theUpper;
0055 
0056   // Initial point using golden section
0057   double aX = aA + MathUtils::THE_GOLDEN_SECTION * (aB - aA);
0058   double aW = aX;
0059   double aV = aX;
0060 
0061   double aFx = 0.0;
0062   if (!theFunc.Value(aX, aFx))
0063   {
0064     aResult.Status = Status::NumericalError;
0065     return aResult;
0066   }
0067   double aFw = aFx;
0068   double aFv = aFx;
0069 
0070   double aD = 0.0; // Current step
0071   double aE = 0.0; // Previous step
0072 
0073   for (int anIter = 0; anIter < theConfig.MaxIterations; ++anIter)
0074   {
0075     const double aXm   = 0.5 * (aA + aB);
0076     const double aTol1 = theConfig.XTolerance * std::abs(aX) + MathUtils::THE_ZERO_TOL / 10.0;
0077     const double aTol2 = 2.0 * aTol1;
0078 
0079     aResult.NbIterations = anIter + 1;
0080 
0081     // Check convergence
0082     if (std::abs(aX - aXm) <= (aTol2 - 0.5 * (aB - aA)))
0083     {
0084       aResult.Status = Status::OK;
0085       aResult.Root   = aX;
0086       aResult.Value  = aFx;
0087       return aResult;
0088     }
0089 
0090     double aU            = 0.0;
0091     bool   aUseParabolic = false;
0092 
0093     // Try parabolic interpolation if step is large enough
0094     if (std::abs(aE) > aTol1)
0095     {
0096       // Parabolic fit through x, w, v
0097       const double aR = (aX - aW) * (aFx - aFv);
0098       double       aQ = (aX - aV) * (aFx - aFw);
0099       double       aP = (aX - aV) * aQ - (aX - aW) * aR;
0100       aQ              = 2.0 * (aQ - aR);
0101 
0102       if (aQ > 0.0)
0103       {
0104         aP = -aP;
0105       }
0106       else
0107       {
0108         aQ = -aQ;
0109       }
0110 
0111       const double aETmp = aE;
0112       aE                 = aD;
0113 
0114       // Check if parabolic step is acceptable
0115       if (std::abs(aP) < std::abs(0.5 * aQ * aETmp) && aP > aQ * (aA - aX) && aP < aQ * (aB - aX))
0116       {
0117         aD = aP / aQ;
0118         aU = aX + aD;
0119 
0120         // Don't evaluate too close to bounds
0121         if ((aU - aA) < aTol2 || (aB - aU) < aTol2)
0122         {
0123           aD = MathUtils::SignTransfer(aTol1, aXm - aX);
0124         }
0125         aUseParabolic = true;
0126       }
0127     }
0128 
0129     if (!aUseParabolic)
0130     {
0131       // Golden section step
0132       aE = (aX < aXm) ? (aB - aX) : (aA - aX);
0133       aD = MathUtils::THE_GOLDEN_SECTION * aE;
0134     }
0135 
0136     // Ensure step is at least aTol1
0137     if (std::abs(aD) >= aTol1)
0138     {
0139       aU = aX + aD;
0140     }
0141     else
0142     {
0143       aU = aX + MathUtils::SignTransfer(aTol1, aD);
0144     }
0145 
0146     double aFu = 0.0;
0147     if (!theFunc.Value(aU, aFu))
0148     {
0149       aResult.Status = Status::NumericalError;
0150       aResult.Root   = aX;
0151       aResult.Value  = aFx;
0152       return aResult;
0153     }
0154 
0155     // Update bracket and best points
0156     if (aFu <= aFx)
0157     {
0158       if (aU < aX)
0159       {
0160         aB = aX;
0161       }
0162       else
0163       {
0164         aA = aX;
0165       }
0166 
0167       aV  = aW;
0168       aW  = aX;
0169       aX  = aU;
0170       aFv = aFw;
0171       aFw = aFx;
0172       aFx = aFu;
0173     }
0174     else
0175     {
0176       if (aU < aX)
0177       {
0178         aA = aU;
0179       }
0180       else
0181       {
0182         aB = aU;
0183       }
0184 
0185       if (aFu <= aFw || aW == aX)
0186       {
0187         aV  = aW;
0188         aW  = aU;
0189         aFv = aFw;
0190         aFw = aFu;
0191       }
0192       else if (aFu <= aFv || aV == aX || aV == aW)
0193       {
0194         aV  = aU;
0195         aFv = aFu;
0196       }
0197     }
0198   }
0199 
0200   // Maximum iterations reached
0201   aResult.Status = Status::MaxIterations;
0202   aResult.Root   = aX;
0203   aResult.Value  = aFx;
0204   return aResult;
0205 }
0206 
0207 //! Golden section search for 1D minimization.
0208 //! Simpler than Brent but with guaranteed linear convergence.
0209 //! Does not attempt parabolic interpolation.
0210 //!
0211 //! @tparam Function type with Value(double theX, double& theF) method
0212 //! @param theFunc function to minimize
0213 //! @param theLower lower bound of search interval
0214 //! @param theUpper upper bound of search interval
0215 //! @param theConfig solver configuration
0216 //! @return result containing minimum location and value
0217 template <typename Function>
0218 ScalarResult Golden(Function&     theFunc,
0219                     double        theLower,
0220                     double        theUpper,
0221                     const Config& theConfig = Config())
0222 {
0223   ScalarResult aResult;
0224 
0225   constexpr double aR = 0.618033988749895; // (sqrt(5) - 1) / 2
0226   constexpr double aC = 1.0 - aR;
0227 
0228   double aA = theLower;
0229   double aB = theUpper;
0230 
0231   // Initialize interior points
0232   double aX1 = aA + aC * (aB - aA);
0233   double aX2 = aA + aR * (aB - aA);
0234 
0235   double aF1 = 0.0;
0236   double aF2 = 0.0;
0237 
0238   if (!theFunc.Value(aX1, aF1))
0239   {
0240     aResult.Status = Status::NumericalError;
0241     return aResult;
0242   }
0243   if (!theFunc.Value(aX2, aF2))
0244   {
0245     aResult.Status = Status::NumericalError;
0246     return aResult;
0247   }
0248 
0249   for (int anIter = 0; anIter < theConfig.MaxIterations; ++anIter)
0250   {
0251     aResult.NbIterations = anIter + 1;
0252 
0253     // Check convergence
0254     if ((aB - aA) < theConfig.XTolerance * (std::abs(aX1) + std::abs(aX2)))
0255     {
0256       aResult.Status = Status::OK;
0257       if (aF1 < aF2)
0258       {
0259         aResult.Root  = aX1;
0260         aResult.Value = aF1;
0261       }
0262       else
0263       {
0264         aResult.Root  = aX2;
0265         aResult.Value = aF2;
0266       }
0267       return aResult;
0268     }
0269 
0270     if (aF1 < aF2)
0271     {
0272       // Minimum is in [a, x2]
0273       aB  = aX2;
0274       aX2 = aX1;
0275       aF2 = aF1;
0276       aX1 = aA + aC * (aB - aA);
0277       if (!theFunc.Value(aX1, aF1))
0278       {
0279         aResult.Status = Status::NumericalError;
0280         aResult.Root   = aX2;
0281         aResult.Value  = aF2;
0282         return aResult;
0283       }
0284     }
0285     else
0286     {
0287       // Minimum is in [x1, b]
0288       aA  = aX1;
0289       aX1 = aX2;
0290       aF1 = aF2;
0291       aX2 = aA + aR * (aB - aA);
0292       if (!theFunc.Value(aX2, aF2))
0293       {
0294         aResult.Status = Status::NumericalError;
0295         aResult.Root   = aX1;
0296         aResult.Value  = aF1;
0297         return aResult;
0298       }
0299     }
0300   }
0301 
0302   // Maximum iterations reached
0303   aResult.Status = Status::MaxIterations;
0304   if (aF1 < aF2)
0305   {
0306     aResult.Root  = aX1;
0307     aResult.Value = aF1;
0308   }
0309   else
0310   {
0311     aResult.Root  = aX2;
0312     aResult.Value = aF2;
0313   }
0314   return aResult;
0315 }
0316 
0317 //! Brent's method with automatic bracket search.
0318 //! First attempts to bracket a minimum, then applies Brent's method.
0319 //!
0320 //! @tparam Function type with Value(double theX, double& theF) method
0321 //! @param theFunc function to minimize
0322 //! @param theGuess initial guess
0323 //! @param theStep initial step size for bracket search
0324 //! @param theConfig solver configuration
0325 //! @return result containing minimum location and value
0326 template <typename Function>
0327 ScalarResult BrentWithBracket(Function&     theFunc,
0328                               double        theGuess,
0329                               double        theStep   = 1.0,
0330                               const Config& theConfig = Config())
0331 {
0332   ScalarResult aResult;
0333 
0334   // Try to bracket minimum
0335   MathUtils::MinBracketResult aBracket =
0336     MathUtils::BracketMinimum(theFunc, theGuess, theGuess + theStep);
0337 
0338   if (!aBracket.IsValid)
0339   {
0340     aResult.Status = Status::InvalidInput;
0341     return aResult;
0342   }
0343 
0344   // Apply Brent's method on the bracket
0345   return Brent(theFunc, aBracket.A, aBracket.C, theConfig);
0346 }
0347 
0348 } // namespace MathOpt
0349 
0350 #endif // _MathOpt_Brent_HeaderFile