| 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;
}