0029858: Modeling Data - Regression in GeomAPI_ExtremaCurveCurve
[occt.git] / src / math / math_GlobOptMin.cxx
index 2560aa2..d908eef 100644 (file)
 #include <Standard_Real.hxx>
 #include <Precision.hxx>
 
+//=======================================================================
+//function : DistanceToBorder
+//purpose  :
+//=======================================================================
+static Standard_Real DistanceToBorder(const math_Vector & theX,
+                                      const math_Vector & theMin,
+                                      const math_Vector & theMax)
+{
+  Standard_Real aDist = RealLast();
+
+  for (Standard_Integer anIdx = theMin.Lower(); anIdx <= theMin.Upper(); ++anIdx)
+  {
+    const Standard_Real aDist1 = Abs (theX(anIdx) - theMin(anIdx));
+    const Standard_Real aDist2 = Abs (theX(anIdx) - theMax(anIdx));
+
+    aDist = Min (aDist, Min (aDist1, aDist2));
+  }
+
+  return aDist;
+}
+
 
 //=======================================================================
 //function : math_GlobOptMin
@@ -41,18 +62,21 @@ math_GlobOptMin::math_GlobOptMin(math_MultipleVarFunction* theFunc,
   myB(1, myN),
   myGlobA(1, myN),
   myGlobB(1, myN),
+  myIsConstLocked(Standard_False),
   myX(1, myN),
   myTmp(1, myN),
   myV(1, myN),
   myMaxV(1, myN),
-  myExpandCoeff(1, myN),
   myCellSize(0, myN - 1),
-  myFilter(theFunc->NbVariables())
+  myFilter(theFunc->NbVariables()),
+  myCont(2),
+  myF(Precision::Infinite())
 {
   Standard_Integer i;
 
   myFunc = theFunc;
   myC = theC;
+  myInitC = theC;
   myIsFindSingleSolution = Standard_False;
   myFunctionalMinimalValue = -Precision::Infinite();
   myZ = -1;
@@ -72,12 +96,6 @@ math_GlobOptMin::math_GlobOptMin(math_MultipleVarFunction* theFunc,
     myMaxV(i) = (myB(i) - myA(i)) / 3.0;
   }
 
-  myExpandCoeff(1) = 1.0;
-  for(i = 2; i <= myN; i++)
-  {
-    myExpandCoeff(i) = (myB(i) - myA(i)) / (myB(i - 1) - myA(i - 1));
-  }
-
   myTol = theDiscretizationTol;
   mySameTol = theSameTol;
 
@@ -85,13 +103,14 @@ math_GlobOptMin::math_GlobOptMin(math_MultipleVarFunction* theFunc,
   Standard_Integer aSolNb = Standard_Integer(Pow(3.0, Standard_Real(myN)));
   myMinCellFilterSol = Max(2 * aSolNb, aMaxSquareSearchSol);
   initCellSize();
+  ComputeInitSol();
 
   myDone = Standard_False;
 }
 
 //=======================================================================
 //function : SetGlobalParams
-//purpose  : Set params without memory allocation.
+//purpose  : Set parameters without memory allocation.
 //=======================================================================
 void math_GlobOptMin::SetGlobalParams(math_MultipleVarFunction* theFunc,
                                       const math_Vector& theA,
@@ -104,6 +123,7 @@ void math_GlobOptMin::SetGlobalParams(math_MultipleVarFunction* theFunc,
 
   myFunc = theFunc;
   myC = theC;
+  myInitC = theC;
   myZ = -1;
   mySolCount = 0;
 
@@ -121,23 +141,18 @@ void math_GlobOptMin::SetGlobalParams(math_MultipleVarFunction* theFunc,
     myMaxV(i) = (myB(i) - myA(i)) / 3.0;
   }
 
-  myExpandCoeff(1) = 1.0;
-  for(i = 2; i <= myN; i++)
-  {
-    myExpandCoeff(i) = (myB(i) - myA(i)) / (myB(i - 1) - myA(i - 1));
-  }
-
   myTol = theDiscretizationTol;
   mySameTol = theSameTol;
 
   initCellSize();
+  ComputeInitSol();
 
   myDone = Standard_False;
 }
 
 //=======================================================================
 //function : SetLocalParams
-//purpose  : Set params without memory allocation.
+//purpose  : Set parameters without memory allocation.
 //=======================================================================
 void math_GlobOptMin::SetLocalParams(const math_Vector& theLocalA,
                                      const math_Vector& theLocalB)
@@ -145,8 +160,6 @@ void math_GlobOptMin::SetLocalParams(const math_Vector& theLocalA,
   Standard_Integer i;
 
   myZ = -1;
-  mySolCount = 0;
-
   for(i = 1; i <= myN; i++)
   {
     myA(i) = theLocalA(i);
@@ -158,12 +171,6 @@ void math_GlobOptMin::SetLocalParams(const math_Vector& theLocalA,
     myMaxV(i) = (myB(i) - myA(i)) / 3.0;
   }
 
-  myExpandCoeff(1) = 1.0;
-  for(i = 2; i <= myN; i++)
-  {
-    myExpandCoeff(i) = (myB(i) - myA(i)) / (myB(i - 1) - myA(i - 1));
-  }
-
   myDone = Standard_False;
 }
 
@@ -189,14 +196,6 @@ void math_GlobOptMin::GetTol(Standard_Real& theDiscretizationTol,
   theSameTol = mySameTol;
 }
 
-//=======================================================================
-//function : ~math_GlobOptMin
-//purpose  : 
-//=======================================================================
-math_GlobOptMin::~math_GlobOptMin()
-{
-}
-
 //=======================================================================
 //function : Perform
 //purpose  : Compute Global extremum point
@@ -204,31 +203,36 @@ math_GlobOptMin::~math_GlobOptMin()
 // In this algo indexes started from 1, not from 0.
 void math_GlobOptMin::Perform(const Standard_Boolean isFindSingleSolution)
 {
-  Standard_Integer i;
+  myDone = Standard_False;
 
   // Compute parameters range
   Standard_Real minLength = RealLast();
   Standard_Real maxLength = RealFirst();
-  for(i = 1; i <= myN; i++)
+  for(Standard_Integer i = 1; i <= myN; i++)
   {
     Standard_Real currentLength = myB(i) - myA(i);
     if (currentLength < minLength)
       minLength = currentLength;
     if (currentLength > maxLength)
       maxLength = currentLength;
+
+    myV(i) = 0.0;
   }
 
   if (minLength < Precision::PConfusion())
   {
     #ifdef OCCT_DEBUG
-    cout << "math_GlobOptMin::Perform(): Degenerated parameters space" << endl;
+    std::cout << "math_GlobOptMin::Perform(): Degenerated parameters space" << std::endl;
     #endif
 
     return;
   }
 
-  // Compute initial values for myF, myY, myC.
-  computeInitialValues();
+  if (!myIsConstLocked)
+  {
+    // Compute initial value for myC.
+    computeInitialValues();
+  }
 
   myE1 = minLength * myTol;
   myE2 = maxLength * myTol;
@@ -236,8 +240,7 @@ void math_GlobOptMin::Perform(const Standard_Boolean isFindSingleSolution)
   myIsFindSingleSolution = isFindSingleSolution;
   if (isFindSingleSolution)
   {
-    // Run local optimization 
-    // if current value better than optimal.
+    // Run local optimization if current value better than optimal.
     myE3 = 0.0;
   }
   else
@@ -248,13 +251,14 @@ void math_GlobOptMin::Perform(const Standard_Boolean isFindSingleSolution)
       myE3 = - maxLength * myTol * myC / 4.0;
   }
 
-  // Search single solution and current solution in its neighbourhood.
+  // Search single solution and current solution in its neighborhood.
   if (CheckFunctionalStopCriteria())
   {
     myDone = Standard_True;
     return;
   }
 
+  myLastStep = 0.0;
   isFirstCellFilterInvoke = Standard_True;
   computeGlobalExtremum(myN);
 
@@ -272,7 +276,8 @@ Standard_Boolean math_GlobOptMin::computeLocalExtremum(const math_Vector& thePnt
   Standard_Integer i;
 
   //Newton method
-  if (dynamic_cast<math_MultipleVarFunctionWithHessian*>(myFunc))
+  if (myCont >= 2 &&
+      dynamic_cast<math_MultipleVarFunctionWithHessian*>(myFunc))
   {
     math_MultipleVarFunctionWithHessian* aTmp = 
       dynamic_cast<math_MultipleVarFunctionWithHessian*> (myFunc);
@@ -284,31 +289,38 @@ Standard_Boolean math_GlobOptMin::computeLocalExtremum(const math_Vector& thePnt
     {
       newtonMinimum.Location(theOutPnt);
       theVal = newtonMinimum.Minimum();
+
+      if (isInside(theOutPnt))
+        return Standard_True;
     }
-    else return Standard_False;
-  } else
+  }
 
   // BFGS method used.
-  if (dynamic_cast<math_MultipleVarFunctionWithGradient*>(myFunc))
+  if (myCont >= 1 &&
+      dynamic_cast<math_MultipleVarFunctionWithGradient*>(myFunc))
   {
     math_MultipleVarFunctionWithGradient* aTmp =
       dynamic_cast<math_MultipleVarFunctionWithGradient*> (myFunc);
     math_BFGS bfgs(aTmp->NbVariables());
+    bfgs.SetBoundary(myGlobA, myGlobB);
     bfgs.Perform(*aTmp, thePnt);
+
     if (bfgs.IsDone())
     {
       bfgs.Location(theOutPnt);
       theVal = bfgs.Minimum();
+
+      if (isInside(theOutPnt))
+        return Standard_True;
     }
-    else return Standard_False;
-  } else
+  }
 
   // Powell method used.
   if (dynamic_cast<math_MultipleVarFunction*>(myFunc))
   {
     math_Matrix m(1, myN, 1, myN, 0.0);
     for(i = 1; i <= myN; i++)
-      m(1, 1) = 1.0;
+      m(i, i) = 1.0;
 
     math_Powell powell(*myFunc, 1e-10);
     powell.Perform(*myFunc, thePnt, m);
@@ -317,14 +329,13 @@ Standard_Boolean math_GlobOptMin::computeLocalExtremum(const math_Vector& thePnt
     {
       powell.Location(theOutPnt);
       theVal = powell.Minimum();
+
+      if (isInside(theOutPnt))
+        return Standard_True;
     }
-    else return Standard_False;
   }
 
-  if (isInside(theOutPnt))
-    return Standard_True;
-  else
-    return Standard_False;
+  return Standard_False;
 }
 
 //=======================================================================
@@ -333,40 +344,17 @@ Standard_Boolean math_GlobOptMin::computeLocalExtremum(const math_Vector& thePnt
 //=======================================================================
 void math_GlobOptMin::computeInitialValues()
 {
+  const Standard_Real aMinLC = 0.01;
+  const Standard_Real aMaxLC = 1000.;
+  const Standard_Real aMinEps = 0.1;
+  const Standard_Real aMaxEps = 100.;
   Standard_Integer i;
   math_Vector aCurrPnt(1, myN);
   math_Vector aBestPnt(1, myN);
   math_Vector aParamStep(1, myN);
   Standard_Real aCurrVal = RealLast();
-  Standard_Real aBestVal = RealLast();
 
-  // Check functional value in midpoint, low and upp point border and
-  // in each point try to perform local optimization.
-  aBestPnt = (myA + myB) * 0.5;
-  myFunc->Value(aBestPnt, aBestVal);
-
-  for(i = 1; i <= 3; i++)
-  {
-    aCurrPnt = myA + (myB - myA) * (i - 1) / 2.0;
-
-    if(computeLocalExtremum(aCurrPnt, aCurrVal, aCurrPnt))
-    {
-      // Local Extremum finds better solution than current point.
-      if (aCurrVal < aBestVal)
-      {
-        aBestVal = aCurrVal;
-        aBestPnt = aCurrPnt;
-      }
-    }
-  }
-
-  myF = aBestVal;
-  myY.Clear();
-  for(i = 1; i <= myN; i++)
-    myY.Append(aBestPnt(i));
-  mySolCount++;
-
-  // Lipschitz const approximation
+  // Lipchitz const approximation.
   Standard_Real aLipConst = 0.0, aPrevValDiag, aPrevValProj;
   Standard_Integer aPntNb = 13;
   myFunc->Value(myA, aPrevValDiag);
@@ -389,16 +377,12 @@ void math_GlobOptMin::computeInitialValues()
     aPrevValProj = aCurrVal;
   }
 
+  myC = myInitC;
   aLipConst *= Sqrt(myN) / aStep;
-
-  if (aLipConst < myC * 0.1)
-  {
-    myC = Max(aLipConst * 0.1, 0.01);
-  }
-  else if (aLipConst > myC * 10)
-  {
-    myC = Min(myC * 2, 30.0);
-  }
+  if (aLipConst < myC * aMinEps)
+    myC = Max(aLipConst * aMinEps, aMinLC);
+  else if (aLipConst > myC * aMaxEps)
+    myC = Min(myC * aMaxEps, aMaxLC);
 }
 
 //=======================================================================
@@ -408,18 +392,16 @@ void math_GlobOptMin::computeInitialValues()
 void math_GlobOptMin::computeGlobalExtremum(Standard_Integer j)
 {
   Standard_Integer i;
-  Standard_Real d; // Functional in moved point.
+  Standard_Real d = RealLast(), aPrevVal; // Functional in original and moved points.
   Standard_Real val = RealLast(); // Local extrema computed in moved point.
   Standard_Real aStepBestValue = RealLast();
-  Standard_Real aRealStep = 0.0;
   math_Vector aStepBestPoint(1, myN);
-  Standard_Boolean isInside = Standard_False;
-  Standard_Real r;
-  Standard_Boolean isReached = Standard_False;
+  Standard_Boolean isInside = Standard_False,
+                   isReached = Standard_False;
+
+  Standard_Real r1, r2, r;
 
-  for(myX(j) = myA(j) + myE1;
-     (myX(j) < myB(j) + myE1) && (!isReached);
-      myX(j) += myV(j))
+  for(myX(j) = myA(j) + myE1; !isReached; myX(j) += myV(j))
   {
     if (myX(j) > myB(j))
     {
@@ -433,52 +415,42 @@ void math_GlobOptMin::computeGlobalExtremum(Standard_Integer j)
     if (j == 1)
     {
       isInside = Standard_False;
+      aPrevVal = d;
       myFunc->Value(myX, d);
-      r = (d + myZ * myC * aRealStep - myF) * myZ;
+      r1 = (d + myZ * myC * myLastStep - myF) * myZ; // Evtushenko estimation.
+      r2 = ((d + aPrevVal - myC * myLastStep) * 0.5 - myF) * myZ; // Shubert / Piyavsky estimation.
+      r = Min(r1, r2);
       if(r > myE3)
       {
-        isInside = computeLocalExtremum(myX, val, myTmp);
-      }
-      aStepBestValue = (isInside && (val < d))? val : d;
-      aStepBestPoint = (isInside && (val < d))? myTmp : myX;
+        Standard_Real aSaveParam = myX(1);
 
-      // Solutions are close to each other 
-      // and it is allowed to have more than one solution.
-      if (Abs(aStepBestValue - myF) < mySameTol * 0.01 &&
-          !myIsFindSingleSolution)
-      {
-        if (!isStored(aStepBestPoint))
+        // Piyavsky midpoint estimation.
+        Standard_Real aParam = (2 * myX(1) - myV(1) ) * 0.5 + (aPrevVal - d) * 0.5 / myC;
+        if (Precision::IsInfinite(aPrevVal))
+          aParam = myX(1) - myV(1) * 0.5; // Protection from upper dimension step.
+
+        myX(1) = aParam;
+        Standard_Real aVal = 0;
+        myFunc->Value(myX, aVal);
+        myX(1) = aSaveParam;
+
+        if ( (aVal < d && aVal < aPrevVal) ||
+              DistanceToBorder(myX, myA, myB) < myE1 ) // Condition optimization case near the border.
         {
-          if ((aStepBestValue - myF) * myZ > 0.0)
-            myF = aStepBestValue;
-          for(i = 1; i <= myN; i++)
-            myY.Append(aStepBestPoint(i));
-          mySolCount++;
+          isInside = computeLocalExtremum(myX, val, myTmp);
         }
       }
+      aStepBestValue = (isInside && (val < d))? val : d;
+      aStepBestPoint = (isInside && (val < d))? myTmp : myX;
 
-      // New best solution:
-      // new point is out of (mySameTol * 0.01) surrounding or
-      // new point is better than old + single point search.
-      Standard_Real aFunctionalDelta = (aStepBestValue - myF) * myZ;
-      if (aFunctionalDelta > mySameTol * 0.01 ||
-         (aFunctionalDelta > 0.0 && myIsFindSingleSolution))
-      {
-        mySolCount = 0;
-        myF = aStepBestValue;
-        myY.Clear();
-        for(i = 1; i <= myN; i++)
-          myY.Append(aStepBestPoint(i));
-        mySolCount++;
-
-        isFirstCellFilterInvoke = Standard_True;
-      }
+      // Check point and value on the current step to be optimal.
+      checkAddCandidate(aStepBestPoint, aStepBestValue);
 
       if (CheckFunctionalStopCriteria())
         return; // Best possible value is obtained.
 
-      aRealStep = myE2 + Abs(myF - d) / myC;
-      myV(1) = Min(aRealStep, myMaxV(1));
+      myV(1) = Min(myE2 + Abs(myF - d) / myC, myMaxV(1));
+      myLastStep = myV(1);
     }
     else
     {
@@ -489,10 +461,9 @@ void math_GlobOptMin::computeGlobalExtremum(Standard_Integer j)
       for(i = 1; i < j; i++)
         myV(i) = 0.0;
     }
-    // Compute step in (j + 1) dimension according to scale.
     if (j < myN)
     {
-      Standard_Real aUpperDimStep =  myV(j) * myExpandCoeff(j + 1);
+      Standard_Real aUpperDimStep =  Max(myV(j), myE2);
       if (myV(j + 1) > aUpperDimStep)
       {
         if (aUpperDimStep > myMaxV(j + 1)) // Case of too big step.
@@ -584,51 +555,6 @@ Standard_Boolean math_GlobOptMin::isStored(const math_Vector& thePnt)
   return Standard_False;
 }
 
-//=======================================================================
-//function : NbExtrema
-//purpose  :
-//=======================================================================
-Standard_Integer math_GlobOptMin::NbExtrema()
-{
-  return mySolCount;
-}
-
-//=======================================================================
-//function : GetF
-//purpose  :
-//=======================================================================
-Standard_Real math_GlobOptMin::GetF()
-{
-  return myF;
-}
-
-//=======================================================================
-//function : SetFunctionalMinimalValue
-//purpose  :
-//=======================================================================
-void math_GlobOptMin::SetFunctionalMinimalValue(const Standard_Real theMinimalValue)
-{
-  myFunctionalMinimalValue = theMinimalValue;
-}
-
-//=======================================================================
-//function : GetFunctionalMinimalValue
-//purpose  :
-//=======================================================================
-Standard_Real math_GlobOptMin::GetFunctionalMinimalValue()
-{
-  return myFunctionalMinimalValue;
-}
-
-//=======================================================================
-//function : IsDone
-//purpose  :
-//=======================================================================
-Standard_Boolean math_GlobOptMin::isDone()
-{
-  return myDone;
-}
-
 //=======================================================================
 //function : Points
 //purpose  :
@@ -660,10 +586,73 @@ void math_GlobOptMin::initCellSize()
 //=======================================================================
 Standard_Boolean math_GlobOptMin::CheckFunctionalStopCriteria()
 {
-  // Search single solution and current solution in its neighbourhood.
+  // Search single solution and current solution in its neighborhood.
   if (myIsFindSingleSolution &&
       Abs (myF - myFunctionalMinimalValue) < mySameTol * 0.01)
     return Standard_True;
 
   return Standard_False;
 }
+
+//=======================================================================
+//function : ComputeInitSol
+//purpose  :
+//=======================================================================
+void math_GlobOptMin::ComputeInitSol()
+{
+  Standard_Real aVal;
+  math_Vector aPnt(1, myN);
+
+  // Check functional value in midpoint. It is necessary since local optimization
+  // algorithm may fail and return nothing. This is a protection from uninitialized
+  // variables.
+  aPnt = (myGlobA + myGlobB) * 0.5;
+  myFunc->Value(aPnt, aVal);
+  checkAddCandidate(aPnt, aVal);
+
+  // Run local optimization from lower corner, midpoint, and upper corner.
+  for(Standard_Integer i = 1; i <= 3; i++)
+  {
+    aPnt = myA + (myB - myA) * (i - 1) / 2.0;
+
+    if(computeLocalExtremum(aPnt, aVal, aPnt))
+      checkAddCandidate(aPnt, aVal);
+  }
+}
+
+//=======================================================================
+//function : checkAddCandidate
+//purpose  :
+//=======================================================================
+void math_GlobOptMin::checkAddCandidate(const math_Vector&  thePnt,
+                                        const Standard_Real theValue)
+{
+  if (Abs(theValue - myF) < mySameTol * 0.01 && // Value in point is close to optimal value.
+      !myIsFindSingleSolution)                  // Several optimal solutions are allowed.
+  {
+    if (!isStored(thePnt))
+    {
+      if ((theValue - myF) * myZ > 0.0)
+        myF = theValue;
+      for (Standard_Integer j = 1; j <= myN; j++)
+        myY.Append(thePnt(j));
+      mySolCount++;
+    }
+  }
+
+  // New best solution:
+  // new point is out of (mySameTol * 0.01) surrounding or
+  // new point is better than old and single point search.
+  Standard_Real aDelta = (theValue - myF) * myZ;
+  if (aDelta > mySameTol * 0.01 ||
+     (aDelta > 0.0 && myIsFindSingleSolution))
+  {
+    myF = theValue;
+    myY.Clear();
+    for (Standard_Integer j = 1; j <= myN; j++)
+      myY.Append(thePnt(j));
+    mySolCount = 1;
+
+    isFirstCellFilterInvoke = Standard_True;
+  }
+}