Adjusting testing case due to changes in algorithm behavior.
[occt.git] / src / math / math_GlobOptMin.cxx
CommitLineData
4bbaf12b 1// Created on: 2014-01-20
2// Created by: Alexaner Malyshev
3// Copyright (c) 2014-2014 OPEN CASCADE SAS
4//
5// This file is part of Open CASCADE Technology software library.
6//
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.
12//
13// Alternatively, this file may be used under the terms of Open CASCADE
14// commercial license or contractual agreement
15
16#include <math_GlobOptMin.hxx>
17
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>
4bbaf12b 24#include <Standard_Integer.hxx>
25#include <Standard_Real.hxx>
e8746a26 26#include <Precision.hxx>
4bbaf12b 27
4bbaf12b 28
29//=======================================================================
30//function : math_GlobOptMin
31//purpose : Constructor
32//=======================================================================
33math_GlobOptMin::math_GlobOptMin(math_MultipleVarFunction* theFunc,
34 const math_Vector& theA,
35 const math_Vector& theB,
5493d334 36 const Standard_Real theC,
37 const Standard_Real theDiscretizationTol,
38 const Standard_Real theSameTol)
4bbaf12b 39: myN(theFunc->NbVariables()),
40 myA(1, myN),
41 myB(1, myN),
42 myGlobA(1, myN),
43 myGlobB(1, myN),
44 myX(1, myN),
45 myTmp(1, myN),
5493d334 46 myV(1, myN),
3f733bb1 47 myMaxV(1, myN),
48 myExpandCoeff(1, myN)
4bbaf12b 49{
50 Standard_Integer i;
51
52 myFunc = theFunc;
53 myC = theC;
54 myZ = -1;
55 mySolCount = 0;
56
57 for(i = 1; i <= myN; i++)
58 {
59 myGlobA(i) = theA(i);
60 myGlobB(i) = theB(i);
61
62 myA(i) = theA(i);
63 myB(i) = theB(i);
64 }
65
5493d334 66 for(i = 1; i <= myN; i++)
67 {
68 myMaxV(i) = (myB(i) - myA(i)) / 3.0;
69 }
70
3f733bb1 71 myExpandCoeff(1) = 1.0;
72 for(i = 2; i <= myN; i++)
73 {
74 myExpandCoeff(i) = (myB(i) - myA(i)) / (myB(i - 1) - myA(i - 1));
75 }
76
5493d334 77 myTol = theDiscretizationTol;
78 mySameTol = theSameTol;
79
4bbaf12b 80 myDone = Standard_False;
81}
82
83//=======================================================================
84//function : SetGlobalParams
85//purpose : Set params without memory allocation.
86//=======================================================================
87void math_GlobOptMin::SetGlobalParams(math_MultipleVarFunction* theFunc,
88 const math_Vector& theA,
89 const math_Vector& theB,
5493d334 90 const Standard_Real theC,
91 const Standard_Real theDiscretizationTol,
92 const Standard_Real theSameTol)
4bbaf12b 93{
94 Standard_Integer i;
95
96 myFunc = theFunc;
97 myC = theC;
98 myZ = -1;
99 mySolCount = 0;
100
101 for(i = 1; i <= myN; i++)
102 {
103 myGlobA(i) = theA(i);
104 myGlobB(i) = theB(i);
105
106 myA(i) = theA(i);
107 myB(i) = theB(i);
108 }
109
3f733bb1 110 for(i = 1; i <= myN; i++)
111 {
112 myMaxV(i) = (myB(i) - myA(i)) / 3.0;
113 }
114
115 myExpandCoeff(1) = 1.0;
116 for(i = 2; i <= myN; i++)
117 {
118 myExpandCoeff(i) = (myB(i) - myA(i)) / (myB(i - 1) - myA(i - 1));
119 }
120
5493d334 121 myTol = theDiscretizationTol;
122 mySameTol = theSameTol;
123
4bbaf12b 124 myDone = Standard_False;
125}
126
127//=======================================================================
128//function : SetLocalParams
129//purpose : Set params without memory allocation.
130//=======================================================================
131void math_GlobOptMin::SetLocalParams(const math_Vector& theLocalA,
132 const math_Vector& theLocalB)
133{
134 Standard_Integer i;
135
136 myZ = -1;
137 mySolCount = 0;
138
139 for(i = 1; i <= myN; i++)
140 {
141 myA(i) = theLocalA(i);
142 myB(i) = theLocalB(i);
143 }
144
5493d334 145 for(i = 1; i <= myN; i++)
146 {
147 myMaxV(i) = (myB(i) - myA(i)) / 3.0;
148 }
149
3f733bb1 150 myExpandCoeff(1) = 1.0;
151 for(i = 2; i <= myN; i++)
152 {
153 myExpandCoeff(i) = (myB(i) - myA(i)) / (myB(i - 1) - myA(i - 1));
154 }
155
4bbaf12b 156 myDone = Standard_False;
157}
158
5493d334 159//=======================================================================
160//function : SetTol
161//purpose : Set algorithm tolerances.
162//=======================================================================
163void math_GlobOptMin::SetTol(const Standard_Real theDiscretizationTol,
164 const Standard_Real theSameTol)
165{
166 myTol = theDiscretizationTol;
167 mySameTol = theSameTol;
168}
169
170//=======================================================================
171//function : GetTol
172//purpose : Get algorithm tolerances.
173//=======================================================================
174void math_GlobOptMin::GetTol(Standard_Real& theDiscretizationTol,
175 Standard_Real& theSameTol)
176{
177 theDiscretizationTol = myTol;
178 theSameTol = mySameTol;
179}
180
4bbaf12b 181//=======================================================================
182//function : ~math_GlobOptMin
183//purpose :
184//=======================================================================
185math_GlobOptMin::~math_GlobOptMin()
186{
187}
188
189//=======================================================================
190//function : Perform
191//purpose : Compute Global extremum point
192//=======================================================================
193// In this algo indexes started from 1, not from 0.
194void math_GlobOptMin::Perform()
195{
196 Standard_Integer i;
197
198 // Compute parameters range
199 Standard_Real minLength = RealLast();
200 Standard_Real maxLength = RealFirst();
201 for(i = 1; i <= myN; i++)
202 {
203 Standard_Real currentLength = myB(i) - myA(i);
204 if (currentLength < minLength)
205 minLength = currentLength;
206 if (currentLength > maxLength)
207 maxLength = currentLength;
208 }
209
e8746a26 210 if (minLength < Precision::PConfusion())
211 {
212 #ifdef OCCT_DEBUG
213 cout << "math_GlobOptMin::Perform(): Degenerated parameters space" << endl;
214 #endif
215
216 return;
217 }
218
219 // Compute initial values for myF, myY, myC.
220 computeInitialValues();
221
797d11c6 222 myE1 = minLength * myTol;
223 myE2 = maxLength * myTol;
224 if (myC > 1.0)
225 myE3 = - maxLength * myTol / 4.0;
226 else
227 myE3 = - maxLength * myTol * myC / 4.0;
4bbaf12b 228
229 computeGlobalExtremum(myN);
230
231 myDone = Standard_True;
4bbaf12b 232}
233
234//=======================================================================
235//function : computeLocalExtremum
236//purpose :
237//=======================================================================
238Standard_Boolean math_GlobOptMin::computeLocalExtremum(const math_Vector& thePnt,
239 Standard_Real& theVal,
240 math_Vector& theOutPnt)
241{
242 Standard_Integer i;
243
244 //Newton method
245 if (dynamic_cast<math_MultipleVarFunctionWithHessian*>(myFunc))
246 {
247 math_MultipleVarFunctionWithHessian* myTmp =
248 dynamic_cast<math_MultipleVarFunctionWithHessian*> (myFunc);
249
250 math_NewtonMinimum newtonMinimum(*myTmp, thePnt);
251 if (newtonMinimum.IsDone())
252 {
253 newtonMinimum.Location(theOutPnt);
254 theVal = newtonMinimum.Minimum();
255 }
256 else return Standard_False;
257 } else
258
259 // BFGS method used.
260 if (dynamic_cast<math_MultipleVarFunctionWithGradient*>(myFunc))
261 {
262 math_MultipleVarFunctionWithGradient* myTmp =
263 dynamic_cast<math_MultipleVarFunctionWithGradient*> (myFunc);
07f1a2e6 264 math_BFGS bfgs(myTmp->NbVariables());
265 bfgs.Perform(*myTmp, thePnt);
4bbaf12b 266 if (bfgs.IsDone())
267 {
268 bfgs.Location(theOutPnt);
269 theVal = bfgs.Minimum();
270 }
271 else return Standard_False;
272 } else
273
274 // Powell method used.
275 if (dynamic_cast<math_MultipleVarFunction*>(myFunc))
276 {
277 math_Matrix m(1, myN, 1, myN, 0.0);
278 for(i = 1; i <= myN; i++)
279 m(1, 1) = 1.0;
280
281 math_Powell powell(*myFunc, thePnt, m, 1e-10);
282
283 if (powell.IsDone())
284 {
285 powell.Location(theOutPnt);
286 theVal = powell.Minimum();
287 }
288 else return Standard_False;
289 }
290
291 if (isInside(theOutPnt))
292 return Standard_True;
293 else
294 return Standard_False;
295}
296
797d11c6 297//=======================================================================
298//function : computeInitialValues
299//purpose :
300//=======================================================================
301void math_GlobOptMin::computeInitialValues()
302{
303 Standard_Integer i;
304 math_Vector aCurrPnt(1, myN);
305 math_Vector aBestPnt(1, myN);
e8746a26 306 math_Vector aParamStep(1, myN);
797d11c6 307 Standard_Real aCurrVal = RealLast();
308 Standard_Real aBestVal = RealLast();
309
310 // Check functional value in midpoint, low and upp point border and
311 // in each point try to perform local optimization.
312 aBestPnt = (myA + myB) * 0.5;
313 myFunc->Value(aBestPnt, aBestVal);
314
315 for(i = 1; i <= 3; i++)
316 {
317 aCurrPnt = myA + (myB - myA) * (i - 1) / 2.0;
318
319 if(computeLocalExtremum(aCurrPnt, aCurrVal, aCurrPnt))
320 {
321 // Local Extremum finds better solution than current point.
322 if (aCurrVal < aBestVal)
323 {
324 aBestVal = aCurrVal;
325 aBestPnt = aCurrPnt;
326 }
327 }
328 }
329
330 myF = aBestVal;
331 myY.Clear();
332 for(i = 1; i <= myN; i++)
333 myY.Append(aBestPnt(i));
334 mySolCount++;
335
336 // Lipschitz const approximation
e8746a26 337 Standard_Real aLipConst = 0.0, aPrevValDiag, aPrevValProj;
797d11c6 338 Standard_Integer aPntNb = 13;
e8746a26 339 myFunc->Value(myA, aPrevValDiag);
340 aPrevValProj = aPrevValDiag;
797d11c6 341 Standard_Real aStep = (myB - myA).Norm() / aPntNb;
e8746a26 342 aParamStep = (myB - myA) / aPntNb;
797d11c6 343 for(i = 1; i <= aPntNb; i++)
344 {
e8746a26 345 aCurrPnt = myA + aParamStep * i;
797d11c6 346
e8746a26 347 // Walk over diagonal.
348 myFunc->Value(aCurrPnt, aCurrVal);
349 aLipConst = Max (Abs(aCurrVal - aPrevValDiag), aLipConst);
350 aPrevValDiag = aCurrVal;
797d11c6 351
e8746a26 352 // Walk over diag in projected space aPnt(1) = myA(1) = const.
353 aCurrPnt(1) = myA(1);
354 myFunc->Value(aCurrPnt, aCurrVal);
355 aLipConst = Max (Abs(aCurrVal - aPrevValProj), aLipConst);
356 aPrevValProj = aCurrVal;
797d11c6 357 }
e8746a26 358
359 aLipConst *= Sqrt(myN) / aStep;
797d11c6 360
361 if (aLipConst < myC * 0.1)
362 {
363 myC = Max(aLipConst * 0.1, 0.01);
364 }
365 else if (aLipConst > myC * 10)
366 {
367 myC = Min(myC * 2, 30.0);
368 }
369}
370
4bbaf12b 371//=======================================================================
372//function : ComputeGlobalExtremum
373//purpose :
374//=======================================================================
375void math_GlobOptMin::computeGlobalExtremum(Standard_Integer j)
376{
377 Standard_Integer i;
378 Standard_Real d; // Functional in moved point.
379 Standard_Real val = RealLast(); // Local extrema computed in moved point.
3f733bb1 380 Standard_Real aStepBestValue = RealLast();
381 Standard_Real aRealStep = 0.0;
382 math_Vector aStepBestPoint(1, myN);
4bbaf12b 383 Standard_Boolean isInside = Standard_False;
384 Standard_Real r;
385
5493d334 386
4bbaf12b 387 for(myX(j) = myA(j) + myE1; myX(j) < myB(j) + myE1; myX(j) += myV(j))
388 {
389 if (myX(j) > myB(j))
390 myX(j) = myB(j);
391
392 if (j == 1)
393 {
394 isInside = Standard_False;
395 myFunc->Value(myX, d);
3f733bb1 396 r = (d + myZ * myC * aRealStep - myF) * myZ;
4bbaf12b 397 if(r > myE3)
398 {
399 isInside = computeLocalExtremum(myX, val, myTmp);
400 }
3f733bb1 401 aStepBestValue = (isInside && (val < d))? val : d;
402 aStepBestPoint = (isInside && (val < d))? myTmp : myX;
4bbaf12b 403
404 // Solutions are close to each other.
3f733bb1 405 if (Abs(aStepBestValue - myF) < mySameTol * 0.01)
4bbaf12b 406 {
3f733bb1 407 if (!isStored(aStepBestPoint))
4bbaf12b 408 {
3f733bb1 409 if ((aStepBestValue - myF) * myZ > 0.0)
410 myF = aStepBestValue;
4bbaf12b 411 for(i = 1; i <= myN; i++)
3f733bb1 412 myY.Append(aStepBestPoint(i));
4bbaf12b 413 mySolCount++;
414 }
415 }
416
417 // New best solution.
3f733bb1 418 if ((aStepBestValue - myF) * myZ > mySameTol * 0.01)
4bbaf12b 419 {
420 mySolCount = 0;
3f733bb1 421 myF = aStepBestValue;
4bbaf12b 422 myY.Clear();
423 for(i = 1; i <= myN; i++)
3f733bb1 424 myY.Append(aStepBestPoint(i));
4bbaf12b 425 mySolCount++;
426 }
427
3f733bb1 428 aRealStep = myE2 + Abs(myF - d) / myC;
429 myV(1) = Min(aRealStep, myMaxV(1));
4bbaf12b 430 }
431 else
432 {
433 myV(j) = RealLast() / 2.0;
434 computeGlobalExtremum(j - 1);
3f733bb1 435
436 // Nullify steps on lower dimensions.
437 for(i = 1; i < j; i++)
438 myV(i) = 0.0;
4bbaf12b 439 }
3f733bb1 440 // Compute step in (j + 1) dimension according to scale.
441 if (j < myN)
4bbaf12b 442 {
3f733bb1 443 Standard_Real aUpperDimStep = myV(j) * myExpandCoeff(j + 1);
444 if (myV(j + 1) > aUpperDimStep)
445 {
446 if (aUpperDimStep > myMaxV(j + 1)) // Case of too big step.
447 myV(j + 1) = myMaxV(j + 1);
448 else
449 myV(j + 1) = aUpperDimStep;
450 }
4bbaf12b 451 }
452 }
453}
454
455//=======================================================================
456//function : IsInside
457//purpose :
458//=======================================================================
459Standard_Boolean math_GlobOptMin::isInside(const math_Vector& thePnt)
460{
461 Standard_Integer i;
462
463 for(i = 1; i <= myN; i++)
464 {
465 if (thePnt(i) < myGlobA(i) || thePnt(i) > myGlobB(i))
466 return Standard_False;
467 }
468
469 return Standard_True;
470}
471//=======================================================================
472//function : IsStored
473//purpose :
474//=======================================================================
475Standard_Boolean math_GlobOptMin::isStored(const math_Vector& thePnt)
476{
477 Standard_Integer i,j;
478 Standard_Boolean isSame = Standard_True;
479
480 for(i = 0; i < mySolCount; i++)
481 {
482 isSame = Standard_True;
483 for(j = 1; j <= myN; j++)
484 {
5493d334 485 if ((Abs(thePnt(j) - myY(i * myN + j))) > (myB(j) - myA(j)) * mySameTol)
4bbaf12b 486 {
487 isSame = Standard_False;
488 break;
489 }
490 }
491 if (isSame == Standard_True)
492 return Standard_True;
493
494 }
495 return Standard_False;
496}
497
498//=======================================================================
499//function : NbExtrema
500//purpose :
501//=======================================================================
502Standard_Integer math_GlobOptMin::NbExtrema()
503{
504 return mySolCount;
505}
506
507//=======================================================================
508//function : GetF
509//purpose :
510//=======================================================================
511Standard_Real math_GlobOptMin::GetF()
512{
513 return myF;
514}
515
516//=======================================================================
517//function : IsDone
518//purpose :
519//=======================================================================
520Standard_Boolean math_GlobOptMin::isDone()
521{
522 return myDone;
523}
524
525//=======================================================================
526//function : Points
527//purpose :
528//=======================================================================
529void math_GlobOptMin::Points(const Standard_Integer theIndex, math_Vector& theSol)
530{
531 Standard_Integer j;
532
533 for(j = 1; j <= myN; j++)
534 theSol(j) = myY((theIndex - 1) * myN + j);
535}