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_Deriv_HeaderFile
0015 #define _MathUtils_Deriv_HeaderFile
0016 
0017 #include <math_Vector.hxx>
0018 #include <math_Matrix.hxx>
0019 #include <MathUtils_Core.hxx>
0020 
0021 #include <cmath>
0022 
0023 //! Core utilities for modern math solvers.
0024 namespace MathUtils
0025 {
0026 
0027 //! Central difference derivative approximation for scalar functions.
0028 //! f'(x) ~= (f(x+h) - f(x-h)) / (2h)
0029 //! Accuracy: O(h^2)
0030 //!
0031 //! @tparam Function type with Value(double theX, double& theF) method
0032 //! @param theFunc function to differentiate
0033 //! @param theX point at which to evaluate derivative
0034 //! @param theDeriv computed derivative value
0035 //! @param theStep step size (default 1e-8)
0036 //! @return true if successful
0037 template <typename Function>
0038 bool CentralDifference(Function& theFunc, double theX, double& theDeriv, double theStep = 1.0e-8)
0039 {
0040   double aFPlus  = 0.0;
0041   double aFMinus = 0.0;
0042 
0043   if (!theFunc.Value(theX + theStep, aFPlus))
0044   {
0045     return false;
0046   }
0047   if (!theFunc.Value(theX - theStep, aFMinus))
0048   {
0049     return false;
0050   }
0051 
0052   theDeriv = (aFPlus - aFMinus) / (2.0 * theStep);
0053   return true;
0054 }
0055 
0056 //! Forward difference derivative (one-sided).
0057 //! f'(x) ~= (f(x+h) - f(x)) / h
0058 //! Accuracy: O(h)
0059 //! Useful when central difference is not possible (e.g., at boundaries).
0060 //!
0061 //! @tparam Function type with Value(double theX, double& theF) method
0062 //! @param theFunc function to differentiate
0063 //! @param theX point at which to evaluate derivative
0064 //! @param theFx function value at theX (if already computed)
0065 //! @param theDeriv computed derivative value
0066 //! @param theStep step size (default 1e-8)
0067 //! @return true if successful
0068 template <typename Function>
0069 bool ForwardDifference(Function& theFunc,
0070                        double    theX,
0071                        double    theFx,
0072                        double&   theDeriv,
0073                        double    theStep = 1.0e-8)
0074 {
0075   double aFPlus = 0.0;
0076 
0077   if (!theFunc.Value(theX + theStep, aFPlus))
0078   {
0079     return false;
0080   }
0081 
0082   theDeriv = (aFPlus - theFx) / theStep;
0083   return true;
0084 }
0085 
0086 //! Numerical gradient using central differences for N-D functions.
0087 //! df/dx[i] ~= (f(x + h[i]*e[i]) - f(x - h[i]*e[i])) / (2*h[i])
0088 //!
0089 //! @tparam Function type with Value(const math_Vector&, double&) method
0090 //! @param theFunc function to differentiate
0091 //! @param theX point at which to evaluate gradient (temporarily modified)
0092 //! @param theGrad output gradient vector (same dimension as theX)
0093 //! @param theStep step size (default 1e-8)
0094 //! @return true if successful
0095 template <typename Function>
0096 bool NumericalGradient(Function&    theFunc,
0097                        math_Vector& theX,
0098                        math_Vector& theGrad,
0099                        double       theStep = 1.0e-8)
0100 {
0101   const int aLower = theX.Lower();
0102   const int aUpper = theX.Upper();
0103 
0104   for (int i = aLower; i <= aUpper; ++i)
0105   {
0106     const double aXi     = theX(i);
0107     double       aFPlus  = 0.0;
0108     double       aFMinus = 0.0;
0109 
0110     // Forward perturbation
0111     theX(i) = aXi + theStep;
0112     if (!theFunc.Value(theX, aFPlus))
0113     {
0114       theX(i) = aXi;
0115       return false;
0116     }
0117 
0118     // Backward perturbation
0119     theX(i) = aXi - theStep;
0120     if (!theFunc.Value(theX, aFMinus))
0121     {
0122       theX(i) = aXi;
0123       return false;
0124     }
0125 
0126     // Restore original value
0127     theX(i) = aXi;
0128 
0129     // Central difference
0130     theGrad(i) = (aFPlus - aFMinus) / (2.0 * theStep);
0131   }
0132 
0133   return true;
0134 }
0135 
0136 //! Numerical gradient with adaptive step size.
0137 //! Uses step proportional to |x[i]| for better conditioning.
0138 //!
0139 //! @tparam Function type with Value(const math_Vector&, double&) method
0140 //! @param theFunc function to differentiate
0141 //! @param theX point at which to evaluate gradient
0142 //! @param theGrad output gradient vector
0143 //! @param theRelStep relative step size (default 1e-8)
0144 //! @return true if successful
0145 template <typename Function>
0146 bool NumericalGradientAdaptive(Function&    theFunc,
0147                                math_Vector& theX,
0148                                math_Vector& theGrad,
0149                                double       theRelStep = 1.0e-8)
0150 {
0151   const int aLower = theX.Lower();
0152   const int aUpper = theX.Upper();
0153 
0154   for (int i = aLower; i <= aUpper; ++i)
0155   {
0156     const double aXi = theX(i);
0157     // Adaptive step: larger for larger |x|, with minimum floor
0158     const double aStep = theRelStep * std::max(1.0, std::abs(aXi));
0159 
0160     double aFPlus  = 0.0;
0161     double aFMinus = 0.0;
0162 
0163     theX(i) = aXi + aStep;
0164     if (!theFunc.Value(theX, aFPlus))
0165     {
0166       theX(i) = aXi;
0167       return false;
0168     }
0169 
0170     theX(i) = aXi - aStep;
0171     if (!theFunc.Value(theX, aFMinus))
0172     {
0173       theX(i) = aXi;
0174       return false;
0175     }
0176 
0177     theX(i)    = aXi;
0178     theGrad(i) = (aFPlus - aFMinus) / (2.0 * aStep);
0179   }
0180 
0181   return true;
0182 }
0183 
0184 //! Numerical Jacobian matrix for vector-valued functions.
0185 //! J[i,j] = dF[i]/dx[j] ~= (F[i](x + h[j]*e[j]) - F[i](x - h[j]*e[j])) / (2*h[j])
0186 //!
0187 //! @tparam Function type with Value(const math_Vector& x, math_Vector& F) method
0188 //! @param theFunc vector-valued function F: R^n -> R^m
0189 //! @param theX point at which to evaluate Jacobian (n-dimensional)
0190 //! @param theJac output Jacobian matrix (m x n)
0191 //! @param theStep step size (default 1e-8)
0192 //! @return true if successful
0193 template <typename Function>
0194 bool NumericalJacobian(Function&    theFunc,
0195                        math_Vector& theX,
0196                        math_Matrix& theJac,
0197                        double       theStep = 1.0e-8)
0198 {
0199   const int aNbRows = theJac.RowNumber();
0200   const int aNbCols = theJac.ColNumber();
0201 
0202   math_Vector aFPlus(1, aNbRows);
0203   math_Vector aFMinus(1, aNbRows);
0204 
0205   for (int j = 1; j <= aNbCols; ++j)
0206   {
0207     const int    aIdx = theX.Lower() + j - 1;
0208     const double aXj  = theX(aIdx);
0209 
0210     // Forward perturbation
0211     theX(aIdx) = aXj + theStep;
0212     if (!theFunc.Value(theX, aFPlus))
0213     {
0214       theX(aIdx) = aXj;
0215       return false;
0216     }
0217 
0218     // Backward perturbation
0219     theX(aIdx) = aXj - theStep;
0220     if (!theFunc.Value(theX, aFMinus))
0221     {
0222       theX(aIdx) = aXj;
0223       return false;
0224     }
0225 
0226     // Restore
0227     theX(aIdx) = aXj;
0228 
0229     // Fill column of Jacobian
0230     for (int i = 1; i <= aNbRows; ++i)
0231     {
0232       theJac(i, j) = (aFPlus(i) - aFMinus(i)) / (2.0 * theStep);
0233     }
0234   }
0235 
0236   return true;
0237 }
0238 
0239 //! Numerical Hessian matrix using finite differences.
0240 //! H[i,j] = d^2f/dx[i]dx[j]
0241 //! Uses central differences on gradient.
0242 //!
0243 //! @tparam Function type with Value(const math_Vector&, double&) method
0244 //! @param theFunc scalar function f: R^n -> R
0245 //! @param theX point at which to evaluate Hessian (n-dimensional)
0246 //! @param theHess output Hessian matrix (n x n, symmetric)
0247 //! @param theStep step size (default 1e-5, larger than gradient step)
0248 //! @return true if successful
0249 template <typename Function>
0250 bool NumericalHessian(Function&    theFunc,
0251                       math_Vector& theX,
0252                       math_Matrix& theHess,
0253                       double       theStep = 1.0e-5)
0254 {
0255   const int aLower = theX.Lower();
0256   const int aUpper = theX.Upper();
0257 
0258   double aFx = 0.0;
0259   if (!theFunc.Value(theX, aFx))
0260   {
0261     return false;
0262   }
0263 
0264   // Diagonal elements: d^2f/dx[i]^2 ~= (f(x+h[i]*e[i]) - 2f(x) + f(x-h[i]*e[i])) / h^2
0265   for (int i = aLower; i <= aUpper; ++i)
0266   {
0267     const double aXi     = theX(i);
0268     double       aFPlus  = 0.0;
0269     double       aFMinus = 0.0;
0270 
0271     theX(i) = aXi + theStep;
0272     if (!theFunc.Value(theX, aFPlus))
0273     {
0274       theX(i) = aXi;
0275       return false;
0276     }
0277 
0278     theX(i) = aXi - theStep;
0279     if (!theFunc.Value(theX, aFMinus))
0280     {
0281       theX(i) = aXi;
0282       return false;
0283     }
0284 
0285     theX(i) = aXi;
0286 
0287     const int aMatIdx         = i - aLower + 1;
0288     theHess(aMatIdx, aMatIdx) = (aFPlus - 2.0 * aFx + aFMinus) / (theStep * theStep);
0289   }
0290 
0291   // Off-diagonal elements: d^2f/dx[i]dx[j]
0292   // ~= (f(x+h[i]*e[i]+h[j]*e[j]) - f(x+h[i]*e[i]-h[j]*e[j])
0293   //    - f(x-h[i]*e[i]+h[j]*e[j]) + f(x-h[i]*e[i]-h[j]*e[j])) / (4h^2)
0294   for (int i = aLower; i <= aUpper; ++i)
0295   {
0296     for (int j = i + 1; j <= aUpper; ++j)
0297     {
0298       const double aXi  = theX(i);
0299       const double aXj  = theX(j);
0300       double       aFpp = 0.0, aFpm = 0.0, aFmp = 0.0, aFmm = 0.0;
0301 
0302       // f(x + h[i]*e[i] + h[j]*e[j])
0303       theX(i) = aXi + theStep;
0304       theX(j) = aXj + theStep;
0305       if (!theFunc.Value(theX, aFpp))
0306       {
0307         theX(i) = aXi;
0308         theX(j) = aXj;
0309         return false;
0310       }
0311 
0312       // f(x + h[i]*e[i] - h[j]*e[j])
0313       theX(j) = aXj - theStep;
0314       if (!theFunc.Value(theX, aFpm))
0315       {
0316         theX(i) = aXi;
0317         theX(j) = aXj;
0318         return false;
0319       }
0320 
0321       // f(x - h[i]*e[i] - h[j]*e[j])
0322       theX(i) = aXi - theStep;
0323       if (!theFunc.Value(theX, aFmm))
0324       {
0325         theX(i) = aXi;
0326         theX(j) = aXj;
0327         return false;
0328       }
0329 
0330       // f(x - h[i]*e[i] + h[j]*e[j])
0331       theX(j) = aXj + theStep;
0332       if (!theFunc.Value(theX, aFmp))
0333       {
0334         theX(i) = aXi;
0335         theX(j) = aXj;
0336         return false;
0337       }
0338 
0339       // Restore
0340       theX(i) = aXi;
0341       theX(j) = aXj;
0342 
0343       const int    aMatI = i - aLower + 1;
0344       const int    aMatJ = j - aLower + 1;
0345       const double aHij  = (aFpp - aFpm - aFmp + aFmm) / (4.0 * theStep * theStep);
0346 
0347       // Symmetric
0348       theHess(aMatI, aMatJ) = aHij;
0349       theHess(aMatJ, aMatI) = aHij;
0350     }
0351   }
0352 
0353   return true;
0354 }
0355 
0356 //! Second derivative using central difference.
0357 //! f''(x) ~= (f(x+h) - 2f(x) + f(x-h)) / h^2
0358 //!
0359 //! @tparam Function type with Value(double theX, double& theF) method
0360 //! @param theFunc function to differentiate
0361 //! @param theX point at which to evaluate second derivative
0362 //! @param theFx function value at theX (if already computed)
0363 //! @param theD2f computed second derivative value
0364 //! @param theStep step size (default 1e-5)
0365 //! @return true if successful
0366 template <typename Function>
0367 bool SecondDerivative(Function& theFunc,
0368                       double    theX,
0369                       double    theFx,
0370                       double&   theD2f,
0371                       double    theStep = 1.0e-5)
0372 {
0373   double aFPlus  = 0.0;
0374   double aFMinus = 0.0;
0375 
0376   if (!theFunc.Value(theX + theStep, aFPlus))
0377   {
0378     return false;
0379   }
0380   if (!theFunc.Value(theX - theStep, aFMinus))
0381   {
0382     return false;
0383   }
0384 
0385   theD2f = (aFPlus - 2.0 * theFx + aFMinus) / (theStep * theStep);
0386   return true;
0387 }
0388 
0389 } // namespace MathUtils
0390 
0391 #endif // _MathUtils_Deriv_HeaderFile