Back to home page

EIC code displayed by LXR

 
 

    


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

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_Bracket_HeaderFile
0015 #define _MathUtils_Bracket_HeaderFile
0016 
0017 #include <MathUtils_Core.hxx>
0018 
0019 #include <algorithm>
0020 #include <cmath>
0021 #include <utility>
0022 
0023 //! Modern math solver utilities.
0024 namespace MathUtils
0025 {
0026 
0027 //! Result of root bracketing operation.
0028 struct BracketResult
0029 {
0030   bool   IsValid = false; //!< True if valid bracket found
0031   double A       = 0.0;   //!< Lower bound
0032   double B       = 0.0;   //!< Upper bound
0033   double Fa      = 0.0;   //!< Function value at A
0034   double Fb      = 0.0;   //!< Function value at B
0035 };
0036 
0037 //! Bracket a root by expanding interval until sign change is found.
0038 //! Starting from [theA, theB], expands outward using golden ratio.
0039 //! @tparam Function type with Value(double theX, double& theF) method
0040 //! @param theFunc function to bracket
0041 //! @param theA initial lower bound
0042 //! @param theB initial upper bound
0043 //! @param theMaxIter maximum expansion iterations
0044 //! @return bracketing result
0045 template <typename Function>
0046 BracketResult BracketRoot(Function& theFunc, double theA, double theB, int theMaxIter = 50)
0047 {
0048   BracketResult aResult;
0049   aResult.A = theA;
0050   aResult.B = theB;
0051 
0052   if (!theFunc.Value(aResult.A, aResult.Fa))
0053   {
0054     return aResult;
0055   }
0056   if (!theFunc.Value(aResult.B, aResult.Fb))
0057   {
0058     return aResult;
0059   }
0060 
0061   for (int i = 0; i < theMaxIter; ++i)
0062   {
0063     if (aResult.Fa * aResult.Fb < 0.0)
0064     {
0065       aResult.IsValid = true;
0066       // Ensure A < B
0067       if (aResult.A > aResult.B)
0068       {
0069         std::swap(aResult.A, aResult.B);
0070         std::swap(aResult.Fa, aResult.Fb);
0071       }
0072       return aResult;
0073     }
0074 
0075     // Expand the interval using golden ratio
0076     if (std::abs(aResult.Fa) < std::abs(aResult.Fb))
0077     {
0078       aResult.A += THE_GOLDEN_RATIO * (aResult.A - aResult.B);
0079       if (!theFunc.Value(aResult.A, aResult.Fa))
0080       {
0081         return aResult;
0082       }
0083     }
0084     else
0085     {
0086       aResult.B += THE_GOLDEN_RATIO * (aResult.B - aResult.A);
0087       if (!theFunc.Value(aResult.B, aResult.Fb))
0088       {
0089         return aResult;
0090       }
0091     }
0092   }
0093 
0094   return aResult;
0095 }
0096 
0097 //! Result of minimum bracketing operation.
0098 struct MinBracketResult
0099 {
0100   bool   IsValid = false; //!< True if valid bracket found (Fb < Fa and Fb < Fc)
0101   double A       = 0.0;   //!< Left bound
0102   double B       = 0.0;   //!< Middle point (minimum location estimate)
0103   double C       = 0.0;   //!< Right bound
0104   double Fa      = 0.0;   //!< Function value at A
0105   double Fb      = 0.0;   //!< Function value at B
0106   double Fc      = 0.0;   //!< Function value at C
0107 };
0108 
0109 //! Options for minimum bracketing.
0110 struct MinBracketOptions
0111 {
0112   int    MaxIterations = 50;    //!< Maximum iterations
0113   bool   UseLimits     = false; //!< Enable hard limits for parameter
0114   double LeftLimit     = 0.0;   //!< Left hard limit (inclusive)
0115   double RightLimit    = 0.0;   //!< Right hard limit (inclusive)
0116   bool   HasFA         = false; //!< True if FA is precomputed
0117   bool   HasFB         = false; //!< True if FB is precomputed
0118   double FA            = 0.0;   //!< Precomputed f(A)
0119   double FB            = 0.0;   //!< Precomputed f(B)
0120 };
0121 
0122 namespace detail
0123 {
0124 inline double Limited(double theValue, const MinBracketOptions& theOptions)
0125 {
0126   if (!theOptions.UseLimits)
0127   {
0128     return theValue;
0129   }
0130   return std::max(theOptions.LeftLimit, std::min(theOptions.RightLimit, theValue));
0131 }
0132 
0133 template <typename Function>
0134 bool LimitAndMayBeSwap(Function&                theFunc,
0135                        const MinBracketOptions& theOptions,
0136                        const double             theA,
0137                        double&                  theB,
0138                        double&                  theFB,
0139                        double&                  theC,
0140                        double&                  theFC)
0141 {
0142   theC = Limited(theC, theOptions);
0143   if (std::abs(theB - theC) < THE_ZERO_TOL)
0144   {
0145     return false;
0146   }
0147   if (!theFunc.Value(theC, theFC))
0148   {
0149     return false;
0150   }
0151 
0152   // Keep B between A and C
0153   if ((theA - theB) * (theB - theC) < 0.0)
0154   {
0155     std::swap(theB, theC);
0156     std::swap(theFB, theFC);
0157   }
0158   return true;
0159 }
0160 } // namespace detail
0161 
0162 //! Bracket a minimum by finding three points a < b < c with f(b) < f(a) and f(b) < f(c).
0163 //! Uses golden section expansion with parabolic interpolation.
0164 //! @tparam Function type with Value(double theX, double& theF) method
0165 //! @param theFunc function to bracket
0166 //! @param theA initial point A
0167 //! @param theB initial point B (should be to the right of A in descent direction)
0168 //! @param theOptions bracketing options
0169 //! @return bracketing result
0170 template <typename Function>
0171 MinBracketResult BracketMinimum(Function&                theFunc,
0172                                 double                   theA,
0173                                 double                   theB,
0174                                 const MinBracketOptions& theOptions = MinBracketOptions())
0175 {
0176   MinBracketResult aResult;
0177   if (theOptions.MaxIterations < 1)
0178   {
0179     return aResult;
0180   }
0181   if (theOptions.UseLimits && theOptions.LeftLimit > theOptions.RightLimit)
0182   {
0183     return aResult;
0184   }
0185 
0186   aResult.A = detail::Limited(theA, theOptions);
0187   aResult.B = detail::Limited(theB, theOptions);
0188   if (std::abs(aResult.A - aResult.B) < THE_ZERO_TOL)
0189   {
0190     return aResult;
0191   }
0192 
0193   const bool isUseFA =
0194     theOptions.HasFA && (!theOptions.UseLimits || std::abs(aResult.A - theA) < THE_ZERO_TOL);
0195   const bool isUseFB =
0196     theOptions.HasFB && (!theOptions.UseLimits || std::abs(aResult.B - theB) < THE_ZERO_TOL);
0197 
0198   if (isUseFA)
0199   {
0200     aResult.Fa = theOptions.FA;
0201   }
0202   else if (!theFunc.Value(aResult.A, aResult.Fa))
0203   {
0204     return aResult;
0205   }
0206 
0207   if (isUseFB)
0208   {
0209     aResult.Fb = theOptions.FB;
0210   }
0211   else if (!theFunc.Value(aResult.B, aResult.Fb))
0212   {
0213     return aResult;
0214   }
0215 
0216   // Ensure we go downhill from A to B
0217   if (aResult.Fb > aResult.Fa)
0218   {
0219     std::swap(aResult.A, aResult.B);
0220     std::swap(aResult.Fa, aResult.Fb);
0221   }
0222 
0223   // Initial guess for C using golden ratio
0224   aResult.C = aResult.B + THE_GOLDEN_RATIO * (aResult.B - aResult.A);
0225   if (theOptions.UseLimits)
0226   {
0227     if (!detail::LimitAndMayBeSwap(theFunc,
0228                                    theOptions,
0229                                    aResult.A,
0230                                    aResult.B,
0231                                    aResult.Fb,
0232                                    aResult.C,
0233                                    aResult.Fc))
0234     {
0235       return aResult;
0236     }
0237   }
0238   else if (!theFunc.Value(aResult.C, aResult.Fc))
0239   {
0240     return aResult;
0241   }
0242 
0243   // Keep expanding until we bracket a minimum
0244   for (int anIter = 0; anIter < theOptions.MaxIterations && aResult.Fb >= aResult.Fc; ++anIter)
0245   {
0246     // Parabolic extrapolation
0247     const double aR     = (aResult.B - aResult.A) * (aResult.Fb - aResult.Fc);
0248     const double aQ     = (aResult.B - aResult.C) * (aResult.Fb - aResult.Fa);
0249     const double aDenom = 2.0 * SignTransfer(std::max(std::abs(aQ - aR), THE_ZERO_TOL), aQ - aR);
0250 
0251     double aU = aResult.B - ((aResult.B - aResult.C) * aQ - (aResult.B - aResult.A) * aR) / aDenom;
0252 
0253     double aULim = aResult.B + 100.0 * (aResult.C - aResult.B);
0254     if (theOptions.UseLimits)
0255     {
0256       aULim = detail::Limited(aULim, theOptions);
0257     }
0258     double aFu = 0.0;
0259 
0260     if ((aResult.B - aU) * (aU - aResult.C) > 0.0)
0261     {
0262       // U is between B and C
0263       if (!theFunc.Value(aU, aFu))
0264       {
0265         return aResult;
0266       }
0267 
0268       if (aFu < aResult.Fc)
0269       {
0270         aResult.A       = aResult.B;
0271         aResult.B       = aU;
0272         aResult.Fa      = aResult.Fb;
0273         aResult.Fb      = aFu;
0274         aResult.IsValid = true;
0275         return aResult;
0276       }
0277       else if (aFu > aResult.Fb)
0278       {
0279         aResult.C       = aU;
0280         aResult.Fc      = aFu;
0281         aResult.IsValid = true;
0282         return aResult;
0283       }
0284 
0285       // Parabolic step didn't help, use golden section
0286       aU = aResult.C + THE_GOLDEN_RATIO * (aResult.C - aResult.B);
0287       if (theOptions.UseLimits)
0288       {
0289         if (!detail::LimitAndMayBeSwap(theFunc,
0290                                        theOptions,
0291                                        aResult.B,
0292                                        aResult.C,
0293                                        aResult.Fc,
0294                                        aU,
0295                                        aFu))
0296         {
0297           return aResult;
0298         }
0299       }
0300       else if (!theFunc.Value(aU, aFu))
0301       {
0302         return aResult;
0303       }
0304     }
0305     else if ((aResult.C - aU) * (aU - aULim) > 0.0)
0306     {
0307       // U is between C and limit
0308       if (theOptions.UseLimits)
0309       {
0310         if (!detail::LimitAndMayBeSwap(theFunc,
0311                                        theOptions,
0312                                        aResult.B,
0313                                        aResult.C,
0314                                        aResult.Fc,
0315                                        aU,
0316                                        aFu))
0317         {
0318           return aResult;
0319         }
0320       }
0321       else if (!theFunc.Value(aU, aFu))
0322       {
0323         return aResult;
0324       }
0325 
0326       if (aFu < aResult.Fc)
0327       {
0328         aResult.B  = aResult.C;
0329         aResult.C  = aU;
0330         aU         = aResult.C + THE_GOLDEN_RATIO * (aResult.C - aResult.B);
0331         aResult.Fb = aResult.Fc;
0332         aResult.Fc = aFu;
0333         if (theOptions.UseLimits)
0334         {
0335           if (!detail::LimitAndMayBeSwap(theFunc,
0336                                          theOptions,
0337                                          aResult.B,
0338                                          aResult.C,
0339                                          aResult.Fc,
0340                                          aU,
0341                                          aFu))
0342           {
0343             return aResult;
0344           }
0345         }
0346         else if (!theFunc.Value(aU, aFu))
0347         {
0348           return aResult;
0349         }
0350       }
0351     }
0352     else if ((aU - aULim) * (aULim - aResult.C) >= 0.0)
0353     {
0354       // U is beyond limit
0355       aU = aULim;
0356       if (theOptions.UseLimits)
0357       {
0358         if (!detail::LimitAndMayBeSwap(theFunc,
0359                                        theOptions,
0360                                        aResult.B,
0361                                        aResult.C,
0362                                        aResult.Fc,
0363                                        aU,
0364                                        aFu))
0365         {
0366           return aResult;
0367         }
0368       }
0369       else if (!theFunc.Value(aU, aFu))
0370       {
0371         return aResult;
0372       }
0373     }
0374     else
0375     {
0376       // Default golden section step
0377       aU = aResult.C + THE_GOLDEN_RATIO * (aResult.C - aResult.B);
0378       if (theOptions.UseLimits)
0379       {
0380         if (!detail::LimitAndMayBeSwap(theFunc,
0381                                        theOptions,
0382                                        aResult.B,
0383                                        aResult.C,
0384                                        aResult.Fc,
0385                                        aU,
0386                                        aFu))
0387         {
0388           return aResult;
0389         }
0390       }
0391       else if (!theFunc.Value(aU, aFu))
0392       {
0393         return aResult;
0394       }
0395     }
0396 
0397     // Shift points
0398     aResult.A  = aResult.B;
0399     aResult.B  = aResult.C;
0400     aResult.C  = aU;
0401     aResult.Fa = aResult.Fb;
0402     aResult.Fb = aResult.Fc;
0403     aResult.Fc = aFu;
0404   }
0405 
0406   aResult.IsValid = (aResult.Fb < aResult.Fa && aResult.Fb < aResult.Fc);
0407 
0408   // Ensure A < B < C ordering
0409   if (aResult.IsValid && aResult.A > aResult.C)
0410   {
0411     std::swap(aResult.A, aResult.C);
0412     std::swap(aResult.Fa, aResult.Fc);
0413   }
0414 
0415   if (aResult.IsValid && !(aResult.A < aResult.B && aResult.B < aResult.C))
0416   {
0417     aResult.IsValid = false;
0418   }
0419 
0420   return aResult;
0421 }
0422 
0423 //! Backward-compatible convenience overload with only max-iterations argument.
0424 template <typename Function>
0425 MinBracketResult BracketMinimum(Function& theFunc, double theA, double theB, int theMaxIter)
0426 {
0427   MinBracketOptions anOptions;
0428   anOptions.MaxIterations = theMaxIter;
0429   return BracketMinimum(theFunc, theA, theB, anOptions);
0430 }
0431 
0432 } // namespace MathUtils
0433 
0434 #endif // _MathUtils_Bracket_HeaderFile