qub_dfpmin.cpp.html mathcode2html   
 Source file:   qub_dfpmin.cpp
 Converted:   Sun Aug 7 2016 at 13:45:52
 This documentation file will not reflect any later changes in the source file.

$$\phantom{******** If you see this on the webpage then the browser could not locate *********}$$
$$\phantom{******** jsMath/easy/load.js or the variable root is set wrong in this file *********}$$
$$\newcommand{\vector}[1]{\left[\begin{array}{c} #1 \end{array}\right]}$$ $$\newenvironment{matrix}{\left[\begin{array}{cccccccccc}} {\end{array}\right]}$$ $$\newcommand{\A}{{\cal A}}$$ $$\newcommand{\W}{{\cal W}}$$

/* Copyright 1998-2013 Research Foundation State University of New York   */

/* This file is part of QUB Express.                                      */

/* QUB Express is free software; you can redistribute it and/or modify    */
/* it under the terms of the GNU General Public License as published by   */
/* the Free Software Foundation, either version 3 of the License, or      */
/* (at your option) any later version.                                    */

/* QUB Express 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 General Public License for more details.                           */

/* You should have received a copy of the GNU General Public License,     */
/* named LICENSE.txt, in the QUB Express program directory.  If not, see  */
/* <http://www.gnu.org/licenses/>.                                        */

#include <math.h>
#include <string.h>

#ifdef _WINDOWS
#include <float.h>
#define finite _finite
#endif

#include "qubfast.h"
#include "qub_dfpmin.h"

#include "matrix.h"
#include "milutil.h"

#include <iostream>
using std::cerr;
using std::endl;

class dfp_optimizable{
public:
  virtual ~dfp_optimizable() {}

  virtual bool getStartingZ( fq::vector<double> &z ) = 0;
  virtual bool evaluate( fq::vector<double> &z, fq::vector<double> &gz,
			 double &ll, int &nf, int &ndf, bool flagGrad=true ) = 0;
  virtual bool checkContinue( int iter, int nf, int ndf, double ll,
			      fq::vector<double> &z, fq::vector<double> &gz )=0;
};

class dfp_result{
public:
  fq::vector<double> z;
  double ll;
  int iter;
  int nfcn, ndfcn;
  fq::matrix<double> hessian;
  int err; // ? 
};

// Local Functions
dfp_result dfp_optimize( dfp_optimizable *optable, int maxIter, double convLL, 
			 double convGrad, double stepMax = 1.0 );

bool dfp_lnsrch( fq::vector<double> &z, double ll, fq::vector<double> &g,
		 fq::vector<double> &xi, fq::vector<double> &znew,
		 double &llnew, fq::vector<double> &gnew,
		 double stpmax, int &check, int &nfcn, int &ndfcn,
		 int &flagRund, dfp_optimizable *optable );
	// returns true unless optable->optimize(...) returns false





#define  ITMAX 	   100
#define  ITPRT 	   1
#define  EPS 	      3.0e-8
#define	TOLF 	      1.0e-5
#define	TOLX 	      1.0e-15
#define	TOLG 	      5.0e-3
#define VERY_SMALL    1.0e-15

dfp_result dfp_optimize( dfp_optimizable *optable,
		       int maxIter, double convLL, double convGrad, double stepMax ) {
  /*
  double dsqrarg;
  double dmaxarg1,dmaxarg2;
  double dminarg1,dminarg2;
  int iminarg1,iminarg2;
  int imaxarg1,imaxarg2;
  */
  dfp_result result;
  result.ll = 0.0;
  result.iter = 1; // or 0?
  result.nfcn = 0;
  result.ndfcn = 0;
  result.err = 0;
  if ( ! optable->getStartingZ( result.z ) ) {
    // Message( "blah blah blah invalid starting set\n" );
    result.hessian.resize( result.z.size(), result.z.size() );
	for (int hi=result.z.size()-1; hi>=0; --hi)
		for (int hj=result.z.size()-1; hj>=0; --hj)
			result.hessian[hi][hj] = 1.0;
    result.err = -666; // invalid starting point
    return result;
  }
  result.hessian.resize( result.z.size(), result.z.size() );
  fq::matrix<double> &hessian = result.hessian;
  
  int      n = result.z.size();
  int      check,i,its,j,flagRund;
  double   fac,fad,fae,stpmax,sumdg,sumxi,sum=0.0;
  double   tolf,tolg, tmpLL;
  int      restarts = 2;
  
  fq::vector<double> g( n, 0.0 );
  fq::vector<double> dg( n, 0.0 );
  fq::vector<double> hdg( n, 0.0 );
  fq::vector<double> pnew( n, 0.0 );
  fq::vector<double> gnew( n, 0.0 );
  fq::vector<double> xi( n, 0.0 );
  
lab1:	
   if ( ! optable->evaluate( result.z, g, result.ll,
			     result.nfcn, result.ndfcn ) ) {
     result.err = -1;  // killed; unsuccessful evaluate()
     return result;
   }
   
   //cerr << "reset hessian etc" << endl;
   for (i=0; i<n; i++) {
     for (j=0; j<n; j++)
       hessian[i][j]=0.0;
     hessian[i][i]=1.0;
     xi[i] = -g[i];
     sum += result.z[i]*result.z[i];
   }
   stpmax=stepMax*DMAX(sqrt(sum),(double)n);
   
   //cerr << "iterate" << endl;
   for (its=1; its<=maxIter; its++, result.iter++) {
     if ((its-1)/ITPRT*ITPRT == its-1) {
       //cerr << "check" << endl;
       if ( ! optable->checkContinue( result.iter, result.nfcn,
				      result.ndfcn, result.ll,
				      result.z, g ) ) {
	 result.err = -2;  // killed; checkContinue == false
	 return result;
       }
     }
     
     //cerr << "lnsrch" << endl;
     tmpLL = result.ll;
     if ( ! dfp_lnsrch( result.z, tmpLL, g, xi, pnew, result.ll,
			gnew, stpmax, check, result.nfcn, result.ndfcn,
			flagRund, optable ) ) {
       result.err = -1; // opt.evaluate returned false
       return result;
     }
     optable->evaluate(pnew, gnew, result.ll, result.nfcn, result.ndfcn); // update gradient
     /*
       if ( posieigencount > 5 )
	   { 
		   FREEALL;
		   Message(pvoid,"Positive eigenvalue,modify dead time and run again!");
		   return 0; // killed
	   }

     */
     if ( result.ll > 1e10 ){
       // Message("Warning: Roundoff error and DFPMIN restarts.");
       //cerr << "Warning: Roundoff error and DFPMIN restarts." << endl;
       if ( restarts-- )
	 goto lab1;
       result.ll = tmpLL;
       result.err = -4; // too many restarts after bad ll
       return result;
     }
     
	 if ( tmpLL != 0.0 ) {
		tolf=fabs(result.ll - tmpLL)/fabs(tmpLL);
	 }
	 else {
		 tolf = 1.0;
	 }
     tmpLL = result.ll;
     for (i=0; i<n; i++) {
       xi[i] = pnew[i]-result.z[i];
       result.z[i] = pnew[i];
       dg[i] = g[i];
       g[i]  = gnew[i];
     }
     for (tolg=0.0,i=0; i<n; i++) 
       if (tolg<fabs(g[i])) 
	 tolg=fabs(g[i]);
     if (tolf<convLL && tolg<convGrad && fabs(tmpLL)<1.0e10) {
         result.err = 0;   // successful
	 return result;
     }
     
     for (i=0; i<n; i++)
       dg[i]=g[i]-dg[i];
     for (i=0; i<n; i++) {
       hdg[i]=0.0;
       for (j=0; j<n; j++)
	 hdg[i] += hessian[i][j]*dg[j];
     }
     fac=fae=sumdg=sumxi=0.0;
     for (i=0; i<n; i++) {
       fac += dg[i]*xi[i];
       fae += dg[i]*hdg[i];
       sumdg += DSQR(dg[i]);
       sumxi += DSQR(xi[i]);
     }
     if (fac*fac > EPS*sumdg*sumxi) {
       fac=1.0/fac;
       fad=1.0/fae;
       for (i=0; i<n; i++)
	 dg[i]=fac*xi[i]-fad*hdg[i];
       for (i=0; i<n; i++) 
	 for (j=0; j<n; j++) 
	   hessian[i][j] += fac*xi[i]*xi[j] -
	                    fad*hdg[i]*hdg[j] +
	                    fae*dg[i]*dg[j];
     }
     for (i=0; i<n; i++) {
       xi[i]=0.0;
       for (j=0; j<n; j++)
	 xi[i] -= hessian[i][j]*g[j];
     }
   }
   
   // Message("Warning: Iteration in dfpmin exceeds the limit(%d)",maxIter);
   //cerr << "Warning: Iteration in dfpmin exceeds the limit (" << maxIter
   // << ")" << endl;
   result.err = -3;      // exceeds the maximum number of iterations
   return result;
}

#undef ITMAX
#undef ITPRT
#undef TOLF
#undef TOLX
#undef TOLG
#undef EPS


#define	ALF  1.0e-4
#define	TOLX 1.0e-7

bool dfp_lnsrch( fq::vector<double> &z, double ll, fq::vector<double> &g,
		 fq::vector<double> &xxi, fq::vector<double> &znew,
		 double &llnew, fq::vector<double> &gnew,
		 double stpmax, int &check, int &nfcn, int &ndfcn,
		 int &flagRund, dfp_optimizable *optable )
{
  int 	   i; // flagGrad = 1
  int      n = z.size();
  double   a,alam,alam2,alamin,b,disc,f2,fold2,
           rhs1,rhs2,slope,sum,temp,test,tmplam;
  //double dmaxarg1,dmaxarg2;

  fq::vector<double> xi = xxi;
  
  check = flagRund = 0;
  
  for (sum=0.0, i=0; i<n; i++)
    sum += xi[i]*xi[i];
  sum=sqrt(sum);
  if (sum > stpmax)
    for (i=0; i<n; i++)
      xi[i] *= stpmax/sum;
  
  for (slope=0.0, i=0; i<n; i++)
    slope += g[i]*xi[i];
  test=VERY_SMALL; // was 0.0; 9.25.01 Chris
  for (i=0; i<n; i++) {
    temp=fabs(xi[i])/DMAX(fabs(z[i]),1.0);
    if ((temp > test) && finite(temp))
      test=temp;
  }
  alamin=TOLX/test;
  alam=1.0;
  if ( alam < alamin ) {
    znew = z;
    llnew = ll;
    gnew = g;
    return true;
  }
  
  for (;;) {
    for (i=0; i<n; i++)
      znew[i] = z[i] + alam*xi[i];
    // if ( posieigencount > 5 ) return;
    if ( ! optable->evaluate( znew, gnew, llnew, nfcn, ndfcn, false ) )
      return false;
    // (n,p,f,gnew,nf,ndf,flagGrad,pvoid);
    if (alam < alamin) {
      for (i=0; i<n; i++)
	znew[i]=z[i];
      check=1;
      return true;
    }
    else if (llnew <= ll+ALF*alam*slope) {
      return true;
    }
    else {
      if (alam == 1.0)
	tmplam = -slope/(2.0*(llnew-ll-slope));
      else {
	rhs1 = llnew-ll-alam*slope;
	rhs2=f2-fold2-alam2*slope;
	a=(rhs1/(alam*alam)-rhs2/(alam2*alam2))/(alam-alam2);
	b=(-alam2*rhs1/(alam*alam)+alam*rhs2/(alam2*alam2))/(alam-alam2);
	if (a == 0.0)
	  tmplam = -slope/(2.0*b);
	else {
	  disc=b*b-3.0*a*slope;
	  if (disc<0.0) {
	    flagRund=1;
	    return true;
	  }
	  else
	    tmplam=(-b+sqrt(disc))/(3.0*a);
	}
	if (tmplam>0.5*alam) tmplam=0.5*alam;
      }
    }
    alam2=alam;
    f2 = llnew;
    fold2=ll;
    alam=DMAX(tmplam,0.1*alam);
  }
  // return true;
}

#undef ALF
#undef TOLX

class dfp_optable : public dfp_optimizable
{
public:
	dfp_optable( void *call_obj, int npar, double *initPar,
					 dfp_func xfunc, dfp_check xcheck )
		: caller( call_obj ), initZ( npar ), func( xfunc ), check( xcheck )
	{
		memcpy( &(initZ[0]), initPar, npar * sizeof(double) );
	}

  virtual bool getStartingZ( fq::vector<double> &z )
  {
	  z = initZ;
	  return true;
  }

  virtual bool evaluate( fq::vector<double> &z, fq::vector<double> &gz,
			   double &ll, int &nf, int &ndf, bool flagGrad=true )
  {
	  return ( ! func( caller, &(z[0]), &(gz[0]), &ll, &nf, &ndf, (int)flagGrad ) );
  }

  virtual bool checkContinue( int iter, int nf, int ndf, double ll,
			      fq::vector<double> &z, fq::vector<double> &gz )
  {
	  if ( check )
		return ( ! check( caller, iter, nf, ndf, ll, &(z[0]), &(gz[0]) ) );
	  else return true;
  }

private:
  void *caller;
  fq::vector<double> initZ;
  dfp_func func;
  dfp_check check;
};

extern "C" QUBFAST_API double qub_dfpmin( void *call_obj, int npar, double *initPar, double *hessianOut,
					  dfp_func func, dfp_check check,
					  int maxIter, double convLL, double convGrad, double stepMax,
					  int *err )
{
	dfp_optable optable( call_obj, npar, initPar, func, check );
	dfp_result res = dfp_optimize( &optable, maxIter, convLL, convGrad, stepMax );

	*err = res.err;
	
	if ( hessianOut ) {
	  double *hh = hessianOut;
	  for ( int i=0; i<npar; ++i )
	    for ( int j=0; j<npar; ++j,++hh )
	      *hh = res.hessian[i][j];
	}
	
	return res.ll;
}