Back to home page

EIC code displayed by LXR

 
 

    


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

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_Powell_HeaderFile
0015 #define _MathOpt_Powell_HeaderFile
0016 
0017 #include <MathUtils_Types.hxx>
0018 #include <MathUtils_Config.hxx>
0019 #include <MathUtils_Core.hxx>
0020 #include <MathUtils_LineSearch.hxx>
0021 
0022 #include <cmath>
0023 
0024 namespace MathOpt
0025 {
0026 using namespace MathUtils;
0027 
0028 //! Powell's conjugate direction method for N-dimensional minimization.
0029 //! A gradient-free optimization algorithm that uses conjugate directions.
0030 //!
0031 //! Algorithm:
0032 //! 1. Start with N linearly independent directions (coordinate axes)
0033 //! 2. Perform line minimization along each direction
0034 //! 3. Replace one direction with the overall displacement direction
0035 //! 4. Repeat until convergence
0036 //!
0037 //! Advantages:
0038 //! - No gradient required
0039 //! - Generates conjugate directions for quadratic functions
0040 //! - Robust for non-smooth functions
0041 //!
0042 //! Disadvantages:
0043 //! - Slower than gradient methods for smooth functions
0044 //! - May lose direction independence over iterations
0045 //!
0046 //! @tparam Function type with Value(const math_Vector&, double&) method
0047 //! @param theFunc function to minimize
0048 //! @param theStartingPoint initial guess (N-dimensional)
0049 //! @param theConfig solver configuration
0050 //! @return result containing minimum location and value
0051 template <typename Function>
0052 VectorResult Powell(Function&          theFunc,
0053                     const math_Vector& theStartingPoint,
0054                     const Config&      theConfig = Config())
0055 {
0056   VectorResult aResult;
0057 
0058   const int aLower = theStartingPoint.Lower();
0059   const int aUpper = theStartingPoint.Upper();
0060   const int aN     = aUpper - aLower + 1;
0061 
0062   // Current point
0063   math_Vector aX(aLower, aUpper);
0064   aX = theStartingPoint;
0065 
0066   // Function value at current point
0067   double aFx = 0.0;
0068   if (!theFunc.Value(aX, aFx))
0069   {
0070     aResult.Status = Status::NumericalError;
0071     return aResult;
0072   }
0073 
0074   // Initialize direction set to coordinate axes
0075   // Directions stored as rows of matrix
0076   math_Matrix aDirections(1, aN, 1, aN, 0.0);
0077   for (int i = 1; i <= aN; ++i)
0078   {
0079     aDirections(i, i) = 1.0;
0080   }
0081 
0082   // Working vectors
0083   math_Vector aDir(aLower, aUpper);
0084   math_Vector aXOld(aLower, aUpper);
0085   math_Vector aPt(aLower, aUpper);
0086   math_Vector aPtt(aLower, aUpper);
0087   math_Vector aXit(aLower, aUpper);
0088 
0089   for (int anIter = 0; anIter < theConfig.MaxIterations; ++anIter)
0090   {
0091     aResult.NbIterations = anIter + 1;
0092 
0093     const double aFp = aFx;
0094     aXOld            = aX;
0095 
0096     // Track largest decrease and its direction index
0097     double aDel  = 0.0;
0098     int    aIBig = 0;
0099 
0100     // Minimize along each direction
0101     for (int i = 1; i <= aN; ++i)
0102     {
0103       // Extract direction i
0104       for (int j = aLower; j <= aUpper; ++j)
0105       {
0106         aDir(j) = aDirections(i, j - aLower + 1);
0107       }
0108 
0109       const double aFpPrev = aFx;
0110 
0111       // Line minimization along direction
0112       MathUtils::LineSearchResult aLineResult =
0113         MathUtils::ExactLineSearch(theFunc, aX, aDir, 10.0, theConfig.XTolerance);
0114 
0115       if (aLineResult.IsValid)
0116       {
0117         // Update position
0118         for (int j = aLower; j <= aUpper; ++j)
0119         {
0120           aX(j) += aLineResult.Alpha * aDir(j);
0121         }
0122         aFx = aLineResult.FNew;
0123 
0124         // Track direction with largest decrease
0125         const double aDecrease = aFpPrev - aFx;
0126         if (aDecrease > aDel)
0127         {
0128           aDel  = aDecrease;
0129           aIBig = i;
0130         }
0131       }
0132     }
0133 
0134     // Check convergence
0135     if (2.0 * std::abs(aFp - aFx)
0136         <= theConfig.FTolerance * (std::abs(aFp) + std::abs(aFx) + MathUtils::THE_ZERO_TOL))
0137     {
0138       aResult.Status   = Status::OK;
0139       aResult.Solution = aX;
0140       aResult.Value    = aFx;
0141       return aResult;
0142     }
0143 
0144     // Construct extrapolated point and new direction
0145     for (int j = aLower; j <= aUpper; ++j)
0146     {
0147       aPtt(j) = 2.0 * aX(j) - aXOld(j);
0148       aXit(j) = aX(j) - aXOld(j);
0149     }
0150 
0151     // Evaluate at extrapolated point
0152     double aFptt = 0.0;
0153     if (!theFunc.Value(aPtt, aFptt))
0154     {
0155       // If evaluation fails, continue with current directions
0156       continue;
0157     }
0158 
0159     // Check if new direction should be added
0160     if (aFptt < aFp)
0161     {
0162       const double aT = 2.0 * (aFp - 2.0 * aFx + aFptt) * MathUtils::Sqr(aFp - aFx - aDel)
0163                         - aDel * MathUtils::Sqr(aFp - aFptt);
0164 
0165       if (aT < 0.0)
0166       {
0167         // Minimize along new direction
0168         MathUtils::LineSearchResult aLineResult =
0169           MathUtils::ExactLineSearch(theFunc, aX, aXit, 10.0, theConfig.XTolerance);
0170 
0171         if (aLineResult.IsValid)
0172         {
0173           // Update position
0174           for (int j = aLower; j <= aUpper; ++j)
0175           {
0176             aX(j) += aLineResult.Alpha * aXit(j);
0177           }
0178           aFx = aLineResult.FNew;
0179 
0180           // Replace direction with largest decrease
0181           if (aIBig > 0)
0182           {
0183             for (int j = 1; j <= aN; ++j)
0184             {
0185               aDirections(aIBig, j) = aDirections(aN, j);
0186               aDirections(aN, j)    = aXit(aLower + j - 1);
0187             }
0188           }
0189         }
0190       }
0191     }
0192 
0193     // Check X convergence
0194     double aMaxDiff = 0.0;
0195     for (int j = aLower; j <= aUpper; ++j)
0196     {
0197       aMaxDiff = std::max(aMaxDiff, std::abs(aX(j) - aXOld(j)));
0198     }
0199     if (aMaxDiff < theConfig.XTolerance)
0200     {
0201       aResult.Status   = Status::OK;
0202       aResult.Solution = aX;
0203       aResult.Value    = aFx;
0204       return aResult;
0205     }
0206   }
0207 
0208   // Maximum iterations reached
0209   aResult.Status   = Status::MaxIterations;
0210   aResult.Solution = aX;
0211   aResult.Value    = aFx;
0212   return aResult;
0213 }
0214 
0215 //! Powell's method with custom initial directions.
0216 //! Allows specifying the initial direction set instead of coordinate axes.
0217 //!
0218 //! @tparam Function type with Value(const math_Vector&, double&) method
0219 //! @param theFunc function to minimize
0220 //! @param theStartingPoint initial guess
0221 //! @param theInitialDirections initial direction set (N x N matrix, directions as rows)
0222 //! @param theConfig solver configuration
0223 //! @return result containing minimum location and value
0224 template <typename Function>
0225 VectorResult PowellWithDirections(Function&          theFunc,
0226                                   const math_Vector& theStartingPoint,
0227                                   const math_Matrix& theInitialDirections,
0228                                   const Config&      theConfig = Config())
0229 {
0230   VectorResult aResult;
0231 
0232   const int aLower = theStartingPoint.Lower();
0233   const int aUpper = theStartingPoint.Upper();
0234   const int aN     = aUpper - aLower + 1;
0235 
0236   // Validate dimensions
0237   if (theInitialDirections.RowNumber() != aN || theInitialDirections.ColNumber() != aN)
0238   {
0239     aResult.Status = Status::InvalidInput;
0240     return aResult;
0241   }
0242 
0243   math_Vector aX(aLower, aUpper);
0244   aX = theStartingPoint;
0245 
0246   double aFx = 0.0;
0247   if (!theFunc.Value(aX, aFx))
0248   {
0249     aResult.Status = Status::NumericalError;
0250     return aResult;
0251   }
0252 
0253   // Copy initial directions
0254   math_Matrix aDirections(1, aN, 1, aN);
0255   aDirections = theInitialDirections;
0256 
0257   math_Vector aDir(aLower, aUpper);
0258   math_Vector aXOld(aLower, aUpper);
0259   math_Vector aPtt(aLower, aUpper);
0260   math_Vector aXit(aLower, aUpper);
0261 
0262   for (int anIter = 0; anIter < theConfig.MaxIterations; ++anIter)
0263   {
0264     aResult.NbIterations = anIter + 1;
0265 
0266     const double aFp = aFx;
0267     aXOld            = aX;
0268 
0269     double aDel  = 0.0;
0270     int    aIBig = 0;
0271 
0272     for (int i = 1; i <= aN; ++i)
0273     {
0274       for (int j = aLower; j <= aUpper; ++j)
0275       {
0276         aDir(j) = aDirections(i, j - aLower + 1);
0277       }
0278 
0279       const double aFpPrev = aFx;
0280 
0281       MathUtils::LineSearchResult aLineResult =
0282         MathUtils::ExactLineSearch(theFunc, aX, aDir, 10.0, theConfig.XTolerance);
0283 
0284       if (aLineResult.IsValid)
0285       {
0286         for (int j = aLower; j <= aUpper; ++j)
0287         {
0288           aX(j) += aLineResult.Alpha * aDir(j);
0289         }
0290         aFx = aLineResult.FNew;
0291 
0292         const double aDecrease = aFpPrev - aFx;
0293         if (aDecrease > aDel)
0294         {
0295           aDel  = aDecrease;
0296           aIBig = i;
0297         }
0298       }
0299     }
0300 
0301     // Check convergence
0302     if (2.0 * std::abs(aFp - aFx)
0303         <= theConfig.FTolerance * (std::abs(aFp) + std::abs(aFx) + MathUtils::THE_ZERO_TOL))
0304     {
0305       aResult.Status   = Status::OK;
0306       aResult.Solution = aX;
0307       aResult.Value    = aFx;
0308       return aResult;
0309     }
0310 
0311     // Construct extrapolated point
0312     for (int j = aLower; j <= aUpper; ++j)
0313     {
0314       aPtt(j) = 2.0 * aX(j) - aXOld(j);
0315       aXit(j) = aX(j) - aXOld(j);
0316     }
0317 
0318     double aFptt = 0.0;
0319     if (!theFunc.Value(aPtt, aFptt))
0320     {
0321       continue;
0322     }
0323 
0324     if (aFptt < aFp)
0325     {
0326       const double aT = 2.0 * (aFp - 2.0 * aFx + aFptt) * MathUtils::Sqr(aFp - aFx - aDel)
0327                         - aDel * MathUtils::Sqr(aFp - aFptt);
0328 
0329       if (aT < 0.0)
0330       {
0331         MathUtils::LineSearchResult aLineResult =
0332           MathUtils::ExactLineSearch(theFunc, aX, aXit, 10.0, theConfig.XTolerance);
0333 
0334         if (aLineResult.IsValid)
0335         {
0336           for (int j = aLower; j <= aUpper; ++j)
0337           {
0338             aX(j) += aLineResult.Alpha * aXit(j);
0339           }
0340           aFx = aLineResult.FNew;
0341 
0342           if (aIBig > 0)
0343           {
0344             for (int j = 1; j <= aN; ++j)
0345             {
0346               aDirections(aIBig, j) = aDirections(aN, j);
0347               aDirections(aN, j)    = aXit(aLower + j - 1);
0348             }
0349           }
0350         }
0351       }
0352     }
0353 
0354     double aMaxDiff = 0.0;
0355     for (int j = aLower; j <= aUpper; ++j)
0356     {
0357       aMaxDiff = std::max(aMaxDiff, std::abs(aX(j) - aXOld(j)));
0358     }
0359     if (aMaxDiff < theConfig.XTolerance)
0360     {
0361       aResult.Status   = Status::OK;
0362       aResult.Solution = aX;
0363       aResult.Value    = aFx;
0364       return aResult;
0365     }
0366   }
0367 
0368   aResult.Status   = Status::MaxIterations;
0369   aResult.Solution = aX;
0370   aResult.Value    = aFx;
0371   return aResult;
0372 }
0373 
0374 } // namespace MathOpt
0375 
0376 #endif // _MathOpt_Powell_HeaderFile