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_Newton_HeaderFile
0015 #define _MathRoot_Newton_HeaderFile
0016 
0017 #include <MathUtils_Types.hxx>
0018 #include <MathUtils_Config.hxx>
0019 #include <MathUtils_Core.hxx>
0020 #include <MathUtils_Convergence.hxx>
0021 
0022 #include <cmath>
0023 
0024 //! Root finding algorithms for scalar functions.
0025 namespace MathRoot
0026 {
0027 using namespace MathUtils;
0028 
0029 //! Newton-Raphson root finding algorithm.
0030 //! Finds x such that f(x) = 0 using Newton's method with derivative.
0031 //!
0032 //! Algorithm:
0033 //! x_{n+1} = x_n - f(x_n) / f'(x_n)
0034 //!
0035 //! Requires a function providing both value and derivative.
0036 //! Converges quadratically near the root for simple roots.
0037 //!
0038 //! @tparam Function type with Values(double theX, double& theF, double& theDf) method
0039 //!         returning bool (true if evaluation succeeded)
0040 //! @param theFunc function object providing value and derivative
0041 //! @param theGuess initial guess for the root
0042 //! @param theConfig solver configuration (tolerances, max iterations)
0043 //! @return result containing root location and convergence status
0044 template <typename Function>
0045 MathUtils::ScalarResult Newton(Function&                theFunc,
0046                                double                   theGuess,
0047                                const MathUtils::Config& theConfig = MathUtils::Config())
0048 {
0049   MathUtils::ScalarResult aResult;
0050   double                  aX   = theGuess;
0051   double                  aFx  = 0.0;
0052   double                  aDfx = 0.0;
0053 
0054   for (int anIter = 0; anIter < theConfig.MaxIterations; ++anIter)
0055   {
0056     const double anXOld = aX;
0057 
0058     // Evaluate function and derivative
0059     if (!theFunc.Values(aX, aFx, aDfx))
0060     {
0061       aResult.Status       = MathUtils::Status::NumericalError;
0062       aResult.Root         = aX;
0063       aResult.NbIterations = anIter;
0064       return aResult;
0065     }
0066 
0067     // Check for zero derivative (stationary point)
0068     if (MathUtils::IsZero(aDfx))
0069     {
0070       // Try to continue with a small perturbation if not converged
0071       if (!MathUtils::IsFConverged(aFx, theConfig.FTolerance))
0072       {
0073         aResult.Status       = MathUtils::Status::NumericalError;
0074         aResult.Root         = aX;
0075         aResult.Value        = aFx;
0076         aResult.Derivative   = aDfx;
0077         aResult.NbIterations = anIter;
0078         return aResult;
0079       }
0080       // Zero derivative at a root is fine (multiple root)
0081     }
0082     else
0083     {
0084       // Newton step
0085       aX -= aFx / aDfx;
0086     }
0087 
0088     aResult.NbIterations = anIter + 1;
0089 
0090     // Check convergence
0091     if (MathUtils::IsConverged(anXOld, aX, aFx, theConfig))
0092     {
0093       aResult.Status     = MathUtils::Status::OK;
0094       aResult.Root       = aX;
0095       aResult.Value      = aFx;
0096       aResult.Derivative = aDfx;
0097       return aResult;
0098     }
0099   }
0100 
0101   // Maximum iterations reached
0102   aResult.Status     = MathUtils::Status::MaxIterations;
0103   aResult.Root       = aX;
0104   aResult.Value      = aFx;
0105   aResult.Derivative = aDfx;
0106   return aResult;
0107 }
0108 
0109 //! Newton-Raphson with bounds checking.
0110 //! Falls back to bisection step if Newton step goes outside bounds.
0111 //! More robust than pure Newton for ill-conditioned problems.
0112 //!
0113 //! @tparam Function type with Values(double theX, double& theF, double& theDf) method
0114 //! @param theFunc function object providing value and derivative
0115 //! @param theGuess initial guess for the root
0116 //! @param theLower lower bound of search interval
0117 //! @param theUpper upper bound of search interval
0118 //! @param theConfig solver configuration
0119 //! @return result containing root location and convergence status
0120 template <typename Function>
0121 MathUtils::ScalarResult NewtonBounded(Function&                theFunc,
0122                                       double                   theGuess,
0123                                       double                   theLower,
0124                                       double                   theUpper,
0125                                       const MathUtils::Config& theConfig = MathUtils::Config())
0126 {
0127   MathUtils::ScalarResult aResult;
0128 
0129   // Clamp initial guess to bounds
0130   double aX   = MathUtils::Clamp(theGuess, theLower, theUpper);
0131   double aXLo = theLower;
0132   double aXHi = theUpper;
0133 
0134   double aFx    = 0.0;
0135   double aDfx   = 0.0;
0136   double aFLo   = 0.0;
0137   double aFHi   = 0.0;
0138   double aDummy = 0.0;
0139 
0140   // Initialize bounds with function values
0141   if (!theFunc.Values(aXLo, aFLo, aDummy))
0142   {
0143     aResult.Status = MathUtils::Status::NumericalError;
0144     return aResult;
0145   }
0146   if (!theFunc.Values(aXHi, aFHi, aDummy))
0147   {
0148     aResult.Status = MathUtils::Status::NumericalError;
0149     return aResult;
0150   }
0151 
0152   // Check if bounds bracket a root
0153   const bool aBracketed = (aFLo * aFHi < 0.0);
0154 
0155   for (int anIter = 0; anIter < theConfig.MaxIterations; ++anIter)
0156   {
0157     const double anXOld = aX;
0158 
0159     // Evaluate function and derivative at current point
0160     if (!theFunc.Values(aX, aFx, aDfx))
0161     {
0162       aResult.Status       = MathUtils::Status::NumericalError;
0163       aResult.Root         = aX;
0164       aResult.NbIterations = anIter;
0165       return aResult;
0166     }
0167 
0168     aResult.NbIterations = anIter + 1;
0169 
0170     // Check convergence
0171     if (MathUtils::IsFConverged(aFx, theConfig.FTolerance))
0172     {
0173       aResult.Status     = MathUtils::Status::OK;
0174       aResult.Root       = aX;
0175       aResult.Value      = aFx;
0176       aResult.Derivative = aDfx;
0177       return aResult;
0178     }
0179 
0180     // Compute Newton step
0181     double aXNew = aX;
0182     if (!MathUtils::IsZero(aDfx))
0183     {
0184       aXNew = aX - aFx / aDfx;
0185     }
0186 
0187     // Check if Newton step is within bounds
0188     if (aXNew < aXLo || aXNew > aXHi)
0189     {
0190       // Fall back to bisection if bracketed
0191       if (aBracketed)
0192       {
0193         aXNew = 0.5 * (aXLo + aXHi);
0194       }
0195       else
0196       {
0197         // Just clamp to bounds
0198         aXNew = MathUtils::Clamp(aXNew, aXLo, aXHi);
0199       }
0200     }
0201 
0202     aX = aXNew;
0203 
0204     // Update bracket if root is bracketed
0205     if (aBracketed)
0206     {
0207       if (aFx * aFLo < 0.0)
0208       {
0209         aXHi = anXOld;
0210         aFHi = aFx;
0211       }
0212       else
0213       {
0214         aXLo = anXOld;
0215         aFLo = aFx;
0216       }
0217     }
0218 
0219     // Check X convergence
0220     if (MathUtils::IsXConverged(anXOld, aX, theConfig.XTolerance))
0221     {
0222       // Re-evaluate at final position
0223       theFunc.Values(aX, aFx, aDfx);
0224       aResult.Status     = MathUtils::Status::OK;
0225       aResult.Root       = aX;
0226       aResult.Value      = aFx;
0227       aResult.Derivative = aDfx;
0228       return aResult;
0229     }
0230   }
0231 
0232   // Maximum iterations reached
0233   aResult.Status     = MathUtils::Status::MaxIterations;
0234   aResult.Root       = aX;
0235   aResult.Value      = aFx;
0236   aResult.Derivative = aDfx;
0237   return aResult;
0238 }
0239 
0240 } // namespace MathRoot
0241 
0242 #endif // _MathRoot_Newton_HeaderFile