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_All_HeaderFile
0015 #define _MathRoot_All_HeaderFile
0016 
0017 #include <MathUtils_Types.hxx>
0018 #include <MathUtils_Config.hxx>
0019 #include <MathRoot_Multiple.hxx>
0020 #include <math_Vector.hxx>
0021 
0022 #include <NCollection_DynamicArray.hxx>
0023 
0024 #include <cmath>
0025 
0026 namespace MathRoot
0027 {
0028 using namespace MathUtils;
0029 
0030 //! Represents an interval where the function is null (within tolerance).
0031 struct NullInterval
0032 {
0033   double A     = 0.0; //!< Interval start
0034   double B     = 0.0; //!< Interval end
0035   int    State = 0;   //!< State number (for parametric curves)
0036 };
0037 
0038 //! Result for all roots finder including null intervals.
0039 struct AllRootsResult
0040 {
0041   MathUtils::Status                      Status = MathUtils::Status::NotConverged;
0042   NCollection_DynamicArray<double>       Roots;         //!< Isolated root locations
0043   NCollection_DynamicArray<int>          RootStates;    //!< State numbers for roots
0044   NCollection_DynamicArray<NullInterval> NullIntervals; //!< Intervals where function is null
0045 
0046   bool IsDone() const { return Status == MathUtils::Status::OK; }
0047 
0048   explicit operator bool() const { return IsDone(); }
0049 
0050   int NbRoots() const { return Roots.Length(); }
0051 
0052   int NbIntervals() const { return NullIntervals.Length(); }
0053 };
0054 
0055 //! Helper function to find multiple roots with offset.
0056 //! @tparam Func function type
0057 //! @param theFunc function to analyze
0058 //! @param theA interval start
0059 //! @param theB interval end
0060 //! @param theNbSamples number of sample points
0061 //! @param theEpsX tolerance for root x-value
0062 //! @param theEpsF tolerance for function value
0063 //! @param theOffset offset value (find roots of f(x) - offset = 0)
0064 //! @return MultipleResult with found roots
0065 template <typename Func>
0066 MultipleResult FindMultipleRoots(Func&  theFunc,
0067                                  double theA,
0068                                  double theB,
0069                                  int    theNbSamples,
0070                                  double theEpsX,
0071                                  double theEpsF,
0072                                  double theOffset = 0.0)
0073 {
0074   MultipleConfig aConfig;
0075   aConfig.NbSamples  = theNbSamples;
0076   aConfig.XTolerance = theEpsX;
0077   aConfig.FTolerance = theEpsF;
0078   aConfig.Offset     = theOffset;
0079   return FindAllRoots(theFunc, theA, theB, aConfig);
0080 }
0081 
0082 //! Find all roots of a function using sampling and refinement.
0083 //!
0084 //! Uses a sample of the function to find:
0085 //! 1. Null intervals: where |F(x)| <= EpsNul for consecutive sample points
0086 //! 2. Isolated roots: single points where F(x) = 0
0087 //!
0088 //! The algorithm:
0089 //! 1. Evaluates F at sample points
0090 //! 2. Identifies null intervals where |F| <= EpsNul for 2+ consecutive points
0091 //! 3. Refines interval boundaries using root finding
0092 //! 4. Finds isolated roots between null intervals using MultipleRoots
0093 //!
0094 //! @tparam Func function type with:
0095 //!   - bool Value(double, double&)
0096 //!   - bool Values(double, double&, double&) for derivative
0097 //! @param theFunc function with derivative to analyze
0098 //! @param theSamples sample points array
0099 //! @param theEpsX tolerance for root x-value
0100 //! @param theEpsF tolerance for function value at root
0101 //! @param theEpsNul tolerance for null interval detection
0102 //! @return AllRootsResult with roots and null intervals
0103 template <typename Func>
0104 AllRootsResult FindAllRootsWithIntervals(Func&              theFunc,
0105                                          const math_Vector& theSamples,
0106                                          double             theEpsX   = 1.0e-10,
0107                                          double             theEpsF   = 1.0e-10,
0108                                          double             theEpsNul = 1.0e-10)
0109 {
0110   AllRootsResult aResult;
0111 
0112   const int aNbp = theSamples.Length();
0113   if (aNbp < 2)
0114   {
0115     aResult.Status = MathUtils::Status::InvalidInput;
0116     return aResult;
0117   }
0118 
0119   const int aLower = theSamples.Lower();
0120 
0121   // Evaluate function at first sample point
0122   double aVal, aPrevVal;
0123   if (!theFunc.Value(theSamples(aLower), aPrevVal))
0124   {
0125     aResult.Status = MathUtils::Status::NotConverged;
0126     return aResult;
0127   }
0128 
0129   bool aPrevNul = std::abs(aPrevVal) <= theEpsNul;
0130   if (!aPrevNul)
0131   {
0132     // Save non-null value for later use
0133   }
0134 
0135   bool   aInInterval = false;
0136   bool   aNulStart   = false;
0137   bool   aNulEnd     = false;
0138   double aDebNul = 0.0, aFinNul = 0.0;
0139   double aValSav = aPrevVal;
0140 
0141   NCollection_DynamicArray<double> aIntervalStarts, aIntervalEnds;
0142 
0143   // Scan through samples to find null intervals
0144   for (int i = 1; i < aNbp; ++i)
0145   {
0146     if (!theFunc.Value(theSamples(aLower + i), aVal))
0147     {
0148       aResult.Status = MathUtils::Status::NotConverged;
0149       return aResult;
0150     }
0151 
0152     bool aCurNul = std::abs(aVal) <= theEpsNul;
0153     if (!aCurNul)
0154     {
0155       aValSav = aVal;
0156     }
0157 
0158     if (aInInterval && !aCurNul)
0159     {
0160       // End of null interval
0161       aInInterval = false;
0162       aIntervalStarts.Append(aDebNul);
0163 
0164       // Refine end of null interval
0165       double aCst = (aVal > 0.0) ? theEpsNul : -theEpsNul;
0166 
0167       // Use root finding to locate precise boundary
0168       MultipleResult aRes = FindMultipleRoots(theFunc,
0169                                               theSamples(aLower + i - 1),
0170                                               theSamples(aLower + i),
0171                                               10,
0172                                               theEpsX,
0173                                               theEpsF,
0174                                               aCst);
0175       if (aRes.IsDone() && aRes.NbRoots() > 0)
0176       {
0177         aFinNul = aRes.Roots[0];
0178       }
0179       else
0180       {
0181         aFinNul = theSamples(aLower + i - 1);
0182       }
0183 
0184       // Try opposite sign
0185       aCst       = -aCst;
0186       auto aRes2 = FindMultipleRoots(theFunc,
0187                                      theSamples(aLower + i - 1),
0188                                      theSamples(aLower + i),
0189                                      10,
0190                                      theEpsX,
0191                                      theEpsF,
0192                                      aCst);
0193       if (aRes2.IsDone() && aRes2.NbRoots() > 0)
0194       {
0195         if (aRes2.Roots[0] < aFinNul)
0196         {
0197           aFinNul = aRes2.Roots[0];
0198         }
0199       }
0200 
0201       aIntervalEnds.Append(aFinNul);
0202     }
0203     else if (!aInInterval && aPrevNul && aCurNul)
0204     {
0205       // Start of null interval
0206       aInInterval = true;
0207       if (i == 1)
0208       {
0209         aDebNul   = theSamples(aLower);
0210         aNulStart = true;
0211       }
0212       else
0213       {
0214         // Refine start of null interval
0215         double aCst = (aValSav > 0.0) ? theEpsNul : -theEpsNul;
0216 
0217         MultipleResult aRes = FindMultipleRoots(theFunc,
0218                                                 theSamples(aLower + i - 2),
0219                                                 theSamples(aLower + i - 1),
0220                                                 10,
0221                                                 theEpsX,
0222                                                 theEpsF,
0223                                                 aCst);
0224         if (aRes.IsDone() && aRes.NbRoots() > 0)
0225         {
0226           aDebNul = aRes.Roots[aRes.NbRoots() - 1];
0227         }
0228         else
0229         {
0230           aDebNul = theSamples(aLower + i - 1);
0231         }
0232 
0233         // Try opposite sign
0234         aCst       = -aCst;
0235         auto aRes2 = FindMultipleRoots(theFunc,
0236                                        theSamples(aLower + i - 2),
0237                                        theSamples(aLower + i - 1),
0238                                        10,
0239                                        theEpsX,
0240                                        theEpsF,
0241                                        aCst);
0242         if (aRes2.IsDone() && aRes2.NbRoots() > 0)
0243         {
0244           if (aRes2.Roots[aRes2.NbRoots() - 1] > aDebNul)
0245           {
0246             aDebNul = aRes2.Roots[aRes2.NbRoots() - 1];
0247           }
0248         }
0249       }
0250     }
0251 
0252     aPrevNul = aCurNul;
0253   }
0254 
0255   // Handle interval ending at last sample
0256   if (aInInterval)
0257   {
0258     aIntervalStarts.Append(aDebNul);
0259     aFinNul = theSamples(aLower + aNbp - 1);
0260     aIntervalEnds.Append(aFinNul);
0261     aNulEnd = true;
0262   }
0263 
0264   // Store null intervals
0265   for (int k = 0; k < aIntervalStarts.Length(); ++k)
0266   {
0267     NullInterval anInt;
0268     anInt.A = aIntervalStarts.Value(k);
0269     anInt.B = aIntervalEnds.Value(k);
0270     aResult.NullIntervals.Append(anInt);
0271   }
0272 
0273   const double aSampleFirst = theSamples(aLower);
0274   const double aSampleLast  = theSamples(aLower + aNbp - 1);
0275 
0276   // Find isolated roots between null intervals
0277   if (aIntervalStarts.IsEmpty())
0278   {
0279     // No null intervals - find all roots in entire range
0280     MultipleResult aRes =
0281       FindMultipleRoots(theFunc, aSampleFirst, aSampleLast, aNbp, theEpsX, theEpsF);
0282     if (aRes.IsDone())
0283     {
0284       for (int j = 0; j < aRes.NbRoots(); ++j)
0285       {
0286         aResult.Roots.Append(aRes.Roots[j]);
0287         aResult.RootStates.Append(0);
0288       }
0289     }
0290   }
0291   else
0292   {
0293     // Find roots before first null interval
0294     if (!aNulStart)
0295     {
0296       double aStart = aSampleFirst;
0297       double aEnd   = aIntervalStarts.Value(0);
0298       int    aNbrpt =
0299         std::max(3,
0300                  static_cast<int>(std::abs((aEnd - aStart) / (aSampleLast - aSampleFirst)) * aNbp));
0301 
0302       MultipleResult aRes = FindMultipleRoots(theFunc, aStart, aEnd, aNbrpt, theEpsX, theEpsF);
0303       if (aRes.IsDone())
0304       {
0305         for (int j = 0; j < aRes.NbRoots(); ++j)
0306         {
0307           aResult.Roots.Append(aRes.Roots[j]);
0308           aResult.RootStates.Append(0);
0309         }
0310       }
0311     }
0312 
0313     // Find roots between null intervals
0314     for (int k = 1; k < aIntervalStarts.Length(); ++k)
0315     {
0316       double aStart = aIntervalEnds.Value(k - 1);
0317       double aEnd   = aIntervalStarts.Value(k);
0318       int    aNbrpt =
0319         std::max(3,
0320                  static_cast<int>(std::abs((aEnd - aStart) / (aSampleLast - aSampleFirst)) * aNbp));
0321 
0322       MultipleResult aRes = FindMultipleRoots(theFunc, aStart, aEnd, aNbrpt, theEpsX, theEpsF);
0323       if (aRes.IsDone())
0324       {
0325         for (int j = 0; j < aRes.NbRoots(); ++j)
0326         {
0327           aResult.Roots.Append(aRes.Roots[j]);
0328           aResult.RootStates.Append(0);
0329         }
0330       }
0331     }
0332 
0333     // Find roots after last null interval
0334     if (!aNulEnd)
0335     {
0336       double aStart = aIntervalEnds.Value(aIntervalEnds.Length() - 1);
0337       double aEnd   = aSampleLast;
0338       int    aNbrpt =
0339         std::max(3,
0340                  static_cast<int>(std::abs((aEnd - aStart) / (aSampleLast - aSampleFirst)) * aNbp));
0341 
0342       MultipleResult aRes = FindMultipleRoots(theFunc, aStart, aEnd, aNbrpt, theEpsX, theEpsF);
0343       if (aRes.IsDone())
0344       {
0345         for (int j = 0; j < aRes.NbRoots(); ++j)
0346         {
0347           aResult.Roots.Append(aRes.Roots[j]);
0348           aResult.RootStates.Append(0);
0349         }
0350       }
0351     }
0352   }
0353 
0354   aResult.Status = MathUtils::Status::OK;
0355   return aResult;
0356 }
0357 
0358 //! Find all roots using uniform sampling.
0359 //!
0360 //! @tparam Func function type with Value and Values methods
0361 //! @param theFunc function with derivative
0362 //! @param theA interval start
0363 //! @param theB interval end
0364 //! @param theNbSamples number of sample points
0365 //! @param theEpsX tolerance for root x-value
0366 //! @param theEpsF tolerance for function value
0367 //! @param theEpsNul tolerance for null interval detection
0368 //! @return AllRootsResult with roots and null intervals
0369 template <typename Func>
0370 AllRootsResult FindAllRootsWithIntervals(Func&  theFunc,
0371                                          double theA,
0372                                          double theB,
0373                                          int    theNbSamples,
0374                                          double theEpsX   = 1.0e-10,
0375                                          double theEpsF   = 1.0e-10,
0376                                          double theEpsNul = 1.0e-10)
0377 {
0378   math_Vector  aSamples(0, theNbSamples - 1);
0379   const double aStep = (theB - theA) / (theNbSamples - 1);
0380   for (int i = 0; i < theNbSamples; ++i)
0381   {
0382     aSamples(i) = theA + i * aStep;
0383   }
0384   // Ensure last point is exactly theB
0385   aSamples(theNbSamples - 1) = theB;
0386 
0387   return FindAllRootsWithIntervals(theFunc, aSamples, theEpsX, theEpsF, theEpsNul);
0388 }
0389 
0390 } // namespace MathRoot
0391 
0392 #endif // _MathRoot_All_HeaderFile