// $Id: mmdb_math_bfgsmin.cpp $ // ================================================================= // // CCP4 Coordinate Library: support of coordinate-related // functionality in protein crystallography applications. // // Copyright (C) Eugene Krissinel 2000-2013. // // This library is free software: you can redistribute it and/or // modify it under the terms of the GNU Lesser General Public // License version 3, modified in accordance with the provisions // of the license to address the requirements of UK law. // // You should have received a copy of the modified GNU Lesser // General Public License along with this library. If not, copies // may be downloaded from http://www.ccp4.ac.uk/ccp4license.php // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU Lesser General Public License for more details. // // ================================================================= // // 12.09.13 <-- Date of Last Modification. // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // ----------------------------------------------------------------- // // **** Module : BFGSMin // ~~~~~~~~~ // **** Classes : mmdb::math::BFGSMin ( minimization driver ) // ~~~~~~~~~ // // (C) E. Krissinel 2000-2013 // // ================================================================= // #include #include "mmdb_math_bfgsmin.h" namespace mmdb { namespace math { // ============================================================== BFGSMin::BFGSMin() { MFunc = NULL; MFuncData = NULL; PFunc = NULL; PFuncData = NULL; N = 0; NAlloc = 0; Hsn = NULL; TL = NULL; LL = NULL; XOpt = NULL; XPlus = NULL; Sx = NULL; SN = NULL; HDiag = NULL; GradX = NULL; GPlus = NULL; StepSize = NULL; FNeighbor = NULL; us = NULL; uy = NULL; ut = NULL; Freese = NULL; Func = 0.0; FPlus = 0.0; FOpt = 0.0; TakenLambda = 0.0; ForDiff = false; CalcHess = false; Etha = 0.0; SqrtEtha = 0.0; CubertEtha = 0.0; TpF = 1.0; GrdEps = 0.0; StpEps = 0.0; MxStep = MaxReal; CnsMax = 0; MaxItn = 100; TermCode = BFGS_NoTermination; ModF = false; } BFGSMin::~BFGSMin() { FreeMemory(); } void BFGSMin::MinFunc ( rvector X, realtype & F ) { if (MFunc) (*MFunc)(MFuncData,N,X,F); else F = 0.0; } void BFGSMin::MinFunc1 ( rvector X, realtype & F ) { int i; MinFunc ( X,F ); if (ModF && (FEtha) Etha = MachEps; } SqrtEtha = sqrt(Etha); CubertEtha = Exp ( log(Etha)/3.0 ); if (Etha>0.01) { TermCode = BFGS_TooFewDigits; return; } if (TypF<=0.0) TpF = 1.0; else TpF = TypF; S1 = Exp(log(MachEps)/3.0); if (GrdTol>0.0) GrdEps = GrdTol; else { GrdEps = sqrt(Etha); if (S1>GrdEps) GrdEps = S1; } if (StpTol>0.0) StpEps = StpTol; else StpEps = Exp ( log(MachEps)*2.0/3.0 ); if (MaxStp>0.0) MxStep = MaxStp; else { S1 = 0.0; S2 = 0.0; for (i=1;i<=N;i++) { S0 = Sx[i]; S0 *= Sx[i]; S2 += S0; S0 *= x0[i]; S1 += S0*x0[i]; } S1 = sqrt(S1); S2 = sqrt(S2); if (S2>S1) MxStep = S2; else MxStep = S1; MxStep *= 1000.0; } if (ItnLmt>0) MaxItn = ItnLmt; else MaxItn = 100; TermCode = BFGS_NoTermination; } // ------------------------------------------------------------------- void BFGSMin::UMStop0 ( rvector x0, rvector Grad ) { int i; realtype S,Fmax,St; CnsMax = 0; if (TpF>fabs(Func)) Fmax = TpF; else Fmax = fabs(Func); S = 0.0; for (i=1;i<=N;i++) { St = fabs(x0[i]); if (1.0/Sx[i]>St) St = 1.0/Sx[i]; St = fabs(Grad[i])*St/Fmax; if (St>S) S = St; } if (S>=0.001*GrdEps) TermCode = BFGS_NoTermination; else TermCode = BFGS_SmallGradient; } // ------------------------------------------------------------------- void BFGSMin::UMStop ( rvector x0, rvector Grad, int RetCode, int ItnCnt, bool MaxTkn ) { // A7.2.1 : Checking the Stop Conditions int i; realtype Max1,Max2,MaxGrad,MaxStep, BB1,BB2; TermCode = BFGS_NoTermination; if (RetCode==1) TermCode = BFGS_LineSearchComplete; else { if (fabs(FPlus)>TpF) Max2 = fabs(FPlus); else Max2 = TpF; MaxGrad = 0.0; MaxStep = 0.0; for (i=1;i<=N;i++) { BB1 = fabs(XPlus[i]); BB2 = 1.0/Sx[i]; if (BB1>BB2) Max1 = BB1; else Max1 = BB2; BB1 = fabs(Grad[i])*Max1/Max2; if (BB1>MaxGrad) MaxGrad = BB1; BB2 = fabs(XPlus[i]-x0[i])/Max1; if (BB2>MaxStep) MaxStep = BB2; } if (MaxGradMaxItn) TermCode = BFGS_IterationLimit; else if (MaxTkn) { CnsMax++; if (CnsMax==5) TermCode = BFGS_LargeSteps; } else CnsMax = 0; } } // ------------------------------------------------------------------- void BFGSMin::MdHess ( rmatrix H, rvector HDg ) { // A5.5.1 : Setting up the hessian of model int i,j; realtype MaxDiag,MaxOff, MinEv,Mue,MaxPosDiag; realtype MaxOffl,MinDiag,MaxEv,MaxAdd,Sdd,OffRow; realtype BB; // Scaling for (i=1;i<=N;i++) for (j=i;j<=N;j++) H[i][j] /= (Sx[i]*Sx[j]); MaxDiag = H[1][1]; MinDiag = H[1][1]; MaxOff = 0.0; for (i=1;i<=N;i++) { if (H[i][i]>MaxDiag) MaxDiag = H[i][i]; if (H[i][i]MaxOff) MaxOff = BB; } } MaxPosDiag = 0.0; if (MaxDiag>MaxPosDiag) MaxPosDiag = MaxDiag; // Computing the shift of the spectra (the Mue) if (MinDiag>SqrtEps*MaxPosDiag) Mue = 0.0; else { Mue = 2.0*(MaxPosDiag-MinDiag)*SqrtEps-MinDiag; MaxDiag += Mue; } BB = MaxOff*(1.0+2.0*SqrtEps); if (BB>MaxDiag) { Mue = Mue+(MaxOff-MaxDiag)+2.0*SqrtEps*MaxOff; MaxDiag = BB; } if (MaxDiag==0.0) { // H = 0 Mue = 1.0; MaxDiag = 1.0; } if (Mue>0.0) for (i=1;i<=N;i++) Hsn[i][i] += Mue; MaxOffl = MaxOff/N; if (MaxDiag>MaxOffl) MaxOffl = MaxDiag; MaxOffl = sqrt(MaxOffl); for (i=1;i<=N;i++) HDg[i] = H[i][i]; PbCholDecomp ( N,HDg,MaxOffl,MachEps,H,MaxAdd ); if (MaxAdd>0.0) { MaxEv = HDg[1]; MinEv = HDg[1]; for (i=1;i<=N;i++) { OffRow = 0.0; if (i>1) for (j=1;jMaxEv) MaxEv = BB; BB = HDg[i]-OffRow; if (BBBB2) StepSizeJ = BB1; else StepSizeJ = BB2; if (X[j]<0.0) StepSizeJ = -StepSizeJ; StepSizeJ *= SqrtEtha; TempJ = X[j]; X[j] += StepSizeJ; StepSizeJ = X[j]-TempJ; MinFunc1 ( X,Fj ); if (TermCode!=BFGS_NoTermination) return; G[j] = (Fj-Fc)/StepSizeJ; X[j] = TempJ; Freese[j] = false; if (TL) { if ((fabs(X[j]-TL[j])<=StepSizeJ) && (G[j]<0.0)) { G[j] = 0.0; Freese[j] = true; } } if (LL) { if ((fabs(X[j]-LL[j])<=StepSizeJ) && (G[j]>0.0)) { G[j] = 0.0; Freese[j] = true; } } } } // ------------------------------------------------------------------- void BFGSMin::CDGrad ( rvector X, rvector G ) { // A5.6.4 : Central Differencies Approximation of // Gradient realtype StepSizeJ,TempJ,Fp,Fm, BB1,BB2; int j; for (j=1;j<=N;j++) { BB1 = fabs(X[j]); BB2 = 1.0/Sx[j]; if (BB1>BB2) StepSizeJ = BB1; else StepSizeJ = BB2; if (X[j]<0.0) StepSizeJ = -StepSizeJ; StepSizeJ *= CubertEtha; TempJ = X[j]; X[j] += StepSizeJ; StepSizeJ = X[j]-TempJ; MinFunc1 ( X,Fp ); if (TermCode!=BFGS_NoTermination) return; X[j] = TempJ-StepSizeJ; MinFunc1 ( X,Fm ); if (TermCode!=BFGS_NoTermination) return; G[j] = (Fp-Fm)/(2.0*StepSizeJ); X[j] = TempJ; } } // ------------------------------------------------------------------- void BFGSMin::Gradient ( rvector X, rvector G, realtype Fc ) { if (ForDiff) FDGrad ( X,G,Fc ); else CDGrad ( X,G ); } // ------------------------------------------------------------------- void BFGSMin::FDHessF ( realtype Fc, rvector X ) { // A5.6.2 : Finite-Difference Approximation of // the Hessian employing only the // function's values int i,j; realtype S,TempI,Fii,TempJ,Fij, BB1,BB2; for (i=1;i<=N;i++) if (!Freese[i]) { BB1 = fabs(X[i]); BB2 = 1.0/Sx[i]; if (BB1>BB2) S = BB1; else S = BB2; if (X[i]<0.0) S = -S; StepSize[i] = S*CubertEtha; TempI = X[i]; X[i] += StepSize[i]; StepSize[i] = X[i]-TempI; MinFunc1 ( X,FNeighbor[i] ); X[i] = TempI; if (TermCode!=BFGS_NoTermination) return; } for (i=1;i<=N;i++) if (!Freese[i]) { TempI = X[i]; X[i] += 2.0*StepSize[i]; MinFunc1 ( X,Fii ); if (TermCode!=BFGS_NoTermination) return; Hsn[i][i] = (( Fc -FNeighbor[i] ) + ( Fii-FNeighbor[i] )) / (StepSize[i]*StepSize[i]); X[i] = TempI+StepSize[i]; if (iTemp) Temp = TpF; for (i=1;i<=N;i++) { H[i][i] = Temp*Sx[i]*Sx[i]; if (isqrt(MachEps*NormS*NormY)) { if (AnalGrad) Tol = Etha; else Tol = sqrt(Etha); SkipUpdate = true; for (i=1;i<=N;i++) { tt = 0.0; for (j=1;j<=i;j++) tt += H[j][i]*us[j]; if (itt) tt = BB; if (fabs(uy[i]-ut[i])>=Tol*tt) SkipUpdate = false; } if (!SkipUpdate) { Temp2 = 0.0; for (i=1;i<=N;i++) Temp2 += us[i]*ut[i]; for (i=1;i<=N;i++) for (j=i;j<=N;j++) H[i][j] += uy[i]*uy[j]/Temp1 - ut[i]*ut[j]/Temp2; } } } // ------------------------------------------------------------------- void BFGSMin::Choose_Lambda ( rvector X, rvector S, realtype & Lambda0 ) { int i; realtype SS; for (i=1;i<=N;i++) if ((S[i]!=0.0) && (!Freese[i])) { SS = X[i] + Lambda0*S[i]; if (TL) { if (SS>TL[i]) Lambda0 = (TL[i]-X[i])/S[i]/(1.0+MachEps); } if (LL) { if (SSMxStep) { // restrict Newtonian step to MxStep S = MxStep/NewtLn; for (i=1;i<=N;i++) P[i] *= S; NewtLn = MxStep; } InitSp = 0.0; RelLng = 0.0; Lambda0 = 1.0; Choose_Lambda ( px0,P,Lambda0 ); for (i=1;i<=N;i++) { InitSp += G[i]*P[i]; B1 = fabs(px0[i]); B2 = 1.0/Sx[i]; if (B1>B2) S = B1; else S = B2; S = fabs(P[i])/S; if (S>RelLng) RelLng = S; } InitSp *= Lambda0; MinLam = StpEps/RelLng; Lambda = Lambda0; do { for (i=1;i<=N;i++) XPlus[i] = px0[i] + Lambda*P[i]; MinFunc1 ( XPlus,FPlus ); if (TermCode!=BFGS_NoTermination) return; if (FPlus<=pFunc+Alpha*Lambda*InitSp) { RetCode = 0; MaxTkn = (Lambda==Lambda0) && (NewtLn>0.99*MxStep); } else if (Lambda0.1*Lambda) Lambda = LamTem; else { Lambda *= 0.1; Lambda0 = Lambda; } if (Lambda>Lambda0) { Lambda = Lambda0; RetCode = 0; for (i=1;i<=N;i++) XPlus[i] = px0[i] + Lambda*P[i]; } } else { B1 = FPlus - pFunc - Lambda*InitSp; B2 = FplPre - pFunc - LamPre*InitSp; A = ( B1/(Lambda*Lambda) - B2/(LamPre*LamPre) ) / ( Lambda - LamPre ); B = ( -LamPre*B1/(Lambda*Lambda) + Lambda*B2/(LamPre*LamPre) ) / ( Lambda - LamPre ); Disc = B*B - 3.0*A*InitSp; if (A==0.0) LamTem = -InitSp/(2.0*B); else LamTem = (-B+sqrt(RMax(Disc,0.0)))/(3.0*A); B1 = 0.5*Lambda; if (B10.1*Lambda) Lambda = LamTem; else { Lambda *= 0.1; Lambda0 = Lambda; } if (Lambda>Lambda0) { Lambda = Lambda0; RetCode = 0; for (i=1;i<=N;i++) XPlus[i] = px0[i] + Lambda*P[i]; } } } while (RetCode>=2); TakenLambda = Lambda; } // ------------------------------------------------------------------- void BFGSMin::GetMemory() { if (N!=NAlloc) { FreeMemory(); GetMatrixMemory ( Hsn , N,N, 1,1 ); GetVectorMemory ( GPlus , N, 1 ); GetVectorMemory ( GradX , N, 1 ); GetVectorMemory ( HDiag , N, 1 ); GetVectorMemory ( SN , N, 1 ); GetVectorMemory ( Sx , N, 1 ); GetVectorMemory ( XPlus , N, 1 ); GetVectorMemory ( XOpt , N, 1 ); GetVectorMemory ( Freese, N, 1 ); if (CalcHess) { GetVectorMemory ( StepSize , N, 1 ); GetVectorMemory ( FNeighbor, N, 1 ); } else { GetVectorMemory ( us , N, 1 ); GetVectorMemory ( uy , N, 1 ); GetVectorMemory ( ut , N, 1 ); } NAlloc = N; } } void BFGSMin::FreeMemory() { if (NAlloc>0) { FreeVectorMemory ( us , 1 ); FreeVectorMemory ( uy , 1 ); FreeVectorMemory ( ut , 1 ); FreeVectorMemory ( Freese , 1 ); FreeVectorMemory ( StepSize , 1 ); FreeVectorMemory ( FNeighbor, 1 ); FreeVectorMemory ( XOpt , 1 ); FreeVectorMemory ( XPlus , 1 ); FreeVectorMemory ( Sx , 1 ); FreeVectorMemory ( SN , 1 ); FreeVectorMemory ( HDiag , 1 ); FreeVectorMemory ( GradX , 1 ); FreeVectorMemory ( GPlus , 1 ); FreeMatrixMemory ( Hsn , NAlloc, 1,1 ); } NAlloc = 0; } // ------------------------------------------------------------------- void BFGSMin::Relax() { int i; if (FPlus>FOpt) { for (i=1;i<=N;i++) XPlus[i] = XOpt[i]; FPlus = FOpt; } else { for (i=1;i<=N;i++) XOpt[i] = XPlus[i]; FOpt = FPlus; } } void BFGSMin::CopyPlus ( rvector x0 ) { int i; for (i=1;i<=N;i++) { x0 [i] = XPlus[i]; GradX[i] = GPlus[i]; } Func = FPlus; } // ------------------------------------------------------------------- void BFGSMin::BFGS_Driver ( int MinN, rvector x0, rvector TypX, realtype & FuncValue, int & TerminationCode, int Digits, int ItnLmt, realtype TypF, realtype GrdTol, realtype StpTol, realtype MaxStp, bool Hess, rvector LowLimit, rvector TopLimit ) { // D6.1.1 : Unconstrained Minimization Driver int i,RetCode; int ItnCnt; bool MaxTkn; TL = TopLimit; LL = LowLimit; ForDiff = true; N = MinN; CalcHess = Hess; ModF = false; GetMemory(); UMInCk ( x0,TypX,Digits,TypF, GrdTol,StpTol,MaxStp, ItnLmt ); if (TermCode!=BFGS_NoTermination) { FreeMemory(); FuncValue = Func; TerminationCode = TermCode; return; } ItnCnt = 0; MinFunc1 ( x0,Func ); if (TermCode!=BFGS_NoTermination) { FreeMemory(); FuncValue = Func; TerminationCode = TermCode; return; } FOpt = Func; FPlus = Func; for (i=1;i<=N;i++) { XOpt [i] = x0[i]; XPlus[i] = x0[i]; } ModF = true; Gradient ( x0,GradX,Func ); Print ( ItnCnt,x0,GradX,Func ); for (i=1;i<=N;i++) GPlus[i] = GradX[i]; if (TermCode!=BFGS_NoTermination) { Relax (); CopyPlus ( x0 ); FreeMemory(); FuncValue = Func; TerminationCode = TermCode; return; } UMStop0 ( x0,GradX ); if (TermCode!=BFGS_NoTermination) { FreeMemory(); FuncValue = Func; TerminationCode = TermCode; return; } if (!CalcHess) InitHessUnFac ( Func,Hsn ); RetCode = 0; while (TermCode==BFGS_NoTermination) { ItnCnt++; if (RetCode>=0) { if (CalcHess) { FDHessF ( Func,x0 ); if (TermCode!=BFGS_NoTermination) { Relax (); CopyPlus ( x0 ); FreeMemory(); FuncValue = Func; TerminationCode = TermCode; return; } } MdHess ( Hsn,HDiag ); } ChSolve ( N,Hsn,GradX,SN ); LineSearch ( x0,GradX,SN,Func,RetCode,MaxTkn ); if ((RetCode==1) && ForDiff) { RetCode = -1; ForDiff = false; } else Relax(); if (TermCode!=BFGS_NoTermination) { Relax (); CopyPlus ( x0 ); FreeMemory(); FuncValue = Func; TerminationCode = TermCode; return; } else Gradient ( XPlus,GPlus,FPlus ); if (TermCode!=BFGS_NoTermination) { Relax (); CopyPlus ( x0 ); FreeMemory(); FuncValue = Func; TerminationCode = TermCode; return; } if (RetCode>=0) { UMStop ( x0,GPlus,RetCode,ItnCnt,MaxTkn ); if ((!CalcHess) && (TermCode==BFGS_NoTermination)) BFGSUnFac ( x0,XPlus,GradX,GPlus,false,HDiag,Hsn ); } CopyPlus ( x0 ); Print ( ItnCnt, x0,GradX,Func ); } Relax (); FreeMemory(); FuncValue = Func; TerminationCode = TermCode; } } // namespace math } // namespace mmdb