// Copyright (c) 1997-1999 Matra Datavision // Copyright (c) 1999-2014 OPEN CASCADE SAS // // This file is part of Open CASCADE Technology software library. // // This library is free software; you can redistribute it and/or modify it under // the terms of the GNU Lesser General Public License version 2.1 as published // by the Free Software Foundation, with special exception defined in the file // OCCT_LGPL_EXCEPTION.txt. Consult the file LICENSE_LGPL_21.txt included in OCCT // distribution for complete text of the license and disclaimer of any warranty. // // Alternatively, this file may be used under the terms of Open CASCADE // commercial license or contractual agreement. //#ifndef OCCT_DEBUG #define No_Standard_RangeError #define No_Standard_OutOfRange #define No_Standard_DimensionError //#endif #include #include #include #include #include #include #include #include #define ITMAX 100 #define EPS 1e-14 #define EPSEPS 2e-14 #define MAXBIS 100 #ifdef OCCT_DEBUG static Standard_Boolean myDebug = 0; static Standard_Integer nbsolve = 0; #endif class DerivFunction: public math_Function { math_FunctionWithDerivative *myF; public: DerivFunction(math_FunctionWithDerivative& theF) : myF (&theF) {} virtual Standard_Boolean Value(const Standard_Real theX, Standard_Real& theFval) { return myF->Derivative(theX, theFval); } }; static void AppendRoot(TColStd_SequenceOfReal& Sol, TColStd_SequenceOfInteger& NbStateSol, const Standard_Real X, math_FunctionWithDerivative& F, // const Standard_Real K, const Standard_Real , const Standard_Real dX) { Standard_Integer n=Sol.Length(); Standard_Real t; #ifdef OCCT_DEBUG if (myDebug) { std::cout << " Ajout de la solution numero : " << n+1 << std::endl; std::cout << " Valeur de la racine :" << X << std::endl; } #endif if(n==0) { Sol.Append(X); F.Value(X,t); NbStateSol.Append(F.GetStateNumber()); } else { Standard_Integer i=1; Standard_Integer pl=n+1; while(i<=n) { t=Sol.Value(i); if(t >= X) { pl=i; i=n; } if(Abs(X-t) <= dX) { pl=0; i=n; } i++; } //-- while if(pl>n) { Sol.Append(X); F.Value(X,t); NbStateSol.Append(F.GetStateNumber()); } else if(pl>0) { Sol.InsertBefore(pl,X); F.Value(X,t); NbStateSol.InsertBefore(pl,F.GetStateNumber()); } } } static void Solve(math_FunctionWithDerivative& F, const Standard_Real K, const Standard_Real x1, const Standard_Real y1, const Standard_Real x2, const Standard_Real y2, const Standard_Real tol, const Standard_Real dX, TColStd_SequenceOfReal& Sol, TColStd_SequenceOfInteger& NbStateSol) { #ifdef OCCT_DEBUG if (myDebug) { std::cout <<"--> Resolution :" << ++nbsolve << std::endl; std::cout <<" x1 =" << x1 << " y1 =" << y1 << std::endl; std::cout <<" x2 =" << x2 << " y2 =" << y2 << std::endl; } #endif Standard_Integer iter=0; Standard_Real tols2 = 0.5*tol; Standard_Real a,b,c,d=0,e=0,fa,fb,fc,p,q,r,s,tol1,xm,min1,min2; a=x1;b=c=x2;fa=y1; fb=fc=y2; for(iter=1;iter<=ITMAX;iter++) { if((fb>0.0 && fc>0.0) || (fb<0.0 && fc<0.0)) { c=a; fc=fa; e=d=b-a; } if(Abs(fc)1e-10 || Dp<-1e-10) { Xp = Xp-(Yp-K)/Dp; } if(Xp<=x2 && Xp>=x1) { F.Value(Xp,Yp); Yp-=K; if(Abs(Yp)=0); AppendRoot(Sol,NbStateSol,b,F,K,dX); return; } if(Abs(e)>=tol1 && Abs(fa)>Abs(fb)) { s=fb/fa; if(a==c) { p=xm*s; p+=p; q=1.0-s; } else { q=fa/fc; r=fb/fc; p=s*((xm+xm)*q*(q-r)-(b-a)*(r-1.0)); q=(q-1.0)*(r-1.0)*(s-1.0); } if(p>0.0) { q=-q; } p=Abs(p); min1=3.0*xm*q-Abs(tol1*q); min2=Abs(e*q); if((p+p)<( (min1tol1) { b+=d; } else { if(xm>=0) b+=Abs(tol1); else b+=-Abs(tol1); } F.Value(b,fb); fb-=K; } #ifdef OCCT_DEBUG std::cout<<" Non Convergence dans math_FunctionRoots.cxx "< XN) X=XN; Ok=F.Value(X,aux); if(Ok) ptrval(++Nvalid) = aux - K; // ptrval(i)-=K; } //-- Toute la fonction est nulle ? if( Nvalid < N ) { Done = Standard_False; return; } AllNull=Standard_True; // for(i=0;AllNull && i<=N;i++) { for(i=0;AllNull && i<=N;i++) { if(ptrval(i)>EpsNull || ptrval(i)<-EpsNull) { AllNull=Standard_False; } } if(AllNull) { //-- tous les points echantillons sont dans la tolerance } else { //-- Il y a des points hors tolerance //-- on detecte les changements de signes STRICTS Standard_Integer ip1; // Standard_Boolean chgtsign=Standard_False; Standard_Real tol = EpsX; Standard_Real X2; for(i=0,ip1=1,X=X0;i XN) X2 = XN; if(ptrval(i)<0.0) { if(ptrval(ip1)>0.0) { //-- -------------------------------------------------- //-- changement de signe dans Xi Xi+1 Solve(F,K,X,ptrval(i),X2,ptrval(ip1),tol,NEpsX,Sol,NbStateSol); } } else { if(ptrval(ip1)<0.0) { //-- -------------------------------------------------- //-- changement de signe dans Xi Xi+1 Solve(F,K,X,ptrval(i),X2,ptrval(ip1),tol,NEpsX,Sol,NbStateSol); } } } //-- On detecte les cas ou la fct s annule sur des Xi et est //-- non nulle au voisinage de Xi //-- //-- On prend 2 points u0,u1 au voisinage de Xi //-- Si (F(u0)-K)*(F(u1)-K) <0 on lance une recherche //-- Sinon si (F(u0)-K)*(F(u1)-K) !=0 on insere le point X for(i=0; i<=N; i++) { if(ptrval(i)==0) { // Standard_Real Val,Deriv; X=X0+i*dx; if (X>XN) X=XN; Standard_Real u0,u1; u0=dx*0.5; u1=X+u0; u0+=X; if(u0XN) u0=XN; if(u1XN) u1=XN; Standard_Real y0,y1; F.Value(u0,y0); y0-=K; F.Value(u1,y1); y1-=K; if(y0*y1 < 0.0) { Solve(F,K,u0,y0,u1,y1,tol,NEpsX,Sol,NbStateSol); } else { if(y0!=0.0 || y1!=0.0) { AppendRoot(Sol,NbStateSol,X,F,K,NEpsX); } } } } //-- -------------------------------------------------------------------------------- //-- Il faut traiter differement le cas des points en bout : if(ptrval(0)<=EpsF && ptrval(0)>=-EpsF) { AppendRoot(Sol,NbStateSol,X0,F,K,NEpsX); } if(ptrval(N)<=EpsF && ptrval(N)>=-EpsF) { AppendRoot(Sol,NbStateSol,XN,F,K,NEpsX); } //-- -------------------------------------------------------------------------------- //-- -------------------------------------------------------------------------------- //-- On detecte les zones ou on a sur les points echantillons un minimum avec f(x)>0 //-- un maximum avec f(x)<0 //-- On reprend une discretisation plus fine au voisinage de ces extremums //-- //-- Recherche d un minima positif Standard_Real xm,ym,dym,xm1,xp1; Standard_Real majdx = 5.0*dx; Standard_Boolean Rediscr; // Standard_Real ptrvalbis[MAXBIS]; Standard_Integer im1=0; ip1=2; for(i=1,xm=X0+dx; i XN) xm=XN; if(ptrval(i)>0.0) { if((ptrval(im1)>ptrval(i)) && (ptrval(ip1)>ptrval(i))) { //-- Peut on traverser l axe Ox //-- -------------- Estimation a partir de Xim1 xm1=xm-dx; if(xm1 < X0) xm1=X0; F.Values(xm1,ym,dym); ym-=K; if(dym<-1e-10 || dym>1e-10) { // normalement dym < 0 Standard_Real t = ym / dym; //-- t=xm-x* = (ym-0)/dym if(t -majdx) { Rediscr = Standard_True; } } //-- -------------- Estimation a partir de Xip1 if(Rediscr==Standard_False) { xp1=xm+dx; if(xp1 > XN ) xp1=XN; F.Values(xp1,ym,dym); ym-=K; if(dym<-1e-10 || dym>1e-10) { // normalement dym > 0 Standard_Real t = ym / dym; //-- t=xm-x* = (ym-0)/dym if(t -majdx) { Rediscr = Standard_True; } } } } } else if(ptrval(i)<0.0) { if((ptrval(im1)1e-10 || dym<-1e-10) { // normalement dym > 0 Standard_Real t = ym / dym; //-- t=xm-x* = (ym-0)/dym if(t -majdx) { Rediscr = Standard_True; } } //-- -------------- Estimation a partir de Xim1 if(Rediscr==Standard_False) { xm1=xm-dx; if(xm1 < X0) xm1=X0; F.Values(xm1,ym,dym); ym-=K; if(dym>1e-10 || dym<-1e-10) { // normalement dym < 0 Standard_Real t = ym / dym; //-- t=xm-x* = (ym-0)/dym if(t -majdx) { Rediscr = Standard_True; } } } } } if(Rediscr) { Standard_Real x0 = xm - dx; Standard_Real x3 = xm + dx; if (x0 < X0) x0 = X0; if (x3 > XN) x3 = XN; Standard_Real aSolX1 = 0., aSolX2 = 0.; Standard_Real aVal1 = 0., aVal2 = 0.; Standard_Real aDer1 = 0., aDer2 = 0.; Standard_Boolean isSol1 = Standard_False; Standard_Boolean isSol2 = Standard_False; //-- ---------------------------------------------------- //-- Find minimum of the function |F| between x0 and x3 //-- by searching for the zero of the function derivative DerivFunction aDerF(F); math_BracketedRoot aBR(aDerF, x0, x3, _EpsX); if (aBR.IsDone()) { aSolX1 = aBR.Root(); F.Value(aSolX1, aVal1); aVal1 = Abs(aVal1); if (aVal1 < EpsF) { isSol1 = Standard_True; aDer1 = aBR.Value(); } } //-- -------------------------------------------------- //-- On recherche un extrema entre x0 et x3 //-- x1 et x2 sont tels que x0 |f(x1)| et |f(x3)| > |f(x2)| //-- //-- En entree : a=xm-dx b=xm c=xm+dx Standard_Real x1, x2, f0, f3; Standard_Real R = 0.61803399; Standard_Real C = 1.0 - R; Standard_Real tolCR = NEpsX*10.0; f0 = ptrval(im1); f3 = ptrval(ip1); Standard_Boolean recherche_minimum = (f0 > 0.0); if (Abs(x3 - xm) > Abs(x0 - xm)) { x1 = xm; x2 = xm + C*(x3 - xm); } else { x2 = xm; x1 = xm - C*(xm - x0); } Standard_Real f1, f2; F.Value(x1, f1); f1 -= K; F.Value(x2, f2); f2 -= K; //-- printf("\n *************** RECHERCHE MINIMUM **********\n"); Standard_Real tolX = 0.001 * NEpsX; while (Abs(x3 - x0) > tolCR*(Abs(x1) + Abs(x2)) && (Abs(x1 - x2) > tolX)) { //-- printf("\n (%10.5g,%10.5g) (%10.5g,%10.5g) (%10.5g,%10.5g) (%10.5g,%10.5g) ", //-- x0,f0,x1,f1,x2,f2,x3,f3); if (recherche_minimum) { if (f2 < f1) { x0 = x1; x1 = x2; x2 = R*x1 + C*x3; f0 = f1; f1 = f2; F.Value(x2, f2); f2 -= K; } else { x3 = x2; x2 = x1; x1 = R*x2 + C*x0; f3 = f2; f2 = f1; F.Value(x1, f1); f1 -= K; } } else { if (f2 > f1) { x0 = x1; x1 = x2; x2 = R*x1 + C*x3; f0 = f1; f1 = f2; F.Value(x2, f2); f2 -= K; } else { x3 = x2; x2 = x1; x1 = R*x2 + C*x0; f3 = f2; f2 = f1; F.Value(x1, f1); f1 -= K; } } //-- On ne fait pas que chercher des extremas. Il faut verifier //-- si on ne tombe pas sur une racine if (f1*f0 < 0.0) { //-- printf("\n Recherche entre (%10.5g,%10.5g) (%10.5g,%10.5g) ",x0,f0,x1,f1); Solve(F, K, x0, f0, x1, f1, tol, NEpsX, Sol, NbStateSol); } if (f2*f3 < 0.0) { //-- printf("\n Recherche entre (%10.5g,%10.5g) (%10.5g,%10.5g) ",x2,f2,x3,f3); Solve(F, K, x2, f2, x3, f3, tol, NEpsX, Sol, NbStateSol); } } if ((recherche_minimum && f1f2)) { //-- x1,f(x1) minimum if (Abs(f1) < EpsF) { isSol2 = Standard_True; aSolX2 = x1; aVal2 = Abs(f1); } } else { //-- x2.f(x2) minimum if (Abs(f2) < EpsF) { isSol2 = Standard_True; aSolX2 = x2; aVal2 = Abs(f2); } } // Choose the best solution between aSolX1, aSolX2 if (isSol1 && isSol2) { if (aVal2 - aVal1 > EpsF) AppendRoot(Sol, NbStateSol, aSolX1, F, K, NEpsX); else if (aVal1 - aVal2 > EpsF) AppendRoot(Sol, NbStateSol, aSolX2, F, K, NEpsX); else { aDer1 = Abs(aDer1); F.Derivative(aSolX2, aDer2); aDer2 = Abs(aDer2); if (aDer1 < aDer2) AppendRoot(Sol, NbStateSol, aSolX1, F, K, NEpsX); else AppendRoot(Sol, NbStateSol, aSolX2, F, K, NEpsX); } } else if (isSol1) AppendRoot(Sol, NbStateSol, aSolX1, F, K, NEpsX); else if(isSol2) AppendRoot(Sol, NbStateSol, aSolX2, F, K, NEpsX); } //-- Recherche d un extrema } //-- for } #if NEWSEQ #ifdef MATH_FUNCTIONROOTS_CHECK { StaticSol.Clear(); Standard_Integer n=Sol.Length(); for(Standard_Integer ii=1;ii<=n;ii++) { StaticSol.Append(Sol.Value(ii)); } Sol.Clear(); NbStateSol.Clear(); } #endif #endif #endif } #ifdef MATH_FUNCTIONROOTS_OLDCODE { //-- ******************************************************************************** //-- ANCIEN TRAITEMENT //-- ******************************************************************************** // calculate all the real roots of a function within the range // A..B. without condition on A and B // a solution X is found when // abs(Xi - Xi-1) <= EpsX and abs(F(Xi)-K) <= Epsf. // The function is considered as null between A and B if // abs(F-K) <= EpsNull within this range. Standard_Real EpsX = _EpsX; //-- Cas ou le parametre va de 100000000 a 1000000001 //-- Il ne faut pas EpsX = 0.000...001 car dans ce cas //-- U + Nn*EpsX == U Standard_Real Lowr,Upp; Standard_Real Increment; Standard_Real Null2; Standard_Real FLowr,FUpp,DFLowr,DFUpp; Standard_Real U,Xu; Standard_Real Fxu,DFxu,FFxu,DFFxu; Standard_Real Fyu,DFyu,FFyu,DFFyu; Standard_Boolean Finish; Standard_Real FFi; Standard_Integer Nbiter = 30; Standard_Integer Iter; Standard_Real Ambda,T; Standard_Real AA,BB,CC; Standard_Integer Nn; Standard_Real Alfa1=0,Alfa2; Standard_Real OldDF = RealLast(); Standard_Real Standard_Underflow = 1e-32 ; //-- RealSmall(); Standard_Boolean Ok; Done = Standard_False; StdFail_NotDone_Raise_if(NbSample <= 0, " "); // initialisation if (A > B) { Lowr=B; Upp=A; } else { Lowr=A; Upp=B; } Increment = (Upp-Lowr)/NbSample; StdFail_NotDone_Raise_if(Increment < EpsX, " "); Done = Standard_True; //-- On teste si EpsX est trop petit (ie : U+Nn*EpsX == U ) Standard_Real DeltaU = Abs(Upp)+Abs(Lowr); Standard_Real NEpsX = 0.0000000001 * DeltaU; if(EpsX < NEpsX) { EpsX = NEpsX; //-- std::cout<<" \n EpsX Init = "<<_EpsX<<" devient : (deltaU : "<n2) n=n2; for(Standard_Integer i=1;i<=n;i++) { Standard_Real t = Sol(i)-StaticSol(i); if(Abs(t)>NEpsX) { printf("\n mathFunctionRoots : i:%d/%d delta: %g",i,n,t); } } } #endif #endif } #endif } void math_FunctionRoots::Dump(Standard_OStream& o) const { o << "math_FunctionRoots "; if(Done) { o << " Status = Done \n"; o << " Number of solutions = " << Sol.Length() << std::endl; for (Standard_Integer i = 1; i <= Sol.Length(); i++) { o << " Solution Number " << i << "= " << Sol.Value(i) << std::endl; } } else { o<< " Status = not Done \n"; } }