1 // Created on: 2014-01-20
2 // Created by: Alexaner Malyshev
3 // Copyright (c) 2014-2015 OPEN CASCADE SAS
5 // This file is part of Open CASCADE Technology software library.
7 // This library is free software; you can redistribute it and/or modify it under
8 // the terms of the GNU Lesser General Public License version 2.1 as published
9 // by the Free Software Foundation, with special exception defined in the file
10 // OCCT_LGPL_EXCEPTION.txt. Consult the file LICENSE_LGPL_21.txt included in OCCT
11 // distribution for complete text of the license and disclaimer of any warranty.
13 // Alternatively, this file may be used under the terms of Open CASCADE
14 // commercial license or contractual agreement
16 #include <math_GlobOptMin.hxx>
18 #include <math_BFGS.hxx>
19 #include <math_Matrix.hxx>
20 #include <math_MultipleVarFunctionWithGradient.hxx>
21 #include <math_MultipleVarFunctionWithHessian.hxx>
22 #include <math_NewtonMinimum.hxx>
23 #include <math_Powell.hxx>
24 #include <Standard_Integer.hxx>
25 #include <Standard_Real.hxx>
26 #include <Precision.hxx>
29 //=======================================================================
30 //function : math_GlobOptMin
31 //purpose : Constructor
32 //=======================================================================
33 math_GlobOptMin::math_GlobOptMin(math_MultipleVarFunction* theFunc,
34 const math_Vector& theA,
35 const math_Vector& theB,
36 const Standard_Real theC,
37 const Standard_Real theDiscretizationTol,
38 const Standard_Real theSameTol)
39 : myN(theFunc->NbVariables()),
44 myIsConstLocked(Standard_False),
49 myExpandCoeff(1, myN),
50 myCellSize(0, myN - 1),
51 myFilter(theFunc->NbVariables()),
59 myIsFindSingleSolution = Standard_False;
60 myFunctionalMinimalValue = -Precision::Infinite();
64 for(i = 1; i <= myN; i++)
73 for(i = 1; i <= myN; i++)
75 myMaxV(i) = (myB(i) - myA(i)) / 3.0;
78 myExpandCoeff(1) = 1.0;
79 for(i = 2; i <= myN; i++)
81 myExpandCoeff(i) = (myB(i) - myA(i)) / (myB(i - 1) - myA(i - 1));
84 myTol = theDiscretizationTol;
85 mySameTol = theSameTol;
87 const Standard_Integer aMaxSquareSearchSol = 200;
88 Standard_Integer aSolNb = Standard_Integer(Pow(3.0, Standard_Real(myN)));
89 myMinCellFilterSol = Max(2 * aSolNb, aMaxSquareSearchSol);
93 myDone = Standard_False;
96 //=======================================================================
97 //function : SetGlobalParams
98 //purpose : Set parameters without memory allocation.
99 //=======================================================================
100 void math_GlobOptMin::SetGlobalParams(math_MultipleVarFunction* theFunc,
101 const math_Vector& theA,
102 const math_Vector& theB,
103 const Standard_Real theC,
104 const Standard_Real theDiscretizationTol,
105 const Standard_Real theSameTol)
115 for(i = 1; i <= myN; i++)
117 myGlobA(i) = theA(i);
118 myGlobB(i) = theB(i);
124 for(i = 1; i <= myN; i++)
126 myMaxV(i) = (myB(i) - myA(i)) / 3.0;
129 myExpandCoeff(1) = 1.0;
130 for(i = 2; i <= myN; i++)
132 myExpandCoeff(i) = (myB(i) - myA(i)) / (myB(i - 1) - myA(i - 1));
135 myTol = theDiscretizationTol;
136 mySameTol = theSameTol;
141 myDone = Standard_False;
144 //=======================================================================
145 //function : SetLocalParams
146 //purpose : Set parameters without memory allocation.
147 //=======================================================================
148 void math_GlobOptMin::SetLocalParams(const math_Vector& theLocalA,
149 const math_Vector& theLocalB)
154 for(i = 1; i <= myN; i++)
156 myA(i) = theLocalA(i);
157 myB(i) = theLocalB(i);
160 for(i = 1; i <= myN; i++)
162 myMaxV(i) = (myB(i) - myA(i)) / 3.0;
165 myExpandCoeff(1) = 1.0;
166 for(i = 2; i <= myN; i++)
168 myExpandCoeff(i) = (myB(i) - myA(i)) / (myB(i - 1) - myA(i - 1));
171 myDone = Standard_False;
174 //=======================================================================
176 //purpose : Set algorithm tolerances.
177 //=======================================================================
178 void math_GlobOptMin::SetTol(const Standard_Real theDiscretizationTol,
179 const Standard_Real theSameTol)
181 myTol = theDiscretizationTol;
182 mySameTol = theSameTol;
185 //=======================================================================
187 //purpose : Get algorithm tolerances.
188 //=======================================================================
189 void math_GlobOptMin::GetTol(Standard_Real& theDiscretizationTol,
190 Standard_Real& theSameTol)
192 theDiscretizationTol = myTol;
193 theSameTol = mySameTol;
196 //=======================================================================
197 //function : ~math_GlobOptMin
199 //=======================================================================
200 math_GlobOptMin::~math_GlobOptMin()
204 //=======================================================================
206 //purpose : Compute Global extremum point
207 //=======================================================================
208 // In this algo indexes started from 1, not from 0.
209 void math_GlobOptMin::Perform(const Standard_Boolean isFindSingleSolution)
213 // Compute parameters range
214 Standard_Real minLength = RealLast();
215 Standard_Real maxLength = RealFirst();
216 for(i = 1; i <= myN; i++)
218 Standard_Real currentLength = myB(i) - myA(i);
219 if (currentLength < minLength)
220 minLength = currentLength;
221 if (currentLength > maxLength)
222 maxLength = currentLength;
225 if (minLength < Precision::PConfusion())
228 cout << "math_GlobOptMin::Perform(): Degenerated parameters space" << endl;
234 if (!myIsConstLocked)
236 // Compute initial value for myC.
237 computeInitialValues();
240 myE1 = minLength * myTol;
241 myE2 = maxLength * myTol;
243 myIsFindSingleSolution = isFindSingleSolution;
244 if (isFindSingleSolution)
246 // Run local optimization if current value better than optimal.
252 myE3 = - maxLength * myTol / 4.0;
254 myE3 = - maxLength * myTol * myC / 4.0;
257 // Search single solution and current solution in its neighborhood.
258 if (CheckFunctionalStopCriteria())
260 myDone = Standard_True;
265 isFirstCellFilterInvoke = Standard_True;
266 computeGlobalExtremum(myN);
268 myDone = Standard_True;
271 //=======================================================================
272 //function : computeLocalExtremum
274 //=======================================================================
275 Standard_Boolean math_GlobOptMin::computeLocalExtremum(const math_Vector& thePnt,
276 Standard_Real& theVal,
277 math_Vector& theOutPnt)
283 dynamic_cast<math_MultipleVarFunctionWithHessian*>(myFunc))
285 math_MultipleVarFunctionWithHessian* aTmp =
286 dynamic_cast<math_MultipleVarFunctionWithHessian*> (myFunc);
287 math_NewtonMinimum newtonMinimum(*aTmp);
288 newtonMinimum.SetBoundary(myGlobA, myGlobB);
289 newtonMinimum.Perform(*aTmp, thePnt);
291 if (newtonMinimum.IsDone())
293 newtonMinimum.Location(theOutPnt);
294 theVal = newtonMinimum.Minimum();
296 else return Standard_False;
301 dynamic_cast<math_MultipleVarFunctionWithGradient*>(myFunc))
303 math_MultipleVarFunctionWithGradient* aTmp =
304 dynamic_cast<math_MultipleVarFunctionWithGradient*> (myFunc);
305 math_BFGS bfgs(aTmp->NbVariables());
306 bfgs.Perform(*aTmp, thePnt);
309 bfgs.Location(theOutPnt);
310 theVal = bfgs.Minimum();
312 else return Standard_False;
315 // Powell method used.
316 if (dynamic_cast<math_MultipleVarFunction*>(myFunc))
318 math_Matrix m(1, myN, 1, myN, 0.0);
319 for(i = 1; i <= myN; i++)
322 math_Powell powell(*myFunc, 1e-10);
323 powell.Perform(*myFunc, thePnt, m);
327 powell.Location(theOutPnt);
328 theVal = powell.Minimum();
330 else return Standard_False;
333 if (isInside(theOutPnt))
334 return Standard_True;
336 return Standard_False;
339 //=======================================================================
340 //function : computeInitialValues
342 //=======================================================================
343 void math_GlobOptMin::computeInitialValues()
346 math_Vector aCurrPnt(1, myN);
347 math_Vector aBestPnt(1, myN);
348 math_Vector aParamStep(1, myN);
349 Standard_Real aCurrVal = RealLast();
351 // Lipchitz const approximation.
352 Standard_Real aLipConst = 0.0, aPrevValDiag, aPrevValProj;
353 Standard_Integer aPntNb = 13;
354 myFunc->Value(myA, aPrevValDiag);
355 aPrevValProj = aPrevValDiag;
356 Standard_Real aStep = (myB - myA).Norm() / aPntNb;
357 aParamStep = (myB - myA) / aPntNb;
358 for(i = 1; i <= aPntNb; i++)
360 aCurrPnt = myA + aParamStep * i;
362 // Walk over diagonal.
363 myFunc->Value(aCurrPnt, aCurrVal);
364 aLipConst = Max (Abs(aCurrVal - aPrevValDiag), aLipConst);
365 aPrevValDiag = aCurrVal;
367 // Walk over diag in projected space aPnt(1) = myA(1) = const.
368 aCurrPnt(1) = myA(1);
369 myFunc->Value(aCurrPnt, aCurrVal);
370 aLipConst = Max (Abs(aCurrVal - aPrevValProj), aLipConst);
371 aPrevValProj = aCurrVal;
375 aLipConst *= Sqrt(myN) / aStep;
376 if (aLipConst < myC * 0.1)
377 myC = Max(aLipConst * 0.1, 0.01);
378 else if (aLipConst > myC * 5)
379 myC = Min(myC * 5, 50.0);
381 // Clear all solutions except one.
382 if (myY.Size() != myN)
384 for(i = 1; i <= myN; i++)
385 aBestPnt(i) = myY(i);
387 for(i = 1; i <= myN; i++)
388 myY.Append(aBestPnt(i));
393 //=======================================================================
394 //function : ComputeGlobalExtremum
396 //=======================================================================
397 void math_GlobOptMin::computeGlobalExtremum(Standard_Integer j)
400 Standard_Real d; // Functional in moved point.
401 Standard_Real val = RealLast(); // Local extrema computed in moved point.
402 Standard_Real aStepBestValue = RealLast();
403 math_Vector aStepBestPoint(1, myN);
404 Standard_Boolean isInside = Standard_False;
406 Standard_Boolean isReached = Standard_False;
409 for(myX(j) = myA(j) + myE1;
410 (myX(j) < myB(j) + myE1) && (!isReached);
416 isReached = Standard_True;
419 if (CheckFunctionalStopCriteria())
420 return; // Best possible value is obtained.
424 isInside = Standard_False;
425 myFunc->Value(myX, d);
426 r = (d + myZ * myC * myLastStep - myF) * myZ;
429 isInside = computeLocalExtremum(myX, val, myTmp);
431 aStepBestValue = (isInside && (val < d))? val : d;
432 aStepBestPoint = (isInside && (val < d))? myTmp : myX;
434 // Solutions are close to each other
435 // and it is allowed to have more than one solution.
436 if (Abs(aStepBestValue - myF) < mySameTol * 0.01 &&
437 !myIsFindSingleSolution)
439 if (!isStored(aStepBestPoint))
441 if ((aStepBestValue - myF) * myZ > 0.0)
442 myF = aStepBestValue;
443 for(i = 1; i <= myN; i++)
444 myY.Append(aStepBestPoint(i));
449 // New best solution:
450 // new point is out of (mySameTol * 0.01) surrounding or
451 // new point is better than old + single point search.
452 Standard_Real aFunctionalDelta = (aStepBestValue - myF) * myZ;
453 if (aFunctionalDelta > mySameTol * 0.01 ||
454 (aFunctionalDelta > 0.0 && myIsFindSingleSolution))
457 myF = aStepBestValue;
459 for(i = 1; i <= myN; i++)
460 myY.Append(aStepBestPoint(i));
463 isFirstCellFilterInvoke = Standard_True;
466 if (CheckFunctionalStopCriteria())
467 return; // Best possible value is obtained.
469 myV(1) = Min(myE2 + Abs(myF - d) / myC, myMaxV(1));
474 myV(j) = RealLast() / 2.0;
475 computeGlobalExtremum(j - 1);
477 // Nullify steps on lower dimensions.
478 for(i = 1; i < j; i++)
481 // Compute step in (j + 1) dimension according to scale.
484 Standard_Real aUpperDimStep = myV(j) * myExpandCoeff(j + 1);
485 if (myV(j + 1) > aUpperDimStep)
487 if (aUpperDimStep > myMaxV(j + 1)) // Case of too big step.
488 myV(j + 1) = myMaxV(j + 1);
490 myV(j + 1) = aUpperDimStep;
496 //=======================================================================
497 //function : IsInside
499 //=======================================================================
500 Standard_Boolean math_GlobOptMin::isInside(const math_Vector& thePnt)
504 for(i = 1; i <= myN; i++)
506 if (thePnt(i) < myGlobA(i) || thePnt(i) > myGlobB(i))
507 return Standard_False;
510 return Standard_True;
512 //=======================================================================
513 //function : IsStored
515 //=======================================================================
516 Standard_Boolean math_GlobOptMin::isStored(const math_Vector& thePnt)
518 Standard_Integer i,j;
519 Standard_Boolean isSame = Standard_True;
520 math_Vector aTol(1, myN);
521 aTol = (myB - myA) * mySameTol;
523 // C1 * n^2 = C2 * 3^dim * n
524 if (mySolCount < myMinCellFilterSol)
526 for(i = 0; i < mySolCount; i++)
528 isSame = Standard_True;
529 for(j = 1; j <= myN; j++)
531 if ((Abs(thePnt(j) - myY(i * myN + j))) > aTol(j))
533 isSame = Standard_False;
537 if (isSame == Standard_True)
538 return Standard_True;
543 NCollection_CellFilter_Inspector anInspector(myN, Precision::PConfusion());
544 if (isFirstCellFilterInvoke)
546 myFilter.Reset(myCellSize);
548 // Copy initial data into cell filter.
549 for(Standard_Integer aSolIdx = 0; aSolIdx < mySolCount; aSolIdx++)
551 math_Vector aVec(1, myN);
552 for(Standard_Integer aSolDim = 1; aSolDim <= myN; aSolDim++)
553 aVec(aSolDim) = myY(aSolIdx * myN + aSolDim);
555 myFilter.Add(aVec, aVec);
559 isFirstCellFilterInvoke = Standard_False;
561 math_Vector aLow(1, myN), anUp(1, myN);
562 anInspector.Shift(thePnt, myCellSize, aLow, anUp);
564 anInspector.ClearFind();
565 anInspector.SetCurrent(thePnt);
566 myFilter.Inspect(aLow, anUp, anInspector);
567 if (!anInspector.isFind())
569 // Point is out of close cells, add new one.
570 myFilter.Add(thePnt, thePnt);
573 return Standard_False;
576 //=======================================================================
577 //function : NbExtrema
579 //=======================================================================
580 Standard_Integer math_GlobOptMin::NbExtrema()
585 //=======================================================================
588 //=======================================================================
589 Standard_Real math_GlobOptMin::GetF()
594 //=======================================================================
595 //function : SetFunctionalMinimalValue
597 //=======================================================================
598 void math_GlobOptMin::SetFunctionalMinimalValue(const Standard_Real theMinimalValue)
600 myFunctionalMinimalValue = theMinimalValue;
603 //=======================================================================
604 //function : GetFunctionalMinimalValue
606 //=======================================================================
607 Standard_Real math_GlobOptMin::GetFunctionalMinimalValue()
609 return myFunctionalMinimalValue;
612 //=======================================================================
615 //=======================================================================
616 Standard_Boolean math_GlobOptMin::isDone()
621 //=======================================================================
624 //=======================================================================
625 void math_GlobOptMin::Points(const Standard_Integer theIndex, math_Vector& theSol)
629 for(j = 1; j <= myN; j++)
630 theSol(j) = myY((theIndex - 1) * myN + j);
633 //=======================================================================
634 //function : initCellSize
636 //=======================================================================
637 void math_GlobOptMin::initCellSize()
639 for(Standard_Integer anIdx = 1; anIdx <= myN; anIdx++)
641 myCellSize(anIdx - 1) = (myGlobB(anIdx) - myGlobA(anIdx))
642 * Precision::PConfusion() / (2.0 * Sqrt(2.0));
646 //=======================================================================
647 //function : CheckFunctionalStopCriteria
649 //=======================================================================
650 Standard_Boolean math_GlobOptMin::CheckFunctionalStopCriteria()
652 // Search single solution and current solution in its neighborhood.
653 if (myIsFindSingleSolution &&
654 Abs (myF - myFunctionalMinimalValue) < mySameTol * 0.01)
655 return Standard_True;
657 return Standard_False;
660 //=======================================================================
661 //function : ComputeInitSol
663 //=======================================================================
664 void math_GlobOptMin::ComputeInitSol()
666 Standard_Real aCurrVal, aBestVal;
667 math_Vector aCurrPnt(1, myN);
668 math_Vector aBestPnt(1, myN);
669 math_Vector aParamStep(1, myN);
670 // Check functional value in midpoint, lower and upper border points and
671 // in each point try to perform local optimization.
672 aBestPnt = (myGlobA + myGlobB) * 0.5;
673 myFunc->Value(aBestPnt, aBestVal);
676 for(i = 1; i <= 3; i++)
678 aCurrPnt = myA + (myB - myA) * (i - 1) / 2.0;
680 if(computeLocalExtremum(aCurrPnt, aCurrVal, aCurrPnt))
682 // Local search tries to find better solution than current point.
683 if (aCurrVal < aBestVal)
693 for(i = 1; i <= myN; i++)
694 myY.Append(aBestPnt(i));
697 myDone = Standard_False;
700 //=======================================================================
701 //function : SetLipConstState
703 //=======================================================================
704 void math_GlobOptMin::SetLipConstState(const Standard_Boolean theFlag)
706 myIsConstLocked = theFlag;