12280 lines
489 KiB
Plaintext
12280 lines
489 KiB
Plaintext
//+------------------------------------------------------------------+
|
|
//| optimization.mqh |
|
|
//| Copyright 2003-2012 Sergey Bochkanov (ALGLIB project) |
|
|
//| Copyright 2012-2017, MetaQuotes Software Corp. |
|
|
//| https://www.mql5.com |
|
|
//+------------------------------------------------------------------+
|
|
//| Implementation of ALGLIB library in MetaQuotes Language 5 |
|
|
//| |
|
|
//| The features of the library include: |
|
|
//| - Linear algebra (direct algorithms, EVD, SVD) |
|
|
//| - Solving systems of linear and non-linear equations |
|
|
//| - Interpolation |
|
|
//| - Optimization |
|
|
//| - FFT (Fast Fourier Transform) |
|
|
//| - Numerical integration |
|
|
//| - Linear and nonlinear least-squares fitting |
|
|
//| - Ordinary differential equations |
|
|
//| - Computation of special functions |
|
|
//| - Descriptive statistics and hypothesis testing |
|
|
//| - Data analysis - classification, regression |
|
|
//| - Implementing linear algebra algorithms, interpolation, etc. |
|
|
//| in high-precision arithmetic (using MPFR) |
|
|
//| |
|
|
//| This file 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 (www.fsf.org); either |
|
|
//| version 2 of the License, or (at your option) any later version. |
|
|
//| |
|
|
//| 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 General Public License for more details. |
|
|
//+------------------------------------------------------------------+
|
|
#include "matrix.mqh"
|
|
#include "ap.mqh"
|
|
#include "alglibinternal.mqh"
|
|
#include "linalg.mqh"
|
|
//+------------------------------------------------------------------+
|
|
//| This object stores state of the nonlinear CG optimizer. |
|
|
//| You should use ALGLIB functions to work with this object. |
|
|
//+------------------------------------------------------------------+
|
|
class CMinCGState
|
|
{
|
|
public:
|
|
//--- variables
|
|
int m_n;
|
|
double m_epsg;
|
|
double m_epsf;
|
|
double m_epsx;
|
|
int m_maxits;
|
|
double m_stpmax;
|
|
double m_suggestedstep;
|
|
bool m_xrep;
|
|
bool m_drep;
|
|
int m_cgtype;
|
|
int m_prectype;
|
|
int m_vcnt;
|
|
double m_diffstep;
|
|
int m_nfev;
|
|
int m_mcstage;
|
|
int m_k;
|
|
double m_fold;
|
|
double m_stp;
|
|
double m_curstpmax;
|
|
double m_laststep;
|
|
double m_lastscaledstep;
|
|
int m_mcinfo;
|
|
bool m_innerresetneeded;
|
|
bool m_terminationneeded;
|
|
double m_trimthreshold;
|
|
int m_rstimer;
|
|
double m_f;
|
|
bool m_needf;
|
|
bool m_needfg;
|
|
bool m_xupdated;
|
|
bool m_algpowerup;
|
|
bool m_lsstart;
|
|
bool m_lsend;
|
|
RCommState m_rstate;
|
|
int m_repiterationscount;
|
|
int m_repnfev;
|
|
int m_repterminationtype;
|
|
int m_debugrestartscount;
|
|
CLinMinState m_lstate;
|
|
double m_fbase;
|
|
double m_fm2;
|
|
double m_fm1;
|
|
double m_fp1;
|
|
double m_fp2;
|
|
double m_betahs;
|
|
double m_betady;
|
|
//--- arrays
|
|
double m_xk[];
|
|
double m_dk[];
|
|
double m_xn[];
|
|
double m_dn[];
|
|
double m_d[];
|
|
double m_x[];
|
|
double m_yk[];
|
|
double m_s[];
|
|
double m_g[];
|
|
double m_diagh[];
|
|
double m_diaghl2[];
|
|
double m_work0[];
|
|
double m_work1[];
|
|
//--- matrix
|
|
CMatrixDouble m_vcorr;
|
|
//--- constructor, destructor
|
|
CMinCGState(void);
|
|
~CMinCGState(void);
|
|
//--- copy
|
|
void Copy(CMinCGState &obj);
|
|
};
|
|
//+------------------------------------------------------------------+
|
|
//| Constructor without parameters |
|
|
//+------------------------------------------------------------------+
|
|
CMinCGState::CMinCGState(void)
|
|
{
|
|
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Destructor |
|
|
//+------------------------------------------------------------------+
|
|
CMinCGState::~CMinCGState(void)
|
|
{
|
|
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Copy |
|
|
//+------------------------------------------------------------------+
|
|
void CMinCGState::Copy(CMinCGState &obj)
|
|
{
|
|
//--- copy variables
|
|
m_n=obj.m_n;
|
|
m_epsg=obj.m_epsg;
|
|
m_epsf=obj.m_epsf;
|
|
m_epsx=obj.m_epsx;
|
|
m_maxits=obj.m_maxits;
|
|
m_stpmax=obj.m_stpmax;
|
|
m_suggestedstep=obj.m_suggestedstep;
|
|
m_xrep=obj.m_xrep;
|
|
m_drep=obj.m_drep;
|
|
m_cgtype=obj.m_cgtype;
|
|
m_prectype=obj.m_prectype;
|
|
m_vcnt=obj.m_vcnt;
|
|
m_diffstep=obj.m_diffstep;
|
|
m_nfev=obj.m_nfev;
|
|
m_mcstage=obj.m_mcstage;
|
|
m_k=obj.m_k;
|
|
m_fold=obj.m_fold;
|
|
m_stp=obj.m_stp;
|
|
m_curstpmax=obj.m_curstpmax;
|
|
m_laststep=obj.m_laststep;
|
|
m_lastscaledstep=obj.m_lastscaledstep;
|
|
m_mcinfo=obj.m_mcinfo;
|
|
m_innerresetneeded=obj.m_innerresetneeded;
|
|
m_terminationneeded=obj.m_terminationneeded;
|
|
m_trimthreshold=obj.m_trimthreshold;
|
|
m_rstimer=obj.m_rstimer;
|
|
m_f=obj.m_f;
|
|
m_needf=obj.m_needf;
|
|
m_needfg=obj.m_needfg;
|
|
m_xupdated=obj.m_xupdated;
|
|
m_algpowerup=obj.m_algpowerup;
|
|
m_lsstart=obj.m_lsstart;
|
|
m_lsend=obj.m_lsend;
|
|
m_repiterationscount=obj.m_repiterationscount;
|
|
m_repnfev=obj.m_repnfev;
|
|
m_repterminationtype=obj.m_repterminationtype;
|
|
m_debugrestartscount=obj.m_debugrestartscount;
|
|
m_fbase=obj.m_fbase;
|
|
m_fm2=obj.m_fm2;
|
|
m_fm1=obj.m_fm1;
|
|
m_fp1=obj.m_fp1;
|
|
m_fp2=obj.m_fp2;
|
|
m_betahs=obj.m_betahs;
|
|
m_betady=obj.m_betady;
|
|
m_rstate.Copy(obj.m_rstate);
|
|
m_lstate.Copy(obj.m_lstate);
|
|
//--- copy arrays
|
|
ArrayCopy(m_xk,obj.m_xk);
|
|
ArrayCopy(m_dk,obj.m_dk);
|
|
ArrayCopy(m_xn,obj.m_xn);
|
|
ArrayCopy(m_dn,obj.m_dn);
|
|
ArrayCopy(m_d,obj.m_d);
|
|
ArrayCopy(m_x,obj.m_x);
|
|
ArrayCopy(m_yk,obj.m_yk);
|
|
ArrayCopy(m_s,obj.m_s);
|
|
ArrayCopy(m_g,obj.m_g);
|
|
ArrayCopy(m_diagh,obj.m_diagh);
|
|
ArrayCopy(m_diaghl2,obj.m_diaghl2);
|
|
ArrayCopy(m_work0,obj.m_work0);
|
|
ArrayCopy(m_work1,obj.m_work1);
|
|
//--- matrix
|
|
m_vcorr=obj.m_vcorr;
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| This object stores state of the nonlinear CG optimizer. |
|
|
//| You should use ALGLIB functions to work with this object. |
|
|
//+------------------------------------------------------------------+
|
|
class CMinCGStateShell
|
|
{
|
|
private:
|
|
CMinCGState m_innerobj;
|
|
|
|
public:
|
|
//--- constructors, destructor
|
|
CMinCGStateShell(void);
|
|
CMinCGStateShell(CMinCGState &obj);
|
|
~CMinCGStateShell(void);
|
|
//--- methods
|
|
bool GetNeedF(void);
|
|
void SetNeedF(const bool b);
|
|
bool GetNeedFG(void);
|
|
void SetNeedFG(const bool b);
|
|
bool GetXUpdated(void);
|
|
void SetXUpdated(const bool b);
|
|
double GetF(void);
|
|
void SetF(const double d);
|
|
CMinCGState *GetInnerObj(void);
|
|
};
|
|
//+------------------------------------------------------------------+
|
|
//| Constructor without parameters |
|
|
//+------------------------------------------------------------------+
|
|
CMinCGStateShell::CMinCGStateShell(void)
|
|
{
|
|
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Copy constructor |
|
|
//+------------------------------------------------------------------+
|
|
CMinCGStateShell::CMinCGStateShell(CMinCGState &obj)
|
|
{
|
|
//--- copy
|
|
m_innerobj.Copy(obj);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Destructor |
|
|
//+------------------------------------------------------------------+
|
|
CMinCGStateShell::~CMinCGStateShell(void)
|
|
{
|
|
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Returns the value of the variable needf |
|
|
//+------------------------------------------------------------------+
|
|
bool CMinCGStateShell::GetNeedF(void)
|
|
{
|
|
//--- return result
|
|
return(m_innerobj.m_needf);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Changing the value of the variable needf |
|
|
//+------------------------------------------------------------------+
|
|
void CMinCGStateShell::SetNeedF(const bool b)
|
|
{
|
|
//--- change value
|
|
m_innerobj.m_needf=b;
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Returns the value of the variable needfg |
|
|
//+------------------------------------------------------------------+
|
|
bool CMinCGStateShell::GetNeedFG(void)
|
|
{
|
|
//--- return result
|
|
return(m_innerobj.m_needfg);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Changing the value of the variable needfg |
|
|
//+------------------------------------------------------------------+
|
|
void CMinCGStateShell::SetNeedFG(const bool b)
|
|
{
|
|
//--- change value
|
|
m_innerobj.m_needfg=b;
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Returns the value of the variable xupdated |
|
|
//+------------------------------------------------------------------+
|
|
bool CMinCGStateShell::GetXUpdated(void)
|
|
{
|
|
//--- return result
|
|
return(m_innerobj.m_xupdated);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Changing the value of the variable xupdated |
|
|
//+------------------------------------------------------------------+
|
|
void CMinCGStateShell::SetXUpdated(const bool b)
|
|
{
|
|
//--- change value
|
|
m_innerobj.m_xupdated=b;
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Returns the value of the variable f |
|
|
//+------------------------------------------------------------------+
|
|
double CMinCGStateShell::GetF(void)
|
|
{
|
|
//--- return result
|
|
return(m_innerobj.m_f);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Changing the value of the variable f |
|
|
//+------------------------------------------------------------------+
|
|
void CMinCGStateShell::SetF(const double d)
|
|
{
|
|
//--- change value
|
|
m_innerobj.m_f=d;
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Return object of class |
|
|
//+------------------------------------------------------------------+
|
|
CMinCGState *CMinCGStateShell::GetInnerObj(void)
|
|
{
|
|
//--- return result
|
|
return(GetPointer(m_innerobj));
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Auxiliary class for CMinCG |
|
|
//+------------------------------------------------------------------+
|
|
class CMinCGReport
|
|
{
|
|
public:
|
|
int m_iterationscount;
|
|
int m_nfev;
|
|
int m_terminationtype;
|
|
//--- constructor, destructor
|
|
CMinCGReport(void);
|
|
~CMinCGReport(void);
|
|
//--- copy
|
|
void Copy(CMinCGReport &obj);
|
|
};
|
|
//+------------------------------------------------------------------+
|
|
//| Constructor without parameters |
|
|
//+------------------------------------------------------------------+
|
|
CMinCGReport::CMinCGReport(void)
|
|
{
|
|
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Destructor |
|
|
//+------------------------------------------------------------------+
|
|
CMinCGReport::~CMinCGReport(void)
|
|
{
|
|
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Copy |
|
|
//+------------------------------------------------------------------+
|
|
void CMinCGReport::Copy(CMinCGReport &obj)
|
|
{
|
|
//--- copy variables
|
|
m_iterationscount=obj.m_iterationscount;
|
|
m_nfev=obj.m_nfev;
|
|
m_terminationtype=obj.m_terminationtype;
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| This class is a shell for class CMinCGReport |
|
|
//+------------------------------------------------------------------+
|
|
class CMinCGReportShell
|
|
{
|
|
private:
|
|
CMinCGReport m_innerobj;
|
|
public:
|
|
//--- constructors, destructor
|
|
CMinCGReportShell(void);
|
|
CMinCGReportShell(CMinCGReport &obj);
|
|
~CMinCGReportShell(void);
|
|
//--- methods
|
|
int GetIterationsCount(void);
|
|
void SetIterationsCount(const int i);
|
|
int GetNFev(void);
|
|
void SetNFev(const int i);
|
|
int GetTerminationType(void);
|
|
void SetTerminationType(const int i);
|
|
CMinCGReport *GetInnerObj(void);
|
|
};
|
|
//+------------------------------------------------------------------+
|
|
//| Constructor without parameters |
|
|
//+------------------------------------------------------------------+
|
|
CMinCGReportShell::CMinCGReportShell(void)
|
|
{
|
|
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Copy constructor |
|
|
//+------------------------------------------------------------------+
|
|
CMinCGReportShell::CMinCGReportShell(CMinCGReport &obj)
|
|
{
|
|
//--- copy
|
|
m_innerobj.Copy(obj);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Destructor |
|
|
//+------------------------------------------------------------------+
|
|
CMinCGReportShell::~CMinCGReportShell(void)
|
|
{
|
|
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Returns the value of the variable iterationscount |
|
|
//+------------------------------------------------------------------+
|
|
int CMinCGReportShell::GetIterationsCount(void)
|
|
{
|
|
//--- return result
|
|
return(m_innerobj.m_iterationscount);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Changing the value of the variable iterationscount |
|
|
//+------------------------------------------------------------------+
|
|
void CMinCGReportShell::SetIterationsCount(const int i)
|
|
{
|
|
//--- change value
|
|
m_innerobj.m_iterationscount=i;
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Returns the value of the variable nfev |
|
|
//+------------------------------------------------------------------+
|
|
int CMinCGReportShell::GetNFev(void)
|
|
{
|
|
//--- return result
|
|
return(m_innerobj.m_nfev);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Changing the value of the variable nfev |
|
|
//+------------------------------------------------------------------+
|
|
void CMinCGReportShell::SetNFev(const int i)
|
|
{
|
|
//--- change value
|
|
m_innerobj.m_nfev=i;
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Returns the value of the variable terminationtype |
|
|
//+------------------------------------------------------------------+
|
|
int CMinCGReportShell::GetTerminationType(void)
|
|
{
|
|
//--- return result
|
|
return(m_innerobj.m_terminationtype);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Changing the value of the variable terminationtype |
|
|
//+------------------------------------------------------------------+
|
|
void CMinCGReportShell::SetTerminationType(const int i)
|
|
{
|
|
//--- change value
|
|
m_innerobj.m_terminationtype=i;
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Return object of class |
|
|
//+------------------------------------------------------------------+
|
|
CMinCGReport *CMinCGReportShell::GetInnerObj(void)
|
|
{
|
|
//--- return result
|
|
return(GetPointer(m_innerobj));
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Conjugate gradient optimizer |
|
|
//+------------------------------------------------------------------+
|
|
class CMinCG
|
|
{
|
|
private:
|
|
//--- private methods
|
|
static void ClearRequestFields(CMinCGState &state);
|
|
static void PreconditionedMultiply(CMinCGState &state,double &x[],double &work0[],double &work1[]);
|
|
static double PreconditionedMultiply2(CMinCGState &state,double &x[],double &y[],double &work0[],double &work1[]);
|
|
static void MinCGInitInternal(const int n,const double diffstep,CMinCGState &state);
|
|
//--- auxiliary functions for MinCGIteration
|
|
static void Func_lbl_rcomm(CMinCGState &state,int n,int i,double betak,double v,double vv);
|
|
static bool Func_lbl_18(CMinCGState &state,int &n,int &i,double &betak,double &v,double &vv);
|
|
static bool Func_lbl_19(CMinCGState &state,int &n,int &i,double &betak,double &v,double &vv);
|
|
static bool Func_lbl_22(CMinCGState &state,int &n,int &i,double &betak,double &v,double &vv);
|
|
static bool Func_lbl_24(CMinCGState &state,int &n,int &i,double &betak,double &v,double &vv);
|
|
static bool Func_lbl_26(CMinCGState &state,int &n,int &i,double &betak,double &v,double &vv);
|
|
static bool Func_lbl_28(CMinCGState &state,int &n,int &i,double &betak,double &v,double &vv);
|
|
static bool Func_lbl_30(CMinCGState &state,int &n,int &i,double &betak,double &v,double &vv);
|
|
static bool Func_lbl_31(CMinCGState &state,int &n,int &i,double &betak,double &v,double &vv);
|
|
static bool Func_lbl_33(CMinCGState &state,int &n,int &i,double &betak,double &v,double &vv);
|
|
static bool Func_lbl_34(CMinCGState &state,int &n,int &i,double &betak,double &v,double &vv);
|
|
static bool Func_lbl_37(CMinCGState &state,int &n,int &i,double &betak,double &v,double &vv);
|
|
static bool Func_lbl_39(CMinCGState &state,int &n,int &i,double &betak,double &v,double &vv);
|
|
public:
|
|
//--- class constants
|
|
static const int m_rscountdownlen;
|
|
static const double m_gtol;
|
|
//--- constructor, destructor
|
|
CMinCG(void);
|
|
~CMinCG(void);
|
|
//--- public methods
|
|
static void MinCGCreate(const int n,double &x[],CMinCGState &state);
|
|
static void MinCGCreateF(const int n,double &x[],const double diffstep,CMinCGState &state);
|
|
static void MinCGSetCond(CMinCGState &state,const double epsg,const double epsf,double epsx,const int maxits);
|
|
static void MinCGSetScale(CMinCGState &state,double &s[]);
|
|
static void MinCGSetXRep(CMinCGState &state,const bool needxrep);
|
|
static void MinCGSetDRep(CMinCGState &state,const bool needdrep);
|
|
static void MinCGSetCGType(CMinCGState &state,int cgtype);
|
|
static void MinCGSetStpMax(CMinCGState &state,const double stpmax);
|
|
static void MinCGSuggestStep(CMinCGState &state,const double stp);
|
|
static void MinCGSetPrecDefault(CMinCGState &state);
|
|
static void MinCGSetPrecDiag(CMinCGState &state,double &d[]);
|
|
static void MinCGSetPrecScale(CMinCGState &state);
|
|
static void MinCGResults(CMinCGState &state,double &x[],CMinCGReport &rep);
|
|
static void MinCGResultsBuf(CMinCGState &state,double &x[],CMinCGReport &rep);
|
|
static void MinCGRestartFrom(CMinCGState &state,double &x[]);
|
|
static void MinCGSetPrecDiagFast(CMinCGState &state,double &d[]);
|
|
static void MinCGSetPrecLowRankFast(CMinCGState &state,double &d1[],double &c[],CMatrixDouble &v,const int vcnt);
|
|
static void MinCGSetPrecVarPart(CMinCGState &state,double &d2[]);
|
|
static bool MinCGIteration(CMinCGState &state);
|
|
};
|
|
//+------------------------------------------------------------------+
|
|
//| Initialize constants |
|
|
//+------------------------------------------------------------------+
|
|
const int CMinCG::m_rscountdownlen=10;
|
|
const double CMinCG::m_gtol=0.3;
|
|
//+------------------------------------------------------------------+
|
|
//| Constructor without parameters |
|
|
//+------------------------------------------------------------------+
|
|
CMinCG::CMinCG(void)
|
|
{
|
|
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Destructor |
|
|
//+------------------------------------------------------------------+
|
|
CMinCG::~CMinCG(void)
|
|
{
|
|
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| NONLINEAR CONJUGATE GRADIENT METHOD |
|
|
//| DESCRIPTION: |
|
|
//| The subroutine minimizes function F(x) of N arguments by using |
|
|
//| one of the nonlinear conjugate gradient methods. |
|
|
//| These CG methods are globally convergent (even on non-convex |
|
|
//| functions) as long as grad(f) is Lipschitz continuous in a some |
|
|
//| neighborhood of the L = { x : f(x)<=f(x0) }. |
|
|
//| REQUIREMENTS: |
|
|
//| Algorithm will request following information during its |
|
|
//| operation: |
|
|
//| * function value F and its gradient G (simultaneously) at given |
|
|
//| point X |
|
|
//| USAGE: |
|
|
//| 1. User initializes algorithm state with MinCGCreate() call |
|
|
//| 2. User tunes solver parameters with MinCGSetCond(), |
|
|
//| MinCGSetStpMax() and other functions |
|
|
//| 3. User calls MinCGOptimize() function which takes algorithm |
|
|
//| state and pointer (delegate, etc.) to callback function which |
|
|
//| calculates F/G. |
|
|
//| 4. User calls MinCGResults() to get solution |
|
|
//| 5. Optionally, user may call MinCGRestartFrom() to solve another |
|
|
//| problem with same N but another starting point and/or another |
|
|
//| function. MinCGRestartFrom() allows to reuse already |
|
|
//| initialized structure. |
|
|
//| INPUT PARAMETERS: |
|
|
//| N - problem dimension, N>0: |
|
|
//| * if given, only leading N elements of X are used|
|
|
//| * if not given, automatically determined from |
|
|
//| size of X |
|
|
//| X - starting point, array[0..N-1]. |
|
|
//| OUTPUT PARAMETERS: |
|
|
//| State - structure which stores algorithm state |
|
|
//+------------------------------------------------------------------+
|
|
static void CMinCG::MinCGCreate(const int n,double &x[],CMinCGState &state)
|
|
{
|
|
//--- check
|
|
if(!CAp::Assert(n>=1,__FUNCTION__+": N too small!"))
|
|
return;
|
|
//--- check
|
|
if(!CAp::Assert(CAp::Len(x)>=n,__FUNCTION__+": Length(X)<N!"))
|
|
return;
|
|
//--- check
|
|
if(!CAp::Assert(CApServ::IsFiniteVector(x,n),__FUNCTION__+": X contains infinite or NaN values!"))
|
|
return;
|
|
//--- function call
|
|
MinCGInitInternal(n,0.0,state);
|
|
//--- function call
|
|
MinCGRestartFrom(state,x);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| The subroutine is finite difference variant of MinCGCreate(). |
|
|
//| It uses finite differences in order to differentiate target |
|
|
//| function. |
|
|
//| Description below contains information which is specific to this |
|
|
//| function only. We recommend to read comments on MinCGCreate() in |
|
|
//| order to get more information about creation of CG optimizer. |
|
|
//| INPUT PARAMETERS: |
|
|
//| N - problem dimension, N>0: |
|
|
//| * if given, only leading N elements of X are |
|
|
//| used |
|
|
//| * if not given, automatically determined from |
|
|
//| size of X |
|
|
//| X - starting point, array[0..N-1]. |
|
|
//| DiffStep- differentiation step, >0 |
|
|
//| OUTPUT PARAMETERS: |
|
|
//| State - structure which stores algorithm state |
|
|
//| NOTES: |
|
|
//| 1. algorithm uses 4-point central formula for differentiation. |
|
|
//| 2. differentiation step along I-th axis is equal to |
|
|
//| DiffStep*S[I] where S[] is scaling vector which can be set by |
|
|
//| MinCGSetScale() call. |
|
|
//| 3. we recommend you to use moderate values of differentiation |
|
|
//| step. Too large step will result in too large truncation |
|
|
//| errors, while too small step will result in too large |
|
|
//| numerical errors. 1.0E-6 can be good value to start with. |
|
|
//| 4. Numerical differentiation is very inefficient - one gradient |
|
|
//| calculation needs 4*N function evaluations. This function will|
|
|
//| work for any N - either small (1...10), moderate (10...100) or|
|
|
//| large (100...). However, performance penalty will be too |
|
|
//| severe for any N's except for small ones. |
|
|
//| We should also say that code which relies on numerical |
|
|
//| differentiation is less robust and precise. L-BFGS needs |
|
|
//| exact gradient values. Imprecise gradient may slow down |
|
|
//| convergence, especially on highly nonlinear problems. |
|
|
//| Thus we recommend to use this function for fast prototyping |
|
|
//| on small- dimensional problems only, and to implement |
|
|
//| analytical gradient as soon as possible. |
|
|
//+------------------------------------------------------------------+
|
|
static void CMinCG::MinCGCreateF(const int n,double &x[],const double diffstep,
|
|
CMinCGState &state)
|
|
{
|
|
//--- check
|
|
if(!CAp::Assert(n>=1,__FUNCTION__+": N too small!"))
|
|
return;
|
|
//--- check
|
|
if(!CAp::Assert(CAp::Len(x)>=n,__FUNCTION__+": Length(X)<N!"))
|
|
return;
|
|
//--- check
|
|
if(!CAp::Assert(CApServ::IsFiniteVector(x,n),__FUNCTION__+": X contains infinite or NaN values!"))
|
|
return;
|
|
//--- check
|
|
if(!CAp::Assert(CMath::IsFinite(diffstep),__FUNCTION__+": DiffStep is infinite or NaN!"))
|
|
return;
|
|
//--- check
|
|
if(!CAp::Assert(diffstep>0.0,__FUNCTION__+": DiffStep is non-positive!"))
|
|
return;
|
|
//--- function call
|
|
MinCGInitInternal(n,diffstep,state);
|
|
//--- function call
|
|
MinCGRestartFrom(state,x);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| This function sets stopping conditions for CG optimization |
|
|
//| algorithm. |
|
|
//| INPUT PARAMETERS: |
|
|
//| State - structure which stores algorithm state |
|
|
//| EpsG - >=0 |
|
|
//| The subroutine finishes its work if the condition|
|
|
//| |v|<EpsG is satisfied, where: |
|
|
//| * |.| means Euclidian norm |
|
|
//| * v - scaled gradient vector, v[i]=g[i]*s[i] |
|
|
//| * g - gradient |
|
|
//| * s - scaling coefficients set by MinCGSetScale()|
|
|
//| EpsF - >=0 |
|
|
//| The subroutine finishes its work if on k+1-th |
|
|
//| iteration the condition |F(k+1)-F(k)| <= |
|
|
//| <= EpsF*max{|F(k)|,|F(k+1)|,1} is satisfied. |
|
|
//| EpsX - >=0 |
|
|
//| The subroutine finishes its work if on k+1-th |
|
|
//| iteration the condition |v|<=EpsX is fulfilled, |
|
|
//| where: |
|
|
//| * |.| means Euclidian norm |
|
|
//| * v - scaled step vector, v[i]=dx[i]/s[i] |
|
|
//| * dx - ste pvector, dx=X(k+1)-X(k) |
|
|
//| * s - scaling coefficients set by MinCGSetScale()|
|
|
//| MaxIts - maximum number of iterations. If MaxIts=0, the |
|
|
//| number of iterations is unlimited. |
|
|
//| Passing EpsG=0, EpsF=0, EpsX=0 and MaxIts=0 (simultaneously) will|
|
|
//| lead to automatic stopping criterion selection (small EpsX). |
|
|
//+------------------------------------------------------------------+
|
|
static void CMinCG::MinCGSetCond(CMinCGState &state,const double epsg,
|
|
const double epsf,double epsx,const int maxits)
|
|
{
|
|
//--- check
|
|
if(!CAp::Assert(CMath::IsFinite(epsg),__FUNCTION__+": EpsG is not finite number!"))
|
|
return;
|
|
//--- check
|
|
if(!CAp::Assert(epsg>=0.0,__FUNCTION__+": negative EpsG!"))
|
|
return;
|
|
//--- check
|
|
if(!CAp::Assert(CMath::IsFinite(epsf),__FUNCTION__+": EpsF is not finite number!"))
|
|
return;
|
|
//--- check
|
|
if(!CAp::Assert(epsf>=0.0,__FUNCTION__+": negative EpsF!"))
|
|
return;
|
|
//--- check
|
|
if(!CAp::Assert(CMath::IsFinite(epsx),__FUNCTION__+": EpsX is not finite number!"))
|
|
return;
|
|
//--- check
|
|
if(!CAp::Assert(epsx>=0.0,__FUNCTION__+": negative EpsX!"))
|
|
return;
|
|
//--- check
|
|
if(!CAp::Assert(maxits>=0,__FUNCTION__+": negative MaxIts!"))
|
|
return;
|
|
//--- check
|
|
if(epsg==0.0 && epsf==0.0 && epsx==0.0 && maxits==0)
|
|
epsx=1.0E-6;
|
|
//--- change values
|
|
state.m_epsg=epsg;
|
|
state.m_epsf=epsf;
|
|
state.m_epsx=epsx;
|
|
state.m_maxits=maxits;
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| This function sets scaling coefficients for CG optimizer. |
|
|
//| ALGLIB optimizers use scaling matrices to test stopping |
|
|
//| conditions (step size and gradient are scaled before comparison |
|
|
//| with tolerances). Scale of the I-th variable is a translation |
|
|
//| invariant measure of: |
|
|
//| a) "how large" the variable is |
|
|
//| b) how large the step should be to make significant changes in |
|
|
//| the function |
|
|
//| Scaling is also used by finite difference variant of CG |
|
|
//| optimizer - step along I-th axis is equal to DiffStep*S[I]. |
|
|
//| In most optimizers (and in the CG too) scaling is NOT a form of |
|
|
//| preconditioning. It just affects stopping conditions. You should |
|
|
//| set preconditioner by separate call to one of the |
|
|
//| MinCGSetPrec...() functions. |
|
|
//| There is special preconditioning mode, however, which uses |
|
|
//| scaling coefficients to form diagonal preconditioning matrix. |
|
|
//| You can turn this mode on, if you want. But you should understand|
|
|
//| that scaling is not the same thing as preconditioning - these are|
|
|
//| two different, although related forms of tuning solver. |
|
|
//| INPUT PARAMETERS: |
|
|
//| State - structure stores algorithm state |
|
|
//| S - array[N], non-zero scaling coefficients |
|
|
//| S[i] may be negative, sign doesn't matter. |
|
|
//+------------------------------------------------------------------+
|
|
static void CMinCG::MinCGSetScale(CMinCGState &state,double &s[])
|
|
{
|
|
//--- create a variable
|
|
int i=0;
|
|
//--- check
|
|
if(!CAp::Assert(CAp::Len(s)>=state.m_n,__FUNCTION__+": Length(S)<N"))
|
|
return;
|
|
for(i=0;i<=state.m_n-1;i++)
|
|
{
|
|
//--- check
|
|
if(!CAp::Assert(CMath::IsFinite(s[i]),__FUNCTION__+": S contains infinite or NAN elements"))
|
|
return;
|
|
//--- check
|
|
if(!CAp::Assert(s[i]!=0.0,__FUNCTION__+": S contains zero elements"))
|
|
return;
|
|
state.m_s[i]=MathAbs(s[i]);
|
|
}
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| This function turns on/off reporting. |
|
|
//| INPUT PARAMETERS: |
|
|
//| State - structure which stores algorithm state |
|
|
//| NeedXRep- whether iteration reports are needed or not |
|
|
//| If NeedXRep is True, algorithm will call rep() callback function |
|
|
//| if it is provided to MinCGOptimize(). |
|
|
//+------------------------------------------------------------------+
|
|
static void CMinCG::MinCGSetXRep(CMinCGState &state,const bool needxrep)
|
|
{
|
|
//--- change value
|
|
state.m_xrep=needxrep;
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| This function turns on/off line search reports. |
|
|
//| These reports are described in more details in developer-only |
|
|
//| comments on MinCGState object. |
|
|
//| INPUT PARAMETERS: |
|
|
//| State - structure which stores algorithm state |
|
|
//| NeedDRep- whether line search reports are needed or not |
|
|
//| This function is intended for private use only. Turning it on |
|
|
//| artificially may cause program failure. |
|
|
//+------------------------------------------------------------------+
|
|
static void CMinCG::MinCGSetDRep(CMinCGState &state,const bool needdrep)
|
|
{
|
|
//--- change value
|
|
state.m_drep=needdrep;
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| This function sets CG algorithm. |
|
|
//| INPUT PARAMETERS: |
|
|
//| State - structure which stores algorithm state |
|
|
//| CGType - algorithm type: |
|
|
//| * -1 automatic selection of the best |
|
|
//| algorithm |
|
|
//| * 0 DY (Dai and Yuan) algorithm |
|
|
//| * 1 Hybrid DY-HS algorithm |
|
|
//+------------------------------------------------------------------+
|
|
static void CMinCG::MinCGSetCGType(CMinCGState &state,int cgtype)
|
|
{
|
|
//--- check
|
|
if(!CAp::Assert(cgtype>=-1 && cgtype<=1,__FUNCTION__+": incorrect CGType!"))
|
|
return;
|
|
//--- check
|
|
if(cgtype==-1)
|
|
cgtype=1;
|
|
//--- change value
|
|
state.m_cgtype=cgtype;
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| This function sets maximum step length |
|
|
//| INPUT PARAMETERS: |
|
|
//| State - structure which stores algorithm state |
|
|
//| StpMax - maximum step length, >=0. Set StpMax to 0.0, if |
|
|
//| you don't want to limit step length. |
|
|
//| Use this subroutine when you optimize target function which |
|
|
//| contains exp() or other fast growing functions, and optimization |
|
|
//| algorithm makes too large steps which leads to overflow. This |
|
|
//| function allows us to reject steps that are too large (and |
|
|
//| therefore expose us to the possible overflow) without actually |
|
|
//| calculating function value at the x+stp*d. |
|
|
//+------------------------------------------------------------------+
|
|
static void CMinCG::MinCGSetStpMax(CMinCGState &state,const double stpmax)
|
|
{
|
|
//--- check
|
|
if(!CAp::Assert(CMath::IsFinite(stpmax),__FUNCTION__+": StpMax is not finite!"))
|
|
return;
|
|
//--- check
|
|
if(!CAp::Assert(stpmax>=0.0,__FUNCTION__+": StpMax<0!"))
|
|
return;
|
|
//--- change value
|
|
state.m_stpmax=stpmax;
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| This function allows to suggest initial step length to the CG |
|
|
//| algorithm. |
|
|
//| Suggested step length is used as starting point for the line |
|
|
//| search. It can be useful when you have badly scaled problem, i.e.|
|
|
//| when ||grad|| (which is used as initial estimate for the first |
|
|
//| step) is many orders of magnitude different from the desired |
|
|
//| step. |
|
|
//| Line search may fail on such problems without good estimate of |
|
|
//| initial step length. Imagine, for example, problem with |
|
|
//| ||grad||=10^50 and desired step equal to 0.1 Line search |
|
|
//| function will use 10^50 as initial step, then it will decrease |
|
|
//| step length by 2 (up to 20 attempts) and will get 10^44, which is|
|
|
//| still too large. |
|
|
//| This function allows us to tell than line search should be |
|
|
//| started from some moderate step length, like 1.0, so algorithm |
|
|
//| will be able to detect desired step length in a several searches.|
|
|
//| Default behavior (when no step is suggested) is to use |
|
|
//| preconditioner, if it is available, to generate initial estimate |
|
|
//| of step length. |
|
|
//| This function influences only first iteration of algorithm. It |
|
|
//| should be called between MinCGCreate/MinCGRestartFrom() call and |
|
|
//| MinCGOptimize call. Suggested step is ignored if you have |
|
|
//| preconditioner. |
|
|
//| INPUT PARAMETERS: |
|
|
//| State - structure used to store algorithm state. |
|
|
//| Stp - initial estimate of the step length. |
|
|
//| Can be zero (no estimate). |
|
|
//+------------------------------------------------------------------+
|
|
static void CMinCG::MinCGSuggestStep(CMinCGState &state,const double stp)
|
|
{
|
|
//--- check
|
|
if(!CAp::Assert(CMath::IsFinite(stp),__FUNCTION__+": Stp is infinite or NAN"))
|
|
return;
|
|
//--- check
|
|
if(!CAp::Assert(stp>=0.0,__FUNCTION__+": Stp<0"))
|
|
return;
|
|
//--- change value
|
|
state.m_suggestedstep=stp;
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Modification of the preconditioner: preconditioning is turned |
|
|
//| off. |
|
|
//| INPUT PARAMETERS: |
|
|
//| State - structure which stores algorithm state |
|
|
//| NOTE: you can change preconditioner "on the fly", during |
|
|
//| algorithm iterations. |
|
|
//+------------------------------------------------------------------+
|
|
static void CMinCG::MinCGSetPrecDefault(CMinCGState &state)
|
|
{
|
|
//--- change values
|
|
state.m_prectype=0;
|
|
state.m_innerresetneeded=true;
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Modification of the preconditioner: diagonal of approximate |
|
|
//| Hessian is used. |
|
|
//| INPUT PARAMETERS: |
|
|
//| State - structure which stores algorithm state |
|
|
//| D - diagonal of the approximate Hessian, |
|
|
//| array[0..N-1], (if larger, only leading N |
|
|
//| elements are used). |
|
|
//| NOTE: you can change preconditioner "on the fly", during |
|
|
//| algorithm iterations. |
|
|
//| NOTE 2: D[i] should be positive. Exception will be thrown |
|
|
//| otherwise. |
|
|
//| NOTE 3: you should pass diagonal of approximate Hessian - NOT |
|
|
//| ITS INVERSE. |
|
|
//+------------------------------------------------------------------+
|
|
static void CMinCG::MinCGSetPrecDiag(CMinCGState &state,double &d[])
|
|
{
|
|
//--- create a variable
|
|
int i=0;
|
|
//--- check
|
|
if(!CAp::Assert(CAp::Len(d)>=state.m_n,__FUNCTION__+": D is too short"))
|
|
return;
|
|
for(i=0;i<=state.m_n-1;i++)
|
|
{
|
|
//--- check
|
|
if(!CAp::Assert(CMath::IsFinite(d[i]),__FUNCTION__+": D contains infinite or NAN elements"))
|
|
return;
|
|
//--- check
|
|
if(!CAp::Assert((double)(d[i])>0.0,__FUNCTION__+": D contains non-positive elements"))
|
|
return;
|
|
}
|
|
//--- function call
|
|
MinCGSetPrecDiagFast(state,d);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Modification of the preconditioner: scale-based diagonal |
|
|
//| preconditioning. |
|
|
//| This preconditioning mode can be useful when you don't have |
|
|
//| approximate diagonal of Hessian, but you know that your variables|
|
|
//| are badly scaled (for example, one variable is in [1,10], and |
|
|
//| another in [1000,100000]), and most part of the ill-conditioning |
|
|
//| comes from different scales of vars. |
|
|
//| In this case simple scale-based preconditioner, |
|
|
//| with H[i] = 1/(s[i]^2), can greatly improve convergence. |
|
|
//| IMPRTANT: you should set scale of your variables with |
|
|
//| MinCGSetScale() call (before or after MinCGSetPrecScale() call). |
|
|
//| Without knowledge of the scale of your variables scale-based |
|
|
//| preconditioner will be just unit matrix. |
|
|
//| INPUT PARAMETERS: |
|
|
//| State - structure which stores algorithm state |
|
|
//| NOTE: you can change preconditioner "on the fly", during |
|
|
//| algorithm iterations. |
|
|
//+------------------------------------------------------------------+
|
|
static void CMinCG::MinCGSetPrecScale(CMinCGState &state)
|
|
{
|
|
//--- change values
|
|
state.m_prectype=3;
|
|
state.m_innerresetneeded=true;
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Conjugate gradient results |
|
|
//| INPUT PARAMETERS: |
|
|
//| State - algorithm state |
|
|
//| OUTPUT PARAMETERS: |
|
|
//| X - array[0..N-1], solution |
|
|
//| Rep - optimization report: |
|
|
//| * Rep.TerminationType completetion code: |
|
|
//| * 1 relative function improvement is no |
|
|
//| more than EpsF. |
|
|
//| * 2 relative step is no more than EpsX. |
|
|
//| * 4 gradient norm is no more than EpsG |
|
|
//| * 5 MaxIts steps was taken |
|
|
//| * 7 stopping conditions are too |
|
|
//| stringent, further improvement is |
|
|
//| impossible, we return best X found |
|
|
//| so far |
|
|
//| * 8 terminated by user |
|
|
//| * Rep.IterationsCount contains iterations count |
|
|
//| * NFEV countains number of function calculations |
|
|
//+------------------------------------------------------------------+
|
|
static void CMinCG::MinCGResults(CMinCGState &state,double &x[],CMinCGReport &rep)
|
|
{
|
|
//--- reset memory
|
|
ArrayResizeAL(x,0);
|
|
//--- function call
|
|
MinCGResultsBuf(state,x,rep);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Conjugate gradient results |
|
|
//| Buffered implementation of MinCGResults(), which uses |
|
|
//| pre-allocated buffer to store X[]. If buffer size is too small, |
|
|
//| it resizes buffer.It is intended to be used in the inner cycles |
|
|
//| of performance critical algorithms where array reallocation |
|
|
//| penalty is too large to be ignored. |
|
|
//+------------------------------------------------------------------+
|
|
static void CMinCG::MinCGResultsBuf(CMinCGState &state,double &x[],CMinCGReport &rep)
|
|
{
|
|
//--- create a variable
|
|
int i_=0;
|
|
//--- check
|
|
if(CAp::Len(x)<state.m_n)
|
|
ArrayResizeAL(x,state.m_n);
|
|
//--- copy
|
|
for(i_=0;i_<=state.m_n-1;i_++)
|
|
x[i_]=state.m_xn[i_];
|
|
//--- change values
|
|
rep.m_iterationscount=state.m_repiterationscount;
|
|
rep.m_nfev=state.m_repnfev;
|
|
rep.m_terminationtype=state.m_repterminationtype;
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| This subroutine restarts CG algorithm from new point. All |
|
|
//| optimization parameters are left unchanged. |
|
|
//| This function allows to solve multiple optimization problems |
|
|
//| (which must have same number of dimensions) without object |
|
|
//| reallocation penalty. |
|
|
//| INPUT PARAMETERS: |
|
|
//| State - structure used to store algorithm state. |
|
|
//| X - new starting point. |
|
|
//+------------------------------------------------------------------+
|
|
static void CMinCG::MinCGRestartFrom(CMinCGState &state,double &x[])
|
|
{
|
|
//--- create a variable
|
|
int i_=0;
|
|
//--- check
|
|
if(!CAp::Assert(CAp::Len(x)>=state.m_n,__FUNCTION__+": Length(X)<N!"))
|
|
return;
|
|
//--- check
|
|
if(!CAp::Assert(CApServ::IsFiniteVector(x,state.m_n),__FUNCTION__+": X contains infinite or NaN values!"))
|
|
return;
|
|
//--- copy
|
|
for(i_=0;i_<=state.m_n-1;i_++)
|
|
state.m_x[i_]=x[i_];
|
|
//--- function call
|
|
MinCGSuggestStep(state,0.0);
|
|
//--- allocation
|
|
ArrayResizeAL(state.m_rstate.ia,2);
|
|
ArrayResizeAL(state.m_rstate.ra,3);
|
|
//--- change value
|
|
state.m_rstate.stage=-1;
|
|
//--- function call
|
|
ClearRequestFields(state);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Faster version of MinCGSetPrecDiag(), for time-critical parts of |
|
|
//| code, without safety checks. |
|
|
//+------------------------------------------------------------------+
|
|
static void CMinCG::MinCGSetPrecDiagFast(CMinCGState &state,double &d[])
|
|
{
|
|
//--- create a variable
|
|
int i=0;
|
|
//--- function call
|
|
CApServ::RVectorSetLengthAtLeast(state.m_diagh,state.m_n);
|
|
//--- function call
|
|
CApServ::RVectorSetLengthAtLeast(state.m_diaghl2,state.m_n);
|
|
//--- change values
|
|
state.m_prectype=2;
|
|
state.m_vcnt=0;
|
|
state.m_innerresetneeded=true;
|
|
//--- copy
|
|
for(i=0;i<=state.m_n-1;i++)
|
|
{
|
|
state.m_diagh[i]=d[i];
|
|
state.m_diaghl2[i]=0.0;
|
|
}
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| This function sets low-rank preconditioner for Hessian matrix |
|
|
//| H=D+V'*C*V, where: |
|
|
//| * H is a Hessian matrix, which is approximated by D/V/C |
|
|
//| * D=D1+D2 is a diagonal matrix, which includes two positive |
|
|
//| definite terms: |
|
|
//| * constant term D1 (is not updated or infrequently updated) |
|
|
//| * variable term D2 (can be cheaply updated from iteration to |
|
|
//| iteration) |
|
|
//| * V is a low-rank correction |
|
|
//| * C is a diagonal factor of low-rank correction |
|
|
//| Preconditioner P is calculated using approximate Woodburry |
|
|
//| formula: |
|
|
//| P = D^(-1) - D^(-1)*V'*(C^(-1)+V*D1^(-1)*V')^(-1)*V*D^(-1) |
|
|
//| = D^(-1) - D^(-1)*VC'*VC*D^(-1), |
|
|
//| where |
|
|
//| VC = sqrt(B)*V |
|
|
//| B = (C^(-1)+V*D1^(-1)*V')^(-1) |
|
|
//| Note that B is calculated using constant term (D1) only, which |
|
|
//| allows us to update D2 without recalculation of B or VC. Such |
|
|
//| preconditioner is exact when D2 is zero. When D2 is non-zero, it |
|
|
//| is only approximation, but very good and cheap one. |
|
|
//| This function accepts D1, V, C. |
|
|
//| D2 is set to zero by default. |
|
|
//| Cost of this update is O(N*VCnt*VCnt), but D2 can be updated in |
|
|
//| just O(N) by MinCGSetPrecVarPart. |
|
|
//+------------------------------------------------------------------+
|
|
static void CMinCG::MinCGSetPrecLowRankFast(CMinCGState &state,double &d1[],
|
|
double &c[],CMatrixDouble &v,
|
|
const int vcnt)
|
|
{
|
|
//--- create variables
|
|
int i=0;
|
|
int i_=0;
|
|
int j=0;
|
|
int k=0;
|
|
int n=0;
|
|
double t=0;
|
|
//--- create matrix
|
|
CMatrixDouble b;
|
|
//--- check
|
|
if(vcnt==0)
|
|
{
|
|
//--- function call
|
|
MinCGSetPrecDiagFast(state,d1);
|
|
return;
|
|
}
|
|
//--- initialization
|
|
n=state.m_n;
|
|
b.Resize(vcnt,vcnt);
|
|
//--- function call
|
|
CApServ::RVectorSetLengthAtLeast(state.m_diagh,n);
|
|
//--- function call
|
|
CApServ::RVectorSetLengthAtLeast(state.m_diaghl2,n);
|
|
//--- function call
|
|
CApServ::RMatrixSetLengthAtLeast(state.m_vcorr,vcnt,n);
|
|
state.m_prectype=2;
|
|
state.m_vcnt=vcnt;
|
|
state.m_innerresetneeded=true;
|
|
//--- copy
|
|
for(i=0;i<=n-1;i++)
|
|
{
|
|
state.m_diagh[i]=d1[i];
|
|
state.m_diaghl2[i]=0.0;
|
|
}
|
|
//--- calculation
|
|
for(i=0;i<=vcnt-1;i++)
|
|
{
|
|
for(j=i;j<=vcnt-1;j++)
|
|
{
|
|
t=0;
|
|
for(k=0;k<=n-1;k++)
|
|
t=t+v[i][k]*v[j][k]/d1[k];
|
|
b[i].Set(j,t);
|
|
}
|
|
b[i].Set(i,b[i][i]+1.0/c[i]);
|
|
}
|
|
//--- check
|
|
if(!CTrFac::SPDMatrixCholeskyRec(b,0,vcnt,true,state.m_work0))
|
|
{
|
|
state.m_vcnt=0;
|
|
return;
|
|
}
|
|
//--- calculation
|
|
for(i=0;i<=vcnt-1;i++)
|
|
{
|
|
for(i_=0;i_<=n-1;i_++)
|
|
state.m_vcorr[i].Set(i_,v[i][i_]);
|
|
//--- change values
|
|
for(j=0;j<=i-1;j++)
|
|
{
|
|
t=b[j][i];
|
|
for(i_=0;i_<=n-1;i_++)
|
|
state.m_vcorr[i].Set(i_,state.m_vcorr[i][i_]-t*state.m_vcorr[j][i_]);
|
|
}
|
|
t=1/b[i][i];
|
|
//--- change values
|
|
for(i_=0;i_<=n-1;i_++)
|
|
state.m_vcorr[i].Set(i_,t*state.m_vcorr[i][i_]);
|
|
}
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| This function updates variable part (diagonal matrix D2) |
|
|
//| of low-rank preconditioner. |
|
|
//| This update is very cheap and takes just O(N) time. |
|
|
//| It has no effect with default preconditioner. |
|
|
//+------------------------------------------------------------------+
|
|
static void CMinCG::MinCGSetPrecVarPart(CMinCGState &state,double &d2[])
|
|
{
|
|
//--- create variables
|
|
int i=0;
|
|
int n=0;
|
|
//--- initialization
|
|
n=state.m_n;
|
|
//--- copy
|
|
for(i=0;i<=n-1;i++)
|
|
state.m_diaghl2[i]=d2[i];
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Clears request fileds (to be sure that we don't forgot to clear |
|
|
//| something) |
|
|
//+------------------------------------------------------------------+
|
|
static void CMinCG::ClearRequestFields(CMinCGState &state)
|
|
{
|
|
//--- change values
|
|
state.m_needf=false;
|
|
state.m_needfg=false;
|
|
state.m_xupdated=false;
|
|
state.m_lsstart=false;
|
|
state.m_lsend=false;
|
|
state.m_algpowerup=false;
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| This function calculates preconditioned product H^(-1)*x and |
|
|
//| stores result back into X. Work0[] and Work1[] are used as |
|
|
//| temporaries (size must be at least N; this function doesn't |
|
|
//| allocate arrays). |
|
|
//+------------------------------------------------------------------+
|
|
static void CMinCG::PreconditionedMultiply(CMinCGState &state,double &x[],
|
|
double &work0[],double &work1[])
|
|
{
|
|
//--- create variables
|
|
int i=0;
|
|
int n=0;
|
|
int vcnt=0;
|
|
double v=0;
|
|
int i_=0;
|
|
//--- initialization
|
|
n=state.m_n;
|
|
vcnt=state.m_vcnt;
|
|
//--- check
|
|
if(state.m_prectype==0)
|
|
return;
|
|
//--- check
|
|
if(state.m_prectype==3)
|
|
{
|
|
for(i=0;i<=n-1;i++)
|
|
x[i]=x[i]*state.m_s[i]*state.m_s[i];
|
|
//--- exit the function
|
|
return;
|
|
}
|
|
//--- check
|
|
if(!CAp::Assert(state.m_prectype==2,__FUNCTION__+": internal error (unexpected PrecType)"))
|
|
return;
|
|
//--- handle part common for VCnt=0 and VCnt<>0
|
|
for(i=0;i<=n-1;i++)
|
|
x[i]=x[i]/(state.m_diagh[i]+state.m_diaghl2[i]);
|
|
//--- if VCnt>0
|
|
if(vcnt>0)
|
|
{
|
|
//--- calculation work0
|
|
for(i=0;i<=vcnt-1;i++)
|
|
{
|
|
v=0.0;
|
|
for(i_=0;i_<=n-1;i_++)
|
|
v+=state.m_vcorr[i][i_]*x[i_];
|
|
work0[i]=v;
|
|
}
|
|
//--- calculation work1
|
|
for(i=0;i<=n-1;i++)
|
|
work1[i]=0;
|
|
for(i=0;i<=vcnt-1;i++)
|
|
{
|
|
v=work0[i];
|
|
for(i_=0;i_<=n-1;i_++)
|
|
state.m_work1[i_]=state.m_work1[i_]+v*state.m_vcorr[i][i_];
|
|
}
|
|
//--- change x
|
|
for(i=0;i<=n-1;i++)
|
|
x[i]=x[i]-state.m_work1[i]/(state.m_diagh[i]+state.m_diaghl2[i]);
|
|
}
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| This function calculates preconditioned product x'*H^(-1)*y. |
|
|
//| Work0[] and Work1[] are used as temporaries (size must be at |
|
|
//| least N; this function doesn't allocate arrays). |
|
|
//+------------------------------------------------------------------+
|
|
static double CMinCG::PreconditionedMultiply2(CMinCGState &state,double &x[],
|
|
double &y[],double &work0[],
|
|
double &work1[])
|
|
{
|
|
//--- create variables
|
|
double result=0;
|
|
int i=0;
|
|
int n=0;
|
|
int vcnt=0;
|
|
double v0=0;
|
|
double v1=0;
|
|
int i_=0;
|
|
//--- initialization
|
|
n=state.m_n;
|
|
vcnt=state.m_vcnt;
|
|
//--- no preconditioning
|
|
if(state.m_prectype==0)
|
|
{
|
|
v0=0.0;
|
|
for(i_=0;i_<=n-1;i_++)
|
|
v0+=x[i_]*y[i_];
|
|
//--- return result
|
|
return(v0);
|
|
}
|
|
//--- check
|
|
if(state.m_prectype==3)
|
|
{
|
|
result=0;
|
|
for(i=0;i<=n-1;i++)
|
|
result=result+x[i]*state.m_s[i]*state.m_s[i]*y[i];
|
|
//--- return result
|
|
return(result);
|
|
}
|
|
//--- check
|
|
if(!CAp::Assert(state.m_prectype==2,__FUNCTION__+": internal error (unexpected PrecType)"))
|
|
return(EMPTY_VALUE);
|
|
//--- low rank preconditioning
|
|
result=0.0;
|
|
for(i=0;i<=n-1;i++)
|
|
result=result+x[i]*y[i]/(state.m_diagh[i]+state.m_diaghl2[i]);
|
|
//--- check
|
|
if(vcnt>0)
|
|
{
|
|
//--- prepare arrays
|
|
for(i=0;i<=n-1;i++)
|
|
{
|
|
work0[i]=x[i]/(state.m_diagh[i]+state.m_diaghl2[i]);
|
|
work1[i]=y[i]/(state.m_diagh[i]+state.m_diaghl2[i]);
|
|
}
|
|
for(i=0;i<=vcnt-1;i++)
|
|
{
|
|
//--- calculation
|
|
v0=0.0;
|
|
for(i_=0;i_<=n-1;i_++)
|
|
v0+=work0[i_]*state.m_vcorr[i][i_];
|
|
v1=0.0;
|
|
for(i_=0;i_<=n-1;i_++)
|
|
v1+=work1[i_]*state.m_vcorr[i][i_];
|
|
//--- get result
|
|
result=result-v0*v1;
|
|
}
|
|
}
|
|
//--- return result
|
|
return(result);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Internal initialization subroutine |
|
|
//+------------------------------------------------------------------+
|
|
static void CMinCG::MinCGInitInternal(const int n,const double diffstep,
|
|
CMinCGState &state)
|
|
{
|
|
//--- create a variable
|
|
int i=0;
|
|
//--- initialization
|
|
state.m_n=n;
|
|
state.m_diffstep=diffstep;
|
|
//--- function call
|
|
MinCGSetCond(state,0,0,0,0);
|
|
//--- function call
|
|
MinCGSetXRep(state,false);
|
|
//--- function call
|
|
MinCGSetDRep(state,false);
|
|
//--- function call
|
|
MinCGSetStpMax(state,0);
|
|
//--- function call
|
|
MinCGSetCGType(state,-1);
|
|
//--- function call
|
|
MinCGSetPrecDefault(state);
|
|
//--- allocation
|
|
ArrayResizeAL(state.m_xk,n);
|
|
ArrayResizeAL(state.m_dk,n);
|
|
ArrayResizeAL(state.m_xn,n);
|
|
ArrayResizeAL(state.m_dn,n);
|
|
ArrayResizeAL(state.m_x,n);
|
|
ArrayResizeAL(state.m_d,n);
|
|
ArrayResizeAL(state.m_g,n);
|
|
ArrayResizeAL(state.m_work0,n);
|
|
ArrayResizeAL(state.m_work1,n);
|
|
ArrayResizeAL(state.m_yk,n);
|
|
ArrayResizeAL(state.m_s,n);
|
|
//--- copy
|
|
for(i=0;i<=n-1;i++)
|
|
state.m_s[i]=1.0;
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| NOTES: |
|
|
//| 1. This function has two different implementations: one which |
|
|
//| uses exact (analytical) user-supplied gradient, and one which|
|
|
//| uses function value only and numerically differentiates |
|
|
//| function in order to obtain gradient. |
|
|
//| Depending on the specific function used to create optimizer |
|
|
//| object (either MinCGCreate() for analytical gradient or |
|
|
//| MinCGCreateF() for numerical differentiation) you should |
|
|
//| choose appropriate variant of MinCGOptimize() - one which |
|
|
//| accepts function AND gradient or one which accepts function |
|
|
//| ONLY. |
|
|
//| Be careful to choose variant of MinCGOptimize() which |
|
|
//| corresponds to your optimization scheme! Table below lists |
|
|
//| different combinations of callback (function/gradient) passed |
|
|
//| to MinCGOptimize() and specific function used to create |
|
|
//| optimizer. |
|
|
//| | USER PASSED TO MinCGOptimize() |
|
|
//| CREATED WITH | function only | function and gradient |
|
|
//| ------------------------------------------------------------ |
|
|
//| MinCGCreateF() | work FAIL |
|
|
//| MinCGCreate() | FAIL work |
|
|
//| Here "FAIL" denotes inappropriate combinations of optimizer |
|
|
//| creation function and MinCGOptimize() version. Attemps to use |
|
|
//| such combination (for example, to create optimizer with |
|
|
//| MinCGCreateF() and to pass gradient information to |
|
|
//| MinCGOptimize()) will lead to exception being thrown. Either |
|
|
//| you did not pass gradient when it WAS needed or you passed |
|
|
//| gradient when it was NOT needed. |
|
|
//+------------------------------------------------------------------+
|
|
static bool CMinCG::MinCGIteration(CMinCGState &state)
|
|
{
|
|
//--- create variables
|
|
int n=0;
|
|
int i=0;
|
|
double betak=0;
|
|
double v=0;
|
|
double vv=0;
|
|
int i_=0;
|
|
//--- This code initializes locals by:
|
|
//--- * random values determined during code
|
|
//--- generation - on first subroutine call
|
|
//--- * values from previous call - on subsequent calls
|
|
if(state.m_rstate.stage>=0)
|
|
{
|
|
//--- initialization
|
|
n=state.m_rstate.ia[0];
|
|
i=state.m_rstate.ia[1];
|
|
betak=state.m_rstate.ra[0];
|
|
v=state.m_rstate.ra[1];
|
|
vv=state.m_rstate.ra[2];
|
|
}
|
|
else
|
|
{
|
|
//--- initialization
|
|
n=-983;
|
|
i=-989;
|
|
betak=-834;
|
|
v=900;
|
|
vv=-287;
|
|
}
|
|
//--- check
|
|
if(state.m_rstate.stage==0)
|
|
{
|
|
//--- change value
|
|
state.m_needfg=false;
|
|
//--- function call, return result
|
|
return(Func_lbl_18(state,n,i,betak,v,vv));
|
|
}
|
|
//--- check
|
|
if(state.m_rstate.stage==1)
|
|
{
|
|
//--- change values
|
|
state.m_fbase=state.m_f;
|
|
i=0;
|
|
//--- function call, return result
|
|
return(Func_lbl_19(state,n,i,betak,v,vv));
|
|
}
|
|
//--- check
|
|
if(state.m_rstate.stage==2)
|
|
{
|
|
//--- change values
|
|
state.m_fm2=state.m_f;
|
|
state.m_x[i]=v-0.5*state.m_diffstep*state.m_s[i];
|
|
state.m_rstate.stage=3;
|
|
//--- Saving state
|
|
Func_lbl_rcomm(state,n,i,betak,v,vv);
|
|
return(true);
|
|
}
|
|
//--- check
|
|
if(state.m_rstate.stage==3)
|
|
{
|
|
//--- change values
|
|
state.m_fm1=state.m_f;
|
|
state.m_x[i]=v+0.5*state.m_diffstep*state.m_s[i];
|
|
state.m_rstate.stage=4;
|
|
//--- Saving state
|
|
Func_lbl_rcomm(state,n,i,betak,v,vv);
|
|
return(true);
|
|
}
|
|
//--- check
|
|
if(state.m_rstate.stage==4)
|
|
{
|
|
//--- change values
|
|
state.m_fp1=state.m_f;
|
|
state.m_x[i]=v+state.m_diffstep*state.m_s[i];
|
|
state.m_rstate.stage=5;
|
|
//--- Saving state
|
|
Func_lbl_rcomm(state,n,i,betak,v,vv);
|
|
return(true);
|
|
}
|
|
//--- check
|
|
if(state.m_rstate.stage==5)
|
|
{
|
|
//--- change values
|
|
state.m_fp2=state.m_f;
|
|
state.m_x[i]=v;
|
|
state.m_g[i]=(8*(state.m_fp1-state.m_fm1)-(state.m_fp2-state.m_fm2))/(6*state.m_diffstep*state.m_s[i]);
|
|
i=i+1;
|
|
//--- function call, return result
|
|
return(Func_lbl_19(state,n,i,betak,v,vv));
|
|
}
|
|
//--- check
|
|
if(state.m_rstate.stage==6)
|
|
{
|
|
//--- change value
|
|
state.m_algpowerup=false;
|
|
//--- function call, return result
|
|
return(Func_lbl_22(state,n,i,betak,v,vv));
|
|
}
|
|
//--- check
|
|
if(state.m_rstate.stage==7)
|
|
{
|
|
//--- change value
|
|
state.m_xupdated=false;
|
|
//--- function call, return result
|
|
return(Func_lbl_24(state,n,i,betak,v,vv));
|
|
}
|
|
//--- check
|
|
if(state.m_rstate.stage==8)
|
|
{
|
|
//--- change value
|
|
state.m_lsstart=false;
|
|
//--- function call, return result
|
|
return(Func_lbl_28(state,n,i,betak,v,vv));
|
|
}
|
|
//--- check
|
|
if(state.m_rstate.stage==9)
|
|
{
|
|
//--- change value
|
|
state.m_needfg=false;
|
|
//--- function call, return result
|
|
return(Func_lbl_33(state,n,i,betak,v,vv));
|
|
}
|
|
//--- check
|
|
if(state.m_rstate.stage==10)
|
|
{
|
|
//--- change values
|
|
state.m_fbase=state.m_f;
|
|
i=0;
|
|
//--- function call, return result
|
|
return(Func_lbl_34(state,n,i,betak,v,vv));
|
|
}
|
|
//--- check
|
|
if(state.m_rstate.stage==11)
|
|
{
|
|
//--- change values
|
|
state.m_fm2=state.m_f;
|
|
state.m_x[i]=v-0.5*state.m_diffstep*state.m_s[i];
|
|
state.m_rstate.stage=12;
|
|
//--- Saving state
|
|
Func_lbl_rcomm(state,n,i,betak,v,vv);
|
|
return(true);
|
|
}
|
|
//--- check
|
|
if(state.m_rstate.stage==12)
|
|
{
|
|
//--- change values
|
|
state.m_fm1=state.m_f;
|
|
state.m_x[i]=v+0.5*state.m_diffstep*state.m_s[i];
|
|
state.m_rstate.stage=13;
|
|
//--- Saving state
|
|
Func_lbl_rcomm(state,n,i,betak,v,vv);
|
|
return(true);
|
|
}
|
|
//--- check
|
|
if(state.m_rstate.stage==13)
|
|
{
|
|
//--- change values
|
|
state.m_fp1=state.m_f;
|
|
state.m_x[i]=v+state.m_diffstep*state.m_s[i];
|
|
state.m_rstate.stage=14;
|
|
//--- Saving state
|
|
Func_lbl_rcomm(state,n,i,betak,v,vv);
|
|
return(true);
|
|
}
|
|
//--- check
|
|
if(state.m_rstate.stage==14)
|
|
{
|
|
//--- change values
|
|
state.m_fp2=state.m_f;
|
|
state.m_x[i]=v;
|
|
state.m_g[i]=(8*(state.m_fp1-state.m_fm1)-(state.m_fp2-state.m_fm2))/(6*state.m_diffstep*state.m_s[i]);
|
|
i=i+1;
|
|
//--- function call, return result
|
|
return(Func_lbl_34(state,n,i,betak,v,vv));
|
|
}
|
|
//--- check
|
|
if(state.m_rstate.stage==15)
|
|
{
|
|
//--- change value
|
|
state.m_lsend=false;
|
|
//--- function call, return result
|
|
return(Func_lbl_37(state,n,i,betak,v,vv));
|
|
}
|
|
//--- check
|
|
if(state.m_rstate.stage==16)
|
|
{
|
|
//--- change value
|
|
state.m_xupdated=false;
|
|
//--- function call, return result
|
|
return(Func_lbl_39(state,n,i,betak,v,vv));
|
|
}
|
|
//--- Routine body
|
|
//--- Prepare
|
|
n=state.m_n;
|
|
state.m_repterminationtype=0;
|
|
state.m_repiterationscount=0;
|
|
state.m_repnfev=0;
|
|
state.m_debugrestartscount=0;
|
|
//--- Preparations continue:
|
|
//--- * set XK
|
|
//--- * calculate F/G
|
|
//--- * set DK to -G
|
|
//--- * powerup algo (it may change preconditioner)
|
|
//--- * apply preconditioner to DK
|
|
//--- * report update of X
|
|
//--- * check stopping conditions for G
|
|
for(i_=0;i_<=n-1;i_++)
|
|
state.m_xk[i_]=state.m_x[i_];
|
|
//--- change value
|
|
state.m_terminationneeded=false;
|
|
//--- function call
|
|
ClearRequestFields(state);
|
|
//--- check
|
|
if(state.m_diffstep!=0.0)
|
|
{
|
|
state.m_needf=true;
|
|
state.m_rstate.stage=1;
|
|
//--- Saving state
|
|
Func_lbl_rcomm(state,n,i,betak,v,vv);
|
|
//--- return result
|
|
return(true);
|
|
}
|
|
//--- change values
|
|
state.m_needfg=true;
|
|
state.m_rstate.stage=0;
|
|
//--- Saving state
|
|
Func_lbl_rcomm(state,n,i,betak,v,vv);
|
|
//--- return result
|
|
return(true);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Auxiliary function for MinCGIteration. Is a product to get rid of|
|
|
//| the operator unconditional jump goto. |
|
|
//+------------------------------------------------------------------+
|
|
static void CMinCG::Func_lbl_rcomm(CMinCGState &state,int n,int i,
|
|
double betak,double v,double vv)
|
|
{
|
|
//--- save
|
|
state.m_rstate.ia[0]=n;
|
|
state.m_rstate.ia[1]=i;
|
|
state.m_rstate.ra[0]=betak;
|
|
state.m_rstate.ra[1]=v;
|
|
state.m_rstate.ra[2]=vv;
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Auxiliary function for MinCGIteration. Is a product to get rid of|
|
|
//| the operator unconditional jump goto. |
|
|
//+------------------------------------------------------------------+
|
|
static bool CMinCG::Func_lbl_18(CMinCGState &state,int &n,int &i,
|
|
double &betak,double &v,double &vv)
|
|
{
|
|
//--- check
|
|
if(!state.m_drep)
|
|
return(Func_lbl_22(state,n,i,betak,v,vv));
|
|
//--- Report algorithm powerup (if needed)
|
|
ClearRequestFields(state);
|
|
//--- change values
|
|
state.m_algpowerup=true;
|
|
state.m_rstate.stage=6;
|
|
//--- Saving state
|
|
Func_lbl_rcomm(state,n,i,betak,v,vv);
|
|
//--- return result
|
|
return(true);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Auxiliary function for MinCGIteration. Is a product to get rid of|
|
|
//| the operator unconditional jump goto. |
|
|
//+------------------------------------------------------------------+
|
|
static bool CMinCG::Func_lbl_19(CMinCGState &state,int &n,int &i,
|
|
double &betak,double &v,double &vv)
|
|
{
|
|
//--- check
|
|
if(i>n-1)
|
|
{
|
|
state.m_f=state.m_fbase;
|
|
state.m_needf=false;
|
|
//--- function call, return result
|
|
return(Func_lbl_18(state,n,i,betak,v,vv));
|
|
}
|
|
//--- change values
|
|
v=state.m_x[i];
|
|
state.m_x[i]=v-state.m_diffstep*state.m_s[i];
|
|
state.m_rstate.stage=2;
|
|
//--- Saving state
|
|
Func_lbl_rcomm(state,n,i,betak,v,vv);
|
|
//--- return result
|
|
return(true);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Auxiliary function for MinCGIteration. Is a product to get rid of|
|
|
//| the operator unconditional jump goto. |
|
|
//+------------------------------------------------------------------+
|
|
static bool CMinCG::Func_lbl_22(CMinCGState &state,int &n,int &i,
|
|
double &betak,double &v,double &vv)
|
|
{
|
|
//--- function call
|
|
COptServ::TrimPrepare(state.m_f,state.m_trimthreshold);
|
|
for(int i_=0;i_<=n-1;i_++)
|
|
state.m_dk[i_]=-state.m_g[i_];
|
|
//--- function call
|
|
PreconditionedMultiply(state,state.m_dk,state.m_work0,state.m_work1);
|
|
//--- check
|
|
if(!state.m_xrep)
|
|
return(Func_lbl_24(state,n,i,betak,v,vv));
|
|
//--- function call
|
|
ClearRequestFields(state);
|
|
//--- change values
|
|
state.m_xupdated=true;
|
|
state.m_rstate.stage=7;
|
|
//--- Saving state
|
|
Func_lbl_rcomm(state,n,i,betak,v,vv);
|
|
//--- return result
|
|
return(true);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Auxiliary function for MinCGIteration. Is a product to get rid of|
|
|
//| the operator unconditional jump goto. |
|
|
//+------------------------------------------------------------------+
|
|
static bool CMinCG::Func_lbl_24(CMinCGState &state,int &n,int &i,
|
|
double &betak,double &v,double &vv)
|
|
{
|
|
//--- check
|
|
if(state.m_terminationneeded)
|
|
{
|
|
for(int i_=0;i_<=n-1;i_++)
|
|
state.m_xn[i_]=state.m_xk[i_];
|
|
state.m_repterminationtype=8;
|
|
//--- return result
|
|
return(false);
|
|
}
|
|
//--- change value
|
|
v=0;
|
|
for(i=0;i<=n-1;i++)
|
|
v=v+CMath::Sqr(state.m_g[i]*state.m_s[i]);
|
|
//--- check
|
|
if(MathSqrt(v)<=state.m_epsg)
|
|
{
|
|
for(int i_=0;i_<=n-1;i_++)
|
|
state.m_xn[i_]=state.m_xk[i_];
|
|
state.m_repterminationtype=4;
|
|
//--- return result
|
|
return(false);
|
|
}
|
|
//--- change values
|
|
state.m_repnfev=1;
|
|
state.m_k=0;
|
|
state.m_fold=state.m_f;
|
|
//--- Choose initial step.
|
|
//--- Apply preconditioner,if we have something other than default.
|
|
if(state.m_prectype==2||state.m_prectype==3)
|
|
{
|
|
//--- because we use preconditioner,step length must be equal
|
|
//--- to the norm of DK
|
|
v=0.0;
|
|
for(int i_=0;i_<=n-1;i_++)
|
|
v+=state.m_dk[i_]*state.m_dk[i_];
|
|
state.m_laststep=MathSqrt(v);
|
|
}
|
|
else
|
|
{
|
|
//--- No preconditioner is used,we try to use suggested step
|
|
if(state.m_suggestedstep>0.0)
|
|
state.m_laststep=state.m_suggestedstep;
|
|
else
|
|
{
|
|
//--- change value
|
|
v=0.0;
|
|
for(int i_=0;i_<=n-1;i_++)
|
|
v+=state.m_g[i_]*state.m_g[i_];
|
|
v=MathSqrt(v);
|
|
//--- check
|
|
if(state.m_stpmax==0.0)
|
|
state.m_laststep=MathMin(1.0/v,1);
|
|
else
|
|
state.m_laststep=MathMin(1.0/v,state.m_stpmax);
|
|
}
|
|
}
|
|
//--- Main cycle
|
|
state.m_rstimer=m_rscountdownlen;
|
|
//--- function call, return result
|
|
return(Func_lbl_26(state,n,i,betak,v,vv));
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Auxiliary function for MinCGIteration. Is a product to get rid of|
|
|
//| the operator unconditional jump goto. |
|
|
//+------------------------------------------------------------------+
|
|
static bool CMinCG::Func_lbl_26(CMinCGState &state,int &n,int &i,
|
|
double &betak,double &v,double &vv)
|
|
{
|
|
//--- * clear reset flag
|
|
//--- * clear termination flag
|
|
//--- * store G[k] for later calculation of Y[k]
|
|
//--- * prepare starting point and direction and step length for line search
|
|
state.m_innerresetneeded=false;
|
|
state.m_terminationneeded=false;
|
|
for(int i_=0;i_<=n-1;i_++)
|
|
state.m_yk[i_]=-state.m_g[i_];
|
|
for(int i_=0;i_<=n-1;i_++)
|
|
state.m_d[i_]=state.m_dk[i_];
|
|
for(int i_=0;i_<=n-1;i_++)
|
|
state.m_x[i_]=state.m_xk[i_];
|
|
//--- change values
|
|
state.m_mcstage=0;
|
|
state.m_stp=1.0;
|
|
//--- function call
|
|
CLinMin::LinMinNormalized(state.m_d,state.m_stp,n);
|
|
//--- check
|
|
if(state.m_laststep!=0.0)
|
|
state.m_stp=state.m_laststep;
|
|
state.m_curstpmax=state.m_stpmax;
|
|
//--- Report beginning of line search (if needed)
|
|
//--- Terminate algorithm,if user request was detected
|
|
if(!state.m_drep)
|
|
return(Func_lbl_28(state,n,i,betak,v,vv));
|
|
//--- function call
|
|
ClearRequestFields(state);
|
|
//--- change values
|
|
state.m_lsstart=true;
|
|
state.m_rstate.stage=8;
|
|
//--- Saving state
|
|
Func_lbl_rcomm(state,n,i,betak,v,vv);
|
|
//--- return result
|
|
return(true);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Auxiliary function for MinCGIteration. Is a product to get rid of|
|
|
//| the operator unconditional jump goto. |
|
|
//+------------------------------------------------------------------+
|
|
static bool CMinCG::Func_lbl_28(CMinCGState &state,int &n,int &i,
|
|
double &betak,double &v,double &vv)
|
|
{
|
|
//--- check
|
|
if(state.m_terminationneeded)
|
|
{
|
|
for(int i_=0;i_<=n-1;i_++)
|
|
state.m_xn[i_]=state.m_x[i_];
|
|
state.m_repterminationtype=8;
|
|
//--- return result
|
|
return(false);
|
|
}
|
|
//--- Minimization along D
|
|
CLinMin::MCSrch(n,state.m_x,state.m_f,state.m_g,state.m_d,state.m_stp,state.m_curstpmax,m_gtol,state.m_mcinfo,state.m_nfev,state.m_work0,state.m_lstate,state.m_mcstage);
|
|
//--- function call, return result
|
|
return(Func_lbl_30(state,n,i,betak,v,vv));
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Auxiliary function for MinCGIteration. Is a product to get rid of|
|
|
//| the operator unconditional jump goto. |
|
|
//+------------------------------------------------------------------+
|
|
static bool CMinCG::Func_lbl_30(CMinCGState &state,int &n,int &i,
|
|
double &betak,double &v,double &vv)
|
|
{
|
|
//--- check
|
|
if(state.m_mcstage==0)
|
|
return(Func_lbl_31(state,n,i,betak,v,vv));
|
|
//--- Calculate function/gradient using either
|
|
//--- analytical gradient supplied by user
|
|
//--- or finite difference approximation.
|
|
//--- "Trim" function in order to handle near-singularity points.
|
|
ClearRequestFields(state);
|
|
//--- check
|
|
if((double)(state.m_diffstep)!=0.0)
|
|
{
|
|
state.m_needf=true;
|
|
state.m_rstate.stage=10;
|
|
//--- Saving state
|
|
Func_lbl_rcomm(state,n,i,betak,v,vv);
|
|
//--- return result
|
|
return(true);
|
|
}
|
|
//--- change values
|
|
state.m_needfg=true;
|
|
state.m_rstate.stage=9;
|
|
//--- Saving state
|
|
Func_lbl_rcomm(state,n,i,betak,v,vv);
|
|
//--- return result
|
|
return(true);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Auxiliary function for MinCGIteration. Is a product to get rid of|
|
|
//| the operator unconditional jump goto. |
|
|
//+------------------------------------------------------------------+
|
|
static bool CMinCG::Func_lbl_31(CMinCGState &state,int &n,int &i,
|
|
double &betak,double &v,double &vv)
|
|
{
|
|
//--- * report end of line search
|
|
//--- * store current point to XN
|
|
//--- * report iteration
|
|
//--- * terminate algorithm if user request was detected
|
|
if(!state.m_drep)
|
|
return(Func_lbl_37(state,n,i,betak,v,vv));
|
|
//--- Report end of line search (if needed)
|
|
ClearRequestFields(state);
|
|
//--- change values
|
|
state.m_lsend=true;
|
|
state.m_rstate.stage=15;
|
|
//--- Saving state
|
|
Func_lbl_rcomm(state,n,i,betak,v,vv);
|
|
//--- return result
|
|
return(true);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Auxiliary function for MinCGIteration. Is a product to get rid of|
|
|
//| the operator unconditional jump goto. |
|
|
//+------------------------------------------------------------------+
|
|
static bool CMinCG::Func_lbl_33(CMinCGState &state,int &n,int &i,
|
|
double &betak,double &v,double &vv)
|
|
{
|
|
//--- function call
|
|
COptServ::TrimFunction(state.m_f,state.m_g,n,state.m_trimthreshold);
|
|
//--- Call MCSRCH again
|
|
CLinMin::MCSrch(n,state.m_x,state.m_f,state.m_g,state.m_d,state.m_stp,state.m_curstpmax,m_gtol,state.m_mcinfo,state.m_nfev,state.m_work0,state.m_lstate,state.m_mcstage);
|
|
//--- function call, return result
|
|
return(Func_lbl_30(state,n,i,betak,v,vv));
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Auxiliary function for MinCGIteration. Is a product to get rid of|
|
|
//| the operator unconditional jump goto. |
|
|
//+------------------------------------------------------------------+
|
|
static bool CMinCG::Func_lbl_34(CMinCGState &state,int &n,int &i,
|
|
double &betak,double &v,double &vv)
|
|
{
|
|
//--- check
|
|
if(i>n-1)
|
|
{
|
|
state.m_f=state.m_fbase;
|
|
state.m_needf=false;
|
|
//--- function call, return result
|
|
return(Func_lbl_33(state,n,i,betak,v,vv));
|
|
}
|
|
//--- change values
|
|
v=state.m_x[i];
|
|
state.m_x[i]=v-state.m_diffstep*state.m_s[i];
|
|
state.m_rstate.stage=11;
|
|
//--- Saving state
|
|
Func_lbl_rcomm(state,n,i,betak,v,vv);
|
|
//--- return result
|
|
return(true);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Auxiliary function for MinCGIteration. Is a product to get rid of|
|
|
//| the operator unconditional jump goto. |
|
|
//+------------------------------------------------------------------+
|
|
static bool CMinCG::Func_lbl_37(CMinCGState &state,int &n,int &i,
|
|
double &betak,double &v,double &vv)
|
|
{
|
|
//--- copy
|
|
for(int i_=0;i_<=n-1;i_++)
|
|
state.m_xn[i_]=state.m_x[i_];
|
|
//--- check
|
|
if(!state.m_xrep)
|
|
return(Func_lbl_39(state,n,i,betak,v,vv));
|
|
//--- function call
|
|
ClearRequestFields(state);
|
|
//--- change values
|
|
state.m_xupdated=true;
|
|
state.m_rstate.stage=16;
|
|
//--- Saving state
|
|
Func_lbl_rcomm(state,n,i,betak,v,vv);
|
|
//--- return result
|
|
return(true);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Auxiliary function for MinCGIteration. Is a product to get rid of|
|
|
//| the operator unconditional jump goto. |
|
|
//+------------------------------------------------------------------+
|
|
static bool CMinCG::Func_lbl_39(CMinCGState &state,int &n,int &i,
|
|
double &betak,double &v,double &vv)
|
|
{
|
|
//--- check
|
|
if(state.m_terminationneeded)
|
|
{
|
|
for(int i_=0;i_<=n-1;i_++)
|
|
state.m_xn[i_]=state.m_x[i_];
|
|
state.m_repterminationtype=8;
|
|
//--- return result
|
|
return(false);
|
|
}
|
|
//--- Line search is finished.
|
|
//--- * calculate BetaK
|
|
//--- * calculate DN
|
|
//--- * update timers
|
|
//--- * calculate step length
|
|
if(state.m_mcinfo==1 && !state.m_innerresetneeded)
|
|
{
|
|
//--- Standard Wolfe conditions hold
|
|
//--- Calculate Y[K] and D[K]'*Y[K]
|
|
for(int i_=0;i_<=n-1;i_++)
|
|
state.m_yk[i_]=state.m_yk[i_]+state.m_g[i_];
|
|
//--- change value
|
|
vv=0.0;
|
|
for(int i_=0;i_<=n-1;i_++)
|
|
vv+=state.m_yk[i_]*state.m_dk[i_];
|
|
//--- Calculate BetaK according to DY formula
|
|
v=PreconditionedMultiply2(state,state.m_g,state.m_g,state.m_work0,state.m_work1);
|
|
state.m_betady=v/vv;
|
|
//--- Calculate BetaK according to HS formula
|
|
v=PreconditionedMultiply2(state,state.m_g,state.m_yk,state.m_work0,state.m_work1);
|
|
state.m_betahs=v/vv;
|
|
//--- Choose BetaK
|
|
if(state.m_cgtype==0)
|
|
betak=state.m_betady;
|
|
//--- check
|
|
if(state.m_cgtype==1)
|
|
betak=MathMax(0,MathMin(state.m_betady,state.m_betahs));
|
|
}
|
|
else
|
|
{
|
|
//--- Something is wrong (may be function is too wild or too flat)
|
|
//--- or we just have to restart algo.
|
|
//--- We'll set BetaK=0,which will restart CG algorithm.
|
|
//--- We can stop later (during normal checks) if stopping conditions are met.
|
|
betak=0;
|
|
state.m_debugrestartscount=state.m_debugrestartscount+1;
|
|
}
|
|
//--- check
|
|
if(state.m_repiterationscount>0&&state.m_repiterationscount%(3+n)==0)
|
|
{
|
|
//--- clear Beta every N iterations
|
|
betak=0;
|
|
}
|
|
//--- check
|
|
if(state.m_mcinfo==1||state.m_mcinfo==5)
|
|
state.m_rstimer=m_rscountdownlen;
|
|
else
|
|
state.m_rstimer=state.m_rstimer-1;
|
|
for(int i_=0;i_<=n-1;i_++)
|
|
state.m_dn[i_]=-state.m_g[i_];
|
|
//--- function call
|
|
PreconditionedMultiply(state,state.m_dn,state.m_work0,state.m_work1);
|
|
for(int i_=0;i_<=n-1;i_++)
|
|
state.m_dn[i_]=state.m_dn[i_]+betak*state.m_dk[i_];
|
|
//--- change values
|
|
state.m_laststep=0;
|
|
state.m_lastscaledstep=0.0;
|
|
for(i=0;i<=n-1;i++)
|
|
{
|
|
state.m_laststep=state.m_laststep+CMath::Sqr(state.m_d[i]);
|
|
state.m_lastscaledstep=state.m_lastscaledstep+CMath::Sqr(state.m_d[i]/state.m_s[i]);
|
|
}
|
|
//--- change values
|
|
state.m_laststep=state.m_stp*MathSqrt(state.m_laststep);
|
|
state.m_lastscaledstep=state.m_stp*MathSqrt(state.m_lastscaledstep);
|
|
//--- Update information.
|
|
//--- Check stopping conditions.
|
|
state.m_repnfev=state.m_repnfev+state.m_nfev;
|
|
state.m_repiterationscount=state.m_repiterationscount+1;
|
|
//--- check
|
|
if(state.m_repiterationscount>=state.m_maxits&&state.m_maxits>0)
|
|
{
|
|
//--- Too many iterations
|
|
state.m_repterminationtype=5;
|
|
//--- return result
|
|
return(false);
|
|
}
|
|
//--- change value
|
|
v=0;
|
|
for(i=0;i<=n-1;i++)
|
|
v=v+CMath::Sqr(state.m_g[i]*state.m_s[i]);
|
|
//--- check
|
|
if(MathSqrt(v)<=state.m_epsg)
|
|
{
|
|
//--- Gradient is small enough
|
|
state.m_repterminationtype=4;
|
|
//--- return result
|
|
return(false);
|
|
}
|
|
//--- check
|
|
if(!state.m_innerresetneeded)
|
|
{
|
|
//--- These conditions are checked only when no inner reset was requested by user
|
|
if(state.m_fold-state.m_f<=state.m_epsf*MathMax(MathAbs(state.m_fold),MathMax(MathAbs(state.m_f),1.0)))
|
|
{
|
|
//--- F(k+1)-F(k) is small enough
|
|
state.m_repterminationtype=1;
|
|
//--- return result
|
|
return(false);
|
|
}
|
|
//--- check
|
|
if(state.m_lastscaledstep<=state.m_epsx)
|
|
{
|
|
//--- X(k+1)-X(k) is small enough
|
|
state.m_repterminationtype=2;
|
|
//--- return result
|
|
return(false);
|
|
}
|
|
}
|
|
//--- check
|
|
if(state.m_rstimer<=0)
|
|
{
|
|
//--- Too many subsequent restarts
|
|
state.m_repterminationtype=7;
|
|
//--- return result
|
|
return(false);
|
|
}
|
|
//--- Shift Xk/Dk,update other information
|
|
for(int i_=0;i_<=n-1;i_++)
|
|
state.m_xk[i_]=state.m_xn[i_];
|
|
for(int i_=0;i_<=n-1;i_++)
|
|
state.m_dk[i_]=state.m_dn[i_];
|
|
//--- change values
|
|
state.m_fold=state.m_f;
|
|
state.m_k=state.m_k+1;
|
|
//--- function call, return result
|
|
return(Func_lbl_26(state,n,i,betak,v,vv));
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| This object stores nonlinear optimizer state. |
|
|
//| You should use functions provided by MinBLEIC subpackage to work |
|
|
//| with this object |
|
|
//+------------------------------------------------------------------+
|
|
class CMinBLEICState
|
|
{
|
|
public:
|
|
//--- variables
|
|
int m_nmain;
|
|
int m_nslack;
|
|
double m_innerepsg;
|
|
double m_innerepsf;
|
|
double m_innerepsx;
|
|
double m_outerepsx;
|
|
double m_outerepsi;
|
|
int m_maxits;
|
|
bool m_xrep;
|
|
double m_stpmax;
|
|
double m_diffstep;
|
|
int m_prectype;
|
|
double m_f;
|
|
bool m_needf;
|
|
bool m_needfg;
|
|
bool m_xupdated;
|
|
RCommState m_rstate;
|
|
int m_repinneriterationscount;
|
|
int m_repouteriterationscount;
|
|
int m_repnfev;
|
|
int m_repterminationtype;
|
|
double m_repdebugeqerr;
|
|
double m_repdebugfs;
|
|
double m_repdebugff;
|
|
double m_repdebugdx;
|
|
int m_itsleft;
|
|
double m_trimthreshold;
|
|
int m_cecnt;
|
|
int m_cedim;
|
|
double m_v0;
|
|
double m_v1;
|
|
double m_v2;
|
|
double m_t;
|
|
double m_errfeas;
|
|
double m_gnorm;
|
|
double m_mpgnorm;
|
|
double m_mba;
|
|
int m_variabletofreeze;
|
|
double m_valuetofreeze;
|
|
double m_fbase;
|
|
double m_fm2;
|
|
double m_fm1;
|
|
double m_fp1;
|
|
double m_fp2;
|
|
double m_xm1;
|
|
double m_xp1;
|
|
CMinCGState m_cgstate;
|
|
CMinCGReport m_cgrep;
|
|
int m_optdim;
|
|
//--- arrays
|
|
double m_diaghoriginal[];
|
|
double m_diagh[];
|
|
double m_x[];
|
|
double m_g[];
|
|
double m_xcur[];
|
|
double m_xprev[];
|
|
double m_xstart[];
|
|
double m_xend[];
|
|
double m_lastg[];
|
|
int m_ct[];
|
|
double m_xe[];
|
|
bool m_hasbndl[];
|
|
bool m_hasbndu[];
|
|
double m_bndloriginal[];
|
|
double m_bnduoriginal[];
|
|
double m_bndleffective[];
|
|
double m_bndueffective[];
|
|
bool m_activeconstraints[];
|
|
double m_constrainedvalues[];
|
|
double m_transforms[];
|
|
double m_seffective[];
|
|
double m_soriginal[];
|
|
double m_w[];
|
|
double m_tmp0[];
|
|
double m_tmp1[];
|
|
double m_tmp2[];
|
|
double m_r[];
|
|
//--- matrix
|
|
CMatrixDouble m_ceoriginal;
|
|
CMatrixDouble m_ceeffective;
|
|
CMatrixDouble m_cecurrent;
|
|
CMatrixDouble m_lmmatrix;
|
|
//--- constructor, destructor
|
|
CMinBLEICState(void);
|
|
~CMinBLEICState(void);
|
|
//--- copy
|
|
void Copy(CMinBLEICState &obj);
|
|
};
|
|
//+------------------------------------------------------------------+
|
|
//| Constructor without parameters |
|
|
//+------------------------------------------------------------------+
|
|
CMinBLEICState::CMinBLEICState(void)
|
|
{
|
|
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Destructor |
|
|
//+------------------------------------------------------------------+
|
|
CMinBLEICState::~CMinBLEICState(void)
|
|
{
|
|
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Copy |
|
|
//+------------------------------------------------------------------+
|
|
void CMinBLEICState::Copy(CMinBLEICState &obj)
|
|
{
|
|
//--- copy variables
|
|
m_nmain=obj.m_nmain;
|
|
m_nslack=obj.m_nslack;
|
|
m_innerepsg=obj.m_innerepsg;
|
|
m_innerepsf=obj.m_innerepsf;
|
|
m_innerepsx=obj.m_innerepsx;
|
|
m_outerepsx=obj.m_outerepsx;
|
|
m_outerepsi=obj.m_outerepsi;
|
|
m_maxits=obj.m_maxits;
|
|
m_xrep=obj.m_xrep;
|
|
m_stpmax=obj.m_stpmax;
|
|
m_diffstep=obj.m_diffstep;
|
|
m_prectype=obj.m_prectype;
|
|
m_f=obj.m_f;
|
|
m_needf=obj.m_needf;
|
|
m_needfg=obj.m_needfg;
|
|
m_xupdated=obj.m_xupdated;
|
|
m_repinneriterationscount=obj.m_repinneriterationscount;
|
|
m_repouteriterationscount=obj.m_repouteriterationscount;
|
|
m_repnfev=obj.m_repnfev;
|
|
m_repterminationtype=obj.m_repterminationtype;
|
|
m_repdebugeqerr=obj.m_repdebugeqerr;
|
|
m_repdebugfs=obj.m_repdebugfs;
|
|
m_repdebugff=obj.m_repdebugff;
|
|
m_repdebugdx=obj.m_repdebugdx;
|
|
m_itsleft=obj.m_itsleft;
|
|
m_trimthreshold=obj.m_trimthreshold;
|
|
m_cecnt=obj.m_cecnt;
|
|
m_cedim=obj.m_cedim;
|
|
m_v0=obj.m_v0;
|
|
m_v1=obj.m_v1;
|
|
m_v2=obj.m_v2;
|
|
m_t=obj.m_t;
|
|
m_errfeas=obj.m_errfeas;
|
|
m_gnorm=obj.m_gnorm;
|
|
m_mpgnorm=obj.m_mpgnorm;
|
|
m_mba=obj.m_mba;
|
|
m_variabletofreeze=obj.m_variabletofreeze;
|
|
m_valuetofreeze=obj.m_valuetofreeze;
|
|
m_fbase=obj.m_fbase;
|
|
m_fm2=obj.m_fm2;
|
|
m_fm1=obj.m_fm1;
|
|
m_fp1=obj.m_fp1;
|
|
m_fp2=obj.m_fp2;
|
|
m_xm1=obj.m_xm1;
|
|
m_xp1=obj.m_xp1;
|
|
m_optdim=obj.m_optdim;
|
|
m_rstate.Copy(obj.m_rstate);
|
|
m_cgstate.Copy(obj.m_cgstate);
|
|
m_cgrep.Copy(obj.m_cgrep);
|
|
//--- copy arrays
|
|
ArrayCopy(m_diaghoriginal,obj.m_diaghoriginal);
|
|
ArrayCopy(m_diagh,obj.m_diagh);
|
|
ArrayCopy(m_x,obj.m_x);
|
|
ArrayCopy(m_g,obj.m_g);
|
|
ArrayCopy(m_xcur,obj.m_xcur);
|
|
ArrayCopy(m_xprev,obj.m_xprev);
|
|
ArrayCopy(m_xstart,obj.m_xstart);
|
|
ArrayCopy(m_xend,obj.m_xend);
|
|
ArrayCopy(m_lastg,obj.m_lastg);
|
|
ArrayCopy(m_ct,obj.m_ct);
|
|
ArrayCopy(m_xe,obj.m_xe);
|
|
ArrayCopy(m_hasbndl,obj.m_hasbndl);
|
|
ArrayCopy(m_hasbndu,obj.m_hasbndu);
|
|
ArrayCopy(m_bndloriginal,obj.m_bndloriginal);
|
|
ArrayCopy(m_bnduoriginal,obj.m_bnduoriginal);
|
|
ArrayCopy(m_bndleffective,obj.m_bndleffective);
|
|
ArrayCopy(m_bndueffective,obj.m_bndueffective);
|
|
ArrayCopy(m_activeconstraints,obj.m_activeconstraints);
|
|
ArrayCopy(m_constrainedvalues,obj.m_constrainedvalues);
|
|
ArrayCopy(m_transforms,obj.m_transforms);
|
|
ArrayCopy(m_seffective,obj.m_seffective);
|
|
ArrayCopy(m_soriginal,obj.m_soriginal);
|
|
ArrayCopy(m_w,obj.m_w);
|
|
ArrayCopy(m_tmp0,obj.m_tmp0);
|
|
ArrayCopy(m_tmp1,obj.m_tmp1);
|
|
ArrayCopy(m_tmp2,obj.m_tmp2);
|
|
ArrayCopy(m_r,obj.m_r);
|
|
//--- copy matrix
|
|
m_ceoriginal=obj.m_ceoriginal;
|
|
m_ceeffective=obj.m_ceeffective;
|
|
m_cecurrent=obj.m_cecurrent;
|
|
m_lmmatrix=obj.m_lmmatrix;
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| This object stores nonlinear optimizer state. |
|
|
//| You should use functions provided by MinBLEIC subpackage to work |
|
|
//| with this object |
|
|
//+------------------------------------------------------------------+
|
|
class CMinBLEICStateShell
|
|
{
|
|
private:
|
|
CMinBLEICState m_innerobj;
|
|
public:
|
|
//--- constructors, destructor
|
|
CMinBLEICStateShell(void);
|
|
CMinBLEICStateShell(CMinBLEICState &obj);
|
|
~CMinBLEICStateShell(void);
|
|
//--- methods
|
|
bool GetNeedF(void);
|
|
void SetNeedF(const bool b);
|
|
bool GetNeedFG(void);
|
|
void SetNeedFG(const bool b);
|
|
bool GetXUpdated(void);
|
|
void SetXUpdated(const bool b);
|
|
double GetF(void);
|
|
void SetF(const double d);
|
|
CMinBLEICState *GetInnerObj(void);
|
|
};
|
|
//+------------------------------------------------------------------+
|
|
//| Constructor without parameters |
|
|
//+------------------------------------------------------------------+
|
|
CMinBLEICStateShell::CMinBLEICStateShell(void)
|
|
{
|
|
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Copy constructor |
|
|
//+------------------------------------------------------------------+
|
|
CMinBLEICStateShell::CMinBLEICStateShell(CMinBLEICState &obj)
|
|
{
|
|
//--- copy
|
|
m_innerobj.Copy(obj);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Destructor |
|
|
//+------------------------------------------------------------------+
|
|
CMinBLEICStateShell::~CMinBLEICStateShell(void)
|
|
{
|
|
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Returns the value of the variable needf |
|
|
//+------------------------------------------------------------------+
|
|
bool CMinBLEICStateShell::GetNeedF(void)
|
|
{
|
|
//--- return result
|
|
return(m_innerobj.m_needf);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Changing the value of the variable needf |
|
|
//+------------------------------------------------------------------+
|
|
void CMinBLEICStateShell::SetNeedF(const bool b)
|
|
{
|
|
//--- change value
|
|
m_innerobj.m_needf=b;
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Returns the value of the variable needfg |
|
|
//+------------------------------------------------------------------+
|
|
bool CMinBLEICStateShell::GetNeedFG(void)
|
|
{
|
|
//--- return result
|
|
return(m_innerobj.m_needfg);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Changing the value of the variable needfg |
|
|
//+------------------------------------------------------------------+
|
|
void CMinBLEICStateShell::SetNeedFG(const bool b)
|
|
{
|
|
//--- change value
|
|
m_innerobj.m_needfg=b;
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Returns the value of the variable xupdated |
|
|
//+------------------------------------------------------------------+
|
|
bool CMinBLEICStateShell::GetXUpdated(void)
|
|
{
|
|
//--- return result
|
|
return(m_innerobj.m_xupdated);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Changing the value of the variable xupdated |
|
|
//+------------------------------------------------------------------+
|
|
void CMinBLEICStateShell::SetXUpdated(const bool b)
|
|
{
|
|
//--- change value
|
|
m_innerobj.m_xupdated=b;
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Returns the value of the variable f |
|
|
//+------------------------------------------------------------------+
|
|
double CMinBLEICStateShell::GetF(void)
|
|
{
|
|
//--- return result
|
|
return(m_innerobj.m_f);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Changing the value of the variable f |
|
|
//+------------------------------------------------------------------+
|
|
void CMinBLEICStateShell::SetF(const double d)
|
|
{
|
|
//--- change value
|
|
m_innerobj.m_f=d;
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Return object of class |
|
|
//+------------------------------------------------------------------+
|
|
CMinBLEICState *CMinBLEICStateShell::GetInnerObj(void)
|
|
{
|
|
//--- return result
|
|
return(GetPointer(m_innerobj));
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| This structure stores optimization report: |
|
|
//| * InnerIterationsCount number of inner iterations |
|
|
//| * OuterIterationsCount number of outer iterations |
|
|
//| * NFEV number of gradient evaluations |
|
|
//| * TerminationType termination type (see below) |
|
|
//| TERMINATION CODES |
|
|
//| TerminationType field contains completion code,which can be: |
|
|
//| -10 unsupported combination of algorithm settings: |
|
|
//| 1) StpMax is set to non-zero value, |
|
|
//| AND 2) non-default preconditioner is used. |
|
|
//| You can't use both features at the same moment, |
|
|
//| so you have to choose one of them (and to turn |
|
|
//| off another one). |
|
|
//| -3 inconsistent constraints. Feasible point is |
|
|
//| either nonexistent or too hard to find. Try to |
|
|
//| restart optimizer with better initial |
|
|
//| approximation |
|
|
//| 4 conditions on constraints are fulfilled |
|
|
//| with error less than or equal to EpsC |
|
|
//| 5 MaxIts steps was taken |
|
|
//| 7 stopping conditions are too stringent, |
|
|
//| further improvement is impossible, |
|
|
//| X contains best point found so far. |
|
|
//| ADDITIONAL FIELDS |
|
|
//| There are additional fields which can be used for debugging: |
|
|
//| * DebugEqErr error in the equality constraints |
|
|
//| (2-norm) |
|
|
//| * DebugFS f,calculated at projection of initial|
|
|
//| point to the feasible set |
|
|
//| * DebugFF f,calculated at the final point |
|
|
//| * DebugDX |X_start-X_final| |
|
|
//+------------------------------------------------------------------+
|
|
class CMinBLEICReport
|
|
{
|
|
public:
|
|
//--- variables
|
|
int m_inneriterationscount;
|
|
int m_outeriterationscount;
|
|
int m_nfev;
|
|
int m_terminationtype;
|
|
double m_debugeqerr;
|
|
double m_debugfs;
|
|
double m_debugff;
|
|
double m_debugdx;
|
|
//--- constructor, destructor
|
|
CMinBLEICReport(void);
|
|
~CMinBLEICReport(void);
|
|
//--- copy
|
|
void Copy(CMinBLEICReport &obj);
|
|
};
|
|
//+------------------------------------------------------------------+
|
|
//| Constructor without parameters |
|
|
//+------------------------------------------------------------------+
|
|
CMinBLEICReport::CMinBLEICReport(void)
|
|
{
|
|
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Destructor |
|
|
//+------------------------------------------------------------------+
|
|
CMinBLEICReport::~CMinBLEICReport(void)
|
|
{
|
|
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Copy |
|
|
//+------------------------------------------------------------------+
|
|
void CMinBLEICReport::Copy(CMinBLEICReport &obj)
|
|
{
|
|
//--- copy variables
|
|
m_inneriterationscount=obj.m_inneriterationscount;
|
|
m_outeriterationscount=obj.m_outeriterationscount;
|
|
m_nfev=obj.m_nfev;
|
|
m_terminationtype=obj.m_terminationtype;
|
|
m_debugeqerr=obj.m_debugeqerr;
|
|
m_debugfs=obj.m_debugfs;
|
|
m_debugff=obj.m_debugff;
|
|
m_debugdx=obj.m_debugdx;
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| This structure stores optimization report: |
|
|
//| * InnerIterationsCount number of inner iterations |
|
|
//| * OuterIterationsCount number of outer iterations |
|
|
//| * NFEV number of gradient evaluations |
|
|
//| * TerminationType termination type (see below) |
|
|
//| TERMINATION CODES |
|
|
//| TerminationType field contains completion code,which can be: |
|
|
//| -10 unsupported combination of algorithm settings: |
|
|
//| 1) StpMax is set to non-zero value, |
|
|
//| AND 2) non-default preconditioner is used. |
|
|
//| You can't use both features at the same moment, |
|
|
//| so you have to choose one of them (and to turn |
|
|
//| off another one). |
|
|
//| -3 inconsistent constraints. Feasible point is |
|
|
//| either nonexistent or too hard to find. Try to |
|
|
//| restart optimizer with better initial |
|
|
//| approximation |
|
|
//| 4 conditions on constraints are fulfilled |
|
|
//| with error less than or equal to EpsC |
|
|
//| 5 MaxIts steps was taken |
|
|
//| 7 stopping conditions are too stringent, |
|
|
//| further improvement is impossible, |
|
|
//| X contains best point found so far. |
|
|
//| ADDITIONAL FIELDS |
|
|
//| There are additional fields which can be used for debugging: |
|
|
//| * DebugEqErr error in the equality constraints |
|
|
//| (2-norm) |
|
|
//| * DebugFS f,calculated at projection of initial|
|
|
//| point to the feasible set |
|
|
//| * DebugFF f,calculated at the final point |
|
|
//| * DebugDX |X_start-X_final| |
|
|
//+------------------------------------------------------------------+
|
|
class CMinBLEICReportShell
|
|
{
|
|
private:
|
|
CMinBLEICReport m_innerobj;
|
|
public:
|
|
//--- constructors, destructor
|
|
CMinBLEICReportShell(void);
|
|
CMinBLEICReportShell(CMinBLEICReport &obj);
|
|
~CMinBLEICReportShell(void);
|
|
//--- methods
|
|
int GetInnerIterationsCount(void);
|
|
void SetInnerIterationsCount(const int i);
|
|
int GetOuterIterationsCount(void);
|
|
void SetOuterIterationsCount(const int i);
|
|
int GetNFev(void);
|
|
void SetNFev(const int i);
|
|
int GetTerminationType(void);
|
|
void SetTerminationType(const int i);
|
|
double GetDebugEqErr(void);
|
|
void SetDebugEqErr(const double d);
|
|
double GetDebugFS(void);
|
|
void SetDebugFS(const double d);
|
|
double GetDebugFF(void);
|
|
void SetDebugFF(const double d);
|
|
double GetDebugDX(void);
|
|
void SetDebugDX(const double d);
|
|
CMinBLEICReport *GetInnerObj(void);
|
|
};
|
|
//+------------------------------------------------------------------+
|
|
//| Constructor without parameters |
|
|
//+------------------------------------------------------------------+
|
|
CMinBLEICReportShell::CMinBLEICReportShell(void)
|
|
{
|
|
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Copy constructor |
|
|
//+------------------------------------------------------------------+
|
|
CMinBLEICReportShell::CMinBLEICReportShell(CMinBLEICReport &obj)
|
|
{
|
|
//--- copy
|
|
m_innerobj.Copy(obj);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Destructor |
|
|
//+------------------------------------------------------------------+
|
|
CMinBLEICReportShell::~CMinBLEICReportShell(void)
|
|
{
|
|
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Returns the value of the variable inneriterationscount |
|
|
//+------------------------------------------------------------------+
|
|
int CMinBLEICReportShell::GetInnerIterationsCount(void)
|
|
{
|
|
//--- return result
|
|
return(m_innerobj.m_inneriterationscount);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Changing the value of the variable inneriterationscount |
|
|
//+------------------------------------------------------------------+
|
|
void CMinBLEICReportShell::SetInnerIterationsCount(const int i)
|
|
{
|
|
//--- change value
|
|
m_innerobj.m_inneriterationscount=i;
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Returns the value of the variable outeriterationscount |
|
|
//+------------------------------------------------------------------+
|
|
int CMinBLEICReportShell::GetOuterIterationsCount(void)
|
|
{
|
|
//--- return result
|
|
return(m_innerobj.m_outeriterationscount);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Changing the value of the variable outeriterationscount |
|
|
//+------------------------------------------------------------------+
|
|
void CMinBLEICReportShell::SetOuterIterationsCount(const int i)
|
|
{
|
|
//--- change value
|
|
m_innerobj.m_outeriterationscount=i;
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Returns the value of the variable nfev |
|
|
//+------------------------------------------------------------------+
|
|
int CMinBLEICReportShell::GetNFev(void)
|
|
{
|
|
//--- return result
|
|
return(m_innerobj.m_nfev);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Changing the value of the variable nfev |
|
|
//+------------------------------------------------------------------+
|
|
void CMinBLEICReportShell::SetNFev(const int i)
|
|
{
|
|
//--- change value
|
|
m_innerobj.m_nfev=i;
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Returns the value of the variable terminationtype |
|
|
//+------------------------------------------------------------------+
|
|
int CMinBLEICReportShell::GetTerminationType(void)
|
|
{
|
|
//--- return result
|
|
return(m_innerobj.m_terminationtype);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Changing the value of the variable terminationtype |
|
|
//+------------------------------------------------------------------+
|
|
void CMinBLEICReportShell::SetTerminationType(const int i)
|
|
{
|
|
//--- change value
|
|
m_innerobj.m_terminationtype=i;
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Returns the value of the variable debugeqerr |
|
|
//+------------------------------------------------------------------+
|
|
double CMinBLEICReportShell::GetDebugEqErr(void)
|
|
{
|
|
//--- return result
|
|
return(m_innerobj.m_debugeqerr);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Changing the value of the variable debugeqerr |
|
|
//+------------------------------------------------------------------+
|
|
void CMinBLEICReportShell::SetDebugEqErr(const double d)
|
|
{
|
|
//--- change value
|
|
m_innerobj.m_debugeqerr=d;
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Returns the value of the variable debugfs |
|
|
//+------------------------------------------------------------------+
|
|
double CMinBLEICReportShell::GetDebugFS(void)
|
|
{
|
|
//--- return result
|
|
return(m_innerobj.m_debugfs);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Changing the value of the variable debugfs |
|
|
//+------------------------------------------------------------------+
|
|
void CMinBLEICReportShell::SetDebugFS(const double d)
|
|
{
|
|
//--- change value
|
|
m_innerobj.m_debugfs=d;
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Returns the value of the variable debugff |
|
|
//+------------------------------------------------------------------+
|
|
double CMinBLEICReportShell::GetDebugFF(void)
|
|
{
|
|
//--- return result
|
|
return(m_innerobj.m_debugff);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Changing the value of the variable debugff |
|
|
//+------------------------------------------------------------------+
|
|
void CMinBLEICReportShell::SetDebugFF(const double d)
|
|
{
|
|
//--- change value
|
|
m_innerobj.m_debugff=d;
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Returns the value of the variable debugdx |
|
|
//+------------------------------------------------------------------+
|
|
double CMinBLEICReportShell::GetDebugDX(void)
|
|
{
|
|
//--- return result
|
|
return(m_innerobj.m_debugdx);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Changing the value of the variable debugdx |
|
|
//+------------------------------------------------------------------+
|
|
void CMinBLEICReportShell::SetDebugDX(const double d)
|
|
{
|
|
//--- change value
|
|
m_innerobj.m_debugdx=d;
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Return object of class |
|
|
//+------------------------------------------------------------------+
|
|
CMinBLEICReport *CMinBLEICReportShell::GetInnerObj(void)
|
|
{
|
|
//--- return result
|
|
return(GetPointer(m_innerobj));
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Bound constrained optimization with additional linear equality |
|
|
//| and inequality constraints |
|
|
//+------------------------------------------------------------------+
|
|
class CMinBLEIC
|
|
{
|
|
private:
|
|
//--- private methods
|
|
static void ClearRequestFields(CMinBLEICState &state);
|
|
static void UnscalePoint(CMinBLEICState &state,double &xscaled[],double &xunscaled[]);
|
|
static void ProjectPointAndUnscale(CMinBLEICState &state,double &xscaled[],double &xunscaled[],double &rscaled[],double &rnorm2);
|
|
static void ScaleGradientAndExpand(CMinBLEICState &state,double &gunscaled[],double &gscaled[]);
|
|
static void ModifyTargetFunction(CMinBLEICState &state,double &x[],double &r[],const double rnorm2,double &f,double &g[],double &gnorm,double &mpgnorm);
|
|
static bool AdditionalCheckForConstraints(CMinBLEICState &state,double &x[]);
|
|
static void RebuildCEXE(CMinBLEICState &state);
|
|
static void MakeGradientProjection(CMinBLEICState &state,double &pg[]);
|
|
static bool PrepareConstraintMatrix(CMinBLEICState &state,double &x[],double &g[],double &px[],double &pg[]);
|
|
static void MinBLEICInitInternal(const int n,double &x[],const double diffstep,CMinBLEICState &state);
|
|
//--- auxiliary functions for MinBLEICIteration
|
|
static void Func_lbl_rcomm(CMinBLEICState &state,int nmain,int nslack,int m,int i,int j,bool b,double v,double vv);
|
|
static bool Func_lbl_14(CMinBLEICState &state,int &nmain,int &nslack,int &m,int &i,int &j,bool &b,double &v,double &vv);
|
|
static bool Func_lbl_15(CMinBLEICState &state,int &nmain,int &nslack,int &m,int &i,int &j,bool &b,double &v,double &vv);
|
|
static bool Func_lbl_16(CMinBLEICState &state,int &nmain,int &nslack,int &m,int &i,int &j,bool &b,double &v,double &vv);
|
|
static bool Func_lbl_17(CMinBLEICState &state,int &nmain,int &nslack,int &m,int &i,int &j,bool &b,double &v,double &vv);
|
|
static bool Func_lbl_18(CMinBLEICState &state,int &nmain,int &nslack,int &m,int &i,int &j,bool &b,double &v,double &vv);
|
|
static bool Func_lbl_19(CMinBLEICState &state,int &nmain,int &nslack,int &m,int &i,int &j,bool &b,double &v,double &vv);
|
|
static bool Func_lbl_22(CMinBLEICState &state,int &nmain,int &nslack,int &m,int &i,int &j,bool &b,double &v,double &vv);
|
|
static bool Func_lbl_23(CMinBLEICState &state,int &nmain,int &nslack,int &m,int &i,int &j,bool &b,double &v,double &vv);
|
|
static bool Func_lbl_31(CMinBLEICState &state,int &nmain,int &nslack,int &m,int &i,int &j,bool &b,double &v,double &vv);
|
|
public:
|
|
//--- class constants
|
|
static const double m_svdtol;
|
|
static const double m_maxouterits;
|
|
//--- constructor, destructor
|
|
CMinBLEIC(void);
|
|
~CMinBLEIC(void);
|
|
//--- public methods
|
|
static void MinBLEICCreate(const int n,double &x[],CMinBLEICState &state);
|
|
static void MinBLEICCreateF(const int n,double &x[],const double diffstep,CMinBLEICState &state);
|
|
static void MinBLEICSetBC(CMinBLEICState &state,double &bndl[],double &bndu[]);
|
|
static void MinBLEICSetLC(CMinBLEICState &state,CMatrixDouble &c,int &ct[],const int k);
|
|
static void MinBLEICSetInnerCond(CMinBLEICState &state,const double epsg,const double epsf,const double epsx);
|
|
static void MinBLEICSetOuterCond(CMinBLEICState &state,const double epsx,const double epsi);
|
|
static void MinBLEICSetScale(CMinBLEICState &state,double &s[]);
|
|
static void MinBLEICSetPrecDefault(CMinBLEICState &state);
|
|
static void MinBLEICSetPrecDiag(CMinBLEICState &state,double &d[]);
|
|
static void MinBLEICSetPrecScale(CMinBLEICState &state);
|
|
static void MinBLEICSetMaxIts(CMinBLEICState &state,const int maxits);
|
|
static void MinBLEICSetXRep(CMinBLEICState &state,const bool needxrep);
|
|
static void MinBLEICSetStpMax(CMinBLEICState &state,const double stpmax);
|
|
static void MinBLEICResults(CMinBLEICState &state,double &x[],CMinBLEICReport &rep);
|
|
static void MinBLEICResultsBuf(CMinBLEICState &state,double &x[],CMinBLEICReport &rep);
|
|
static void MinBLEICRestartFrom(CMinBLEICState &state,double &x[]);
|
|
static bool MinBLEICIteration(CMinBLEICState &state);
|
|
};
|
|
//+------------------------------------------------------------------+
|
|
//| Initialize constants |
|
|
//+------------------------------------------------------------------+
|
|
const double CMinBLEIC::m_svdtol=100;
|
|
const double CMinBLEIC::m_maxouterits=20;
|
|
//+------------------------------------------------------------------+
|
|
//| Constructor without parameters |
|
|
//+------------------------------------------------------------------+
|
|
CMinBLEIC::CMinBLEIC(void)
|
|
{
|
|
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Destructor |
|
|
//+------------------------------------------------------------------+
|
|
CMinBLEIC::~CMinBLEIC(void)
|
|
{
|
|
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| BOUND CONSTRAINED OPTIMIZATION |
|
|
//| WITH ADDITIONAL LINEAR EQUALITY AND INEQUALITY CONSTRAINTS|
|
|
//| DESCRIPTION: |
|
|
//| The subroutine minimizes function F(x) of N arguments subject to |
|
|
//| any combination of: |
|
|
//| * bound constraints |
|
|
//| * linear inequality constraints |
|
|
//| * linear equality constraints |
|
|
//| REQUIREMENTS: |
|
|
//| * user must provide function value and gradient |
|
|
//| * starting point X0 must be feasible or |
|
|
//| not too far away from the feasible set |
|
|
//| * grad(f) must be Lipschitz continuous on a level set: |
|
|
//| L = { x : f(x)<=f(x0) } |
|
|
//| * function must be defined everywhere on the feasible set F |
|
|
//| USAGE: |
|
|
//| Constrained optimization if far more complex than the |
|
|
//| unconstrained one. Here we give very brief outline of the BLEIC |
|
|
//| optimizer. We strongly recommend you to read examples in the |
|
|
//| ALGLIB Reference Manual and to read ALGLIB User Guide on |
|
|
//| optimization, which is available at |
|
|
//| http://www.alglib.net/optimization/ |
|
|
//| 1. User initializes algorithm state with MinBLEICCreate() call |
|
|
//| 2. USer adds boundary and/or linear constraints by calling |
|
|
//| MinBLEICSetBC() and MinBLEICSetLC() functions. |
|
|
//| 3. User sets stopping conditions for underlying unconstrained |
|
|
//| solver with MinBLEICSetInnerCond() call. |
|
|
//| This function controls accuracy of underlying optimization |
|
|
//| algorithm. |
|
|
//| 4. User sets stopping conditions for outer iteration by calling |
|
|
//| MinBLEICSetOuterCond() function. |
|
|
//| This function controls handling of boundary and inequality |
|
|
//| constraints. |
|
|
//| 5. Additionally, user may set limit on number of internal |
|
|
//| iterations by MinBLEICSetMaxIts() call. |
|
|
//| This function allows to prevent algorithm from looping |
|
|
//| forever. |
|
|
//| 6. User calls MinBLEICOptimize() function which takes algorithm |
|
|
//| state and pointer (delegate, etc.) to callback function |
|
|
//| which calculates F/G. |
|
|
//| 7. User calls MinBLEICResults() to get solution |
|
|
//| 8. Optionally user may call MinBLEICRestartFrom() to solve |
|
|
//| another problem with same N but another starting point. |
|
|
//| MinBLEICRestartFrom() allows to reuse already initialized |
|
|
//| structure. |
|
|
//| INPUT PARAMETERS: |
|
|
//| N - problem dimension, N>0: |
|
|
//| * if given, only leading N elements of X are |
|
|
//| used |
|
|
//| * if not given, automatically determined from |
|
|
//| size ofX |
|
|
//| X - starting point, array[N]: |
|
|
//| * it is better to set X to a feasible point |
|
|
//| * but X can be infeasible, in which case |
|
|
//| algorithm will try to find feasible point |
|
|
//| first, using X as initial approximation. |
|
|
//| OUTPUT PARAMETERS: |
|
|
//| State - structure stores algorithm state |
|
|
//+------------------------------------------------------------------+
|
|
static void CMinBLEIC::MinBLEICCreate(const int n,double &x[],CMinBLEICState &state)
|
|
{
|
|
//--- create matrix
|
|
CMatrixDouble c;
|
|
//--- create array
|
|
int ct[];
|
|
//--- check
|
|
if(!CAp::Assert(n>=1,__FUNCTION__+": N<1"))
|
|
return;
|
|
//--- check
|
|
if(!CAp::Assert(CAp::Len(x)>=n,__FUNCTION__+": Length(X)<N"))
|
|
return;
|
|
//--- check
|
|
if(!CAp::Assert(CApServ::IsFiniteVector(x,n),__FUNCTION__+": X contains infinite or NaN values!"))
|
|
return;
|
|
//--- function call
|
|
MinBLEICInitInternal(n,x,0.0,state);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| The subroutine is finite difference variant of MinBLEICCreate(). |
|
|
//| It uses finite differences in order to differentiate target |
|
|
//| function. |
|
|
//| Description below contains information which is specific to this |
|
|
//| function only. We recommend to read comments on MinBLEICCreate() |
|
|
//| in order to get more information about creation of BLEIC |
|
|
//| optimizer. |
|
|
//| INPUT PARAMETERS: |
|
|
//| N - problem dimension, N>0: |
|
|
//| * if given, only leading N elements of X are used|
|
|
//| * if not given, automatically determined from |
|
|
//| size of X |
|
|
//| X - starting point, array[0..N-1]. |
|
|
//| DiffStep- differentiation step, >0 |
|
|
//| OUTPUT PARAMETERS: |
|
|
//| State - structure which stores algorithm state |
|
|
//| NOTES: |
|
|
//| 1. algorithm uses 4-point central formula for differentiation. |
|
|
//| 2. differentiation step along I-th axis is equal to DiffStep*S[I]|
|
|
//| where S[] is scaling vector which can be set by |
|
|
//| MinBLEICSetScale() call. |
|
|
//| 3. we recommend you to use moderate values of differentiation |
|
|
//| step. Too large step will result in too large truncation |
|
|
//| errors, while too small step will result in too large |
|
|
//| numerical errors. 1.0E-6 can be good value to start with. |
|
|
//| 4. Numerical differentiation is very inefficient - one gradient |
|
|
//| calculation needs 4*N function evaluations. This function will|
|
|
//| work for any N - either small (1...10), moderate (10...100) or|
|
|
//| large (100...). However, performance penalty will be too |
|
|
//| severe for any N's except for small ones. |
|
|
//| We should also say that code which relies on numerical |
|
|
//| differentiation is less robust and precise. CG needs exact |
|
|
//| gradient values. Imprecise gradient may slow down convergence,|
|
|
//| especially on highly nonlinear problems. |
|
|
//| Thus we recommend to use this function for fast prototyping on|
|
|
//| small - dimensional problems only, and to implement analytical|
|
|
//| gradient as soon as possible. |
|
|
//+------------------------------------------------------------------+
|
|
static void CMinBLEIC::MinBLEICCreateF(const int n,double &x[],
|
|
const double diffstep,
|
|
CMinBLEICState &state)
|
|
{
|
|
//--- create matrix
|
|
CMatrixDouble c;
|
|
//--- create array
|
|
int ct[];
|
|
//--- check
|
|
if(!CAp::Assert(n>=1,__FUNCTION__+": N<1"))
|
|
return;
|
|
//--- check
|
|
if(!CAp::Assert(CAp::Len(x)>=n,__FUNCTION__+": Length(X)<N"))
|
|
return;
|
|
//--- check
|
|
if(!CAp::Assert(CApServ::IsFiniteVector(x,n),__FUNCTION__+": X contains infinite or NaN values!"))
|
|
return;
|
|
//--- check
|
|
if(!CAp::Assert(CMath::IsFinite(diffstep),__FUNCTION__+": DiffStep is infinite or NaN!"))
|
|
return;
|
|
//--- check
|
|
if(!CAp::Assert(diffstep>0.0,__FUNCTION__+": DiffStep is non-positive!"))
|
|
return;
|
|
//--- function call
|
|
MinBLEICInitInternal(n,x,diffstep,state);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| This function sets boundary constraints for BLEIC optimizer. |
|
|
//| Boundary constraints are inactive by default (after initial |
|
|
//| creation). They are preserved after algorithm restart with |
|
|
//| MinBLEICRestartFrom(). |
|
|
//| INPUT PARAMETERS: |
|
|
//| State - structure stores algorithm state |
|
|
//| BndL - lower bounds, array[N]. |
|
|
//| If some (all) variables are unbounded, you may |
|
|
//| specify very small number or -INF. |
|
|
//| BndU - upper bounds, array[N]. |
|
|
//| If some (all) variables are unbounded, you may |
|
|
//| specify very large number or +INF. |
|
|
//| NOTE 1: it is possible to specify BndL[i]=BndU[i]. In this case |
|
|
//| I-th variable will be "frozen" at X[i]=BndL[i]=BndU[i]. |
|
|
//| NOTE 2: this solver has following useful properties: |
|
|
//| * bound constraints are always satisfied exactly |
|
|
//| * function is evaluated only INSIDE area specified by bound |
|
|
//| constraints, even when numerical differentiation is used |
|
|
//| (algorithm adjusts nodes according to boundary constraints) |
|
|
//+------------------------------------------------------------------+
|
|
static void CMinBLEIC::MinBLEICSetBC(CMinBLEICState &state,double &bndl[],
|
|
double &bndu[])
|
|
{
|
|
//--- create variables
|
|
int i=0;
|
|
int n=0;
|
|
//--- initialization
|
|
n=state.m_nmain;
|
|
//--- check
|
|
if(!CAp::Assert(CAp::Len(bndl)>=n,__FUNCTION__+": Length(BndL)<N"))
|
|
return;
|
|
//--- check
|
|
if(!CAp::Assert(CAp::Len(bndu)>=n,__FUNCTION__+": Length(BndU)<N"))
|
|
return;
|
|
for(i=0;i<=n-1;i++)
|
|
{
|
|
//--- check
|
|
if(!CAp::Assert(CMath::IsFinite(bndl[i]) || CInfOrNaN::IsNegativeInfinity(bndl[i]),__FUNCTION__+": BndL contains NAN or +INF"))
|
|
return;
|
|
//--- check
|
|
if(!CAp::Assert(CMath::IsFinite(bndu[i]) || CInfOrNaN::IsPositiveInfinity(bndu[i]),__FUNCTION__+": BndU contains NAN or -INF"))
|
|
return;
|
|
//--- change values
|
|
state.m_bndloriginal[i]=bndl[i];
|
|
state.m_hasbndl[i]=CMath::IsFinite(bndl[i]);
|
|
state.m_bnduoriginal[i]=bndu[i];
|
|
state.m_hasbndu[i]=CMath::IsFinite(bndu[i]);
|
|
}
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| This function sets linear constraints for BLEIC optimizer. |
|
|
//| Linear constraints are inactive by default (after initial |
|
|
//| creation). They are preserved after algorithm restart with |
|
|
//| MinBLEICRestartFrom(). |
|
|
//| INPUT PARAMETERS: |
|
|
//| State - structure previously allocated with |
|
|
//| MinBLEICCreate call. |
|
|
//| C - linear constraints, array[K,N+1]. |
|
|
//| Each row of C represents one constraint, either |
|
|
//| equality or inequality (see below): |
|
|
//| * first N elements correspond to coefficients, |
|
|
//| * last element corresponds to the right part. |
|
|
//| All elements of C (including right part) must be |
|
|
//| finite. |
|
|
//| CT - type of constraints, array[K]: |
|
|
//| * if CT[i]>0, then I-th constraint is |
|
|
//| C[i,*]*x >= C[i,n+1] |
|
|
//| * if CT[i]=0, then I-th constraint is |
|
|
//| C[i,*]*x = C[i,n+1] |
|
|
//| * if CT[i]<0, then I-th constraint is |
|
|
//| C[i,*]*x <= C[i,n+1] |
|
|
//| K - number of equality/inequality constraints, K>=0: |
|
|
//| * if given, only leading K elements of C/CT are |
|
|
//| used |
|
|
//| * if not given, automatically determined from |
|
|
//| sizes of C/CT |
|
|
//| NOTE 1: linear (non-bound) constraints are satisfied only |
|
|
//| approximately: |
|
|
//| * there always exists some minor violation (about Epsilon in |
|
|
//| magnitude) due to rounding errors |
|
|
//| * numerical differentiation, if used, may lead to function |
|
|
//| evaluations outside of the feasible area, because algorithm |
|
|
//| does NOT change numerical differentiation formula according to |
|
|
//| linear constraints. |
|
|
//| If you want constraints to be satisfied exactly, try to |
|
|
//| reformulate your problem in such manner that all constraints will|
|
|
//| become boundary ones (this kind of constraints is always |
|
|
//| satisfied exactly, both in the final solution and in all |
|
|
//| intermediate points). |
|
|
//+------------------------------------------------------------------+
|
|
static void CMinBLEIC::MinBLEICSetLC(CMinBLEICState &state,CMatrixDouble &c,
|
|
int &ct[],const int k)
|
|
{
|
|
//--- create variables
|
|
int nmain=0;
|
|
int i=0;
|
|
int i_=0;
|
|
//--- initialization
|
|
nmain=state.m_nmain;
|
|
//--- First,check for errors in the inputs
|
|
if(!CAp::Assert(k>=0,__FUNCTION__+": K<0"))
|
|
return;
|
|
//--- check
|
|
if(!CAp::Assert(CAp::Cols(c)>=nmain+1||k==0,__FUNCTION__+": Cols(C)<N+1"))
|
|
return;
|
|
//--- check
|
|
if(!CAp::Assert(CAp::Rows(c)>=k,__FUNCTION__+": Rows(C)<K"))
|
|
return;
|
|
//--- check
|
|
if(!CAp::Assert(CAp::Len(ct)>=k,__FUNCTION__+": Length(CT)<K"))
|
|
return;
|
|
//--- check
|
|
if(!CAp::Assert(CApServ::IsFiniteMatrix(c,k,nmain+1),__FUNCTION__+": C contains infinite or NaN values!"))
|
|
return;
|
|
//--- Determine number of constraints,
|
|
//--- allocate space and copy
|
|
state.m_cecnt=k;
|
|
//--- function call
|
|
CApServ::RMatrixSetLengthAtLeast(state.m_ceoriginal,state.m_cecnt,nmain+1);
|
|
//--- function call
|
|
CApServ::IVectorSetLengthAtLeast(state.m_ct,state.m_cecnt);
|
|
//--- calculation
|
|
for(i=0;i<=k-1;i++)
|
|
{
|
|
state.m_ct[i]=ct[i];
|
|
for(i_=0;i_<=nmain;i_++)
|
|
state.m_ceoriginal[i].Set(i_,c[i][i_]);
|
|
}
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| This function sets stopping conditions for the underlying |
|
|
//| nonlinear CG optimizer. It controls overall accuracy of solution.|
|
|
//| These conditions should be strict enough in order for algorithm |
|
|
//| to converge. |
|
|
//| INPUT PARAMETERS: |
|
|
//| State - structure which stores algorithm state |
|
|
//| EpsG - >=0 |
|
|
//| The subroutine finishes its work if the condition|
|
|
//| |v|<EpsG is satisfied, where: |
|
|
//| * |.| means Euclidian norm |
|
|
//| * v - scaled gradient vector, v[i]=g[i]*s[i] |
|
|
//| * g - gradient |
|
|
//| * s - scaling coefficients set by |
|
|
//| MinBLEICSetScale() |
|
|
//| EpsF - >=0 |
|
|
//| The subroutine finishes its work if on k+1-th |
|
|
//| iteration the condition |F(k+1)-F(k)| <= |
|
|
//| <= EpsF*max{|F(k)|,|F(k+1)|,1} is satisfied. |
|
|
//| EpsX - >=0 |
|
|
//| The subroutine finishes its work if on k+1-th |
|
|
//| iteration the condition |v|<=EpsX is fulfilled, |
|
|
//| where: |
|
|
//| * |.| means Euclidian norm |
|
|
//| * v - scaled step vector, v[i]=dx[i]/s[i] |
|
|
//| * dx - ste pvector, dx=X(k+1)-X(k) |
|
|
//| * s - scaling coefficients set by |
|
|
//| MinBLEICSetScale() |
|
|
//| Passing EpsG=0, EpsF=0 and EpsX=0 (simultaneously) will lead to |
|
|
//| automatic stopping criterion selection. |
|
|
//| These conditions are used to terminate inner iterations. However,|
|
|
//| you need to tune termination conditions for outer iterations too.|
|
|
//+------------------------------------------------------------------+
|
|
static void CMinBLEIC::MinBLEICSetInnerCond(CMinBLEICState &state,const double epsg,
|
|
const double epsf,const double epsx)
|
|
{
|
|
//--- check
|
|
if(!CAp::Assert(CMath::IsFinite(epsg),__FUNCTION__+": EpsG is not finite number"))
|
|
return;
|
|
//--- check
|
|
if(!CAp::Assert(epsg>=0.0,__FUNCTION__+": negative EpsG"))
|
|
return;
|
|
//--- check
|
|
if(!CAp::Assert(CMath::IsFinite(epsf),__FUNCTION__+": EpsF is not finite number"))
|
|
return;
|
|
//--- check
|
|
if(!CAp::Assert(epsf>=0.0,__FUNCTION__+": negative EpsF"))
|
|
return;
|
|
//--- check
|
|
if(!CAp::Assert(CMath::IsFinite(epsx),__FUNCTION__+": EpsX is not finite number"))
|
|
return;
|
|
//--- check
|
|
if(!CAp::Assert(epsx>=0.0,__FUNCTION__+": negative EpsX"))
|
|
return;
|
|
//--- change values
|
|
state.m_innerepsg=epsg;
|
|
state.m_innerepsf=epsf;
|
|
state.m_innerepsx=epsx;
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| This function sets stopping conditions for outer iteration of |
|
|
//| BLEIC algo. |
|
|
//| These conditions control accuracy of constraint handling and |
|
|
//| amount of infeasibility allowed in the solution. |
|
|
//| INPUT PARAMETERS: |
|
|
//| State - structure which stores algorithm state |
|
|
//| EpsX - >0, stopping condition on outer iteration step |
|
|
//| length |
|
|
//| EpsI - >0, stopping condition on infeasibility |
|
|
//| Both EpsX and EpsI must be non-zero. |
|
|
//| MEANING OF EpsX |
|
|
//| EpsX is a stopping condition for outer iterations. Algorithm will|
|
|
//| stop when solution of the current modified subproblem will be |
|
|
//| within EpsX (using 2-norm) of the previous solution. |
|
|
//| MEANING OF EpsI |
|
|
//| EpsI controls feasibility properties - algorithm won't stop until|
|
|
//| all inequality constraints will be satisfied with error (distance|
|
|
//| from current point to the feasible area) at most EpsI. |
|
|
//+------------------------------------------------------------------+
|
|
static void CMinBLEIC::MinBLEICSetOuterCond(CMinBLEICState &state,const double epsx,
|
|
const double epsi)
|
|
{
|
|
//--- check
|
|
if(!CAp::Assert(CMath::IsFinite(epsx),__FUNCTION__+": EpsX is not finite number"))
|
|
return;
|
|
//--- check
|
|
if(!CAp::Assert(epsx>0.0,__FUNCTION__+": non-positive EpsX"))
|
|
return;
|
|
//--- check
|
|
if(!CAp::Assert(CMath::IsFinite(epsi),__FUNCTION__+": EpsI is not finite number"))
|
|
return;
|
|
//--- check
|
|
if(!CAp::Assert((double)(epsi)>0.0,__FUNCTION__+": non-positive EpsI"))
|
|
return;
|
|
//--- change values
|
|
state.m_outerepsx=epsx;
|
|
state.m_outerepsi=epsi;
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| This function sets scaling coefficients for BLEIC optimizer. |
|
|
//| ALGLIB optimizers use scaling matrices to test stopping |
|
|
//| conditions (step size and gradient are scaled before comparison |
|
|
//| with tolerances). Scale of the I-th variable is a translation |
|
|
//| invariant measure of: |
|
|
//| a) "how large" the variable is |
|
|
//| b) how large the step should be to make significant changes in |
|
|
//| the function |
|
|
//| Scaling is also used by finite difference variant of the |
|
|
//| optimizer - step along I-th axis is equal to DiffStep*S[I]. |
|
|
//| In most optimizers (and in the BLEIC too) scaling is NOT a form |
|
|
//| of preconditioning. It just affects stopping conditions. You |
|
|
//| should set preconditioner by separate call to one of the |
|
|
//| MinBLEICSetPrec...() functions. |
|
|
//| There is a special preconditioning mode, however, which uses |
|
|
//| scaling coefficients to form diagonal preconditioning matrix. |
|
|
//| You can turn this mode on, if you want. But you should understand|
|
|
//| that scaling is not the same thing as preconditioning - these are|
|
|
//| two different, although related forms of tuning solver. |
|
|
//| INPUT PARAMETERS: |
|
|
//| State - structure stores algorithm state |
|
|
//| S - array[N], non-zero scaling coefficients |
|
|
//| S[i] may be negative, sign doesn't matter. |
|
|
//+------------------------------------------------------------------+
|
|
static void CMinBLEIC::MinBLEICSetScale(CMinBLEICState &state,double &s[])
|
|
{
|
|
//--- create a variable
|
|
int i=0;
|
|
//--- check
|
|
if(!CAp::Assert(CAp::Len(s)>=state.m_nmain,__FUNCTION__+": Length(S)<N"))
|
|
return;
|
|
for(i=0;i<=state.m_nmain-1;i++)
|
|
{
|
|
//--- check
|
|
if(!CAp::Assert(CMath::IsFinite(s[i]),__FUNCTION__+": S contains infinite or NAN elements"))
|
|
return;
|
|
//--- check
|
|
if(!CAp::Assert(s[i]!=0.0,__FUNCTION__+": S contains zero elements"))
|
|
return;
|
|
//--- change values
|
|
state.m_soriginal[i]=MathAbs(s[i]);
|
|
}
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Modification of the preconditioner: preconditioning is turned |
|
|
//| off. |
|
|
//| INPUT PARAMETERS: |
|
|
//| State - structure which stores algorithm state |
|
|
//+------------------------------------------------------------------+
|
|
static void CMinBLEIC::MinBLEICSetPrecDefault(CMinBLEICState &state)
|
|
{
|
|
//--- change value
|
|
state.m_prectype=0;
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Modification of the preconditioner: diagonal of approximate |
|
|
//| Hessian is used. |
|
|
//| INPUT PARAMETERS: |
|
|
//| State - structure which stores algorithm state |
|
|
//| D - diagonal of the approximate Hessian, |
|
|
//| array[0..N-1], (if larger, only leading N |
|
|
//| elements are used). |
|
|
//| NOTE 1: D[i] should be positive. Exception will be thrown |
|
|
//| otherwise. |
|
|
//| NOTE 2: you should pass diagonal of approximate Hessian - NOT |
|
|
//| ITS INVERSE. |
|
|
//+------------------------------------------------------------------+
|
|
static void CMinBLEIC::MinBLEICSetPrecDiag(CMinBLEICState &state,double &d[])
|
|
{
|
|
//--- create a variable
|
|
int i=0;
|
|
//--- check
|
|
if(!CAp::Assert(CAp::Len(d)>=state.m_nmain,__FUNCTION__+": D is too short"))
|
|
return;
|
|
for(i=0;i<=state.m_nmain-1;i++)
|
|
{
|
|
//--- check
|
|
if(!CAp::Assert(CMath::IsFinite(d[i]),__FUNCTION__+": D contains infinite or NAN elements"))
|
|
return;
|
|
//--- check
|
|
if(!CAp::Assert((double)(d[i])>0.0,__FUNCTION__+": D contains non-positive elements"))
|
|
return;
|
|
}
|
|
//--- function call
|
|
CApServ::RVectorSetLengthAtLeast(state.m_diaghoriginal,state.m_nmain);
|
|
state.m_prectype=2;
|
|
//--- copy
|
|
for(i=0;i<=state.m_nmain-1;i++)
|
|
state.m_diaghoriginal[i]=d[i];
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Modification of the preconditioner: scale-based diagonal |
|
|
//| preconditioning. |
|
|
//| This preconditioning mode can be useful when you don't have |
|
|
//| approximate diagonal of Hessian, but you know that your variables|
|
|
//| are badly scaled (for example, one variable is in [1,10], and |
|
|
//| another in [1000,100000]), and most part of the ill-conditioning |
|
|
//| comes from different scales of vars. |
|
|
//| In this case simple scale-based preconditioner, with H[i] = |
|
|
//| = 1/(s[i]^2), can greatly improve convergence. |
|
|
//| IMPRTANT: you should set scale of your variables with |
|
|
//| MinBLEICSetScale() call (before or after MinBLEICSetPrecScale() |
|
|
//| call). Without knowledge of the scale of your variables |
|
|
//| scale-based preconditioner will be just unit matrix. |
|
|
//| INPUT PARAMETERS: |
|
|
//| State - structure which stores algorithm state |
|
|
//+------------------------------------------------------------------+
|
|
static void CMinBLEIC::MinBLEICSetPrecScale(CMinBLEICState &state)
|
|
{
|
|
//--- change value
|
|
state.m_prectype=3;
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| This function allows to stop algorithm after specified number of |
|
|
//| inner iterations. |
|
|
//| INPUT PARAMETERS: |
|
|
//| State - structure which stores algorithm state |
|
|
//| MaxIts - maximum number of inner iterations. |
|
|
//| If MaxIts=0, the number of iterations is |
|
|
//| unlimited. |
|
|
//+------------------------------------------------------------------+
|
|
static void CMinBLEIC::MinBLEICSetMaxIts(CMinBLEICState &state,
|
|
const int maxits)
|
|
{
|
|
//--- check
|
|
if(!CAp::Assert(maxits>=0,__FUNCTION__+": negative MaxIts!"))
|
|
return;
|
|
//--- change value
|
|
state.m_maxits=maxits;
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| This function turns on/off reporting. |
|
|
//| INPUT PARAMETERS: |
|
|
//| State - structure which stores algorithm state |
|
|
//| NeedXRep- whether iteration reports are needed or not |
|
|
//| If NeedXRep is True, algorithm will call rep() callback function |
|
|
//| if it is provided to MinBLEICOptimize(). |
|
|
//+------------------------------------------------------------------+
|
|
static void CMinBLEIC::MinBLEICSetXRep(CMinBLEICState &state,
|
|
const bool needxrep)
|
|
{
|
|
//--- change value
|
|
state.m_xrep=needxrep;
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| This function sets maximum step length |
|
|
//| IMPORTANT: this feature is hard to combine with preconditioning. |
|
|
//| You can't set upper limit on step length, when you solve |
|
|
//| optimization problem with linear (non-boundary) constraints AND |
|
|
//| preconditioner turned on. |
|
|
//| When non-boundary constraints are present, you have to either a) |
|
|
//| use preconditioner, or b) use upper limit on step length. YOU |
|
|
//| CAN'T USE BOTH! In this case algorithm will terminate with |
|
|
//| appropriate error code. |
|
|
//| INPUT PARAMETERS: |
|
|
//| State - structure which stores algorithm state |
|
|
//| StpMax - maximum step length, >=0. Set StpMax to 0.0, if |
|
|
//| you don't want to limit step length. |
|
|
//| Use this subroutine when you optimize target function which |
|
|
//| contains exp() or other fast growing functions, and optimization |
|
|
//| algorithm makes too large steps which lead to overflow. This |
|
|
//| function allows us to reject steps that are too large (and |
|
|
//| therefore expose us to the possible overflow) without actually |
|
|
//| calculating function value at the x+stp*d. |
|
|
//+------------------------------------------------------------------+
|
|
static void CMinBLEIC::MinBLEICSetStpMax(CMinBLEICState &state,
|
|
const double stpmax)
|
|
{
|
|
//--- check
|
|
if(!CAp::Assert(CMath::IsFinite(stpmax),__FUNCTION__+": StpMax is not finite!"))
|
|
return;
|
|
//--- check
|
|
if(!CAp::Assert(stpmax>=0.0,__FUNCTION__+": StpMax<0!"))
|
|
return;
|
|
//--- change value
|
|
state.m_stpmax=stpmax;
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| BLEIC results |
|
|
//| INPUT PARAMETERS: |
|
|
//| State - algorithm state |
|
|
//| OUTPUT PARAMETERS: |
|
|
//| X - array[0..N-1], solution |
|
|
//| Rep - optimization report. You should check Rep. |
|
|
//| TerminationType in order to distinguish |
|
|
//| successful termination from unsuccessful one. |
|
|
//| More information about fields of this structure |
|
|
//| can be found in the comments on MinBLEICReport |
|
|
//| datatype. |
|
|
//+------------------------------------------------------------------+
|
|
static void CMinBLEIC::MinBLEICResults(CMinBLEICState &state,double &x[],
|
|
CMinBLEICReport &rep)
|
|
{
|
|
//--- reset memory
|
|
ArrayResizeAL(x,0);
|
|
//--- function call
|
|
MinBLEICResultsBuf(state,x,rep);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| BLEIC results |
|
|
//| Buffered implementation of MinBLEICResults() which uses |
|
|
//| pre-allocated buffer to store X[]. If buffer size is too small, |
|
|
//| it resizes buffer. It is intended to be used in the inner cycles |
|
|
//| of performance critical algorithms where array reallocation |
|
|
//| penalty is too large to be ignored. |
|
|
//+------------------------------------------------------------------+
|
|
static void CMinBLEIC::MinBLEICResultsBuf(CMinBLEICState &state,double &x[],
|
|
CMinBLEICReport &rep)
|
|
{
|
|
//--- create variables
|
|
int i=0;
|
|
int i_=0;
|
|
//--- check
|
|
if(CAp::Len(x)<state.m_nmain)
|
|
ArrayResizeAL(x,state.m_nmain);
|
|
//--- change values
|
|
rep.m_inneriterationscount=state.m_repinneriterationscount;
|
|
rep.m_outeriterationscount=state.m_repouteriterationscount;
|
|
rep.m_nfev=state.m_repnfev;
|
|
rep.m_terminationtype=state.m_repterminationtype;
|
|
//--- check
|
|
if(state.m_repterminationtype>0)
|
|
{
|
|
for(i_=0;i_<=state.m_nmain-1;i_++)
|
|
x[i_]=state.m_xend[i_];
|
|
}
|
|
else
|
|
{
|
|
for(i=0;i<=state.m_nmain-1;i++)
|
|
x[i]=CInfOrNaN::NaN();
|
|
}
|
|
//--- change values
|
|
rep.m_debugeqerr=state.m_repdebugeqerr;
|
|
rep.m_debugfs=state.m_repdebugfs;
|
|
rep.m_debugff=state.m_repdebugff;
|
|
rep.m_debugdx=state.m_repdebugdx;
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| This subroutine restarts algorithm from new point. |
|
|
//| All optimization parameters (including constraints) are left |
|
|
//| unchanged. |
|
|
//| This function allows to solve multiple optimization problems |
|
|
//| (which must have same number of dimensions) without object |
|
|
//| reallocation penalty. |
|
|
//| INPUT PARAMETERS: |
|
|
//| State - structure previously allocated with |
|
|
//| MinBLEICCreate call. |
|
|
//| X - new starting point. |
|
|
//+------------------------------------------------------------------+
|
|
static void CMinBLEIC::MinBLEICRestartFrom(CMinBLEICState &state,double &x[])
|
|
{
|
|
//--- create variables
|
|
int n=0;
|
|
int i_=0;
|
|
//--- initialization
|
|
n=state.m_nmain;
|
|
//--- First,check for errors in the inputs
|
|
if(!CAp::Assert(CAp::Len(x)>=n,__FUNCTION__+": Length(X)<N"))
|
|
return;
|
|
//--- check
|
|
if(!CAp::Assert(CApServ::IsFiniteVector(x,n),__FUNCTION__+": X contains infinite or NaN values!"))
|
|
return;
|
|
//--- Set XC
|
|
for(i_=0;i_<=n-1;i_++)
|
|
state.m_xstart[i_]=x[i_];
|
|
//--- prepare RComm facilities
|
|
ArrayResizeAL(state.m_rstate.ia,5);
|
|
ArrayResizeAL(state.m_rstate.ba,1);
|
|
ArrayResizeAL(state.m_rstate.ra,2);
|
|
state.m_rstate.stage=-1;
|
|
//--- function call
|
|
ClearRequestFields(state);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Clears request fileds (to be sure that we don't forget to clear |
|
|
//| something) |
|
|
//+------------------------------------------------------------------+
|
|
static void CMinBLEIC::ClearRequestFields(CMinBLEICState &state)
|
|
{
|
|
//--- change values
|
|
state.m_needf=false;
|
|
state.m_needfg=false;
|
|
state.m_xupdated=false;
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| This functions "unscales" point, i.e. it makes transformation |
|
|
//| from scaled variables to unscaled ones. Only leading NMain |
|
|
//| variables are copied from XUnscaled to XScaled. |
|
|
//+------------------------------------------------------------------+
|
|
static void CMinBLEIC::UnscalePoint(CMinBLEICState &state,double &xscaled[],
|
|
double &xunscaled[])
|
|
{
|
|
//--- create variables
|
|
int i=0;
|
|
double v=0;
|
|
//--- calculation
|
|
for(i=0;i<=state.m_nmain-1;i++)
|
|
{
|
|
v=xscaled[i]*state.m_transforms[i];
|
|
//--- check
|
|
if(state.m_hasbndl[i])
|
|
{
|
|
//--- check
|
|
if(v<state.m_bndloriginal[i])
|
|
v=state.m_bndloriginal[i];
|
|
}
|
|
//--- check
|
|
if(state.m_hasbndu[i])
|
|
{
|
|
//--- check
|
|
if(v>state.m_bnduoriginal[i])
|
|
v=state.m_bnduoriginal[i];
|
|
}
|
|
xunscaled[i]=v;
|
|
}
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| This function: |
|
|
//| 1. makes projection of XScaled into equality constrained subspace|
|
|
//| (X is modified in-place) |
|
|
//| 2. stores residual from the projection into R |
|
|
//| 3. unscales projected XScaled and stores result into XUnscaled |
|
|
//| with additional enforcement |
|
|
//| It calculates set of additional values which are used later for |
|
|
//| modification of the target function F. |
|
|
//| INPUT PARAMETERS: |
|
|
//| State - optimizer state (we use its fields to get |
|
|
//| information about constraints) |
|
|
//| X - vector being projected |
|
|
//| R - preallocated buffer, used to store residual from |
|
|
//| projection |
|
|
//| OUTPUT PARAMETERS: |
|
|
//| X - projection of input X |
|
|
//| R - residual |
|
|
//| RNorm - residual norm squared, used later to modify |
|
|
//| target function |
|
|
//+------------------------------------------------------------------+
|
|
static void CMinBLEIC::ProjectPointAndUnscale(CMinBLEICState &state,
|
|
double &xscaled[],double &xunscaled[],
|
|
double &rscaled[],double &rnorm2)
|
|
{
|
|
//--- create variables
|
|
double v=0;
|
|
int i=0;
|
|
int nmain=0;
|
|
int nslack=0;
|
|
int i_=0;
|
|
//--- initialization
|
|
rnorm2=0;
|
|
nmain=state.m_nmain;
|
|
nslack=state.m_nslack;
|
|
//--- * subtract XE from XScaled
|
|
//--- * project XScaled
|
|
//--- * calculate norm of deviation from null space,store it in RNorm2
|
|
//--- * calculate residual from projection,store it in R
|
|
//--- * add XE to XScaled
|
|
//--- * unscale variables
|
|
for(i_=0;i_<=nmain+nslack-1;i_++)
|
|
xscaled[i_]=xscaled[i_]-state.m_xe[i_];
|
|
rnorm2=0;
|
|
for(i=0;i<=nmain+nslack-1;i++)
|
|
rscaled[i]=0;
|
|
//--- change values
|
|
for(i=0;i<=nmain+nslack-1;i++)
|
|
{
|
|
//--- check
|
|
if(state.m_activeconstraints[i])
|
|
{
|
|
v=xscaled[i];
|
|
xscaled[i]=0;
|
|
rscaled[i]=rscaled[i]+v;
|
|
rnorm2=rnorm2+CMath::Sqr(v);
|
|
}
|
|
}
|
|
//--- calculation
|
|
for(i=0;i<=state.m_cecnt-1;i++)
|
|
{
|
|
v=0.0;
|
|
for(i_=0;i_<=nmain+nslack-1;i_++)
|
|
v+=xscaled[i_]*state.m_cecurrent[i][i_];
|
|
for(i_=0;i_<=nmain+nslack-1;i_++)
|
|
xscaled[i_]=xscaled[i_]-v*state.m_cecurrent[i][i_];
|
|
for(i_=0;i_<=nmain+nslack-1;i_++)
|
|
rscaled[i_]=rscaled[i_]+v*state.m_cecurrent[i][i_];
|
|
rnorm2=rnorm2+CMath::Sqr(v);
|
|
}
|
|
for(i_=0;i_<=nmain+nslack-1;i_++)
|
|
xscaled[i_]=xscaled[i_]+state.m_xe[i_];
|
|
//--- function call
|
|
UnscalePoint(state,xscaled,xunscaled);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| This function scales and copies NMain elements of GUnscaled into |
|
|
//| GScaled. Other NSlack components of GScaled are set to zero. |
|
|
//+------------------------------------------------------------------+
|
|
static void CMinBLEIC::ScaleGradientAndExpand(CMinBLEICState &state,
|
|
double &gunscaled[],
|
|
double &gscaled[])
|
|
{
|
|
//--- create a variable
|
|
int i=0;
|
|
//--- change values
|
|
for(i=0;i<=state.m_nmain-1;i++)
|
|
gscaled[i]=gunscaled[i]*state.m_transforms[i];
|
|
for(i=0;i<=state.m_nslack-1;i++)
|
|
gscaled[state.m_nmain+i]=0;
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| This subroutine applies modifications to the target function |
|
|
//| given by its value F and gradient G at the projected point X |
|
|
//| which lies in the equality constrained subspace. |
|
|
//| Following modifications are applied: |
|
|
//| * modified barrier functions to handle inequality constraints |
|
|
//| (both F and G are modified) |
|
|
//| * projection of gradient into equality constrained subspace |
|
|
//| (only G is modified) |
|
|
//| * quadratic penalty for deviations from equality constrained |
|
|
//| subspace (both F and G are modified) |
|
|
//| It also calculates gradient norm (three different norms for three|
|
|
//| different types of gradient), feasibility and complementary |
|
|
//| slackness errors. |
|
|
//| INPUT PARAMETERS: |
|
|
//| State - optimizer state (we use its fields to get |
|
|
//| information about constraints) |
|
|
//| X - point (projected into equality constrained |
|
|
//| subspace) |
|
|
//| R - residual from projection |
|
|
//| RNorm2 - residual norm squared |
|
|
//| F - function value at X |
|
|
//| G - function gradient at X |
|
|
//| OUTPUT PARAMETERS: |
|
|
//| F - modified function value at X |
|
|
//| G - modified function gradient at X |
|
|
//| GNorm - 2-norm of unmodified G |
|
|
//| MPGNorm - 2-norm of modified G |
|
|
//| MBA - minimum argument of barrier functions. |
|
|
//| If X is strictly feasible, it is greater than |
|
|
//| zero. |
|
|
//| If X lies on a boundary, it is zero. |
|
|
//| It is negative for infeasible X. |
|
|
//| FIErr - 2-norm of feasibility error with respect to |
|
|
//| inequality/bound constraints |
|
|
//| CSErr - 2-norm of complementarity slackness error |
|
|
//+------------------------------------------------------------------+
|
|
static void CMinBLEIC::ModifyTargetFunction(CMinBLEICState &state,double &x[],
|
|
double &r[],const double rnorm2,
|
|
double &f,double &g[],
|
|
double &gnorm,double &mpgnorm)
|
|
{
|
|
//--- create variables
|
|
double v=0;
|
|
int i=0;
|
|
int nmain=0;
|
|
int nslack=0;
|
|
bool hasconstraints;
|
|
int i_=0;
|
|
//--- initialization
|
|
gnorm=0;
|
|
mpgnorm=0;
|
|
nmain=state.m_nmain;
|
|
nslack=state.m_nslack;
|
|
hasconstraints=false;
|
|
//--- GNorm
|
|
v=0.0;
|
|
for(i_=0;i_<=nmain+nslack-1;i_++)
|
|
v+=g[i_]*g[i_];
|
|
gnorm=MathSqrt(v);
|
|
//--- Process equality constraints:
|
|
//--- * modify F to handle penalty term for equality constraints
|
|
//--- * project gradient on null space of equality constraints
|
|
//--- * add penalty term for equality constraints to gradient
|
|
f=f+rnorm2;
|
|
for(i=0;i<=nmain+nslack-1;i++)
|
|
{
|
|
//--- check
|
|
if(state.m_activeconstraints[i])
|
|
g[i]=0;
|
|
}
|
|
for(i=0;i<=state.m_cecnt-1;i++)
|
|
{
|
|
v=0.0;
|
|
//--- change values
|
|
for(i_=0;i_<=nmain+nslack-1;i_++)
|
|
v+=g[i_]*state.m_cecurrent[i][i_];
|
|
for(i_=0;i_<=nmain+nslack-1;i_++)
|
|
g[i_]=g[i_]-v*state.m_cecurrent[i][i_];
|
|
}
|
|
for(i_=0;i_<=nmain+nslack-1;i_++)
|
|
g[i_]=g[i_]+2*r[i_];
|
|
//--- MPGNorm
|
|
v=0.0;
|
|
for(i_=0;i_<=nmain+nslack-1;i_++)
|
|
v+=g[i_]*g[i_];
|
|
mpgnorm=MathSqrt(v);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| This function makes additional check for constraints which can be|
|
|
//| activated. |
|
|
//| We try activate constraints one by one, but it is possible that |
|
|
//| several constraints should be activated during one iteration. It |
|
|
//| this case only one of them (probably last) will be activated. |
|
|
//| This function will fix it - it will pass through constraints and |
|
|
//| activate those which are at the boundary or beyond it. |
|
|
//| It will return True, if at least one constraint was activated by |
|
|
//| this function. |
|
|
//+------------------------------------------------------------------+
|
|
static bool CMinBLEIC::AdditionalCheckForConstraints(CMinBLEICState &state,
|
|
double &x[])
|
|
{
|
|
//--- create variables
|
|
bool result;
|
|
int i=0;
|
|
int nmain=0;
|
|
int nslack=0;
|
|
//--- initialization
|
|
result=false;
|
|
nmain=state.m_nmain;
|
|
nslack=state.m_nslack;
|
|
//--- calculation
|
|
for(i=0;i<=nmain-1;i++)
|
|
{
|
|
//--- check
|
|
if(!state.m_activeconstraints[i])
|
|
{
|
|
//--- check
|
|
if(state.m_hasbndl[i])
|
|
{
|
|
//--- check
|
|
if(x[i]<=state.m_bndleffective[i])
|
|
{
|
|
state.m_activeconstraints[i]=true;
|
|
state.m_constrainedvalues[i]=state.m_bndleffective[i];
|
|
result=true;
|
|
}
|
|
}
|
|
//--- check
|
|
if(state.m_hasbndu[i])
|
|
{
|
|
//--- check
|
|
if(x[i]>=state.m_bndueffective[i])
|
|
{
|
|
state.m_activeconstraints[i]=true;
|
|
state.m_constrainedvalues[i]=state.m_bndueffective[i];
|
|
result=true;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
for(i=0;i<=nslack-1;i++)
|
|
{
|
|
//--- check
|
|
if(!state.m_activeconstraints[nmain+i])
|
|
{
|
|
//--- check
|
|
if(x[nmain+i]<=0.0)
|
|
{
|
|
state.m_activeconstraints[nmain+i]=true;
|
|
state.m_constrainedvalues[nmain+i]=0;
|
|
result=true;
|
|
}
|
|
}
|
|
}
|
|
//--- return result
|
|
return(result);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| This function rebuilds CECurrent and XE according to current set |
|
|
//| of active bound constraints. |
|
|
//+------------------------------------------------------------------+
|
|
static void CMinBLEIC::RebuildCEXE(CMinBLEICState &state)
|
|
{
|
|
//--- create variables
|
|
int i=0;
|
|
int j=0;
|
|
int k=0;
|
|
int nmain=0;
|
|
int nslack=0;
|
|
double v=0;
|
|
int i_=0;
|
|
//--- initialization
|
|
nmain=state.m_nmain;
|
|
nslack=state.m_nslack;
|
|
//--- function call
|
|
CAblas::RMatrixCopy(state.m_cecnt,nmain+nslack+1,state.m_ceeffective,0,0,state.m_cecurrent,0,0);
|
|
//--- calculation
|
|
for(i=0;i<=state.m_cecnt-1;i++)
|
|
{
|
|
//--- "Subtract" active bound constraints from I-th linear constraint
|
|
for(j=0;j<=nmain+nslack-1;j++)
|
|
{
|
|
//--- check
|
|
if(state.m_activeconstraints[j])
|
|
{
|
|
state.m_cecurrent[i].Set(nmain+nslack,state.m_cecurrent[i][nmain+nslack]-state.m_cecurrent[i][j]*state.m_constrainedvalues[j]);
|
|
state.m_cecurrent[i].Set(j,0.0);
|
|
}
|
|
}
|
|
//--- Reorthogonalize I-th constraint with respect to previous ones
|
|
//--- NOTE: we also update right part,which is CECurrent[...,NMain+NSlack].
|
|
for(k=0;k<=i-1;k++)
|
|
{
|
|
v=0.0;
|
|
for(i_=0;i_<=nmain+nslack-1;i_++)
|
|
v+=state.m_cecurrent[k][i_]*state.m_cecurrent[i][i_];
|
|
for(i_=0;i_<=nmain+nslack;i_++)
|
|
state.m_cecurrent[i].Set(i_,state.m_cecurrent[i][i_]-v*state.m_cecurrent[k][i_]);
|
|
}
|
|
//--- Calculate norm of I-th row of CECurrent. Fill by zeros,if it is
|
|
//--- too small. Normalize otherwise.
|
|
//--- NOTE: we also scale last column of CECurrent (right part)
|
|
v=0.0;
|
|
for(i_=0;i_<=nmain+nslack-1;i_++)
|
|
v+=state.m_cecurrent[i][i_]*state.m_cecurrent[i][i_];
|
|
v=MathSqrt(v);
|
|
//--- check
|
|
if(v>10000*CMath::m_machineepsilon)
|
|
{
|
|
v=1/v;
|
|
for(i_=0;i_<=nmain+nslack;i_++)
|
|
state.m_cecurrent[i].Set(i_,v*state.m_cecurrent[i][i_]);
|
|
}
|
|
else
|
|
{
|
|
for(j=0;j<=nmain+nslack;j++)
|
|
state.m_cecurrent[i].Set(j,0);
|
|
}
|
|
}
|
|
//--- change values
|
|
for(j=0;j<=nmain+nslack-1;j++)
|
|
state.m_xe[j]=0;
|
|
for(i=0;i<=nmain+nslack-1;i++)
|
|
{
|
|
//--- check
|
|
if(state.m_activeconstraints[i])
|
|
state.m_xe[i]=state.m_xe[i]+state.m_constrainedvalues[i];
|
|
}
|
|
//--- change values
|
|
for(i=0;i<=state.m_cecnt-1;i++)
|
|
{
|
|
v=state.m_cecurrent[i][nmain+nslack];
|
|
for(i_=0;i_<=nmain+nslack-1;i_++)
|
|
state.m_xe[i_]=state.m_xe[i_]+v*state.m_cecurrent[i][i_];
|
|
}
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| This function projects gradient onto equality constrained |
|
|
//| subspace |
|
|
//+------------------------------------------------------------------+
|
|
static void CMinBLEIC::MakeGradientProjection(CMinBLEICState &state,
|
|
double &pg[])
|
|
{
|
|
//--- create variables
|
|
int i=0;
|
|
int nmain=0;
|
|
int nslack=0;
|
|
double v=0;
|
|
int i_=0;
|
|
//--- initialization
|
|
nmain=state.m_nmain;
|
|
nslack=state.m_nslack;
|
|
for(i=0;i<=nmain+nslack-1;i++)
|
|
{
|
|
//--- check
|
|
if(state.m_activeconstraints[i])
|
|
pg[i]=0;
|
|
}
|
|
//--- calculation
|
|
for(i=0;i<=state.m_cecnt-1;i++)
|
|
{
|
|
v=0.0;
|
|
for(i_=0;i_<=nmain+nslack-1;i_++)
|
|
v+=pg[i_]*state.m_cecurrent[i][i_];
|
|
for(i_=0;i_<=nmain+nslack-1;i_++)
|
|
pg[i_]=pg[i_]-v*state.m_cecurrent[i][i_];
|
|
}
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| This function prepares equality constrained subproblem: |
|
|
//| 1. X is used to activate constraints (if there are ones which are|
|
|
//| still inactive, but should be activated). |
|
|
//| 2. constraints matrix CEOrt is copied to CECurrent and modified |
|
|
//| according to the list of active bound constraints |
|
|
//| (corresponding elements are filled by zeros and |
|
|
//| reorthogonalized). |
|
|
//| 3. XE - least squares solution of equality constraints - is |
|
|
//| recalculated |
|
|
//| 4. X is copied to PX and projected onto equality constrained |
|
|
//| subspace |
|
|
//| 5. inactive constraints are checked against PX - if there is at |
|
|
//| least one which should be activated, we activate it and move |
|
|
//| back to (2) |
|
|
//| 6. as result, PX is feasible with respect to bound constraints - |
|
|
//| step (5) guarantees it. But PX can be infeasible with respect |
|
|
//| to equality ones, because step (2) is done without checks for |
|
|
//| consistency. As the final step, we check that PX is feasible. |
|
|
//| If not, we return False. True is returned otherwise. |
|
|
//| If this algorithm returned True, then: |
|
|
//| * X is not changed |
|
|
//| * PX contains projection of X onto constrained subspace |
|
|
//| * G is not changed |
|
|
//| * PG contains projection of G onto constrained subspace |
|
|
//| * PX is feasible with respect to all constraints |
|
|
//| * all constraints which are active at PX, are activated |
|
|
//+------------------------------------------------------------------+
|
|
static bool CMinBLEIC::PrepareConstraintMatrix(CMinBLEICState &state,double &x[],
|
|
double &g[],double &px[],double &pg[])
|
|
{
|
|
//--- create variables
|
|
int i=0;
|
|
int nmain=0;
|
|
int nslack=0;
|
|
double v=0;
|
|
double ferr=0;
|
|
int i_=0;
|
|
//--- initialization
|
|
nmain=state.m_nmain;
|
|
nslack=state.m_nslack;
|
|
//--- Step 1
|
|
AdditionalCheckForConstraints(state,x);
|
|
//--- Steps 2-5
|
|
do
|
|
{
|
|
//--- Steps 2-3
|
|
RebuildCEXE(state);
|
|
//--- Step 4
|
|
//--- Calculate PX,PG
|
|
for(i_=0;i_<=nmain+nslack-1;i_++)
|
|
px[i_]=x[i_];
|
|
for(i_=0;i_<=nmain+nslack-1;i_++)
|
|
px[i_]=px[i_]-state.m_xe[i_];
|
|
for(i_=0;i_<=nmain+nslack-1;i_++)
|
|
pg[i_]=g[i_];
|
|
for(i=0;i<=nmain+nslack-1;i++)
|
|
{
|
|
//--- check
|
|
if(state.m_activeconstraints[i])
|
|
{
|
|
px[i]=0;
|
|
pg[i]=0;
|
|
}
|
|
}
|
|
//--- calculation
|
|
for(i=0;i<=state.m_cecnt-1;i++)
|
|
{
|
|
//--- change values
|
|
v=0.0;
|
|
for(i_=0;i_<=nmain+nslack-1;i_++)
|
|
v+=px[i_]*state.m_cecurrent[i][i_];
|
|
for(i_=0;i_<=nmain+nslack-1;i_++)
|
|
px[i_]=px[i_]-v*state.m_cecurrent[i][i_];
|
|
//--- change values
|
|
v=0.0;
|
|
for(i_=0;i_<=nmain+nslack-1;i_++)
|
|
v+=pg[i_]*state.m_cecurrent[i][i_];
|
|
for(i_=0;i_<=nmain+nslack-1;i_++)
|
|
pg[i_]=pg[i_]-v*state.m_cecurrent[i][i_];
|
|
}
|
|
for(i_=0;i_<=nmain+nslack-1;i_++)
|
|
px[i_]=px[i_]+state.m_xe[i_];
|
|
//--- Step 5 (loop condition below)
|
|
}
|
|
while(AdditionalCheckForConstraints(state,px));
|
|
//--- Step 6
|
|
ferr=0;
|
|
for(i=0;i<=state.m_cecnt-1;i++)
|
|
{
|
|
v=0.0;
|
|
for(i_=0;i_<=nmain+nslack-1;i_++)
|
|
v+=px[i_]*state.m_ceeffective[i][i_];
|
|
//--- change values
|
|
v=v-state.m_ceeffective[i][nmain+nslack];
|
|
ferr=MathMax(ferr,MathAbs(v));
|
|
}
|
|
//--- check
|
|
if(ferr<=state.m_outerepsi)
|
|
return(true);
|
|
//--- return result
|
|
return(false);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Internal initialization subroutine |
|
|
//+------------------------------------------------------------------+
|
|
static void CMinBLEIC::MinBLEICInitInternal(const int n,double &x[],
|
|
const double diffstep,
|
|
CMinBLEICState &state)
|
|
{
|
|
//--- create a variable
|
|
int i=0;
|
|
//--- create matrix
|
|
CMatrixDouble c;
|
|
//--- create array
|
|
int ct[];
|
|
//--- initialization
|
|
state.m_nmain=n;
|
|
state.m_optdim=0;
|
|
state.m_diffstep=diffstep;
|
|
//--- allocation
|
|
ArrayResizeAL(state.m_bndloriginal,n);
|
|
ArrayResizeAL(state.m_bndleffective,n);
|
|
ArrayResizeAL(state.m_hasbndl,n);
|
|
ArrayResizeAL(state.m_bnduoriginal,n);
|
|
ArrayResizeAL(state.m_bndueffective,n);
|
|
ArrayResizeAL(state.m_hasbndu,n);
|
|
ArrayResizeAL(state.m_xstart,n);
|
|
ArrayResizeAL(state.m_soriginal,n);
|
|
ArrayResizeAL(state.m_x,n);
|
|
ArrayResizeAL(state.m_g,n);
|
|
for(i=0;i<=n-1;i++)
|
|
{
|
|
state.m_bndloriginal[i]=CInfOrNaN::NegativeInfinity();
|
|
state.m_hasbndl[i]=false;
|
|
state.m_bnduoriginal[i]=CInfOrNaN::PositiveInfinity();
|
|
state.m_hasbndu[i]=false;
|
|
state.m_soriginal[i]=1.0;
|
|
}
|
|
//--- function call
|
|
MinBLEICSetLC(state,c,ct,0);
|
|
//--- function call
|
|
MinBLEICSetInnerCond(state,0.0,0.0,0.0);
|
|
//--- function call
|
|
MinBLEICSetOuterCond(state,1.0E-6,1.0E-6);
|
|
//--- function call
|
|
MinBLEICSetMaxIts(state,0);
|
|
//--- function call
|
|
MinBLEICSetXRep(state,false);
|
|
//--- function call
|
|
MinBLEICSetStpMax(state,0.0);
|
|
//--- function call
|
|
MinBLEICSetPrecDefault(state);
|
|
//--- function call
|
|
MinBLEICRestartFrom(state,x);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| NOTES: |
|
|
//| 1. This function has two different implementations: one which |
|
|
//| uses exact (analytical) user-supplied gradient, and one which |
|
|
//| uses function value only and numerically differentiates |
|
|
//| function in order to obtain gradient. |
|
|
//| Depending on the specific function used to create optimizer |
|
|
//| object (either MinBLEICCreate() for analytical gradient or |
|
|
//| MinBLEICCreateF() for numerical differentiation) you should |
|
|
//| choose appropriate variant of MinBLEICOptimize() - one which |
|
|
//| accepts function AND gradient or one which accepts function |
|
|
//| ONLY. |
|
|
//| Be careful to choose variant of MinBLEICOptimize() which |
|
|
//| corresponds to your optimization scheme! Table below lists |
|
|
//| different combinations of callback (function/gradient) passed |
|
|
//| to MinBLEICOptimize() and specific function used to create |
|
|
//| optimizer. |
|
|
//| | USER PASSED TO MinBLEICOptimize() |
|
|
//| CREATED WITH | function only | function and gradient |
|
|
//| ------------------------------------------------------------ |
|
|
//| MinBLEICCreateF() | work FAIL |
|
|
//| MinBLEICCreate() | FAIL work |
|
|
//| Here "FAIL" denotes inappropriate combinations of optimizer |
|
|
//| creation function and MinBLEICOptimize() version. Attemps to |
|
|
//| use such combination (for example, to create optimizer with |
|
|
//| MinBLEICCreateF() and to pass gradient information to |
|
|
//| MinCGOptimize()) will lead to exception being thrown. Either |
|
|
//| you did not pass gradient when it WAS needed or you passed |
|
|
//| gradient when it was NOT needed. |
|
|
//+------------------------------------------------------------------+
|
|
static bool CMinBLEIC::MinBLEICIteration(CMinBLEICState &state)
|
|
{
|
|
//--- create variables
|
|
int nmain=0;
|
|
int nslack=0;
|
|
int m=0;
|
|
int i=0;
|
|
int j=0;
|
|
double v=0;
|
|
double vv=0;
|
|
bool b;
|
|
int i_=0;
|
|
//--- This code initializes locals by:
|
|
//--- * random values determined during code
|
|
//--- generation - on first subroutine call
|
|
//--- * values from previous call - on subsequent calls
|
|
if(state.m_rstate.stage>=0)
|
|
{
|
|
//--- initialization
|
|
nmain=state.m_rstate.ia[0];
|
|
nslack=state.m_rstate.ia[1];
|
|
m=state.m_rstate.ia[2];
|
|
i=state.m_rstate.ia[3];
|
|
j=state.m_rstate.ia[4];
|
|
b=state.m_rstate.ba[0];
|
|
v=state.m_rstate.ra[0];
|
|
vv=state.m_rstate.ra[1];
|
|
}
|
|
else
|
|
{
|
|
//--- initialization
|
|
nmain=-983;
|
|
nslack=-989;
|
|
m=-834;
|
|
i=900;
|
|
j=-287;
|
|
b=false;
|
|
v=214;
|
|
vv=-338;
|
|
}
|
|
//--- check
|
|
if(state.m_rstate.stage==0)
|
|
{
|
|
//--- change value
|
|
state.m_needfg=false;
|
|
//--- function call, return result
|
|
return(Func_lbl_14(state,nmain,nslack,m,i,j,b,v,vv));
|
|
}
|
|
//--- check
|
|
if(state.m_rstate.stage==1)
|
|
{
|
|
//--- change value
|
|
state.m_needf=false;
|
|
//--- function call, return result
|
|
return(Func_lbl_14(state,nmain,nslack,m,i,j,b,v,vv));
|
|
}
|
|
//--- check
|
|
if(state.m_rstate.stage==2)
|
|
{
|
|
//--- change value
|
|
state.m_needfg=false;
|
|
//--- function call, return result
|
|
return(Func_lbl_22(state,nmain,nslack,m,i,j,b,v,vv));
|
|
}
|
|
//--- check
|
|
if(state.m_rstate.stage==3)
|
|
{
|
|
//--- change values
|
|
state.m_fbase=state.m_f;
|
|
i=0;
|
|
//--- function call, return result
|
|
return(Func_lbl_23(state,nmain,nslack,m,i,j,b,v,vv));
|
|
}
|
|
//--- check
|
|
if(state.m_rstate.stage==4)
|
|
{
|
|
//--- change values
|
|
state.m_fm2=state.m_f;
|
|
state.m_x[i]=v-0.5*state.m_diffstep*state.m_soriginal[i];
|
|
state.m_rstate.stage=5;
|
|
//--- Saving state
|
|
Func_lbl_rcomm(state,nmain,nslack,m,i,j,b,v,vv);
|
|
//--- return result
|
|
return(true);
|
|
}
|
|
//--- check
|
|
if(state.m_rstate.stage==5)
|
|
{
|
|
//--- change values
|
|
state.m_fm1=state.m_f;
|
|
state.m_x[i]=v+0.5*state.m_diffstep*state.m_soriginal[i];
|
|
state.m_rstate.stage=6;
|
|
//--- Saving state
|
|
Func_lbl_rcomm(state,nmain,nslack,m,i,j,b,v,vv);
|
|
//--- return result
|
|
return(true);
|
|
}
|
|
//--- check
|
|
if(state.m_rstate.stage==6)
|
|
{
|
|
//--- change values
|
|
state.m_fp1=state.m_f;
|
|
state.m_x[i]=v+state.m_diffstep*state.m_soriginal[i];
|
|
state.m_rstate.stage=7;
|
|
//--- Saving state
|
|
Func_lbl_rcomm(state,nmain,nslack,m,i,j,b,v,vv);
|
|
//--- return result
|
|
return(true);
|
|
}
|
|
//--- check
|
|
if(state.m_rstate.stage==7)
|
|
{
|
|
//--- change values
|
|
state.m_fp2=state.m_f;
|
|
state.m_g[i]=(8*(state.m_fp1-state.m_fm1)-(state.m_fp2-state.m_fm2))/(6*state.m_diffstep*state.m_soriginal[i]);
|
|
state.m_x[i]=v;
|
|
i=i+1;
|
|
//--- function call, return result
|
|
return(Func_lbl_23(state,nmain,nslack,m,i,j,b,v,vv));
|
|
}
|
|
//--- check
|
|
if(state.m_rstate.stage==8)
|
|
{
|
|
//--- change values
|
|
state.m_fm1=state.m_f;
|
|
state.m_xp1=MathMin(v+state.m_diffstep*state.m_soriginal[i],state.m_bnduoriginal[i]);
|
|
state.m_x[i]=state.m_xp1;
|
|
state.m_rstate.stage=9;
|
|
//--- Saving state
|
|
Func_lbl_rcomm(state,nmain,nslack,m,i,j,b,v,vv);
|
|
//--- return result
|
|
return(true);
|
|
}
|
|
//--- check
|
|
if(state.m_rstate.stage==9)
|
|
{
|
|
//--- change values
|
|
state.m_fp1=state.m_f;
|
|
state.m_g[i]=(state.m_fp1-state.m_fm1)/(state.m_xp1-state.m_xm1);
|
|
state.m_x[i]=v;
|
|
i=i+1;
|
|
//--- function call, return result
|
|
return(Func_lbl_23(state,nmain,nslack,m,i,j,b,v,vv));
|
|
}
|
|
//--- check
|
|
if(state.m_rstate.stage==10)
|
|
{
|
|
//--- change value
|
|
state.m_xupdated=false;
|
|
//--- function call, return result
|
|
return(Func_lbl_17(state,nmain,nslack,m,i,j,b,v,vv));
|
|
}
|
|
//--- check
|
|
if(state.m_rstate.stage==11)
|
|
{
|
|
//--- change value
|
|
state.m_needfg=false;
|
|
//--- function call, return result
|
|
return(Func_lbl_31(state,nmain,nslack,m,i,j,b,v,vv));
|
|
}
|
|
//--- check
|
|
if(state.m_rstate.stage==12)
|
|
{
|
|
//--- change value
|
|
state.m_needf=false;
|
|
//--- function call, return result
|
|
return(Func_lbl_31(state,nmain,nslack,m,i,j,b,v,vv));
|
|
}
|
|
//--- Routine body
|
|
//--- Prepare:
|
|
//--- * calculate number of slack variables
|
|
//--- * initialize locals
|
|
//--- * initialize debug fields
|
|
//--- * make quick check
|
|
nmain=state.m_nmain;
|
|
nslack=0;
|
|
for(i=0;i<=state.m_cecnt-1;i++)
|
|
{
|
|
//--- check
|
|
if(state.m_ct[i]!=0)
|
|
nslack=nslack+1;
|
|
}
|
|
//--- change values
|
|
state.m_nslack=nslack;
|
|
state.m_repterminationtype=0;
|
|
state.m_repinneriterationscount=0;
|
|
state.m_repouteriterationscount=0;
|
|
state.m_repnfev=0;
|
|
state.m_repdebugeqerr=0.0;
|
|
state.m_repdebugfs=CInfOrNaN::NaN();
|
|
state.m_repdebugff=CInfOrNaN::NaN();
|
|
state.m_repdebugdx=CInfOrNaN::NaN();
|
|
//--- check
|
|
if(state.m_stpmax!=0.0 && state.m_prectype!=0)
|
|
{
|
|
state.m_repterminationtype=-10;
|
|
//--- return result
|
|
return(false);
|
|
}
|
|
//--- allocate
|
|
CApServ::RVectorSetLengthAtLeast(state.m_r,nmain+nslack);
|
|
CApServ::RVectorSetLengthAtLeast(state.m_diagh,nmain+nslack);
|
|
CApServ::RVectorSetLengthAtLeast(state.m_tmp0,nmain+nslack);
|
|
CApServ::RVectorSetLengthAtLeast(state.m_tmp1,nmain+nslack);
|
|
CApServ::RVectorSetLengthAtLeast(state.m_tmp2,nmain+nslack);
|
|
CApServ::RMatrixSetLengthAtLeast(state.m_cecurrent,state.m_cecnt,nmain+nslack+1);
|
|
CApServ::BVectorSetLengthAtLeast(state.m_activeconstraints,nmain+nslack);
|
|
CApServ::RVectorSetLengthAtLeast(state.m_constrainedvalues,nmain+nslack);
|
|
CApServ::RVectorSetLengthAtLeast(state.m_lastg,nmain+nslack);
|
|
CApServ::RVectorSetLengthAtLeast(state.m_xe,nmain+nslack);
|
|
CApServ::RVectorSetLengthAtLeast(state.m_xcur,nmain+nslack);
|
|
CApServ::RVectorSetLengthAtLeast(state.m_xprev,nmain+nslack);
|
|
CApServ::RVectorSetLengthAtLeast(state.m_xend,nmain);
|
|
//--- Create/restart optimizer.
|
|
//--- State.OptDim is used to determine current state of optimizer.
|
|
if(state.m_optdim!=nmain+nslack)
|
|
{
|
|
for(i=0;i<=nmain+nslack-1;i++)
|
|
state.m_tmp1[i]=0.0;
|
|
//--- function call
|
|
CMinCG::MinCGCreate(nmain+nslack,state.m_tmp1,state.m_cgstate);
|
|
state.m_optdim=nmain+nslack;
|
|
}
|
|
//--- Prepare transformation.
|
|
//--- MinBLEIC's handling of preconditioner matrix is somewhat unusual -
|
|
//--- instead of incorporating it into algorithm and making implicit
|
|
//--- scaling (as most optimizers do) BLEIC optimizer uses explicit
|
|
//--- scaling - it solves problem in the scaled parameters space S,
|
|
//--- making transition between scaled (S) and unscaled (X) variables
|
|
//--- every time we ask for function value.
|
|
//--- Following fields are calculated here:
|
|
//--- * TransformS X[i]=TransformS[i]*S[i],array[NMain]
|
|
//--- * SEffective "effective" scale of the variables after
|
|
//--- transformation,array[NMain+NSlack]
|
|
CApServ::RVectorSetLengthAtLeast(state.m_transforms,nmain);
|
|
for(i=0;i<=nmain-1;i++)
|
|
{
|
|
//--- check
|
|
if(state.m_prectype==2)
|
|
{
|
|
state.m_transforms[i]=1/MathSqrt(state.m_diaghoriginal[i]);
|
|
continue;
|
|
}
|
|
//--- check
|
|
if(state.m_prectype==3)
|
|
{
|
|
state.m_transforms[i]=state.m_soriginal[i];
|
|
continue;
|
|
}
|
|
state.m_transforms[i]=1;
|
|
}
|
|
//--- function call
|
|
CApServ::RVectorSetLengthAtLeast(state.m_seffective,nmain+nslack);
|
|
for(i=0;i<=nmain-1;i++)
|
|
state.m_seffective[i]=state.m_soriginal[i]/state.m_transforms[i];
|
|
for(i=0;i<=nslack-1;i++)
|
|
state.m_seffective[nmain+i]=1;
|
|
//--- function call
|
|
CMinCG::MinCGSetScale(state.m_cgstate,state.m_seffective);
|
|
//--- Pre-process constraints
|
|
//--- * check consistency of bound constraints
|
|
//--- * add slack vars,convert problem to the bound/equality
|
|
//--- constrained one
|
|
//--- We calculate here:
|
|
//--- * BndLEffective - lower bounds after transformation of variables (see above)
|
|
//--- * BndUEffective - upper bounds after transformation of variables (see above)
|
|
//--- * CEEffective - matrix of equality constraints for transformed variables
|
|
for(i=0;i<=nmain-1;i++)
|
|
{
|
|
//--- check
|
|
if(state.m_hasbndl[i])
|
|
state.m_bndleffective[i]=state.m_bndloriginal[i]/state.m_transforms[i];
|
|
//--- check
|
|
if(state.m_hasbndu[i])
|
|
state.m_bndueffective[i]=state.m_bnduoriginal[i]/state.m_transforms[i];
|
|
}
|
|
for(i=0;i<=nmain-1;i++)
|
|
{
|
|
//--- check
|
|
if(state.m_hasbndl[i] && state.m_hasbndu[i])
|
|
{
|
|
//--- check
|
|
if(state.m_bndleffective[i]>state.m_bndueffective[i])
|
|
{
|
|
state.m_repterminationtype=-3;
|
|
//--- return result
|
|
return(false);
|
|
}
|
|
}
|
|
}
|
|
//--- function call
|
|
CApServ::RMatrixSetLengthAtLeast(state.m_ceeffective,state.m_cecnt,nmain+nslack+1);
|
|
//--- change value
|
|
m=0;
|
|
//--- calculation
|
|
for(i=0;i<=state.m_cecnt-1;i++)
|
|
{
|
|
//--- NOTE: when we add slack variable,we use V=max(abs(CE[i,...])) as
|
|
//--- coefficient before it in order to make linear equations better
|
|
//--- conditioned.
|
|
v=0;
|
|
for(j=0;j<=nmain-1;j++)
|
|
{
|
|
state.m_ceeffective[i].Set(j,state.m_ceoriginal[i][j]*state.m_transforms[j]);
|
|
v=MathMax(v,MathAbs(state.m_ceeffective[i][j]));
|
|
}
|
|
//--- check
|
|
if(v==0.0)
|
|
v=1;
|
|
for(j=0;j<=nslack-1;j++)
|
|
state.m_ceeffective[i].Set(nmain+j,0.0);
|
|
state.m_ceeffective[i].Set(nmain+nslack,state.m_ceoriginal[i][nmain]);
|
|
//--- check
|
|
if(state.m_ct[i]<0)
|
|
{
|
|
state.m_ceeffective[i].Set(nmain+m,v);
|
|
m=m+1;
|
|
}
|
|
//--- check
|
|
if(state.m_ct[i]>0)
|
|
{
|
|
state.m_ceeffective[i].Set(nmain+m,-v);
|
|
m=m+1;
|
|
}
|
|
}
|
|
//--- Find feasible point.
|
|
//--- 0. Convert from unscaled values (as stored in XStart) to scaled
|
|
//--- ones
|
|
//--- 1. calculate values of slack variables such that starting
|
|
//--- point satisfies inequality constraints (after conversion to
|
|
//--- equality ones) as much as possible.
|
|
//--- 2. use PrepareConstraintMatrix() function,which forces X
|
|
//--- to be strictly feasible.
|
|
for(i=0;i<=nmain-1;i++)
|
|
state.m_tmp0[i]=state.m_xstart[i]/state.m_transforms[i];
|
|
//--- change value
|
|
m=0;
|
|
//--- calculation
|
|
for(i=0;i<=state.m_cecnt-1;i++)
|
|
{
|
|
v=0.0;
|
|
for(i_=0;i_<=nmain-1;i_++)
|
|
v+=state.m_ceeffective[i][i_]*state.m_tmp0[i_];
|
|
//--- check
|
|
if(state.m_ct[i]<0)
|
|
{
|
|
state.m_tmp0[nmain+m]=state.m_ceeffective[i][nmain+nslack]-v;
|
|
m=m+1;
|
|
}
|
|
//--- check
|
|
if(state.m_ct[i]>0)
|
|
{
|
|
state.m_tmp0[nmain+m]=v-state.m_ceeffective[i][nmain+nslack];
|
|
m=m+1;
|
|
}
|
|
}
|
|
//--- change values
|
|
for(i=0;i<=nmain+nslack-1;i++)
|
|
state.m_tmp1[i]=0;
|
|
for(i=0;i<=nmain+nslack-1;i++)
|
|
state.m_activeconstraints[i]=false;
|
|
//--- function call
|
|
b=PrepareConstraintMatrix(state,state.m_tmp0,state.m_tmp1,state.m_xcur,state.m_tmp2);
|
|
state.m_repdebugeqerr=0.0;
|
|
//--- calculation
|
|
for(i=0;i<=state.m_cecnt-1;i++)
|
|
{
|
|
v=0.0;
|
|
for(i_=0;i_<=nmain+nslack-1;i_++)
|
|
v+=state.m_ceeffective[i][i_]*state.m_xcur[i_];
|
|
state.m_repdebugeqerr=state.m_repdebugeqerr+CMath::Sqr(v-state.m_ceeffective[i][nmain+nslack]);
|
|
}
|
|
state.m_repdebugeqerr=MathSqrt(state.m_repdebugeqerr);
|
|
//--- check
|
|
if(!b)
|
|
{
|
|
state.m_repterminationtype=-3;
|
|
//--- return result
|
|
return(false);
|
|
}
|
|
//--- Initialize RepDebugFS with function value at initial point
|
|
UnscalePoint(state,state.m_xcur,state.m_x);
|
|
ClearRequestFields(state);
|
|
//--- check
|
|
if(state.m_diffstep!=0.0)
|
|
{
|
|
state.m_needf=true;
|
|
state.m_rstate.stage=1;
|
|
//--- Saving state
|
|
Func_lbl_rcomm(state,nmain,nslack,m,i,j,b,v,vv);
|
|
//--- return result
|
|
return(true);
|
|
}
|
|
//--- change values
|
|
state.m_needfg=true;
|
|
state.m_rstate.stage=0;
|
|
//--- Saving state
|
|
Func_lbl_rcomm(state,nmain,nslack,m,i,j,b,v,vv);
|
|
//--- return result
|
|
return(true);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Auxiliary function for MinBLEICIteration. Is a product to get rid|
|
|
//| of the operator unconditional jump goto. |
|
|
//+------------------------------------------------------------------+
|
|
static void CMinBLEIC::Func_lbl_rcomm(CMinBLEICState &state,int nmain,
|
|
int nslack,int m,int i,int j,
|
|
bool b,double v,double vv)
|
|
{
|
|
//--- save
|
|
state.m_rstate.ia[0]=nmain;
|
|
state.m_rstate.ia[1]=nslack;
|
|
state.m_rstate.ia[2]=m;
|
|
state.m_rstate.ia[3]=i;
|
|
state.m_rstate.ia[4]=j;
|
|
state.m_rstate.ba[0]=b;
|
|
state.m_rstate.ra[0]=v;
|
|
state.m_rstate.ra[1]=vv;
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Auxiliary function for MinBLEICIteration. Is a product to get rid|
|
|
//| of the operator unconditional jump goto. |
|
|
//+------------------------------------------------------------------+
|
|
static bool CMinBLEIC::Func_lbl_14(CMinBLEICState &state,int &nmain,
|
|
int &nslack,int &m,int &i,int &j,
|
|
bool &b,double &v,double &vv)
|
|
{
|
|
//--- function call
|
|
COptServ::TrimPrepare(state.m_f,state.m_trimthreshold);
|
|
state.m_repnfev=state.m_repnfev+1;
|
|
state.m_repdebugfs=state.m_f;
|
|
//--- Outer cycle
|
|
state.m_itsleft=state.m_maxits;
|
|
//--- copy
|
|
for(int i_=0;i_<=nmain+nslack-1;i_++)
|
|
state.m_xprev[i_]=state.m_xcur[i_];
|
|
//--- function call, return result
|
|
return(Func_lbl_15(state,nmain,nslack,m,i,j,b,v,vv));
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Auxiliary function for MinBLEICIteration. Is a product to get rid|
|
|
//| of the operator unconditional jump goto. |
|
|
//+------------------------------------------------------------------+
|
|
static bool CMinBLEIC::Func_lbl_15(CMinBLEICState &state,int &nmain,
|
|
int &nslack,int &m,int &i,int &j,
|
|
bool &b,double &v,double &vv)
|
|
{
|
|
//--- check
|
|
if(!CAp::Assert(state.m_prectype==0 || state.m_stpmax==0.0,"MinBLEIC: internal error (-10)"))
|
|
return(false);
|
|
//--- Inner cycle: CG with projections and penalty functions
|
|
for(int i_=0;i_<=nmain+nslack-1;i_++)
|
|
state.m_tmp0[i_]=state.m_xcur[i_];
|
|
for(i=0;i<=nmain+nslack-1;i++)
|
|
{
|
|
state.m_tmp1[i]=0;
|
|
state.m_activeconstraints[i]=false;
|
|
}
|
|
//--- check
|
|
if(!PrepareConstraintMatrix(state,state.m_tmp0,state.m_tmp1,state.m_xcur,state.m_tmp2))
|
|
{
|
|
state.m_repterminationtype=-3;
|
|
//--- return result
|
|
return(false);
|
|
}
|
|
for(i=0;i<=nmain+nslack-1;i++)
|
|
state.m_activeconstraints[i]=false;
|
|
RebuildCEXE(state);
|
|
//--- function call
|
|
CMinCG::MinCGRestartFrom(state.m_cgstate,state.m_xcur);
|
|
//--- function call
|
|
CMinCG::MinCGSetCond(state.m_cgstate,state.m_innerepsg,state.m_innerepsf,state.m_innerepsx,state.m_itsleft);
|
|
//--- function call
|
|
CMinCG::MinCGSetXRep(state.m_cgstate,state.m_xrep);
|
|
//--- function call
|
|
CMinCG::MinCGSetDRep(state.m_cgstate,true);
|
|
//--- function call
|
|
CMinCG::MinCGSetStpMax(state.m_cgstate,state.m_stpmax);
|
|
//--- function call, return result
|
|
return(Func_lbl_17(state,nmain,nslack,m,i,j,b,v,vv));
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Auxiliary function for MinBLEICIteration. Is a product to get rid|
|
|
//| of the operator unconditional jump goto. |
|
|
//+------------------------------------------------------------------+
|
|
static bool CMinBLEIC::Func_lbl_16(CMinBLEICState &state,int &nmain,
|
|
int &nslack,int &m,int &i,int &j,
|
|
bool &b,double &v,double &vv)
|
|
{
|
|
//--- We've stopped,fill debug information
|
|
state.m_repdebugeqerr=0.0;
|
|
for(i=0;i<=state.m_cecnt-1;i++)
|
|
{
|
|
//--- change value
|
|
v=0.0;
|
|
for(int i_=0;i_<=nmain+nslack-1;i_++)
|
|
v+=state.m_ceeffective[i][i_]*state.m_xcur[i_];
|
|
state.m_repdebugeqerr=state.m_repdebugeqerr+CMath::Sqr(v-state.m_ceeffective[i][nmain+nslack]);
|
|
}
|
|
//--- change values
|
|
state.m_repdebugeqerr=MathSqrt(state.m_repdebugeqerr);
|
|
state.m_repdebugdx=0;
|
|
for(i=0;i<=nmain-1;i++)
|
|
state.m_repdebugdx=state.m_repdebugdx+CMath::Sqr(state.m_xcur[i]-state.m_xstart[i]);
|
|
state.m_repdebugdx=MathSqrt(state.m_repdebugdx);
|
|
//--- return result
|
|
return(false);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Auxiliary function for MinBLEICIteration. Is a product to get rid|
|
|
//| of the operator unconditional jump goto. |
|
|
//+------------------------------------------------------------------+
|
|
static bool CMinBLEIC::Func_lbl_17(CMinBLEICState &state,int &nmain,
|
|
int &nslack,int &m,int &i,int &j,
|
|
bool &b,double &v,double &vv)
|
|
{
|
|
//--- check
|
|
if(!CMinCG::MinCGIteration(state.m_cgstate))
|
|
return(Func_lbl_18(state,nmain,nslack,m,i,j,b,v,vv));
|
|
//--- process different requests/reports of inner optimizer
|
|
if(state.m_cgstate.m_algpowerup)
|
|
{
|
|
for(i=0;i<=nmain+nslack-1;i++)
|
|
state.m_activeconstraints[i]=false;
|
|
//--- cycle
|
|
do
|
|
{
|
|
//--- function call
|
|
RebuildCEXE(state);
|
|
for(int i_=0;i_<=nmain+nslack-1;i_++)
|
|
state.m_tmp1[i_]=state.m_cgstate.m_g[i_];
|
|
//--- function call
|
|
MakeGradientProjection(state,state.m_tmp1);
|
|
b=false;
|
|
for(i=0;i<=nmain-1;i++)
|
|
{
|
|
//--- check
|
|
if(!state.m_activeconstraints[i])
|
|
{
|
|
//--- check
|
|
if(state.m_hasbndl[i])
|
|
{
|
|
//--- check
|
|
if(state.m_cgstate.m_x[i]==state.m_bndleffective[i] && state.m_tmp1[i]>=0.0)
|
|
{
|
|
//--- change values
|
|
state.m_activeconstraints[i]=true;
|
|
state.m_constrainedvalues[i]=state.m_bndleffective[i];
|
|
b=true;
|
|
}
|
|
}
|
|
//--- check
|
|
if(state.m_hasbndu[i])
|
|
{
|
|
//--- check
|
|
if(state.m_cgstate.m_x[i]==state.m_bndueffective[i] && state.m_tmp1[i]<=0.0)
|
|
{
|
|
//--- change values
|
|
state.m_activeconstraints[i]=true;
|
|
state.m_constrainedvalues[i]=state.m_bndueffective[i];
|
|
b=true;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
for(i=0;i<=nslack-1;i++)
|
|
{
|
|
//--- check
|
|
if(!state.m_activeconstraints[nmain+i])
|
|
{
|
|
//--- check
|
|
if(state.m_cgstate.m_x[nmain+i]==0.0 && state.m_tmp1[nmain+i]>=0.0)
|
|
{
|
|
//--- change values
|
|
state.m_activeconstraints[nmain+i]=true;
|
|
state.m_constrainedvalues[nmain+i]=0;
|
|
b=true;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
while(b);
|
|
//--- copy
|
|
for(int i_=0;i_<=nmain+nslack-1;i_++)
|
|
state.m_cgstate.m_g[i_]=state.m_tmp1[i_];
|
|
//--- function call, return result
|
|
return(Func_lbl_17(state,nmain,nslack,m,i,j,b,v,vv));
|
|
}
|
|
//--- check
|
|
if(state.m_cgstate.m_lsstart)
|
|
{
|
|
//--- Beginning of the line search: set upper limit on step size
|
|
//--- to prevent algo from leaving feasible area.
|
|
state.m_variabletofreeze=-1;
|
|
//--- check
|
|
if((double)(state.m_cgstate.m_curstpmax)==0.0)
|
|
state.m_cgstate.m_curstpmax=1.0E50;
|
|
for(i=0;i<=nmain-1;i++)
|
|
{
|
|
//--- check
|
|
if(state.m_hasbndl[i] && state.m_cgstate.m_d[i]<0.0)
|
|
{
|
|
//--- change values
|
|
v=state.m_cgstate.m_curstpmax;
|
|
vv=state.m_cgstate.m_x[i]-state.m_bndleffective[i];
|
|
//--- check
|
|
if(vv<0.0)
|
|
vv=0;
|
|
state.m_cgstate.m_curstpmax=CApServ::SafeMinPosRV(vv,-state.m_cgstate.m_d[i],state.m_cgstate.m_curstpmax);
|
|
//--- check
|
|
if(state.m_cgstate.m_curstpmax<v)
|
|
{
|
|
state.m_variabletofreeze=i;
|
|
state.m_valuetofreeze=state.m_bndleffective[i];
|
|
}
|
|
}
|
|
//--- check
|
|
if(state.m_hasbndu[i] && state.m_cgstate.m_d[i]>0.0)
|
|
{
|
|
//--- change values
|
|
v=state.m_cgstate.m_curstpmax;
|
|
vv=state.m_bndueffective[i]-state.m_cgstate.m_x[i];
|
|
//--- check
|
|
if(vv<0.0)
|
|
vv=0;
|
|
state.m_cgstate.m_curstpmax=CApServ::SafeMinPosRV(vv,state.m_cgstate.m_d[i],state.m_cgstate.m_curstpmax);
|
|
//--- check
|
|
if(state.m_cgstate.m_curstpmax<v)
|
|
{
|
|
state.m_variabletofreeze=i;
|
|
state.m_valuetofreeze=state.m_bndueffective[i];
|
|
}
|
|
}
|
|
}
|
|
for(i=0;i<=nslack-1;i++)
|
|
{
|
|
//--- check
|
|
if(state.m_cgstate.m_d[nmain+i]<0.0)
|
|
{
|
|
//--- change values
|
|
v=state.m_cgstate.m_curstpmax;
|
|
vv=state.m_cgstate.m_x[nmain+i];
|
|
//--- check
|
|
if(vv<0.0)
|
|
vv=0;
|
|
state.m_cgstate.m_curstpmax=CApServ::SafeMinPosRV(vv,-state.m_cgstate.m_d[nmain+i],state.m_cgstate.m_curstpmax);
|
|
//--- check
|
|
if(state.m_cgstate.m_curstpmax<v)
|
|
{
|
|
state.m_variabletofreeze=nmain+i;
|
|
state.m_valuetofreeze=0;
|
|
}
|
|
}
|
|
}
|
|
//--- check
|
|
if(state.m_cgstate.m_curstpmax==0.0)
|
|
{
|
|
//--- change values
|
|
state.m_activeconstraints[state.m_variabletofreeze]=true;
|
|
state.m_constrainedvalues[state.m_variabletofreeze]=state.m_valuetofreeze;
|
|
state.m_cgstate.m_x[state.m_variabletofreeze]=state.m_valuetofreeze;
|
|
state.m_cgstate.m_terminationneeded=true;
|
|
}
|
|
//--- function call, return result
|
|
return(Func_lbl_17(state,nmain,nslack,m,i,j,b,v,vv));
|
|
}
|
|
//--- check
|
|
if(state.m_cgstate.m_lsend)
|
|
{
|
|
//--- Line search just finished.
|
|
//--- Maybe we should activate some constraints?
|
|
b=state.m_cgstate.m_stp>=state.m_cgstate.m_curstpmax && state.m_variabletofreeze>=0;
|
|
//--- check
|
|
if(b)
|
|
{
|
|
state.m_activeconstraints[state.m_variabletofreeze]=true;
|
|
state.m_constrainedvalues[state.m_variabletofreeze]=state.m_valuetofreeze;
|
|
}
|
|
//--- Additional activation of constraints
|
|
b=b || AdditionalCheckForConstraints(state,state.m_cgstate.m_x);
|
|
//--- If at least one constraint was activated we have to rebuild constraint matrices
|
|
if(b)
|
|
{
|
|
//--- copy
|
|
for(int i_=0;i_<=nmain+nslack-1;i_++)
|
|
state.m_tmp0[i_]=state.m_cgstate.m_x[i_];
|
|
for(int i_=0;i_<=nmain+nslack-1;i_++)
|
|
state.m_tmp1[i_]=state.m_lastg[i_];
|
|
//--- check
|
|
if(!PrepareConstraintMatrix(state,state.m_tmp0,state.m_tmp1,state.m_cgstate.m_x,state.m_cgstate.m_g))
|
|
{
|
|
state.m_repterminationtype=-3;
|
|
//--- return result
|
|
return(false);
|
|
}
|
|
state.m_cgstate.m_innerresetneeded=true;
|
|
}
|
|
//--- function call, return result
|
|
return(Func_lbl_17(state,nmain,nslack,m,i,j,b,v,vv));
|
|
}
|
|
//--- check
|
|
if(!state.m_cgstate.m_needfg)
|
|
return(Func_lbl_19(state,nmain,nslack,m,i,j,b,v,vv));
|
|
//--- copy
|
|
for(int i_=0;i_<=nmain+nslack-1;i_++)
|
|
state.m_tmp1[i_]=state.m_cgstate.m_x[i_];
|
|
//--- function call
|
|
ProjectPointAndUnscale(state,state.m_tmp1,state.m_x,state.m_r,vv);
|
|
//--- function call
|
|
ClearRequestFields(state);
|
|
//--- check
|
|
if(state.m_diffstep!=0.0)
|
|
{
|
|
//--- change values
|
|
state.m_needf=true;
|
|
state.m_rstate.stage=3;
|
|
//--- Saving state
|
|
Func_lbl_rcomm(state,nmain,nslack,m,i,j,b,v,vv);
|
|
//--- return result
|
|
return(true);
|
|
}
|
|
//--- change values
|
|
state.m_needfg=true;
|
|
state.m_rstate.stage=2;
|
|
//--- Saving state
|
|
Func_lbl_rcomm(state,nmain,nslack,m,i,j,b,v,vv);
|
|
//--- return result
|
|
return(true);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Auxiliary function for MinBLEICIteration. Is a product to get rid|
|
|
//| of the operator unconditional jump goto. |
|
|
//+------------------------------------------------------------------+
|
|
static bool CMinBLEIC::Func_lbl_18(CMinBLEICState &state,int &nmain,
|
|
int &nslack,int &m,int &i,int &j,
|
|
bool &b,double &v,double &vv)
|
|
{
|
|
//--- function call
|
|
CMinCG::MinCGResults(state.m_cgstate,state.m_xcur,state.m_cgrep);
|
|
//--- function call
|
|
UnscalePoint(state,state.m_xcur,state.m_xend);
|
|
//--- change values
|
|
state.m_repinneriterationscount=state.m_repinneriterationscount+state.m_cgrep.m_iterationscount;
|
|
state.m_repouteriterationscount=state.m_repouteriterationscount+1;
|
|
state.m_repnfev=state.m_repnfev+state.m_cgrep.m_nfev;
|
|
//--- Update RepDebugFF with function value at current point
|
|
UnscalePoint(state,state.m_xcur,state.m_x);
|
|
ClearRequestFields(state);
|
|
//--- check
|
|
if(state.m_diffstep!=0.0)
|
|
{
|
|
//--- change values
|
|
state.m_needf=true;
|
|
state.m_rstate.stage=12;
|
|
//--- Saving state
|
|
Func_lbl_rcomm(state,nmain,nslack,m,i,j,b,v,vv);
|
|
//--- return result
|
|
return(true);
|
|
}
|
|
//--- change values
|
|
state.m_needfg=true;
|
|
state.m_rstate.stage=11;
|
|
//--- Saving state
|
|
Func_lbl_rcomm(state,nmain,nslack,m,i,j,b,v,vv);
|
|
//--- return result
|
|
return(true);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Auxiliary function for MinBLEICIteration. Is a product to get rid|
|
|
//| of the operator unconditional jump goto. |
|
|
//+------------------------------------------------------------------+
|
|
static bool CMinBLEIC::Func_lbl_19(CMinBLEICState &state,int &nmain,
|
|
int &nslack,int &m,int &i,int &j,
|
|
bool &b,double &v,double &vv)
|
|
{
|
|
//--- check
|
|
if(!state.m_cgstate.m_xupdated)
|
|
return(Func_lbl_17(state,nmain,nslack,m,i,j,b,v,vv));
|
|
//--- Report
|
|
UnscalePoint(state,state.m_cgstate.m_x,state.m_x);
|
|
state.m_f=state.m_cgstate.m_f;
|
|
//--- function call
|
|
ClearRequestFields(state);
|
|
//--- change values
|
|
state.m_xupdated=true;
|
|
state.m_rstate.stage=10;
|
|
//--- Saving state
|
|
Func_lbl_rcomm(state,nmain,nslack,m,i,j,b,v,vv);
|
|
//--- return result
|
|
return(true);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Auxiliary function for MinBLEICIteration. Is a product to get rid|
|
|
//| of the operator unconditional jump goto. |
|
|
//+------------------------------------------------------------------+
|
|
static bool CMinBLEIC::Func_lbl_22(CMinBLEICState &state,int &nmain,
|
|
int &nslack,int &m,int &i,int &j,
|
|
bool &b,double &v,double &vv)
|
|
{
|
|
//--- check
|
|
if(state.m_f<state.m_trimthreshold)
|
|
{
|
|
//--- normal processing
|
|
state.m_cgstate.m_f=state.m_f;
|
|
//--- function call
|
|
ScaleGradientAndExpand(state,state.m_g,state.m_cgstate.m_g);
|
|
//--- copy
|
|
for(int i_=0;i_<=nmain+nslack-1;i_++)
|
|
state.m_lastg[i_]=state.m_cgstate.m_g[i_];
|
|
//--- function call
|
|
ModifyTargetFunction(state,state.m_tmp1,state.m_r,vv,state.m_cgstate.m_f,state.m_cgstate.m_g,state.m_gnorm,state.m_mpgnorm);
|
|
}
|
|
else
|
|
{
|
|
//--- function value is too high,trim it
|
|
state.m_cgstate.m_f=state.m_trimthreshold;
|
|
for(i=0;i<=nmain+nslack-1;i++)
|
|
state.m_cgstate.m_g[i]=0.0;
|
|
}
|
|
//--- function call, return result
|
|
return(Func_lbl_17(state,nmain,nslack,m,i,j,b,v,vv));
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Auxiliary function for MinBLEICIteration. Is a product to get rid|
|
|
//| of the operator unconditional jump goto. |
|
|
//+------------------------------------------------------------------+
|
|
static bool CMinBLEIC::Func_lbl_23(CMinBLEICState &state,int &nmain,
|
|
int &nslack,int &m,int &i,int &j,
|
|
bool &b,double &v,double &vv)
|
|
{
|
|
//--- check
|
|
if(i>nmain-1)
|
|
{
|
|
//--- change values
|
|
state.m_f=state.m_fbase;
|
|
state.m_needf=false;
|
|
//--- function call, return result
|
|
return(Func_lbl_22(state,nmain,nslack,m,i,j,b,v,vv));
|
|
}
|
|
//--- change values
|
|
v=state.m_x[i];
|
|
b=false;
|
|
//--- check
|
|
if(state.m_hasbndl[i])
|
|
b=b || v-state.m_diffstep*state.m_soriginal[i]<state.m_bndloriginal[i];
|
|
//--- check
|
|
if(state.m_hasbndu[i])
|
|
b=b || v+state.m_diffstep*state.m_soriginal[i]>state.m_bnduoriginal[i];
|
|
//--- check
|
|
if(b)
|
|
{
|
|
//--- change values
|
|
state.m_xm1=MathMax(v-state.m_diffstep*state.m_soriginal[i],state.m_bndloriginal[i]);
|
|
state.m_x[i]=state.m_xm1;
|
|
state.m_rstate.stage=8;
|
|
//--- Saving state
|
|
Func_lbl_rcomm(state,nmain,nslack,m,i,j,b,v,vv);
|
|
//--- return result
|
|
return(true);
|
|
}
|
|
//--- change values
|
|
state.m_x[i]=v-state.m_diffstep*state.m_soriginal[i];
|
|
state.m_rstate.stage=4;
|
|
//--- Saving state
|
|
Func_lbl_rcomm(state,nmain,nslack,m,i,j,b,v,vv);
|
|
//--- return result
|
|
return(true);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Auxiliary function for MinBLEICIteration. Is a product to get rid|
|
|
//| of the operator unconditional jump goto. |
|
|
//+------------------------------------------------------------------+
|
|
static bool CMinBLEIC::Func_lbl_31(CMinBLEICState &state,int &nmain,
|
|
int &nslack,int &m,int &i,int &j,
|
|
bool &b,double &v,double &vv)
|
|
{
|
|
//--- change values
|
|
state.m_repnfev=state.m_repnfev+1;
|
|
state.m_repdebugff=state.m_f;
|
|
//--- Check for stopping:
|
|
//--- * "normal",outer step size is small enough,infeasibility is within bounds
|
|
//--- * "inconsistent", if Lagrange multipliers increased beyond threshold given by MaxLagrangeMul
|
|
//--- * "too stringent",in other cases
|
|
v=0;
|
|
for(i=0;i<=nmain-1;i++)
|
|
v=v+CMath::Sqr((state.m_xcur[i]-state.m_xprev[i])/state.m_seffective[i]);
|
|
v=MathSqrt(v);
|
|
//--- check
|
|
if(v<=(double)(state.m_outerepsx))
|
|
{
|
|
state.m_repterminationtype=4;
|
|
//--- function call, return result
|
|
return(Func_lbl_16(state,nmain,nslack,m,i,j,b,v,vv));
|
|
}
|
|
//--- check
|
|
if(state.m_maxits>0)
|
|
{
|
|
state.m_itsleft=state.m_itsleft-state.m_cgrep.m_iterationscount;
|
|
//--- check
|
|
if(state.m_itsleft<=0)
|
|
{
|
|
state.m_repterminationtype=5;
|
|
//--- function call, return result
|
|
return(Func_lbl_16(state,nmain,nslack,m,i,j,b,v,vv));
|
|
}
|
|
}
|
|
//--- check
|
|
if(state.m_repouteriterationscount>=m_maxouterits)
|
|
{
|
|
state.m_repterminationtype=5;
|
|
//--- function call, return result
|
|
return(Func_lbl_16(state,nmain,nslack,m,i,j,b,v,vv));
|
|
}
|
|
//--- Next iteration
|
|
for(int i_=0;i_<=nmain+nslack-1;i_++)
|
|
state.m_xprev[i_]=state.m_xcur[i_];
|
|
//--- function call, return result
|
|
return(Func_lbl_15(state,nmain,nslack,m,i,j,b,v,vv));
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Auxiliary class for CMinLBFGS |
|
|
//+------------------------------------------------------------------+
|
|
class CMinLBFGSState
|
|
{
|
|
public:
|
|
//--- variables
|
|
int m_n;
|
|
int m_m;
|
|
double m_epsg;
|
|
double m_epsf;
|
|
double m_epsx;
|
|
int m_maxits;
|
|
bool m_xrep;
|
|
double m_stpmax;
|
|
double m_diffstep;
|
|
int m_nfev;
|
|
int m_mcstage;
|
|
int m_k;
|
|
int m_q;
|
|
int m_p;
|
|
double m_stp;
|
|
double m_fold;
|
|
double m_trimthreshold;
|
|
int m_prectype;
|
|
double m_gammak;
|
|
double m_fbase;
|
|
double m_fm2;
|
|
double m_fm1;
|
|
double m_fp1;
|
|
double m_fp2;
|
|
double m_f;
|
|
bool m_needf;
|
|
bool m_needfg;
|
|
bool m_xupdated;
|
|
RCommState m_rstate;
|
|
int m_repiterationscount;
|
|
int m_repnfev;
|
|
int m_repterminationtype;
|
|
CLinMinState m_lstate;
|
|
//--- arrays
|
|
double m_s[];
|
|
double m_rho[];
|
|
double m_theta[];
|
|
double m_d[];
|
|
double m_work[];
|
|
double m_diagh[];
|
|
double m_autobuf[];
|
|
double m_x[];
|
|
double m_g[];
|
|
//--- matrix
|
|
CMatrixDouble m_yk;
|
|
CMatrixDouble m_sk;
|
|
CMatrixDouble m_denseh;
|
|
//--- constructor, destructor
|
|
CMinLBFGSState(void);
|
|
~CMinLBFGSState(void);
|
|
//--- copy
|
|
void Copy(CMinLBFGSState &obj);
|
|
};
|
|
//+------------------------------------------------------------------+
|
|
//| Constructor without parameters |
|
|
//+------------------------------------------------------------------+
|
|
CMinLBFGSState::CMinLBFGSState(void)
|
|
{
|
|
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Destructor |
|
|
//+------------------------------------------------------------------+
|
|
CMinLBFGSState::~CMinLBFGSState(void)
|
|
{
|
|
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Copy |
|
|
//+------------------------------------------------------------------+
|
|
void CMinLBFGSState::Copy(CMinLBFGSState &obj)
|
|
{
|
|
//--- copy variables
|
|
m_n=obj.m_n;
|
|
m_m=obj.m_m;
|
|
m_epsg=obj.m_epsg;
|
|
m_epsf=obj.m_epsf;
|
|
m_epsx=obj.m_epsx;
|
|
m_maxits=obj.m_maxits;
|
|
m_xrep=obj.m_xrep;
|
|
m_stpmax=obj.m_stpmax;
|
|
m_diffstep=obj.m_diffstep;
|
|
m_nfev=obj.m_nfev;
|
|
m_mcstage=obj.m_mcstage;
|
|
m_k=obj.m_k;
|
|
m_q=obj.m_q;
|
|
m_p=obj.m_p;
|
|
m_stp=obj.m_stp;
|
|
m_fold=obj.m_fold;
|
|
m_trimthreshold=obj.m_trimthreshold;
|
|
m_prectype=obj.m_prectype;
|
|
m_gammak=obj.m_gammak;
|
|
m_fbase=obj.m_fbase;
|
|
m_fm2=obj.m_fm2;
|
|
m_fm1=obj.m_fm1;
|
|
m_fp1=obj.m_fp1;
|
|
m_fp2=obj.m_fp2;
|
|
m_f=obj.m_f;
|
|
m_needf=obj.m_needf;
|
|
m_needfg=obj.m_needfg;
|
|
m_xupdated=obj.m_xupdated;
|
|
m_repiterationscount=obj.m_repiterationscount;
|
|
m_repnfev=obj.m_repnfev;
|
|
m_repterminationtype=obj.m_repterminationtype;
|
|
m_rstate.Copy(obj.m_rstate);
|
|
m_lstate.Copy(obj.m_lstate);
|
|
//--- copy arrays
|
|
ArrayCopy(m_s,obj.m_s);
|
|
ArrayCopy(m_rho,obj.m_rho);
|
|
ArrayCopy(m_theta,obj.m_theta);
|
|
ArrayCopy(m_d,obj.m_d);
|
|
ArrayCopy(m_work,obj.m_work);
|
|
ArrayCopy(m_diagh,obj.m_diagh);
|
|
ArrayCopy(m_autobuf,obj.m_autobuf);
|
|
ArrayCopy(m_x,obj.m_x);
|
|
ArrayCopy(m_g,obj.m_g);
|
|
//--- copy matrix
|
|
m_yk=obj.m_yk;
|
|
m_sk=obj.m_sk;
|
|
m_denseh=obj.m_denseh;
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| This class is a shell for class CMinLBFGSState |
|
|
//+------------------------------------------------------------------+
|
|
class CMinLBFGSStateShell
|
|
{
|
|
private:
|
|
CMinLBFGSState m_innerobj;
|
|
public:
|
|
//--- constructors, destructor
|
|
CMinLBFGSStateShell(void);
|
|
CMinLBFGSStateShell(CMinLBFGSState &obj);
|
|
~CMinLBFGSStateShell(void);
|
|
//--- methods
|
|
bool GetNeedF(void);
|
|
void SetNeedF(const bool b);
|
|
bool GetNeedFG(void);
|
|
void SetNeedFG(const bool b);
|
|
bool GetXUpdated(void);
|
|
void SetXUpdated(const bool b);
|
|
double GetF(void);
|
|
void SetF(const double d);
|
|
CMinLBFGSState *GetInnerObj(void);
|
|
};
|
|
//+------------------------------------------------------------------+
|
|
//| Constructor without parameters |
|
|
//+------------------------------------------------------------------+
|
|
CMinLBFGSStateShell::CMinLBFGSStateShell(void)
|
|
{
|
|
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Copy constructor |
|
|
//+------------------------------------------------------------------+
|
|
CMinLBFGSStateShell::CMinLBFGSStateShell(CMinLBFGSState &obj)
|
|
{
|
|
//--- copy
|
|
m_innerobj.Copy(obj);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Destructor |
|
|
//+------------------------------------------------------------------+
|
|
CMinLBFGSStateShell::~CMinLBFGSStateShell(void)
|
|
{
|
|
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Returns the value of the variable needf |
|
|
//+------------------------------------------------------------------+
|
|
bool CMinLBFGSStateShell::GetNeedF(void)
|
|
{
|
|
//--- return result
|
|
return(m_innerobj.m_needf);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Changing the value of the variable needf |
|
|
//+------------------------------------------------------------------+
|
|
void CMinLBFGSStateShell::SetNeedF(const bool b)
|
|
{
|
|
//--- change value
|
|
m_innerobj.m_needf=b;
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Returns the value of the variable needfg |
|
|
//+------------------------------------------------------------------+
|
|
bool CMinLBFGSStateShell::GetNeedFG(void)
|
|
{
|
|
//--- return result
|
|
return(m_innerobj.m_needfg);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Changing the value of the variable needfg |
|
|
//+------------------------------------------------------------------+
|
|
void CMinLBFGSStateShell::SetNeedFG(const bool b)
|
|
{
|
|
//--- change value
|
|
m_innerobj.m_needfg=b;
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Returns the value of the variable xupdated |
|
|
//+------------------------------------------------------------------+
|
|
bool CMinLBFGSStateShell::GetXUpdated(void)
|
|
{
|
|
//--- return result
|
|
return(m_innerobj.m_xupdated);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Changing the value of the variable xupdated |
|
|
//+------------------------------------------------------------------+
|
|
void CMinLBFGSStateShell::SetXUpdated(const bool b)
|
|
{
|
|
//--- change value
|
|
m_innerobj.m_xupdated=b;
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Returns the value of the variable f |
|
|
//+------------------------------------------------------------------+
|
|
double CMinLBFGSStateShell::GetF(void)
|
|
{
|
|
//--- return result
|
|
return(m_innerobj.m_f);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Changing the value of the variable f |
|
|
//+------------------------------------------------------------------+
|
|
void CMinLBFGSStateShell::SetF(const double d)
|
|
{
|
|
//--- change value
|
|
m_innerobj.m_f=d;
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Return object of class |
|
|
//+------------------------------------------------------------------+
|
|
CMinLBFGSState *CMinLBFGSStateShell::GetInnerObj(void)
|
|
{
|
|
//--- return result
|
|
return(GetPointer(m_innerobj));
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Auxiliary class for CMinLFBFGS |
|
|
//+------------------------------------------------------------------+
|
|
class CMinLBFGSReport
|
|
{
|
|
public:
|
|
//--- variables
|
|
int m_iterationscount;
|
|
int m_nfev;
|
|
int m_terminationtype;
|
|
//--- constructor, destructor
|
|
CMinLBFGSReport(void);
|
|
~CMinLBFGSReport(void);
|
|
//--- copy
|
|
void Copy(CMinLBFGSReport &obj);
|
|
};
|
|
//+------------------------------------------------------------------+
|
|
//| Constructor without parameters |
|
|
//+------------------------------------------------------------------+
|
|
CMinLBFGSReport::CMinLBFGSReport(void)
|
|
{
|
|
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Destructor |
|
|
//+------------------------------------------------------------------+
|
|
CMinLBFGSReport::~CMinLBFGSReport(void)
|
|
{
|
|
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Copy |
|
|
//+------------------------------------------------------------------+
|
|
void CMinLBFGSReport::Copy(CMinLBFGSReport &obj)
|
|
{
|
|
//--- copy variables
|
|
m_iterationscount=obj.m_iterationscount;
|
|
m_nfev=obj.m_nfev;
|
|
m_terminationtype=obj.m_terminationtype;
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| This class is a shell for class CMinLBFGSReport |
|
|
//+------------------------------------------------------------------+
|
|
class CMinLBFGSReportShell
|
|
{
|
|
private:
|
|
CMinLBFGSReport m_innerobj;
|
|
public:
|
|
//--- constructors, destructor
|
|
CMinLBFGSReportShell(void);
|
|
CMinLBFGSReportShell(CMinLBFGSReport &obj);
|
|
~CMinLBFGSReportShell(void);
|
|
//--- methods
|
|
int GetIterationsCount(void);
|
|
void SetIterationsCount(const int i);
|
|
int GetNFev(void);
|
|
void SetNFev(const int i);
|
|
int GetTerminationType(void);
|
|
void SetTerminationType(const int i);
|
|
CMinLBFGSReport *GetInnerObj(void);
|
|
};
|
|
//+------------------------------------------------------------------+
|
|
//| Constructor without parameters |
|
|
//+------------------------------------------------------------------+
|
|
CMinLBFGSReportShell::CMinLBFGSReportShell(void)
|
|
{
|
|
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Copy constructor |
|
|
//+------------------------------------------------------------------+
|
|
CMinLBFGSReportShell::CMinLBFGSReportShell(CMinLBFGSReport &obj)
|
|
{
|
|
//--- copy
|
|
m_innerobj.Copy(obj);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Destructor |
|
|
//+------------------------------------------------------------------+
|
|
CMinLBFGSReportShell::~CMinLBFGSReportShell(void)
|
|
{
|
|
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Returns the value of the variable iterationscount |
|
|
//+------------------------------------------------------------------+
|
|
int CMinLBFGSReportShell::GetIterationsCount(void)
|
|
{
|
|
//--- return result
|
|
return(m_innerobj.m_iterationscount);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Changing the value of the variable iterationscount |
|
|
//+------------------------------------------------------------------+
|
|
void CMinLBFGSReportShell::SetIterationsCount(const int i)
|
|
{
|
|
//--- change value
|
|
m_innerobj.m_iterationscount=i;
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Returns the value of the variable nfev |
|
|
//+------------------------------------------------------------------+
|
|
int CMinLBFGSReportShell::GetNFev(void)
|
|
{
|
|
//--- return result
|
|
return(m_innerobj.m_nfev);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Changing the value of the variable nfev |
|
|
//+------------------------------------------------------------------+
|
|
void CMinLBFGSReportShell::SetNFev(const int i)
|
|
{
|
|
//--- change value
|
|
m_innerobj.m_nfev=i;
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Returns the value of the variable terminationtype |
|
|
//+------------------------------------------------------------------+
|
|
int CMinLBFGSReportShell::GetTerminationType(void)
|
|
{
|
|
//--- return result
|
|
return(m_innerobj.m_terminationtype);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Changing the value of the variable terminationtype |
|
|
//+------------------------------------------------------------------+
|
|
void CMinLBFGSReportShell::SetTerminationType(const int i)
|
|
{
|
|
//--- change value
|
|
m_innerobj.m_terminationtype=i;
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Return object of class |
|
|
//+------------------------------------------------------------------+
|
|
CMinLBFGSReport *CMinLBFGSReportShell::GetInnerObj(void)
|
|
{
|
|
//--- return result
|
|
return(GetPointer(m_innerobj));
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Limited memory BFGS method for large scale optimization |
|
|
//+------------------------------------------------------------------+
|
|
class CMinLBFGS
|
|
{
|
|
private:
|
|
//--- private method
|
|
static void ClearRequestFields(CMinLBFGSState &state);
|
|
//--- auxiliary functions for MinLBFGSiteration
|
|
static void Func_lbl_rcomm(CMinLBFGSState &state,int n,int m,int i,int j,int ic,int mcinfo,double v,double vv);
|
|
static bool Func_lbl_16(CMinLBFGSState &state,int &n,int &m,int &i,int &j,int &ic,int &mcinfo,double &v,double &vv);
|
|
static bool Func_lbl_19(CMinLBFGSState &state,int &n,int &m,int &i,int &j,int &ic,int &mcinfo,double &v,double &vv);
|
|
static bool Func_lbl_21(CMinLBFGSState &state,int &n,int &m,int &i,int &j,int &ic,int &mcinfo,double &v,double &vv);
|
|
static bool Func_lbl_23(CMinLBFGSState &state,int &n,int &m,int &i,int &j,int &ic,int &mcinfo,double &v,double &vv);
|
|
static bool Func_lbl_27(CMinLBFGSState &state,int &n,int &m,int &i,int &j,int &ic,int &mcinfo,double &v,double &vv);
|
|
static bool Func_lbl_30(CMinLBFGSState &state,int &n,int &m,int &i,int &j,int &ic,int &mcinfo,double &v,double &vv);
|
|
public:
|
|
//--- constant
|
|
static const double m_gtol;
|
|
//--- constructor, destructor
|
|
CMinLBFGS(void);
|
|
~CMinLBFGS(void);
|
|
//--- public methods
|
|
static void MinLBFGSCreate(const int n,const int m,double &x[],CMinLBFGSState &state);
|
|
static void MinLBFGSCreateF(const int n,const int m,double &x[],const double diffstep,CMinLBFGSState &state);
|
|
static void MinLBFGSSetCond(CMinLBFGSState &state,const double epsg,const double epsf,double epsx,const int maxits);
|
|
static void MinLBFGSSetXRep(CMinLBFGSState &state,const bool needxrep);
|
|
static void MinLBFGSSetStpMax(CMinLBFGSState &state,const double stpmax);
|
|
static void MinLBFGSSetScale(CMinLBFGSState &state,double &s[]);
|
|
static void MinLBFGSCreateX(const int n,const int m,double &x[],int flags,const double diffstep,CMinLBFGSState &state);
|
|
static void MinLBFGSSetPrecDefault(CMinLBFGSState &state);
|
|
static void MinLBFGSSetPrecCholesky(CMinLBFGSState &state,CMatrixDouble &p,const bool isupper);
|
|
static void MinLBFGSSetPrecDiag(CMinLBFGSState &state,double &d[]);
|
|
static void MinLBFGSSetPrecScale(CMinLBFGSState &state);
|
|
static void MinLBFGSResults(CMinLBFGSState &state,double &x[],CMinLBFGSReport &rep);
|
|
static void MinLBFGSresultsbuf(CMinLBFGSState &state,double &x[],CMinLBFGSReport &rep);
|
|
static void MinLBFGSRestartFrom(CMinLBFGSState &state,double &x[]);
|
|
static bool MinLBFGSIteration(CMinLBFGSState &state);
|
|
};
|
|
//+------------------------------------------------------------------+
|
|
//| Initialize constants |
|
|
//+------------------------------------------------------------------+
|
|
const double CMinLBFGS::m_gtol=0.4;
|
|
//+------------------------------------------------------------------+
|
|
//| Constructor without parameters |
|
|
//+------------------------------------------------------------------+
|
|
CMinLBFGS::CMinLBFGS(void)
|
|
{
|
|
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Destructor |
|
|
//+------------------------------------------------------------------+
|
|
CMinLBFGS::~CMinLBFGS(void)
|
|
{
|
|
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| LIMITED MEMORY BFGS METHOD FOR LARGE SCALE OPTIMIZATION |
|
|
//| DESCRIPTION: |
|
|
//| The subroutine minimizes function F(x) of N arguments by using a |
|
|
//| quasi - Newton method (LBFGS scheme) which is optimized to use a |
|
|
//| minimum amount of memory. |
|
|
//| The subroutine generates the approximation of an inverse Hessian |
|
|
//| matrix by using information about the last M steps of the |
|
|
//| algorithm (instead of N). It lessens a required amount of memory |
|
|
//| from a value of order N^2 to a value of order 2*N*M. |
|
|
//| REQUIREMENTS: |
|
|
//| Algorithm will request following information during its |
|
|
//| operation: |
|
|
//| * function value F and its gradient G (simultaneously) at given |
|
|
//| point X |
|
|
//| USAGE: |
|
|
//| 1. User initializes algorithm state with MinLBFGSCreate() call |
|
|
//| 2. User tunes solver parameters with MinLBFGSSetCond() |
|
|
//| MinLBFGSSetStpMax() and other functions |
|
|
//| 3. User calls MinLBFGSOptimize() function which takes algorithm |
|
|
//| state and pointer (delegate, etc.) to callback function which |
|
|
//| calculates F/G. |
|
|
//| 4. User calls MinLBFGSResults() to get solution |
|
|
//| 5. Optionally user may call MinLBFGSRestartFrom() to solve |
|
|
//| another problem with same N/M but another starting point |
|
|
//| and/or another function. MinLBFGSRestartFrom() allows to reuse|
|
|
//| already initialized structure. |
|
|
//| INPUT PARAMETERS: |
|
|
//| N - problem dimension. N>0 |
|
|
//| M - number of corrections in the BFGS scheme of |
|
|
//| Hessian approximation update. Recommended value: |
|
|
//| 3<=M<=7. The smaller value causes worse |
|
|
//| convergence, the bigger will not cause a |
|
|
//| considerably better convergence, but will cause |
|
|
//| a fall in the performance. M<=N. |
|
|
//| X - initial solution approximation, array[0..N-1]. |
|
|
//| OUTPUT PARAMETERS: |
|
|
//| State - structure which stores algorithm state |
|
|
//| NOTES: |
|
|
//| 1. you may tune stopping conditions with MinLBFGSSetCond() |
|
|
//| function |
|
|
//| 2. if target function contains exp() or other fast growing |
|
|
//| functions, and optimization algorithm makes too large steps |
|
|
//| which leads to overflow, use MinLBFGSSetStpMax() function to |
|
|
//| bound algorithm's steps. However, L-BFGS rarely needs such a |
|
|
//| tuning. |
|
|
//+------------------------------------------------------------------+
|
|
static void CMinLBFGS::MinLBFGSCreate(const int n,const int m,double &x[],
|
|
CMinLBFGSState &state)
|
|
{
|
|
//--- check
|
|
if(!CAp::Assert(n>=1,__FUNCTION__+": N<1!"))
|
|
return;
|
|
//--- check
|
|
if(!CAp::Assert(m>=1,__FUNCTION__+": M<1"))
|
|
return;
|
|
//--- check
|
|
if(!CAp::Assert(m<=n,__FUNCTION__+": M>N"))
|
|
return;
|
|
//--- check
|
|
if(!CAp::Assert(CAp::Len(x)>=n,__FUNCTION__+": Length(X)<N!"))
|
|
return;
|
|
//--- check
|
|
if(!CAp::Assert(CApServ::IsFiniteVector(x,n),__FUNCTION__+": X contains infinite or NaN values!"))
|
|
return;
|
|
//--- function call
|
|
MinLBFGSCreateX(n,m,x,0,0.0,state);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| The subroutine is finite difference variant of MinLBFGSCreate(). |
|
|
//| It uses finite differences in order to differentiate target |
|
|
//| function. |
|
|
//| Description below contains information which is specific to this |
|
|
//| function only. We recommend to read comments on MinLBFGSCreate() |
|
|
//| in order to get more information about creation of LBFGS |
|
|
//| optimizer. |
|
|
//| INPUT PARAMETERS: |
|
|
//| N - problem dimension, N>0: |
|
|
//| * if given, only leading N elements of X are used|
|
|
//| * if not given, automatically determined from |
|
|
//| size of X |
|
|
//| M - number of corrections in the BFGS scheme of |
|
|
//| Hessian approximation update. Recommended value: |
|
|
//| 3<=M<=7. The smaller value causes worse |
|
|
//| convergence, the bigger will not cause a |
|
|
//| considerably better convergence, but will cause a|
|
|
//| fall in the performance. M<=N. |
|
|
//| X - starting point, array[0..N-1]. |
|
|
//| DiffStep- differentiation step, >0 |
|
|
//| OUTPUT PARAMETERS: |
|
|
//| State - structure which stores algorithm state |
|
|
//| NOTES: |
|
|
//| 1. algorithm uses 4-point central formula for differentiation. |
|
|
//| 2. differentiation step along I-th axis is equal to DiffStep*S[I]|
|
|
//| where S[] is scaling vector which can be set by |
|
|
//| MinLBFGSSetScale() call. |
|
|
//| 3. we recommend you to use moderate values of differentiation |
|
|
//| step. Too large step will result in too large truncation |
|
|
//| errors, while too small step will result in too large |
|
|
//| numerical errors. 1.0E-6 can be good value to start with. |
|
|
//| 4. Numerical differentiation is very inefficient - one gradient |
|
|
//| calculation needs 4*N function evaluations. This function will|
|
|
//| work for any N - either small (1...10), moderate (10...100) or|
|
|
//| large (100...). However, performance penalty will be too |
|
|
//| severe for any N's except for small ones. |
|
|
//| We should also say that code which relies on numerical |
|
|
//| differentiation is less robust and precise. LBFGS needs exact |
|
|
//| gradient values. Imprecise gradient may slow down convergence,|
|
|
//| especially on highly nonlinear problems. |
|
|
//| Thus we recommend to use this function for fast prototyping on|
|
|
//| small- dimensional problems only, and to implement analytical |
|
|
//| gradient as soon as possible. |
|
|
//+------------------------------------------------------------------+
|
|
static void CMinLBFGS::MinLBFGSCreateF(const int n,const int m,double &x[],
|
|
const double diffstep,CMinLBFGSState &state)
|
|
{
|
|
//--- check
|
|
if(!CAp::Assert(n>=1,__FUNCTION__+": N too small!"))
|
|
return;
|
|
//--- check
|
|
if(!CAp::Assert(m>=1,__FUNCTION__+": M<1"))
|
|
return;
|
|
//--- check
|
|
if(!CAp::Assert(m<=n,__FUNCTION__+": M>N"))
|
|
return;
|
|
//--- check
|
|
if(!CAp::Assert(CAp::Len(x)>=n,__FUNCTION__+": Length(X)<N!"))
|
|
return;
|
|
//--- check
|
|
if(!CAp::Assert(CApServ::IsFiniteVector(x,n),__FUNCTION__+": X contains infinite or NaN values!"))
|
|
return;
|
|
//--- check
|
|
if(!CAp::Assert(CMath::IsFinite(diffstep),__FUNCTION__+": DiffStep is infinite or NaN!"))
|
|
return;
|
|
//--- check
|
|
if(!CAp::Assert(diffstep>0.0,__FUNCTION__+": DiffStep is non-positive!"))
|
|
return;
|
|
//--- function call
|
|
MinLBFGSCreateX(n,m,x,0,diffstep,state);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| This function sets stopping conditions for L-BFGS optimization |
|
|
//| algorithm. |
|
|
//| INPUT PARAMETERS: |
|
|
//| State - structure which stores algorithm state |
|
|
//| EpsG - >=0 |
|
|
//| The subroutine finishes its work if the condition|
|
|
//| |v|<EpsG is satisfied, where: |
|
|
//| * |.| means Euclidian norm |
|
|
//| * v - scaled gradient vector, v[i]=g[i]*s[i] |
|
|
//| * g - gradient |
|
|
//| * s - scaling coefficients set by |
|
|
//| MinLBFGSSetScale() |
|
|
//| EpsF - >=0 |
|
|
//| The subroutine finishes its work if on k+1-th |
|
|
//| iteration the condition |F(k+1)-F(k)| <= |
|
|
//| <= EpsF*max{|F(k)|,|F(k+1)|,1} is satisfied. |
|
|
//| EpsX - >=0 |
|
|
//| The subroutine finishes its work if on k+1-th |
|
|
//| iteration the condition |v|<=EpsX is fulfilled, |
|
|
//| where: |
|
|
//| * |.| means Euclidian norm |
|
|
//| * v - scaled step vector, v[i]=dx[i]/s[i] |
|
|
//| * dx - ste pvector, dx=X(k+1)-X(k) |
|
|
//| * s - scaling coefficients set by |
|
|
//| MinLBFGSSetScale() |
|
|
//| MaxIts - maximum number of iterations. If MaxIts=0, the |
|
|
//| number of iterations is unlimited. |
|
|
//| Passing EpsG=0, EpsF=0, EpsX=0 and MaxIts=0 (simultaneously) will|
|
|
//| lead to automatic stopping criterion selection (small EpsX). |
|
|
//+------------------------------------------------------------------+
|
|
static void CMinLBFGS::MinLBFGSSetCond(CMinLBFGSState &state,const double epsg,
|
|
const double epsf,double epsx,
|
|
const int maxits)
|
|
{
|
|
//--- check
|
|
if(!CAp::Assert(CMath::IsFinite(epsg),__FUNCTION__+": EpsG is not finite number!"))
|
|
return;
|
|
//--- check
|
|
if(!CAp::Assert(epsg>=0.0,__FUNCTION__+": negative EpsG!"))
|
|
return;
|
|
//--- check
|
|
if(!CAp::Assert(CMath::IsFinite(epsf),__FUNCTION__+": EpsF is not finite number!"))
|
|
return;
|
|
//--- check
|
|
if(!CAp::Assert(epsf>=0.0,__FUNCTION__+": negative EpsF!"))
|
|
return;
|
|
//--- check
|
|
if(!CAp::Assert(CMath::IsFinite(epsx),__FUNCTION__+": EpsX is not finite number!"))
|
|
return;
|
|
//--- check
|
|
if(!CAp::Assert(epsx>=0.0,__FUNCTION__+": negative EpsX!"))
|
|
return;
|
|
//--- check
|
|
if(!CAp::Assert(maxits>=0,__FUNCTION__+": negative MaxIts!"))
|
|
return;
|
|
//--- check
|
|
if(((epsg==0.0 && epsf==0.0) && epsx==0.0) && maxits==0)
|
|
epsx=1.0E-6;
|
|
//--- change values
|
|
state.m_epsg=epsg;
|
|
state.m_epsf=epsf;
|
|
state.m_epsx=epsx;
|
|
state.m_maxits=maxits;
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| This function turns on/off reporting. |
|
|
//| INPUT PARAMETERS: |
|
|
//| State - structure which stores algorithm state |
|
|
//| NeedXRep- whether iteration reports are needed or not |
|
|
//| If NeedXRep is True, algorithm will call rep() callback function |
|
|
//| if it is provided to MinLBFGSOptimize(). |
|
|
//+------------------------------------------------------------------+
|
|
static void CMinLBFGS::MinLBFGSSetXRep(CMinLBFGSState &state,const bool needxrep)
|
|
{
|
|
//--- change value
|
|
state.m_xrep=needxrep;
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| This function sets maximum step length |
|
|
//| INPUT PARAMETERS: |
|
|
//| State - structure which stores algorithm state |
|
|
//| StpMax - maximum step length, >=0. Set StpMax to 0.0 |
|
|
//| (default), if you don't want to limit step |
|
|
//| length. |
|
|
//| Use this subroutine when you optimize target function which |
|
|
//| contains exp() or other fast growing functions, and optimization |
|
|
//| algorithm makes too large steps which leads to overflow. This |
|
|
//| function allows us to reject steps that are too large (and |
|
|
//| therefore expose us to the possible overflow) without actually |
|
|
//| calculating function value at the x+stp*d. |
|
|
//+------------------------------------------------------------------+
|
|
static void CMinLBFGS::MinLBFGSSetStpMax(CMinLBFGSState &state,
|
|
const double stpmax)
|
|
{
|
|
//--- check
|
|
if(!CAp::Assert(CMath::IsFinite(stpmax),__FUNCTION__+": StpMax is not finite!"))
|
|
return;
|
|
//--- check
|
|
if(!CAp::Assert(stpmax>=0.0,__FUNCTION__+": StpMax<0!"))
|
|
return;
|
|
//--- change value
|
|
state.m_stpmax=stpmax;
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| This function sets scaling coefficients for LBFGS optimizer. |
|
|
//| ALGLIB optimizers use scaling matrices to test stopping |
|
|
//| conditions (step size and gradient are scaled before comparison |
|
|
//| with tolerances). Scale of the I-th variable is a translation |
|
|
//| invariant measure of: |
|
|
//| a) "how large" the variable is |
|
|
//| b) how large the step should be to make significant changes in |
|
|
//| the function |
|
|
//| Scaling is also used by finite difference variant of the |
|
|
//| optimizer - step along I-th axis is equal to DiffStep*S[I]. |
|
|
//| In most optimizers (and in the LBFGS too) scaling is NOT a form |
|
|
//| of preconditioning. It just affects stopping conditions. You |
|
|
//| should set preconditioner by separate call to one of the |
|
|
//| MinLBFGSSetPrec...() functions. |
|
|
//| There is special preconditioning mode, however, which uses |
|
|
//| scaling coefficients to form diagonal preconditioning matrix. |
|
|
//| You can turn this mode on, if you want. But you should |
|
|
//| understand that scaling is not the same thing as |
|
|
//| preconditioning - these are two different, although related |
|
|
//| forms of tuning solver. |
|
|
//| INPUT PARAMETERS: |
|
|
//| State - structure stores algorithm state |
|
|
//| S - array[N], non-zero scaling coefficients |
|
|
//| S[i] may be negative, sign doesn't matter. |
|
|
//+------------------------------------------------------------------+
|
|
static void CMinLBFGS::MinLBFGSSetScale(CMinLBFGSState &state,double &s[])
|
|
{
|
|
//--- create a variable
|
|
int i=0;
|
|
//--- check
|
|
if(!CAp::Assert(CAp::Len(s)>=state.m_n,__FUNCTION__+": Length(S)<N"))
|
|
return;
|
|
for(i=0;i<=state.m_n-1;i++)
|
|
{
|
|
//--- check
|
|
if(!CAp::Assert(CMath::IsFinite(s[i]),__FUNCTION__+": S contains infinite or NAN elements"))
|
|
return;
|
|
//--- check
|
|
if(!CAp::Assert(s[i]!=0.0,__FUNCTION__+": S contains zero elements"))
|
|
return;
|
|
state.m_s[i]=MathAbs(s[i]);
|
|
}
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Extended subroutine for internal use only. |
|
|
//| Accepts additional parameters: |
|
|
//| Flags - additional settings: |
|
|
//| * Flags = 0 means no additional settings |
|
|
//| * Flags = 1 "do not allocate memory". used when |
|
|
//| solving a many subsequent tasks with |
|
|
//| same N/M values. First call MUST be |
|
|
//| without this flag bit set, subsequent|
|
|
//| calls of MinLBFGS with same |
|
|
//| MinLBFGSState structure can set Flags|
|
|
//| to 1. |
|
|
//| DiffStep - numerical differentiation step |
|
|
//+------------------------------------------------------------------+
|
|
static void CMinLBFGS::MinLBFGSCreateX(const int n,const int m,double &x[],
|
|
int flags,const double diffstep,
|
|
CMinLBFGSState &state)
|
|
{
|
|
//--- create variables
|
|
bool allocatemem;
|
|
int i=0;
|
|
//--- check
|
|
if(!CAp::Assert(n>=1,__FUNCTION__+": N too small!"))
|
|
return;
|
|
//--- check
|
|
if(!CAp::Assert(m>=1,__FUNCTION__+": M too small!"))
|
|
return;
|
|
//--- check
|
|
if(!CAp::Assert(m<=n,__FUNCTION__+": M too large!"))
|
|
return;
|
|
//--- Initialize
|
|
state.m_diffstep=diffstep;
|
|
state.m_n=n;
|
|
state.m_m=m;
|
|
allocatemem=flags%2==0;
|
|
flags=flags/2;
|
|
//--- check
|
|
if(allocatemem)
|
|
{
|
|
//--- allocation
|
|
ArrayResizeAL(state.m_rho,m);
|
|
ArrayResizeAL(state.m_theta,m);
|
|
state.m_yk.Resize(m,n);
|
|
state.m_sk.Resize(m,n);
|
|
ArrayResizeAL(state.m_d,n);
|
|
ArrayResizeAL(state.m_x,n);
|
|
ArrayResizeAL(state.m_s,n);
|
|
ArrayResizeAL(state.m_g,n);
|
|
ArrayResizeAL(state.m_work,n);
|
|
}
|
|
//--- function call
|
|
MinLBFGSSetCond(state,0,0,0,0);
|
|
//--- function call
|
|
MinLBFGSSetXRep(state,false);
|
|
//--- function call
|
|
MinLBFGSSetStpMax(state,0);
|
|
//--- function call
|
|
MinLBFGSRestartFrom(state,x);
|
|
//--- change values
|
|
for(i=0;i<=n-1;i++)
|
|
state.m_s[i]=1.0;
|
|
state.m_prectype=0;
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Modification of the preconditioner: default preconditioner |
|
|
//| (simple scaling, same for all elements of X) is used. |
|
|
//| INPUT PARAMETERS: |
|
|
//| State - structure which stores algorithm state |
|
|
//| NOTE: you can change preconditioner "on the fly", during |
|
|
//| algorithm iterations. |
|
|
//+------------------------------------------------------------------+
|
|
static void CMinLBFGS::MinLBFGSSetPrecDefault(CMinLBFGSState &state)
|
|
{
|
|
//--- change value
|
|
state.m_prectype=0;
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Modification of the preconditioner: Cholesky factorization of |
|
|
//| approximate Hessian is used. |
|
|
//| INPUT PARAMETERS: |
|
|
//| State - structure which stores algorithm state |
|
|
//| P - triangular preconditioner, Cholesky factorization|
|
|
//| of the approximate Hessian. array[0..N-1,0..N-1],|
|
|
//| (if larger, only leading N elements are used). |
|
|
//| IsUpper - whether upper or lower triangle of P is given |
|
|
//| (other triangle is not referenced) |
|
|
//| After call to this function preconditioner is changed to P (P is |
|
|
//| copied into the internal buffer). |
|
|
//| NOTE: you can change preconditioner "on the fly", during |
|
|
//| algorithm iterations. |
|
|
//| NOTE 2: P should be nonsingular. Exception will be thrown |
|
|
//| otherwise. |
|
|
//+------------------------------------------------------------------+
|
|
static void CMinLBFGS::MinLBFGSSetPrecCholesky(CMinLBFGSState &state,
|
|
CMatrixDouble &p,
|
|
const bool isupper)
|
|
{
|
|
//--- create variables
|
|
int i=0;
|
|
double mx=0;
|
|
//--- check
|
|
if(!CAp::Assert(CApServ::IsFiniteRTrMatrix(p,state.m_n,isupper),__FUNCTION__+": P contains infinite or NAN values!"))
|
|
return;
|
|
//--- initialization
|
|
mx=0;
|
|
for(i=0;i<=state.m_n-1;i++)
|
|
mx=MathMax(mx,MathAbs(p[i][i]));
|
|
//--- check
|
|
if(!CAp::Assert((double)(mx)>0.0,__FUNCTION__+": P is strictly singular!"))
|
|
return;
|
|
//--- check
|
|
if(CAp::Rows(state.m_denseh)<state.m_n||CAp::Cols(state.m_denseh)<state.m_n)
|
|
state.m_denseh.Resize(state.m_n,state.m_n);
|
|
//--- initialization
|
|
state.m_prectype=1;
|
|
//--- check
|
|
if(isupper)
|
|
CAblas::RMatrixCopy(state.m_n,state.m_n,p,0,0,state.m_denseh,0,0);
|
|
else
|
|
CAblas::RMatrixTranspose(state.m_n,state.m_n,p,0,0,state.m_denseh,0,0);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Modification of the preconditioner: diagonal of approximate |
|
|
//| Hessian is used. |
|
|
//| INPUT PARAMETERS: |
|
|
//| State - structure which stores algorithm state |
|
|
//| D - diagonal of the approximate Hessian, |
|
|
//| array[0..N-1], (if larger, only leading N |
|
|
//| elements are used). |
|
|
//| NOTE: you can change preconditioner "on the fly", during |
|
|
//| algorithm iterations. |
|
|
//| NOTE 2: D[i] should be positive. Exception will be thrown |
|
|
//| otherwise. |
|
|
//| NOTE 3: you should pass diagonal of approximate Hessian - NOT |
|
|
//| ITS INVERSE. |
|
|
//+------------------------------------------------------------------+
|
|
static void CMinLBFGS::MinLBFGSSetPrecDiag(CMinLBFGSState &state,double &d[])
|
|
{
|
|
//--- create a variable
|
|
int i=0;
|
|
//--- check
|
|
if(!CAp::Assert(CAp::Len(d)>=state.m_n,__FUNCTION__+": D is too short"))
|
|
return;
|
|
for(i=0;i<=state.m_n-1;i++)
|
|
{
|
|
//--- check
|
|
if(!CAp::Assert(CMath::IsFinite(d[i]),__FUNCTION__+": D contains infinite or NAN elements"))
|
|
return;
|
|
//--- check
|
|
if(!CAp::Assert((double)(d[i])>0.0,__FUNCTION__+": D contains non-positive elements"))
|
|
return;
|
|
}
|
|
//--- function call
|
|
CApServ::RVectorSetLengthAtLeast(state.m_diagh,state.m_n);
|
|
//--- change values
|
|
state.m_prectype=2;
|
|
for(i=0;i<=state.m_n-1;i++)
|
|
state.m_diagh[i]=d[i];
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Modification of the preconditioner: scale-based diagonal |
|
|
//| preconditioning. |
|
|
//| This preconditioning mode can be useful when you don't have |
|
|
//| approximate diagonal of Hessian, but you know that your variables|
|
|
//| are badly scaled (for example, one variable is in [1,10], and |
|
|
//| another in [1000,100000]), and most part of the ill-conditioning |
|
|
//| comes from different scales of vars. |
|
|
//| In this case simple scale-based preconditioner, with H[i] = |
|
|
//| = 1/(s[i]^2), can greatly improve convergence. |
|
|
//| IMPRTANT: you should set scale of your variables with |
|
|
//| MinLBFGSSetScale() call (before or after MinLBFGSSetPrecScale() |
|
|
//| call). Without knowledge of the scale of your variables |
|
|
//| scale-based preconditioner will be just unit matrix. |
|
|
//| INPUT PARAMETERS: |
|
|
//| State - structure which stores algorithm state |
|
|
//+------------------------------------------------------------------+
|
|
static void CMinLBFGS::MinLBFGSSetPrecScale(CMinLBFGSState &state)
|
|
{
|
|
//--- change values
|
|
state.m_prectype=3;
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| L-BFGS algorithm results |
|
|
//| INPUT PARAMETERS: |
|
|
//| State - algorithm state |
|
|
//| OUTPUT PARAMETERS: |
|
|
//| X - array[0..N-1], solution |
|
|
//| Rep - optimization report: |
|
|
//| * Rep.TerminationType completetion code: |
|
|
//| * -2 rounding errors prevent further |
|
|
//| improvement. X contains best point |
|
|
//| found. |
|
|
//| * -1 incorrect parameters were specified |
|
|
//| * 1 relative function improvement is no |
|
|
//| more than EpsF. |
|
|
//| * 2 relative step is no more than EpsX. |
|
|
//| * 4 gradient norm is no more than EpsG |
|
|
//| * 5 MaxIts steps was taken |
|
|
//| * 7 stopping conditions are too |
|
|
//| stringent, further improvement is |
|
|
//| impossible |
|
|
//| * Rep.IterationsCount contains iterations count |
|
|
//| * NFEV countains number of function calculations |
|
|
//+------------------------------------------------------------------+
|
|
static void CMinLBFGS::MinLBFGSResults(CMinLBFGSState &state,double &x[],
|
|
CMinLBFGSReport &rep)
|
|
{
|
|
//--- reset memory
|
|
ArrayResizeAL(x,0);
|
|
//--- function call
|
|
MinLBFGSresultsbuf(state,x,rep);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| L-BFGS algorithm results |
|
|
//| Buffered implementation of MinLBFGSResults which uses |
|
|
//| pre-allocated buffer to store X[]. If buffer size is too small, |
|
|
//| it resizes buffer. It is intended to be used in the inner cycles |
|
|
//| of performance critical algorithms where array reallocation |
|
|
//| penalty is too large to be ignored. |
|
|
//+------------------------------------------------------------------+
|
|
static void CMinLBFGS::MinLBFGSresultsbuf(CMinLBFGSState &state,double &x[],
|
|
CMinLBFGSReport &rep)
|
|
{
|
|
//--- create a variable
|
|
int i_=0;
|
|
//--- check
|
|
if(CAp::Len(x)<state.m_n)
|
|
ArrayResizeAL(x,state.m_n);
|
|
//--- copy
|
|
for(i_=0;i_<=state.m_n-1;i_++)
|
|
x[i_]=state.m_x[i_];
|
|
//--- change values
|
|
rep.m_iterationscount=state.m_repiterationscount;
|
|
rep.m_nfev=state.m_repnfev;
|
|
rep.m_terminationtype=state.m_repterminationtype;
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| This subroutine restarts LBFGS algorithm from new point. All |
|
|
//| optimization parameters are left unchanged. |
|
|
//| This function allows to solve multiple optimization problems |
|
|
//| (which must have same number of dimensions) without object |
|
|
//| reallocation penalty. |
|
|
//| INPUT PARAMETERS: |
|
|
//| State - structure used to store algorithm state |
|
|
//| X - new starting point. |
|
|
//+------------------------------------------------------------------+
|
|
static void CMinLBFGS::MinLBFGSRestartFrom(CMinLBFGSState &state,double &x[])
|
|
{
|
|
//--- create a variable
|
|
int i_=0;
|
|
//--- check
|
|
if(!CAp::Assert(CAp::Len(x)>=state.m_n,__FUNCTION__+": Length(X)<N!"))
|
|
return;
|
|
//--- check
|
|
if(!CAp::Assert(CApServ::IsFiniteVector(x,state.m_n),__FUNCTION__+": X contains infinite or NaN values!"))
|
|
return;
|
|
//--- copy
|
|
for(i_=0;i_<=state.m_n-1;i_++)
|
|
state.m_x[i_]=x[i_];
|
|
//--- allocation
|
|
ArrayResizeAL(state.m_rstate.ia,6);
|
|
ArrayResizeAL(state.m_rstate.ra,2);
|
|
state.m_rstate.stage=-1;
|
|
//--- function call
|
|
ClearRequestFields(state);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Clears request fileds (to be sure that we don't forgot to clear |
|
|
//| something) |
|
|
//+------------------------------------------------------------------+
|
|
static void CMinLBFGS::ClearRequestFields(CMinLBFGSState &state)
|
|
{
|
|
//--- change values
|
|
state.m_needf=false;
|
|
state.m_needfg=false;
|
|
state.m_xupdated=false;
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| NOTES: |
|
|
//| 1. This function has two different implementations: one which |
|
|
//| uses exact (analytical) user-supplied gradient, and one which |
|
|
//| uses function value only and numerically differentiates |
|
|
//| function in order to obtain gradient. |
|
|
//| Depending on the specific function used to create optimizer |
|
|
//| object (either MinLBFGSCreate() for analytical gradient or |
|
|
//| MinLBFGSCreateF() for numerical differentiation) you should |
|
|
//| choose appropriate variant of MinLBFGSOptimize() - one which |
|
|
//| accepts function AND gradient or one which accepts function |
|
|
//| ONLY. |
|
|
//| Be careful to choose variant of MinLBFGSOptimize() which |
|
|
//| corresponds to your optimization scheme! Table below lists |
|
|
//| different combinations of callback (function/gradient) passed |
|
|
//| to MinLBFGSOptimize() and specific function used to create |
|
|
//| optimizer. |
|
|
//| | USER PASSED TO MinLBFGSOptimize() |
|
|
//| CREATED WITH | function only | function and gradient |
|
|
//| ------------------------------------------------------------ |
|
|
//| MinLBFGSCreateF() | work FAIL |
|
|
//| MinLBFGSCreate() | FAIL work |
|
|
//| Here "FAIL" denotes inappropriate combinations of optimizer |
|
|
//| creation function and MinLBFGSOptimize() version. Attemps to |
|
|
//| use such combination (for example, to create optimizer with |
|
|
//| MinLBFGSCreateF() and to pass gradient information to |
|
|
//| MinCGOptimize()) will lead to exception being thrown. Either |
|
|
//| you did not pass gradient when it WAS needed or you passed |
|
|
//| gradient when it was NOT needed. |
|
|
//+------------------------------------------------------------------+
|
|
static bool CMinLBFGS::MinLBFGSIteration(CMinLBFGSState &state)
|
|
{
|
|
//--- create variables
|
|
int n=0;
|
|
int m=0;
|
|
int i=0;
|
|
int j=0;
|
|
int ic=0;
|
|
int mcinfo=0;
|
|
double v=0;
|
|
double vv=0;
|
|
int i_=0;
|
|
//--- Reverse communication preparations
|
|
//--- I know it looks ugly, but it works the same way
|
|
//--- anywhere from C++ to Python.
|
|
//--- This code initializes locals by:
|
|
//--- * random values determined during code
|
|
//--- generation-on first subroutine call
|
|
//--- * values from previous call-on subsequent calls
|
|
if(state.m_rstate.stage>=0)
|
|
{
|
|
//--- initialization
|
|
n=state.m_rstate.ia[0];
|
|
m=state.m_rstate.ia[1];
|
|
i=state.m_rstate.ia[2];
|
|
j=state.m_rstate.ia[3];
|
|
ic=state.m_rstate.ia[4];
|
|
mcinfo=state.m_rstate.ia[5];
|
|
v=state.m_rstate.ra[0];
|
|
vv=state.m_rstate.ra[1];
|
|
}
|
|
else
|
|
{
|
|
//--- initialization
|
|
n=-983;
|
|
m=-989;
|
|
i=-834;
|
|
j=900;
|
|
ic=-287;
|
|
mcinfo=364;
|
|
v=214;
|
|
vv=-338;
|
|
}
|
|
//--- check
|
|
if(state.m_rstate.stage==0)
|
|
{
|
|
//--- change value
|
|
state.m_needfg=false;
|
|
//--- function call
|
|
COptServ::TrimPrepare(state.m_f,state.m_trimthreshold);
|
|
//--- check
|
|
if(!state.m_xrep)
|
|
return(Func_lbl_19(state,n,m,i,j,ic,mcinfo,v,vv));
|
|
//--- function call
|
|
ClearRequestFields(state);
|
|
//--- change values
|
|
state.m_xupdated=true;
|
|
state.m_rstate.stage=6;
|
|
//--- Saving state
|
|
Func_lbl_rcomm(state,n,m,i,j,ic,mcinfo,v,vv);
|
|
//--- return result
|
|
return(true);
|
|
}
|
|
//--- check
|
|
if(state.m_rstate.stage==1)
|
|
{
|
|
//--- change values
|
|
state.m_fbase=state.m_f;
|
|
i=0;
|
|
//--- function call, return result
|
|
return(Func_lbl_16(state,n,m,i,j,ic,mcinfo,v,vv));
|
|
}
|
|
//--- check
|
|
if(state.m_rstate.stage==2)
|
|
{
|
|
//--- change values
|
|
state.m_fm2=state.m_f;
|
|
state.m_x[i]=v-0.5*state.m_diffstep*state.m_s[i];
|
|
state.m_rstate.stage=3;
|
|
//--- Saving state
|
|
Func_lbl_rcomm(state,n,m,i,j,ic,mcinfo,v,vv);
|
|
//--- return result
|
|
return(true);
|
|
}
|
|
//--- check
|
|
if(state.m_rstate.stage==3)
|
|
{
|
|
//--- change values
|
|
state.m_fm1=state.m_f;
|
|
state.m_x[i]=v+0.5*state.m_diffstep*state.m_s[i];
|
|
state.m_rstate.stage=4;
|
|
//--- Saving state
|
|
Func_lbl_rcomm(state,n,m,i,j,ic,mcinfo,v,vv);
|
|
//--- return result
|
|
return(true);
|
|
}
|
|
//--- check
|
|
if(state.m_rstate.stage==4)
|
|
{
|
|
//--- change values
|
|
state.m_fp1=state.m_f;
|
|
state.m_x[i]=v+state.m_diffstep*state.m_s[i];
|
|
state.m_rstate.stage=5;
|
|
//--- Saving state
|
|
Func_lbl_rcomm(state,n,m,i,j,ic,mcinfo,v,vv);
|
|
//--- return result
|
|
return(true);
|
|
}
|
|
//--- check
|
|
if(state.m_rstate.stage==5)
|
|
{
|
|
//--- change values
|
|
state.m_fp2=state.m_f;
|
|
state.m_x[i]=v;
|
|
state.m_g[i]=(8*(state.m_fp1-state.m_fm1)-(state.m_fp2-state.m_fm2))/(6*state.m_diffstep*state.m_s[i]);
|
|
i=i+1;
|
|
//--- function call, return result
|
|
return(Func_lbl_16(state,n,m,i,j,ic,mcinfo,v,vv));
|
|
}
|
|
//--- check
|
|
if(state.m_rstate.stage==6)
|
|
{
|
|
//--- change value
|
|
state.m_xupdated=false;
|
|
//--- function call, return result
|
|
return(Func_lbl_19(state,n,m,i,j,ic,mcinfo,v,vv));
|
|
}
|
|
//--- check
|
|
if(state.m_rstate.stage==7)
|
|
{
|
|
//--- change value
|
|
state.m_needfg=false;
|
|
//--- function call
|
|
COptServ::TrimFunction(state.m_f,state.m_g,n,state.m_trimthreshold);
|
|
//--- function call
|
|
CLinMin::MCSrch(n,state.m_x,state.m_f,state.m_g,state.m_d,state.m_stp,state.m_stpmax,m_gtol,mcinfo,state.m_nfev,state.m_work,state.m_lstate,state.m_mcstage);
|
|
//--- function call, return result
|
|
return(Func_lbl_23(state,n,m,i,j,ic,mcinfo,v,vv));
|
|
}
|
|
//--- check
|
|
if(state.m_rstate.stage==8)
|
|
{
|
|
//--- change values
|
|
state.m_fbase=state.m_f;
|
|
i=0;
|
|
//--- function call, return result
|
|
return(Func_lbl_27(state,n,m,i,j,ic,mcinfo,v,vv));
|
|
}
|
|
//--- check
|
|
if(state.m_rstate.stage==9)
|
|
{
|
|
//--- change values
|
|
state.m_fm2=state.m_f;
|
|
state.m_x[i]=v-0.5*state.m_diffstep*state.m_s[i];
|
|
state.m_rstate.stage=10;
|
|
//--- Saving state
|
|
Func_lbl_rcomm(state,n,m,i,j,ic,mcinfo,v,vv);
|
|
//--- return result
|
|
return(true);
|
|
}
|
|
//--- check
|
|
if(state.m_rstate.stage==10)
|
|
{
|
|
//--- change values
|
|
state.m_fm1=state.m_f;
|
|
state.m_x[i]=v+0.5*state.m_diffstep*state.m_s[i];
|
|
state.m_rstate.stage=11;
|
|
//--- Saving state
|
|
Func_lbl_rcomm(state,n,m,i,j,ic,mcinfo,v,vv);
|
|
//--- return result
|
|
return(true);
|
|
}
|
|
//--- check
|
|
if(state.m_rstate.stage==11)
|
|
{
|
|
//--- change values
|
|
state.m_fp1=state.m_f;
|
|
state.m_x[i]=v+state.m_diffstep*state.m_s[i];
|
|
state.m_rstate.stage=12;
|
|
//--- Saving state
|
|
Func_lbl_rcomm(state,n,m,i,j,ic,mcinfo,v,vv);
|
|
//--- return result
|
|
return(true);
|
|
}
|
|
//--- check
|
|
if(state.m_rstate.stage==12)
|
|
{
|
|
//--- change values
|
|
state.m_fp2=state.m_f;
|
|
state.m_x[i]=v;
|
|
state.m_g[i]=(8*(state.m_fp1-state.m_fm1)-(state.m_fp2-state.m_fm2))/(6*state.m_diffstep*state.m_s[i]);
|
|
i=i+1;
|
|
//--- function call, return result
|
|
return(Func_lbl_27(state,n,m,i,j,ic,mcinfo,v,vv));
|
|
}
|
|
//--- check
|
|
if(state.m_rstate.stage==13)
|
|
{
|
|
//--- change value
|
|
state.m_xupdated=false;
|
|
//--- function call, return result
|
|
return(Func_lbl_30(state,n,m,i,j,ic,mcinfo,v,vv));
|
|
}
|
|
//--- Routine body
|
|
//--- Unload frequently used variables from State structure
|
|
//--- (just for typing convinience)
|
|
n=state.m_n;
|
|
m=state.m_m;
|
|
state.m_repterminationtype=0;
|
|
state.m_repiterationscount=0;
|
|
state.m_repnfev=0;
|
|
//--- Calculate F/G at the initial point
|
|
ClearRequestFields(state);
|
|
//--- check
|
|
if(state.m_diffstep!=0.0)
|
|
{
|
|
//--- change values
|
|
state.m_needf=true;
|
|
state.m_rstate.stage=1;
|
|
//--- Saving state
|
|
Func_lbl_rcomm(state,n,m,i,j,ic,mcinfo,v,vv);
|
|
//--- return result
|
|
return(true);
|
|
}
|
|
//--- change values
|
|
state.m_needfg=true;
|
|
state.m_rstate.stage=0;
|
|
//--- Saving state
|
|
Func_lbl_rcomm(state,n,m,i,j,ic,mcinfo,v,vv);
|
|
//--- return result
|
|
return(true);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Auxiliary function for MinLBFGSIteration. Is a product to get |
|
|
//| rid of the operator unconditional jump goto. |
|
|
//+------------------------------------------------------------------+
|
|
static void CMinLBFGS::Func_lbl_rcomm(CMinLBFGSState &state,int n,int m,
|
|
int i,int j,int ic,int mcinfo,
|
|
double v,double vv)
|
|
{
|
|
//--- save
|
|
state.m_rstate.ia[0]=n;
|
|
state.m_rstate.ia[1]=m;
|
|
state.m_rstate.ia[2]=i;
|
|
state.m_rstate.ia[3]=j;
|
|
state.m_rstate.ia[4]=ic;
|
|
state.m_rstate.ia[5]=mcinfo;
|
|
state.m_rstate.ra[0]=v;
|
|
state.m_rstate.ra[1]=vv;
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Auxiliary function for MinLBFGSIteration. Is a product to get |
|
|
//| rid of the operator unconditional jump goto. |
|
|
//+------------------------------------------------------------------+
|
|
static bool CMinLBFGS::Func_lbl_16(CMinLBFGSState &state,int &n,int &m,
|
|
int &i,int &j,int &ic,int &mcinfo,
|
|
double &v,double &vv)
|
|
{
|
|
//--- check
|
|
if(i>n-1)
|
|
{
|
|
//--- change values
|
|
state.m_f=state.m_fbase;
|
|
state.m_needf=false;
|
|
//--- function call
|
|
COptServ::TrimPrepare(state.m_f,state.m_trimthreshold);
|
|
//--- check
|
|
if(!state.m_xrep)
|
|
return(Func_lbl_19(state,n,m,i,j,ic,mcinfo,v,vv));
|
|
//--- function call
|
|
ClearRequestFields(state);
|
|
//--- change values
|
|
state.m_xupdated=true;
|
|
state.m_rstate.stage=6;
|
|
//--- Saving state
|
|
Func_lbl_rcomm(state,n,m,i,j,ic,mcinfo,v,vv);
|
|
//--- return result
|
|
return(true);
|
|
}
|
|
//--- change values
|
|
v=state.m_x[i];
|
|
state.m_x[i]=v-state.m_diffstep*state.m_s[i];
|
|
state.m_rstate.stage=2;
|
|
//--- Saving state
|
|
Func_lbl_rcomm(state,n,m,i,j,ic,mcinfo,v,vv);
|
|
//--- return result
|
|
return(true);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Auxiliary function for MinLBFGSIteration. Is a product to get |
|
|
//| rid of the operator unconditional jump goto. |
|
|
//+------------------------------------------------------------------+
|
|
static bool CMinLBFGS::Func_lbl_19(CMinLBFGSState &state,int &n,int &m,
|
|
int &i,int &j,int &ic,int &mcinfo,
|
|
double &v,double &vv)
|
|
{
|
|
//--- change values
|
|
state.m_repnfev=1;
|
|
state.m_fold=state.m_f;
|
|
//--- calculation
|
|
v=0;
|
|
for(i=0;i<=n-1;i++)
|
|
v=v+CMath::Sqr(state.m_g[i]*state.m_s[i]);
|
|
//--- check
|
|
if(MathSqrt(v)<=state.m_epsg)
|
|
{
|
|
state.m_repterminationtype=4;
|
|
//--- return result
|
|
return(false);
|
|
}
|
|
//--- Choose initial step and direction.
|
|
//--- Apply preconditioner, if we have something other than default.
|
|
for(int i_=0;i_<=n-1;i_++)
|
|
state.m_d[i_]=-state.m_g[i_];
|
|
//--- check
|
|
if(state.m_prectype==0)
|
|
{
|
|
//--- Default preconditioner is used, but we can't use it before iterations will start
|
|
v=0.0;
|
|
for(int i_=0;i_<=n-1;i_++)
|
|
v+=state.m_g[i_]*state.m_g[i_];
|
|
v=MathSqrt(v);
|
|
//--- check
|
|
if(state.m_stpmax==0.0)
|
|
state.m_stp=MathMin(1.0/v,1);
|
|
else
|
|
state.m_stp=MathMin(1.0/v,state.m_stpmax);
|
|
}
|
|
//--- check
|
|
if(state.m_prectype==1)
|
|
{
|
|
//--- Cholesky preconditioner is used
|
|
CFbls::FblsCholeskySolve(state.m_denseh,1.0,n,true,state.m_d,state.m_autobuf);
|
|
state.m_stp=1;
|
|
}
|
|
//--- check
|
|
if(state.m_prectype==2)
|
|
{
|
|
//--- diagonal approximation is used
|
|
for(i=0;i<=n-1;i++)
|
|
state.m_d[i]=state.m_d[i]/state.m_diagh[i];
|
|
state.m_stp=1;
|
|
}
|
|
//--- check
|
|
if(state.m_prectype==3)
|
|
{
|
|
//--- scale-based preconditioner is used
|
|
for(i=0;i<=n-1;i++)
|
|
state.m_d[i]=state.m_d[i]*state.m_s[i]*state.m_s[i];
|
|
state.m_stp=1;
|
|
}
|
|
//--- Main cycle
|
|
state.m_k=0;
|
|
//--- function call, return result
|
|
return(Func_lbl_21(state,n,m,i,j,ic,mcinfo,v,vv));
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Auxiliary function for MinLBFGSIteration. Is a product to get |
|
|
//| rid of the operator unconditional jump goto. |
|
|
//+------------------------------------------------------------------+
|
|
static bool CMinLBFGS::Func_lbl_21(CMinLBFGSState &state,int &n,int &m,
|
|
int &i,int &j,int &ic,int &mcinfo,
|
|
double &v,double &vv)
|
|
{
|
|
//--- Main cycle: prepare to 1-D line search
|
|
state.m_p=state.m_k % m;
|
|
state.m_q=MathMin(state.m_k, m-1);
|
|
//--- Store X[k], G[k]
|
|
for(int i_=0;i_<=n-1;i_++)
|
|
state.m_sk[state.m_p].Set(i_,-state.m_x[i_]);
|
|
for(int i_=0;i_<=n-1;i_++)
|
|
state.m_yk[state.m_p].Set(i_,-state.m_g[i_]);
|
|
//--- Minimize F(x+alpha*d)
|
|
//--- Calculate S[k], Y[k]
|
|
state.m_mcstage=0;
|
|
//--- check
|
|
if(state.m_k!=0)
|
|
state.m_stp=1.0;
|
|
//--- function call
|
|
CLinMin::LinMinNormalized(state.m_d,state.m_stp,n);
|
|
//--- function call
|
|
CLinMin::MCSrch(n,state.m_x,state.m_f,state.m_g,state.m_d,state.m_stp,state.m_stpmax,m_gtol,mcinfo,state.m_nfev,state.m_work,state.m_lstate,state.m_mcstage);
|
|
//--- function call, return result
|
|
return(Func_lbl_23(state,n,m,i,j,ic,mcinfo,v,vv));
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Auxiliary function for MinLBFGSIteration. Is a product to get |
|
|
//| rid of the operator unconditional jump goto. |
|
|
//+------------------------------------------------------------------+
|
|
static bool CMinLBFGS::Func_lbl_23(CMinLBFGSState &state,int &n,int &m,
|
|
int &i,int &j,int &ic,int &mcinfo,
|
|
double &v,double &vv)
|
|
{
|
|
//--- check
|
|
if(state.m_mcstage==0)
|
|
{
|
|
//--- check
|
|
if(!state.m_xrep)
|
|
return(Func_lbl_30(state,n,m,i,j,ic,mcinfo,v,vv));
|
|
//--- report
|
|
ClearRequestFields(state);
|
|
//--- change values
|
|
state.m_xupdated=true;
|
|
state.m_rstate.stage=13;
|
|
//--- Saving state
|
|
Func_lbl_rcomm(state,n,m,i,j,ic,mcinfo,v,vv);
|
|
//--- return result
|
|
return(true);
|
|
}
|
|
//--- function call
|
|
ClearRequestFields(state);
|
|
//--- check
|
|
if((double)(state.m_diffstep)!=0.0)
|
|
{
|
|
//--- change values
|
|
state.m_needf=true;
|
|
state.m_rstate.stage=8;
|
|
//--- Saving state
|
|
Func_lbl_rcomm(state,n,m,i,j,ic,mcinfo,v,vv);
|
|
//--- return result
|
|
return(true);
|
|
}
|
|
//--- change values
|
|
state.m_needfg=true;
|
|
state.m_rstate.stage=7;
|
|
//--- Saving state
|
|
Func_lbl_rcomm(state,n,m,i,j,ic,mcinfo,v,vv);
|
|
//--- return result
|
|
return(true);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Auxiliary function for MinLBFGSIteration. Is a product to get |
|
|
//| rid of the operator unconditional jump goto. |
|
|
//+------------------------------------------------------------------+
|
|
static bool CMinLBFGS::Func_lbl_27(CMinLBFGSState &state,int &n,int &m,
|
|
int &i,int &j,int &ic,int &mcinfo,
|
|
double &v,double &vv)
|
|
{
|
|
//--- check
|
|
if(i>n-1)
|
|
{
|
|
//--- change values
|
|
state.m_f=state.m_fbase;
|
|
state.m_needf=false;
|
|
//--- function call
|
|
COptServ::TrimFunction(state.m_f,state.m_g,n,state.m_trimthreshold);
|
|
//--- function call
|
|
CLinMin::MCSrch(n,state.m_x,state.m_f,state.m_g,state.m_d,state.m_stp,state.m_stpmax,m_gtol,mcinfo,state.m_nfev,state.m_work,state.m_lstate,state.m_mcstage);
|
|
//--- function call, return result
|
|
return(Func_lbl_23(state,n,m,i,j,ic,mcinfo,v,vv));
|
|
}
|
|
//--- change values
|
|
v=state.m_x[i];
|
|
state.m_x[i]=v-state.m_diffstep*state.m_s[i];
|
|
state.m_rstate.stage=9;
|
|
//--- Saving state
|
|
Func_lbl_rcomm(state,n,m,i,j,ic,mcinfo,v,vv);
|
|
//--- return result
|
|
return(true);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Auxiliary function for MinLBFGSIteration. Is a product to get |
|
|
//| rid of the operator unconditional jump goto. |
|
|
//+------------------------------------------------------------------+
|
|
static bool CMinLBFGS::Func_lbl_30(CMinLBFGSState &state,int &n,int &m,
|
|
int &i,int &j,int &ic,int &mcinfo,
|
|
double &v,double &vv)
|
|
{
|
|
//--- change values
|
|
state.m_repnfev=state.m_repnfev+state.m_nfev;
|
|
state.m_repiterationscount=state.m_repiterationscount+1;
|
|
//--- calculation
|
|
for(int i_=0;i_<=n-1;i_++)
|
|
state.m_sk[state.m_p].Set(i_,state.m_sk[state.m_p][i_]+state.m_x[i_]);
|
|
for(int i_=0;i_<=n-1;i_++)
|
|
state.m_yk[state.m_p].Set(i_,state.m_yk[state.m_p][i_]+state.m_g[i_]);
|
|
//--- Stopping conditions
|
|
if(state.m_repiterationscount>=state.m_maxits&&state.m_maxits>0)
|
|
{
|
|
//--- Too many iterations
|
|
state.m_repterminationtype=5;
|
|
//--- return result
|
|
return(false);
|
|
}
|
|
//--- change value
|
|
v=0;
|
|
for(i=0;i<=n-1;i++)
|
|
v=v+CMath::Sqr(state.m_g[i]*state.m_s[i]);
|
|
//--- check
|
|
if(MathSqrt(v)<=state.m_epsg)
|
|
{
|
|
//--- Gradient is small enough
|
|
state.m_repterminationtype=4;
|
|
//--- return result
|
|
return(false);
|
|
}
|
|
//--- check
|
|
if(state.m_fold-state.m_f<=state.m_epsf*MathMax(MathAbs(state.m_fold),MathMax(MathAbs(state.m_f),1.0)))
|
|
{
|
|
//--- F(k+1)-F(k) is small enough
|
|
state.m_repterminationtype=1;
|
|
//--- return result
|
|
return(false);
|
|
}
|
|
//--- change value
|
|
v=0;
|
|
for(i=0;i<=n-1;i++)
|
|
v=v+CMath::Sqr(state.m_sk[state.m_p][i]/state.m_s[i]);
|
|
//--- check
|
|
if((double)(MathSqrt(v))<=(double)(state.m_epsx))
|
|
{
|
|
//--- X(k+1)-X(k) is small enough
|
|
state.m_repterminationtype=2;
|
|
//--- return result
|
|
return(false);
|
|
}
|
|
//--- If Wolfe conditions are satisfied, we can update
|
|
//--- limited memory model.
|
|
//--- However, if conditions are not satisfied (NFEV limit is met,
|
|
//--- function is too wild, ...), we'll skip L-BFGS update
|
|
if(mcinfo!=1)
|
|
{
|
|
//--- Skip update.
|
|
//--- In such cases we'll initialize search direction by
|
|
//--- antigradient vector, because it leads to more
|
|
//--- transparent code with less number of special cases
|
|
state.m_fold=state.m_f;
|
|
for(int i_=0;i_<=n-1;i_++)
|
|
state.m_d[i_]=-state.m_g[i_];
|
|
}
|
|
else
|
|
{
|
|
//--- Calculate Rho[k], GammaK
|
|
v=0.0;
|
|
for(int i_=0;i_<=n-1;i_++)
|
|
v+=state.m_yk[state.m_p][i_]*state.m_sk[state.m_p][i_];
|
|
//--- change value
|
|
vv=0.0;
|
|
for(int i_=0;i_<=n-1;i_++)
|
|
vv+=state.m_yk[state.m_p][i_]*state.m_yk[state.m_p][i_];
|
|
//--- check
|
|
if(v==0.0 || vv==0.0)
|
|
{
|
|
//--- Rounding errors make further iterations impossible.
|
|
state.m_repterminationtype=-2;
|
|
//--- return result
|
|
return(false);
|
|
}
|
|
//--- change values
|
|
state.m_rho[state.m_p]=1/v;
|
|
state.m_gammak=v/vv;
|
|
//--- Calculate d(k+1)=-H(k+1)*g(k+1)
|
|
//--- for I:=K downto K-Q do
|
|
//--- V=s(i)^T * work(iteration:I)
|
|
//--- theta(i)=V
|
|
//--- work(iteration:I+1)=work(iteration:I)-V*Rho(i)*y(i)
|
|
//--- work(last iteration)=H0*work(last iteration)-preconditioner
|
|
//--- for I:=K-Q to K do
|
|
//--- V=y(i)^T*work(iteration:I)
|
|
//--- work(iteration:I+1)=work(iteration:I) +(-V+theta(i))*Rho(i)*s(i)
|
|
//--- NOW WORK CONTAINS d(k+1)
|
|
for(int i_=0;i_<=n-1;i_++)
|
|
state.m_work[i_]=state.m_g[i_];
|
|
for(i=state.m_k;i>=state.m_k-state.m_q;i--)
|
|
{
|
|
ic=i%m;
|
|
v=0.0;
|
|
for(int i_=0;i_<=n-1;i_++)
|
|
v+=state.m_sk[ic][i_]*state.m_work[i_];
|
|
//--- change values
|
|
state.m_theta[ic]=v;
|
|
vv=v*state.m_rho[ic];
|
|
for(int i_=0;i_<=n-1;i_++)
|
|
state.m_work[i_]=state.m_work[i_]-vv*state.m_yk[ic][i_];
|
|
}
|
|
//--- check
|
|
if(state.m_prectype==0)
|
|
{
|
|
//--- Simple preconditioner is used
|
|
v=state.m_gammak;
|
|
for(int i_=0;i_<=n-1;i_++)
|
|
state.m_work[i_]=v*state.m_work[i_];
|
|
}
|
|
//--- check
|
|
if(state.m_prectype==1)
|
|
{
|
|
//--- Cholesky preconditioner is used
|
|
CFbls::FblsCholeskySolve(state.m_denseh,1,n,true,state.m_work,state.m_autobuf);
|
|
}
|
|
//--- check
|
|
if(state.m_prectype==2)
|
|
{
|
|
//--- diagonal approximation is used
|
|
for(i=0;i<=n-1;i++)
|
|
{
|
|
state.m_work[i]=state.m_work[i]/state.m_diagh[i];
|
|
}
|
|
}
|
|
//--- check
|
|
if(state.m_prectype==3)
|
|
{
|
|
//--- scale-based preconditioner is used
|
|
for(i=0;i<=n-1;i++)
|
|
state.m_work[i]=state.m_work[i]*state.m_s[i]*state.m_s[i];
|
|
}
|
|
//--- calculation
|
|
for(i=state.m_k-state.m_q;i<=state.m_k;i++)
|
|
{
|
|
ic=i%m;
|
|
v=0.0;
|
|
for(int i_=0;i_<=n-1;i_++)
|
|
v+=state.m_yk[ic][i_]*state.m_work[i_];
|
|
//--- change value
|
|
vv=state.m_rho[ic]*(-v+state.m_theta[ic]);
|
|
for(int i_=0;i_<=n-1;i_++)
|
|
{
|
|
state.m_work[i_]=state.m_work[i_]+vv*state.m_sk[ic][i_];
|
|
}
|
|
}
|
|
for(int i_=0;i_<=n-1;i_++)
|
|
state.m_d[i_]=-state.m_work[i_];
|
|
//--- Next step
|
|
state.m_fold=state.m_f;
|
|
state.m_k=state.m_k+1;
|
|
}
|
|
//--- function call, return result
|
|
return(Func_lbl_21(state,n,m,i,j,ic,mcinfo,v,vv));
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| This object stores nonlinear optimizer state. |
|
|
//| You should use functions provided by MinQP subpackage to work |
|
|
//| with this object |
|
|
//+------------------------------------------------------------------+
|
|
class CMinQPState
|
|
{
|
|
public:
|
|
//--- variables
|
|
int m_n;
|
|
int m_algokind;
|
|
int m_akind;
|
|
bool m_havex;
|
|
double m_constterm;
|
|
int m_repinneriterationscount;
|
|
int m_repouteriterationscount;
|
|
int m_repncholesky;
|
|
int m_repnmv;
|
|
int m_repterminationtype;
|
|
CApBuff m_buf;
|
|
//--- arrays
|
|
double m_diaga[];
|
|
double m_b[];
|
|
double m_bndl[];
|
|
double m_bndu[];
|
|
bool m_havebndl[];
|
|
bool m_havebndu[];
|
|
double m_xorigin[];
|
|
double m_startx[];
|
|
double m_xc[];
|
|
double m_gc[];
|
|
int m_activeconstraints[];
|
|
int m_prevactiveconstraints[];
|
|
double m_workbndl[];
|
|
double m_workbndu[];
|
|
double m_tmp0[];
|
|
double m_tmp1[];
|
|
int m_itmp0[];
|
|
int m_p2[];
|
|
double m_bufb[];
|
|
double m_bufx[];
|
|
//--- matrix
|
|
CMatrixDouble m_densea;
|
|
CMatrixDouble m_bufa;
|
|
//--- constructor, destructor
|
|
CMinQPState(void);
|
|
~CMinQPState(void);
|
|
//--- copy
|
|
void Copy(CMinQPState &obj);
|
|
};
|
|
//+------------------------------------------------------------------+
|
|
//| Constructor without parameters |
|
|
//+------------------------------------------------------------------+
|
|
CMinQPState::CMinQPState(void)
|
|
{
|
|
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Destructor |
|
|
//+------------------------------------------------------------------+
|
|
CMinQPState::~CMinQPState(void)
|
|
{
|
|
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Copy |
|
|
//+------------------------------------------------------------------+
|
|
void CMinQPState::Copy(CMinQPState &obj)
|
|
{
|
|
//--- copy variables
|
|
m_n=obj.m_n;
|
|
m_algokind=obj.m_algokind;
|
|
m_akind=obj.m_akind;
|
|
m_havex=obj.m_havex;
|
|
m_constterm=obj.m_constterm;
|
|
m_repinneriterationscount=obj.m_repinneriterationscount;
|
|
m_repouteriterationscount=obj.m_repouteriterationscount;
|
|
m_repncholesky=obj.m_repncholesky;
|
|
m_repnmv=obj.m_repnmv;
|
|
m_repterminationtype=obj.m_repterminationtype;
|
|
m_buf.Copy(obj.m_buf);
|
|
//--- copy arrays
|
|
ArrayCopy(m_diaga,obj.m_diaga);
|
|
ArrayCopy(m_b,obj.m_b);
|
|
ArrayCopy(m_bndl,obj.m_bndl);
|
|
ArrayCopy(m_bndu,obj.m_bndu);
|
|
ArrayCopy(m_havebndl,obj.m_havebndl);
|
|
ArrayCopy(m_havebndu,obj.m_havebndu);
|
|
ArrayCopy(m_xorigin,obj.m_xorigin);
|
|
ArrayCopy(m_startx,obj.m_startx);
|
|
ArrayCopy(m_xc,obj.m_xc);
|
|
ArrayCopy(m_gc,obj.m_gc);
|
|
ArrayCopy(m_activeconstraints,obj.m_activeconstraints);
|
|
ArrayCopy(m_prevactiveconstraints,obj.m_prevactiveconstraints);
|
|
ArrayCopy(m_workbndl,obj.m_workbndl);
|
|
ArrayCopy(m_workbndu,obj.m_workbndu);
|
|
ArrayCopy(m_tmp0,obj.m_tmp0);
|
|
ArrayCopy(m_tmp1,obj.m_tmp1);
|
|
ArrayCopy(m_itmp0,obj.m_itmp0);
|
|
ArrayCopy(m_p2,obj.m_p2);
|
|
ArrayCopy(m_bufb,obj.m_bufb);
|
|
ArrayCopy(m_bufx,obj.m_bufx);
|
|
//--- copy matrix
|
|
m_densea=obj.m_densea;
|
|
m_bufa=obj.m_bufa;
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| This object stores nonlinear optimizer state. |
|
|
//| You should use functions provided by MinQP subpackage to work |
|
|
//| with this object |
|
|
//+------------------------------------------------------------------+
|
|
class CMinQPStateShell
|
|
{
|
|
private:
|
|
CMinQPState m_innerobj;
|
|
public:
|
|
//--- constructors, destructor
|
|
CMinQPStateShell(void);
|
|
CMinQPStateShell(CMinQPState &obj);
|
|
~CMinQPStateShell(void);
|
|
//--- method
|
|
CMinQPState *GetInnerObj(void);
|
|
};
|
|
//+------------------------------------------------------------------+
|
|
//| Constructor without parameters |
|
|
//+------------------------------------------------------------------+
|
|
CMinQPStateShell::CMinQPStateShell(void)
|
|
{
|
|
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Copy constructor |
|
|
//+------------------------------------------------------------------+
|
|
CMinQPStateShell::CMinQPStateShell(CMinQPState &obj)
|
|
{
|
|
//--- copy
|
|
m_innerobj.Copy(obj);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Destructor |
|
|
//+------------------------------------------------------------------+
|
|
CMinQPStateShell::~CMinQPStateShell(void)
|
|
{
|
|
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Return object of class |
|
|
//+------------------------------------------------------------------+
|
|
CMinQPState *CMinQPStateShell::GetInnerObj(void)
|
|
{
|
|
//--- return result
|
|
return(GetPointer(m_innerobj));
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| This structure stores optimization report: |
|
|
//| * InnerIterationsCount number of inner iterations |
|
|
//| * OuterIterationsCount number of outer iterations |
|
|
//| * NCholesky number of Cholesky decomposition |
|
|
//| * NMV number of matrix-vector products |
|
|
//| (only products calculated as part of |
|
|
//| iterative process are counted) |
|
|
//| * TerminationType completion code (see below) |
|
|
//| Completion codes: |
|
|
//| * -5 inappropriate solver was used: |
|
|
//| * Cholesky solver for semidefinite or indefinite problems|
|
|
//| * Cholesky solver for problems with non-boundary |
|
|
//| constraints |
|
|
//| * -3 inconsistent constraints (or, maybe, feasible point is |
|
|
//| too hard to find). If you are sure that constraints are |
|
|
//| feasible, try to restart optimizer with better initial |
|
|
//| approximation. |
|
|
//| * 4 successful completion |
|
|
//| * 5 MaxIts steps was taken |
|
|
//| * 7 stopping conditions are too stringent, |
|
|
//| further improvement is impossible, |
|
|
//| X contains best point found so far. |
|
|
//+------------------------------------------------------------------+
|
|
class CMinQPReport
|
|
{
|
|
public:
|
|
//--- variables
|
|
int m_inneriterationscount;
|
|
int m_outeriterationscount;
|
|
int m_nmv;
|
|
int m_ncholesky;
|
|
int m_terminationtype;
|
|
//--- constructor, destructor
|
|
CMinQPReport(void);
|
|
~CMinQPReport(void);
|
|
//--- copy
|
|
void Copy(CMinQPReport &obj);
|
|
};
|
|
//+------------------------------------------------------------------+
|
|
//| Constructor without parameters |
|
|
//+------------------------------------------------------------------+
|
|
CMinQPReport::CMinQPReport(void)
|
|
{
|
|
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Destructor |
|
|
//+------------------------------------------------------------------+
|
|
CMinQPReport::~CMinQPReport(void)
|
|
{
|
|
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Copy |
|
|
//+------------------------------------------------------------------+
|
|
void CMinQPReport::Copy(CMinQPReport &obj)
|
|
{
|
|
//--- copy variables
|
|
m_inneriterationscount=obj.m_inneriterationscount;
|
|
m_outeriterationscount=obj.m_outeriterationscount;
|
|
m_nmv=obj.m_nmv;
|
|
m_ncholesky=obj.m_ncholesky;
|
|
m_terminationtype=obj.m_terminationtype;
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| This structure stores optimization report: |
|
|
//| * InnerIterationsCount number of inner iterations |
|
|
//| * OuterIterationsCount number of outer iterations |
|
|
//| * NCholesky number of Cholesky decomposition |
|
|
//| * NMV number of matrix-vector products |
|
|
//| (only products calculated as part of |
|
|
//| iterative process are counted) |
|
|
//| * TerminationType completion code (see below) |
|
|
//| Completion codes: |
|
|
//| * -5 inappropriate solver was used: |
|
|
//| * Cholesky solver for semidefinite or indefinite problems|
|
|
//| * Cholesky solver for problems with non-boundary |
|
|
//| constraints |
|
|
//| * -3 inconsistent constraints (or, maybe, feasible point is |
|
|
//| too hard to find). If you are sure that constraints are |
|
|
//| feasible, try to restart optimizer with better initial |
|
|
//| approximation. |
|
|
//| * 4 successful completion |
|
|
//| * 5 MaxIts steps was taken |
|
|
//| * 7 stopping conditions are too stringent, |
|
|
//| further improvement is impossible, |
|
|
//| X contains best point found so far. |
|
|
//+------------------------------------------------------------------+
|
|
class CMinQPReportShell
|
|
{
|
|
private:
|
|
CMinQPReport m_innerobj;
|
|
public:
|
|
//--- constructors, destructor
|
|
CMinQPReportShell(void);
|
|
CMinQPReportShell(CMinQPReport &obj);
|
|
~CMinQPReportShell(void);
|
|
//--- methods
|
|
int GetInnerIterationsCount(void);
|
|
void SetInnerIterationsCount(const int i);
|
|
int GetOuterIterationsCount(void);
|
|
void SetOuterIterationsCount(const int i);
|
|
int GetNMV(void);
|
|
void SetNMV(const int i);
|
|
int GetNCholesky(void);
|
|
void SetNCholesky(const int i);
|
|
int GetTerminationType(void);
|
|
void SetTerminationType(const int i);
|
|
CMinQPReport *GetInnerObj(void);
|
|
};
|
|
//+------------------------------------------------------------------+
|
|
//| Constructor without parameters |
|
|
//+------------------------------------------------------------------+
|
|
CMinQPReportShell::CMinQPReportShell(void)
|
|
{
|
|
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Copy constructor |
|
|
//+------------------------------------------------------------------+
|
|
CMinQPReportShell::CMinQPReportShell(CMinQPReport &obj)
|
|
{
|
|
//--- copy
|
|
m_innerobj.Copy(obj);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Destructor |
|
|
//+------------------------------------------------------------------+
|
|
CMinQPReportShell::~CMinQPReportShell(void)
|
|
{
|
|
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Returns the value of the variable inneriterationscount |
|
|
//+------------------------------------------------------------------+
|
|
int CMinQPReportShell::GetInnerIterationsCount(void)
|
|
{
|
|
//--- return result
|
|
return(m_innerobj.m_inneriterationscount);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Changing the value of the variable inneriterationscount |
|
|
//+------------------------------------------------------------------+
|
|
void CMinQPReportShell::SetInnerIterationsCount(const int i)
|
|
{
|
|
//--- change value
|
|
m_innerobj.m_inneriterationscount=i;
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Returns the value of the variable outeriterationscount |
|
|
//+------------------------------------------------------------------+
|
|
int CMinQPReportShell::GetOuterIterationsCount(void)
|
|
{
|
|
//--- return result
|
|
return(m_innerobj.m_outeriterationscount);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Changing the value of the variable outeriterationscount |
|
|
//+------------------------------------------------------------------+
|
|
void CMinQPReportShell::SetOuterIterationsCount(const int i)
|
|
{
|
|
//--- change value
|
|
m_innerobj.m_outeriterationscount=i;
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Returns the value of the variable nmv |
|
|
//+------------------------------------------------------------------+
|
|
int CMinQPReportShell::GetNMV(void)
|
|
{
|
|
//--- return result
|
|
return(m_innerobj.m_nmv);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Changing the value of the variable nmv |
|
|
//+------------------------------------------------------------------+
|
|
void CMinQPReportShell::SetNMV(const int i)
|
|
{
|
|
//--- change value
|
|
m_innerobj.m_nmv=i;
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Returns the value of the variable ncholesky |
|
|
//+------------------------------------------------------------------+
|
|
int CMinQPReportShell::GetNCholesky(void)
|
|
{
|
|
//--- return result
|
|
return(m_innerobj.m_ncholesky);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Changing the value of the variable ncholesky |
|
|
//+------------------------------------------------------------------+
|
|
void CMinQPReportShell::SetNCholesky(const int i)
|
|
{
|
|
//--- change value
|
|
m_innerobj.m_ncholesky=i;
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Returns the value of the variable terminationtype |
|
|
//+------------------------------------------------------------------+
|
|
int CMinQPReportShell::GetTerminationType(void)
|
|
{
|
|
//--- return result
|
|
return(m_innerobj.m_terminationtype);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Changing the value of the variable terminationtype |
|
|
//+------------------------------------------------------------------+
|
|
void CMinQPReportShell::SetTerminationType(const int i)
|
|
{
|
|
//--- change value
|
|
m_innerobj.m_terminationtype=i;
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Return object of class |
|
|
//+------------------------------------------------------------------+
|
|
CMinQPReport *CMinQPReportShell::GetInnerObj(void)
|
|
{
|
|
//--- return result
|
|
return(GetPointer(m_innerobj));
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Constrained quadratic programming |
|
|
//+------------------------------------------------------------------+
|
|
class CMinQP
|
|
{
|
|
private:
|
|
//--- private methods
|
|
static void MinQPGrad(CMinQPState &state);
|
|
static double MinQPXTAX(CMinQPState &state,double &x[]);
|
|
public:
|
|
//--- constructor, destructor
|
|
CMinQP(void);
|
|
~CMinQP(void);
|
|
//--- public methods
|
|
static void MinQPCreate(const int n,CMinQPState &state);
|
|
static void MinQPSetLinearTerm(CMinQPState &state,double &b[]);
|
|
static void MinQPSetQuadraticTerm(CMinQPState &state,CMatrixDouble &a,const bool isupper);
|
|
static void MinQPSetStartingPoint(CMinQPState &state,double &x[]);
|
|
static void MinQPSetOrigin(CMinQPState &state,double &xorigin[]);
|
|
static void MinQPSetAlgoCholesky(CMinQPState &state);
|
|
static void MinQPSetBC(CMinQPState &state,double &bndl[],double &bndu[]);
|
|
static void MinQPOptimize(CMinQPState &state);
|
|
static void MinQPResults(CMinQPState &state,double &x[],CMinQPReport &rep);
|
|
static void MinQPResultsBuf(CMinQPState &state,double &x[],CMinQPReport &rep);
|
|
static void MinQPSetLinearTermFast(CMinQPState &state,double &b[]);
|
|
static void MinQPSetQuadraticTermFast(CMinQPState &state,CMatrixDouble &a,const bool isupper,const double s);
|
|
static void MinQPRewriteDiagonal(CMinQPState &state,double &s[]);
|
|
static void MinQPSetStartingPointFast(CMinQPState &state,double &x[]);
|
|
static void MinQPSetOriginFast(CMinQPState &state,double &xorigin[]);
|
|
};
|
|
//+------------------------------------------------------------------+
|
|
//| Constructor without parameters |
|
|
//+------------------------------------------------------------------+
|
|
CMinQP::CMinQP(void)
|
|
{
|
|
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Destructor |
|
|
//+------------------------------------------------------------------+
|
|
CMinQP::~CMinQP(void)
|
|
{
|
|
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| CONSTRAINED QUADRATIC PROGRAMMING |
|
|
//| The subroutine creates QP optimizer. After initial creation, it |
|
|
//| contains default optimization problem with zero quadratic and |
|
|
//| linear terms and no constraints. You should set quadratic/linear |
|
|
//| terms with calls to functions provided by MinQP subpackage. |
|
|
//| INPUT PARAMETERS: |
|
|
//| N - problem size |
|
|
//| OUTPUT PARAMETERS: |
|
|
//| State - optimizer with zero quadratic/linear terms |
|
|
//| and no constraints |
|
|
//+------------------------------------------------------------------+
|
|
static void CMinQP::MinQPCreate(const int n,CMinQPState &state)
|
|
{
|
|
//--- create a variable
|
|
int i=0;
|
|
//--- check
|
|
if(!CAp::Assert(n>=1,__FUNCTION__+": N<1"))
|
|
return;
|
|
//--- initialize QP solver
|
|
state.m_n=n;
|
|
state.m_akind=-1;
|
|
state.m_repterminationtype=0;
|
|
//--- allocation
|
|
ArrayResizeAL(state.m_b,n);
|
|
ArrayResizeAL(state.m_bndl,n);
|
|
ArrayResizeAL(state.m_bndu,n);
|
|
ArrayResizeAL(state.m_workbndl,n);
|
|
ArrayResizeAL(state.m_workbndu,n);
|
|
ArrayResizeAL(state.m_havebndl,n);
|
|
ArrayResizeAL(state.m_havebndu,n);
|
|
ArrayResizeAL(state.m_startx,n);
|
|
ArrayResizeAL(state.m_xorigin,n);
|
|
ArrayResizeAL(state.m_xc,n);
|
|
ArrayResizeAL(state.m_gc,n);
|
|
//--- initialization
|
|
for(i=0;i<=n-1;i++)
|
|
{
|
|
state.m_b[i]=0.0;
|
|
state.m_workbndl[i]=CInfOrNaN::NegativeInfinity();
|
|
state.m_workbndu[i]=CInfOrNaN::PositiveInfinity();
|
|
state.m_havebndl[i]=false;
|
|
state.m_havebndu[i]=false;
|
|
state.m_startx[i]=0.0;
|
|
state.m_xorigin[i]=0.0;
|
|
}
|
|
state.m_havex=false;
|
|
//--- function call
|
|
MinQPSetAlgoCholesky(state);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| This function sets linear term for QP solver. |
|
|
//| By default, linear term is zero. |
|
|
//| INPUT PARAMETERS: |
|
|
//| State - structure which stores algorithm state |
|
|
//| B - linear term, array[N]. |
|
|
//+------------------------------------------------------------------+
|
|
static void CMinQP::MinQPSetLinearTerm(CMinQPState &state,double &b[])
|
|
{
|
|
//--- create a variable
|
|
int n=0;
|
|
//--- initialization
|
|
n=state.m_n;
|
|
//--- check
|
|
if(!CAp::Assert(CAp::Len(b)>=n,__FUNCTION__+": Length(B)<N"))
|
|
return;
|
|
//--- check
|
|
if(!CAp::Assert(CApServ::IsFiniteVector(b,n),__FUNCTION__+": B contains infinite or NaN elements"))
|
|
return;
|
|
//--- function call
|
|
MinQPSetLinearTermFast(state,b);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| This function sets quadratic term for QP solver. |
|
|
//| By default quadratic term is zero. |
|
|
//| IMPORTANT: this solver minimizes following function: |
|
|
//| f(x) = 0.5*x'*A*x + b'*x. |
|
|
//| Note that quadratic term has 0.5 before it. So if you want to |
|
|
//| minimize |
|
|
//| f(x) = x^2 + x |
|
|
//| you should rewrite your problem as follows: |
|
|
//| f(x) = 0.5*(2*x^2) + x |
|
|
//| and your matrix A will be equal to [[2.0]], not to [[1.0]] |
|
|
//| INPUT PARAMETERS: |
|
|
//| State - structure which stores algorithm state |
|
|
//| A - matrix, array[N,N] |
|
|
//| IsUpper - (optional) storage type: |
|
|
//| * if True, symmetric matrix A is given by its |
|
|
//| upper triangle, and the lower triangle isn?t |
|
|
//| used |
|
|
//| * if False, symmetric matrix A is given by its |
|
|
//| lower triangle, and the upper triangle isn?t |
|
|
//| used |
|
|
//| * if not given, both lower and upper triangles |
|
|
//| must be filled. |
|
|
//+------------------------------------------------------------------+
|
|
static void CMinQP::MinQPSetQuadraticTerm(CMinQPState &state,CMatrixDouble &a,
|
|
const bool isupper)
|
|
{
|
|
//--- create a variable
|
|
int n=0;
|
|
//--- initialization
|
|
n=state.m_n;
|
|
//--- check
|
|
if(!CAp::Assert(CAp::Rows(a)>=n,__FUNCTION__+": Rows(A)<N"))
|
|
return;
|
|
//--- check
|
|
if(!CAp::Assert(CAp::Cols(a)>=n,__FUNCTION__+": Cols(A)<N"))
|
|
return;
|
|
//--- check
|
|
if(!CAp::Assert(CApServ::IsFiniteRTrMatrix(a,n,isupper),__FUNCTION__+": A contains infinite or NaN elements"))
|
|
return;
|
|
//--- function call
|
|
MinQPSetQuadraticTermFast(state,a,isupper,0.0);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| This function sets starting point for QP solver. It is useful to |
|
|
//| have good initial approximation to the solution, because it will |
|
|
//| increase speed of convergence and identification of active |
|
|
//| constraints. |
|
|
//| INPUT PARAMETERS: |
|
|
//| State - structure which stores algorithm state |
|
|
//| X - starting point, array[N]. |
|
|
//+------------------------------------------------------------------+
|
|
static void CMinQP::MinQPSetStartingPoint(CMinQPState &state,double &x[])
|
|
{
|
|
//--- create a variable
|
|
int n=0;
|
|
//--- initialization
|
|
n=state.m_n;
|
|
//--- check
|
|
if(!CAp::Assert(CAp::Len(x)>=n,__FUNCTION__+": Length(B)<N"))
|
|
return;
|
|
//--- check
|
|
if(!CAp::Assert(CApServ::IsFiniteVector(x,n),__FUNCTION__+": X contains infinite or NaN elements"))
|
|
return;
|
|
//--- function call
|
|
MinQPSetStartingPointFast(state,x);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| This function sets origin for QP solver. By default, following |
|
|
//| QP program is solved: |
|
|
//| min(0.5*x'*A*x+b'*x) |
|
|
//| This function allows to solve different problem: |
|
|
//| min(0.5*(x-x_origin)'*A*(x-x_origin)+b'*(x-x_origin)) |
|
|
//| INPUT PARAMETERS: |
|
|
//| State - structure which stores algorithm state |
|
|
//| XOrigin - origin, array[N]. |
|
|
//+------------------------------------------------------------------+
|
|
static void CMinQP::MinQPSetOrigin(CMinQPState &state,double &xorigin[])
|
|
{
|
|
//--- create a variable
|
|
int n=0;
|
|
//--- initialization
|
|
n=state.m_n;
|
|
//--- check
|
|
if(!CAp::Assert(CAp::Len(xorigin)>=n,__FUNCTION__+": Length(B)<N"))
|
|
return;
|
|
//--- check
|
|
if(!CAp::Assert(CApServ::IsFiniteVector(xorigin,n),__FUNCTION__+": B contains infinite or NaN elements"))
|
|
return;
|
|
//--- function call
|
|
MinQPSetOriginFast(state,xorigin);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| This function tells solver to use Cholesky-based algorithm. |
|
|
//| Cholesky-based algorithm can be used when: |
|
|
//| * problem is convex |
|
|
//| * there is no constraints or only boundary constraints are |
|
|
//| present |
|
|
//| This algorithm has O(N^3) complexity for unconstrained problem |
|
|
//| and is up to several times slower on bound constrained problems |
|
|
//| (these additional iterations are needed to identify active |
|
|
//| constraints). |
|
|
//| INPUT PARAMETERS: |
|
|
//| State - structure which stores algorithm state |
|
|
//+------------------------------------------------------------------+
|
|
static void CMinQP::MinQPSetAlgoCholesky(CMinQPState &state)
|
|
{
|
|
//--- change value
|
|
state.m_algokind=1;
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| This function sets boundary constraints for QP solver |
|
|
//| Boundary constraints are inactive by default (after initial |
|
|
//| creation). After being set, they are preserved until explicitly |
|
|
//| turned off with another SetBC() call. |
|
|
//| INPUT PARAMETERS: |
|
|
//| State - structure stores algorithm state |
|
|
//| BndL - lower bounds, array[N]. |
|
|
//| If some (all) variables are unbounded, you may |
|
|
//| specify very small number or -INF (latter is |
|
|
//| recommended because it will allow solver to use |
|
|
//| better algorithm). |
|
|
//| BndU - upper bounds, array[N]. |
|
|
//| If some (all) variables are unbounded, you may |
|
|
//| specify very large number or +INF (latter is |
|
|
//| recommended because it will allow solver to use |
|
|
//| better algorithm). |
|
|
//| NOTE: it is possible to specify BndL[i]=BndU[i]. In this case |
|
|
//| I-th variable will be "frozen" at X[i]=BndL[i]=BndU[i]. |
|
|
//+------------------------------------------------------------------+
|
|
static void CMinQP::MinQPSetBC(CMinQPState &state,double &bndl[],double &bndu[])
|
|
{
|
|
//--- create variables
|
|
int i=0;
|
|
int n=0;
|
|
//--- initialization
|
|
n=state.m_n;
|
|
//--- check
|
|
if(!CAp::Assert(CAp::Len(bndl)>=n,__FUNCTION__+": Length(BndL)<N"))
|
|
return;
|
|
//--- check
|
|
if(!CAp::Assert(CAp::Len(bndu)>=n,__FUNCTION__+": Length(BndU)<N"))
|
|
return;
|
|
//--- change values
|
|
for(i=0;i<=n-1;i++)
|
|
{
|
|
//--- check
|
|
if(!CAp::Assert(CMath::IsFinite(bndl[i]) || CInfOrNaN::IsNegativeInfinity(bndl[i]),__FUNCTION__+": BndL contains NAN or +INF"))
|
|
return;
|
|
//--- check
|
|
if(!CAp::Assert(CMath::IsFinite(bndu[i]) || CInfOrNaN::IsPositiveInfinity(bndu[i]),__FUNCTION__+": BndU contains NAN or -INF"))
|
|
return;
|
|
//--- change values
|
|
state.m_bndl[i]=bndl[i];
|
|
state.m_havebndl[i]=CMath::IsFinite(bndl[i]);
|
|
state.m_bndu[i]=bndu[i];
|
|
state.m_havebndu[i]=CMath::IsFinite(bndu[i]);
|
|
}
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| This function solves quadratic programming problem. |
|
|
//| You should call it after setting solver options with |
|
|
//| MinQPSet...() calls. |
|
|
//| INPUT PARAMETERS: |
|
|
//| State - algorithm state |
|
|
//| You should use MinQPResults() function to access results after |
|
|
//| calls to this function. |
|
|
//+------------------------------------------------------------------+
|
|
static void CMinQP::MinQPOptimize(CMinQPState &state)
|
|
{
|
|
//--- create variables
|
|
int n=0;
|
|
int i=0;
|
|
int j=0;
|
|
int k=0;
|
|
int nbc=0;
|
|
int nlc=0;
|
|
int nactive=0;
|
|
int nfree=0;
|
|
double f=0;
|
|
double fprev=0;
|
|
double v=0;
|
|
bool b;
|
|
int i_=0;
|
|
//--- initialization
|
|
n=state.m_n;
|
|
state.m_repterminationtype=-5;
|
|
state.m_repinneriterationscount=0;
|
|
state.m_repouteriterationscount=0;
|
|
state.m_repncholesky=0;
|
|
state.m_repnmv=0;
|
|
//--- check correctness of constraints
|
|
for(i=0;i<=n-1;i++)
|
|
{
|
|
//--- check
|
|
if(state.m_havebndl[i] && state.m_havebndu[i])
|
|
{
|
|
//--- check
|
|
if(state.m_bndl[i]>state.m_bndu[i])
|
|
{
|
|
state.m_repterminationtype=-3;
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
//--- count number of bound and linear constraints
|
|
nbc=0;
|
|
nlc=0;
|
|
for(i=0;i<=n-1;i++)
|
|
{
|
|
//--- check
|
|
if(state.m_havebndl[i])
|
|
nbc=nbc+1;
|
|
//--- check
|
|
if(state.m_havebndu[i])
|
|
nbc=nbc+1;
|
|
}
|
|
//--- Our formulation of quadratic problem includes origin point,
|
|
//--- i.m_e. we have F(x-x_origin) which is minimized subject to
|
|
//--- constraints on x,instead of having simply F(x).
|
|
//--- Here we make transition from non-zero origin to zero one.
|
|
//--- In order to make such transition we have to:
|
|
//--- 1. subtract x_origin from x_start
|
|
//--- 2. modify constraints
|
|
//--- 3. solve problem
|
|
//--- 4. add x_origin to solution
|
|
//--- There is alternate solution - to modify quadratic function
|
|
//--- by expansion of multipliers containing (x-x_origin),but
|
|
//--- we prefer to modify constraints,because it is a) more precise
|
|
//--- and b) easier to to.
|
|
//--- Parts (1)-(2) are done here. After this block is over,
|
|
//--- we have:
|
|
//--- * XC,which stores shifted XStart (if we don't have XStart,
|
|
//--- value of XC will be ignored later)
|
|
//--- * WorkBndL,WorkBndU,which store modified boundary constraints.
|
|
for(i=0;i<=n-1;i++)
|
|
{
|
|
state.m_xc[i]=state.m_startx[i]-state.m_xorigin[i];
|
|
//--- check
|
|
if(state.m_havebndl[i])
|
|
state.m_workbndl[i]=state.m_bndl[i]-state.m_xorigin[i];
|
|
//--- check
|
|
if(state.m_havebndu[i])
|
|
state.m_workbndu[i]=state.m_bndu[i]-state.m_xorigin[i];
|
|
}
|
|
//--- modify starting point XC according to boundary constraints
|
|
if(state.m_havex)
|
|
{
|
|
//--- We have starting point in XC,so we just have to bound it
|
|
for(i=0;i<=n-1;i++)
|
|
{
|
|
//--- check
|
|
if(state.m_havebndl[i])
|
|
{
|
|
//--- check
|
|
if(state.m_xc[i]<state.m_workbndl[i])
|
|
state.m_xc[i]=state.m_workbndl[i];
|
|
}
|
|
//--- check
|
|
if(state.m_havebndu[i])
|
|
{
|
|
//--- check
|
|
if(state.m_xc[i]>state.m_workbndu[i])
|
|
state.m_xc[i]=state.m_workbndu[i];
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
//--- We don't have starting point,so we deduce it from
|
|
//--- constraints (if they are present).
|
|
//--- NOTE: XC contains some meaningless values from previous block
|
|
//--- which are ignored by code below.
|
|
for(i=0;i<=n-1;i++)
|
|
{
|
|
//--- check
|
|
if(state.m_havebndl[i] && state.m_havebndu[i])
|
|
{
|
|
state.m_xc[i]=0.5*(state.m_workbndl[i]+state.m_workbndu[i]);
|
|
//--- check
|
|
if(state.m_xc[i]<state.m_workbndl[i])
|
|
state.m_xc[i]=state.m_workbndl[i];
|
|
//--- check
|
|
if(state.m_xc[i]>state.m_workbndu[i])
|
|
state.m_xc[i]=state.m_workbndu[i];
|
|
//--- continue iteration
|
|
continue;
|
|
}
|
|
//--- check
|
|
if(state.m_havebndl[i])
|
|
{
|
|
state.m_xc[i]=state.m_workbndl[i];
|
|
continue;
|
|
}
|
|
//--- check
|
|
if(state.m_havebndu[i])
|
|
{
|
|
state.m_xc[i]=state.m_workbndu[i];
|
|
continue;
|
|
}
|
|
state.m_xc[i]=0;
|
|
}
|
|
}
|
|
//--- Select algo
|
|
if(state.m_algokind==1 && state.m_akind==0)
|
|
{
|
|
//--- Cholesky-based algorithm for dense bound constrained problems.
|
|
//--- This algorithm exists in two variants:
|
|
//--- * unconstrained one,which can solve problem using only one NxN
|
|
//--- double matrix
|
|
//--- * bound constrained one,which needs two NxN matrices
|
|
//--- We will try to solve problem using unconstrained algorithm,
|
|
//--- and will use bound constrained version only when constraints
|
|
//--- are actually present
|
|
if(nbc==0 && nlc==0)
|
|
{
|
|
//--- "Simple" unconstrained version
|
|
CApServ::RVectorSetLengthAtLeast(state.m_tmp0,n);
|
|
//--- function call
|
|
CApServ::RVectorSetLengthAtLeast(state.m_bufb,n);
|
|
//--- calculation
|
|
state.m_densea[0].Set(0,state.m_diaga[0]);
|
|
for(k=1;k<=n-1;k++)
|
|
{
|
|
for(i_=0;i_<=k-1;i_++)
|
|
state.m_densea[i_].Set(k,state.m_densea[k][i_]);
|
|
state.m_densea[k].Set(k,state.m_diaga[k]);
|
|
}
|
|
//--- change values
|
|
for(i_=0;i_<=n-1;i_++)
|
|
state.m_bufb[i_]=state.m_b[i_];
|
|
state.m_repncholesky=1;
|
|
//--- check
|
|
if(!CTrFac::SPDMatrixCholeskyRec(state.m_densea,0,n,true,state.m_tmp0))
|
|
{
|
|
state.m_repterminationtype=-5;
|
|
return;
|
|
}
|
|
//--- function call
|
|
CFbls::FblsCholeskySolve(state.m_densea,1.0,n,true,state.m_bufb,state.m_tmp0);
|
|
for(i_=0;i_<=n-1;i_++)
|
|
state.m_xc[i_]=-state.m_bufb[i_];
|
|
for(i_=0;i_<=n-1;i_++)
|
|
state.m_xc[i_]=state.m_xc[i_]+state.m_xorigin[i_];
|
|
//--- change values
|
|
state.m_repouteriterationscount=1;
|
|
state.m_repterminationtype=4;
|
|
//--- exit the function
|
|
return;
|
|
}
|
|
//--- General bound constrained algo
|
|
CApServ::RMatrixSetLengthAtLeast(state.m_bufa,n,n);
|
|
//--- function call
|
|
CApServ::RVectorSetLengthAtLeast(state.m_bufb,n);
|
|
//--- function call
|
|
CApServ::RVectorSetLengthAtLeast(state.m_bufx,n);
|
|
//--- function call
|
|
CApServ::IVectorSetLengthAtLeast(state.m_activeconstraints,n);
|
|
//--- function call
|
|
CApServ::IVectorSetLengthAtLeast(state.m_prevactiveconstraints,n);
|
|
//--- function call
|
|
CApServ::RVectorSetLengthAtLeast(state.m_tmp0,n);
|
|
//--- Prepare constraints vectors:
|
|
//--- * ActiveConstraints - constraints active at current step
|
|
//--- * PrevActiveConstraints - constraints which were active at previous step
|
|
//--- Elements of constraints vectors can be:
|
|
//--- * 0 - inactive
|
|
//--- * 1 - active
|
|
//--- * -1 - undefined (used to initialize PrevActiveConstraints before first iteration)
|
|
for(i=0;i<=n-1;i++)
|
|
state.m_prevactiveconstraints[i]=-1;
|
|
//--- Main cycle
|
|
fprev=CMath::m_maxrealnumber;
|
|
while(true)
|
|
{
|
|
//--- * calculate gradient at XC
|
|
//--- * determine active constraints
|
|
//--- * break if there is no free variables or
|
|
//--- there were no changes in the list of active constraints
|
|
MinQPGrad(state);
|
|
nactive=0;
|
|
for(i=0;i<=n-1;i++)
|
|
{
|
|
state.m_activeconstraints[i]=0;
|
|
//--- check
|
|
if(state.m_havebndl[i])
|
|
{
|
|
//--- check
|
|
if(state.m_xc[i]<=state.m_workbndl[i] && state.m_gc[i]>=0.0)
|
|
state.m_activeconstraints[i]=1;
|
|
}
|
|
//--- check
|
|
if(state.m_havebndu[i])
|
|
{
|
|
//--- check
|
|
if(state.m_xc[i]>=state.m_workbndu[i] && state.m_gc[i]<=0.0)
|
|
state.m_activeconstraints[i]=1;
|
|
}
|
|
//--- check
|
|
if(state.m_havebndl[i] && state.m_havebndu[i])
|
|
{
|
|
//--- check
|
|
if(state.m_workbndl[i]==state.m_workbndu[i])
|
|
state.m_activeconstraints[i]=1;
|
|
}
|
|
//--- check
|
|
if(state.m_activeconstraints[i]>0)
|
|
nactive=nactive+1;
|
|
}
|
|
nfree=n-nactive;
|
|
//--- check
|
|
if(nfree==0)
|
|
break;
|
|
b=false;
|
|
for(i=0;i<=n-1;i++)
|
|
{
|
|
//--- check
|
|
if(state.m_activeconstraints[i]!=state.m_prevactiveconstraints[i])
|
|
b=true;
|
|
}
|
|
//--- check
|
|
if(!b)
|
|
break;
|
|
//--- * copy A,B and X to buffer
|
|
//--- * rearrange BufA,BufB and BufX,in such way that active variables come first,
|
|
//--- inactive are moved to the tail. We use sorting subroutine
|
|
//--- to solve this problem.
|
|
state.m_bufa[0].Set(0,state.m_diaga[0]);
|
|
for(k=1;k<=n-1;k++)
|
|
{
|
|
for(i_=0;i_<=k-1;i_++)
|
|
state.m_bufa[k].Set(i_,state.m_densea[k][i_]);
|
|
for(i_=0;i_<=k-1;i_++)
|
|
state.m_bufa[i_].Set(k,state.m_densea[k][i_]);
|
|
state.m_bufa[k].Set(k,state.m_diaga[k]);
|
|
}
|
|
//--- change values
|
|
for(i_=0;i_<=n-1;i_++)
|
|
state.m_bufb[i_]=state.m_b[i_];
|
|
for(i_=0;i_<=n-1;i_++)
|
|
state.m_bufx[i_]=state.m_xc[i_];
|
|
for(i=0;i<=n-1;i++)
|
|
state.m_tmp0[i]=state.m_activeconstraints[i];
|
|
//--- function call
|
|
CTSort::TagSortBuf(state.m_tmp0,n,state.m_itmp0,state.m_p2,state.m_buf);
|
|
for(k=0;k<=n-1;k++)
|
|
{
|
|
//--- check
|
|
if(state.m_p2[k]!=k)
|
|
{
|
|
//--- swap
|
|
v=state.m_bufb[k];
|
|
state.m_bufb[k]=state.m_bufb[state.m_p2[k]];
|
|
state.m_bufb[state.m_p2[k]]=v;
|
|
v=state.m_bufx[k];
|
|
state.m_bufx[k]=state.m_bufx[state.m_p2[k]];
|
|
state.m_bufx[state.m_p2[k]]=v;
|
|
}
|
|
}
|
|
for(i=0;i<=n-1;i++)
|
|
{
|
|
for(i_=0;i_<=n-1;i_++)
|
|
state.m_tmp0[i_]=state.m_bufa[i][i_];
|
|
for(k=0;k<=n-1;k++)
|
|
{
|
|
//--- check
|
|
if(state.m_p2[k]!=k)
|
|
{
|
|
//--- swap
|
|
v=state.m_tmp0[k];
|
|
state.m_tmp0[k]=state.m_tmp0[state.m_p2[k]];
|
|
state.m_tmp0[state.m_p2[k]]=v;
|
|
}
|
|
}
|
|
for(i_=0;i_<=n-1;i_++)
|
|
state.m_bufa[i].Set(i_,state.m_tmp0[i_]);
|
|
}
|
|
for(i=0;i<=n-1;i++)
|
|
{
|
|
//--- check
|
|
if(state.m_p2[i]!=i)
|
|
{
|
|
for(i_=0;i_<=n-1;i_++)
|
|
state.m_tmp0[i_]=state.m_bufa[i][i_];
|
|
for(i_=0;i_<=n-1;i_++)
|
|
state.m_bufa[i].Set(i_,state.m_bufa[state.m_p2[i]][i_]);
|
|
for(i_=0;i_<=n-1;i_++)
|
|
state.m_bufa[state.m_p2[i]].Set(i_,state.m_tmp0[i_]);
|
|
}
|
|
}
|
|
//--- Now we have A and B in BufA and BufB,variables are rearranged
|
|
//--- into two groups: Xf - free variables,Xc - active (fixed) variables,
|
|
//--- and our quadratic problem can be written as
|
|
//--- ( Af Ac ) ( Xf ) ( Xf )
|
|
//--- F(X)=0.5* ( Xf' Xc' ) * ( ) * ( ) + ( Bf' Bc' ) * ( )
|
|
//--- ( Ac' Acc ) ( Xc ) ( Xc )
|
|
//--- we want to convert to the optimization with respect to Xf,
|
|
//--- treating Xc as constant term. After expansion of expression above
|
|
//--- we get
|
|
//--- F(Xf)=0.5*Xf'*Af*Xf + (Bf+Ac*Xc)'*Xf + 0.5*Xc'*Acc*Xc
|
|
//--- We will update BufB using this expression and calculate
|
|
//--- constant term.
|
|
CAblas::RMatrixMVect(nfree,nactive,state.m_bufa,0,nfree,0,state.m_bufx,nfree,state.m_tmp0,0);
|
|
for(i_=0;i_<=nfree-1;i_++)
|
|
state.m_bufb[i_]=state.m_bufb[i_]+state.m_tmp0[i_];
|
|
state.m_constterm=0.0;
|
|
for(i=nfree;i<=n-1;i++)
|
|
{
|
|
state.m_constterm=state.m_constterm+0.5*state.m_bufx[i]*state.m_bufa[i][i]*state.m_bufx[i];
|
|
for(j=i+1;j<=n-1;j++)
|
|
state.m_constterm=state.m_constterm+state.m_bufx[i]*state.m_bufa[i][j]*state.m_bufx[j];
|
|
}
|
|
//--- Now we are ready to minimize F(Xf)...
|
|
state.m_repncholesky=state.m_repncholesky+1;
|
|
//--- check
|
|
if(!CTrFac::SPDMatrixCholeskyRec(state.m_bufa,0,nfree,true,state.m_tmp0))
|
|
{
|
|
state.m_repterminationtype=-5;
|
|
return;
|
|
}
|
|
//--- function call
|
|
CFbls::FblsCholeskySolve(state.m_bufa,1.0,nfree,true,state.m_bufb,state.m_tmp0);
|
|
for(i_=0;i_<=nfree-1;i_++)
|
|
state.m_bufx[i_]=-state.m_bufb[i_];
|
|
//--- ...m_and to copy results back to XC.
|
|
//--- It is done in several steps:
|
|
//--- * original order of variables is restored
|
|
//--- * result is copied back to XC
|
|
//--- * XC is bounded with respect to bound constraints
|
|
for(k=n-1;k>=0;k--)
|
|
{
|
|
//--- check
|
|
if(state.m_p2[k]!=k)
|
|
{
|
|
v=state.m_bufx[k];
|
|
state.m_bufx[k]=state.m_bufx[state.m_p2[k]];
|
|
state.m_bufx[state.m_p2[k]]=v;
|
|
}
|
|
}
|
|
for(i_=0;i_<=n-1;i_++)
|
|
state.m_xc[i_]=state.m_bufx[i_];
|
|
for(i=0;i<=n-1;i++)
|
|
{
|
|
//--- check
|
|
if(state.m_havebndl[i])
|
|
{
|
|
//--- check
|
|
if(state.m_xc[i]<state.m_workbndl[i])
|
|
state.m_xc[i]=state.m_workbndl[i];
|
|
}
|
|
//--- check
|
|
if(state.m_havebndu[i])
|
|
{
|
|
//--- check
|
|
if(state.m_xc[i]>state.m_workbndu[i])
|
|
state.m_xc[i]=state.m_workbndu[i];
|
|
}
|
|
}
|
|
//--- Calculate F,compare it with FPrev.
|
|
//--- Break if F>=FPrev
|
|
//--- (sometimes possible at extremum due to numerical noise).
|
|
f=0.0;
|
|
for(i_=0;i_<=n-1;i_++)
|
|
f+=state.m_b[i_]*state.m_xc[i_];
|
|
f=f+MinQPXTAX(state,state.m_xc);
|
|
//--- check
|
|
if(f>=fprev)
|
|
break;
|
|
fprev=f;
|
|
//--- Update PrevActiveConstraints
|
|
for(i=0;i<=n-1;i++)
|
|
state.m_prevactiveconstraints[i]=state.m_activeconstraints[i];
|
|
//--- Update report-related fields
|
|
state.m_repouteriterationscount=state.m_repouteriterationscount+1;
|
|
}
|
|
//--- change values
|
|
state.m_repterminationtype=4;
|
|
for(i_=0;i_<=n-1;i_++)
|
|
state.m_xc[i_]=state.m_xc[i_]+state.m_xorigin[i_];
|
|
//--- exit the function
|
|
return;
|
|
}
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| QP solver results |
|
|
//| INPUT PARAMETERS: |
|
|
//| State - algorithm state |
|
|
//| OUTPUT PARAMETERS: |
|
|
//| X - array[0..N-1], solution |
|
|
//| Rep - optimization report. You should check Rep. |
|
|
//| TerminationType, which contains completion code, |
|
|
//| and you may check another fields which contain |
|
|
//| another information about algorithm functioning. |
|
|
//+------------------------------------------------------------------+
|
|
static void CMinQP::MinQPResults(CMinQPState &state,double &x[],CMinQPReport &rep)
|
|
{
|
|
//--- reset memory
|
|
ArrayResizeAL(x,0);
|
|
//--- function call
|
|
MinQPResultsBuf(state,x,rep);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| QP results |
|
|
//| Buffered implementation of MinQPResults() which uses |
|
|
//| pre-allocated buffer to store X[]. If buffer size is too small, |
|
|
//| it resizes buffer. It is intended to be used in the inner cycles |
|
|
//| of performance critical algorithms where array reallocation |
|
|
//| penalty is too large to be ignored. |
|
|
//+------------------------------------------------------------------+
|
|
static void CMinQP::MinQPResultsBuf(CMinQPState &state,double &x[],
|
|
CMinQPReport &rep)
|
|
{
|
|
//--- create a variable
|
|
int i_=0;
|
|
//--- check
|
|
if(CAp::Len(x)<state.m_n)
|
|
ArrayResizeAL(x,state.m_n);
|
|
//--- copy
|
|
for(i_=0;i_<=state.m_n-1;i_++)
|
|
x[i_]=state.m_xc[i_];
|
|
//--- change values
|
|
rep.m_inneriterationscount=state.m_repinneriterationscount;
|
|
rep.m_outeriterationscount=state.m_repouteriterationscount;
|
|
rep.m_nmv=state.m_repnmv;
|
|
rep.m_terminationtype=state.m_repterminationtype;
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Fast version of MinQPSetLinearTerm(), which doesn't check its |
|
|
//| arguments. For internal use only. |
|
|
//+------------------------------------------------------------------+
|
|
static void CMinQP::MinQPSetLinearTermFast(CMinQPState &state,double &b[])
|
|
{
|
|
//--- create variables
|
|
int n=0;
|
|
int i_=0;
|
|
//--- initialization
|
|
n=state.m_n;
|
|
for(i_=0;i_<=n-1;i_++)
|
|
state.m_b[i_]=b[i_];
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Fast version of MinQPSetQuadraticTerm(), which doesn't check its |
|
|
//| arguments. |
|
|
//| It accepts additional parameter - shift S, which allows to |
|
|
//| "shift" matrix A by adding s*I to A. S must be positive (although|
|
|
//| it is not checked). |
|
|
//| For internal use only. |
|
|
//+------------------------------------------------------------------+
|
|
static void CMinQP::MinQPSetQuadraticTermFast(CMinQPState &state,CMatrixDouble &a,
|
|
const bool isupper,const double s)
|
|
{
|
|
//--- create variables
|
|
int k=0;
|
|
int n=0;
|
|
int i_=0;
|
|
//--- We store off-diagonal part of A in the lower triangle of DenseA.
|
|
//--- Diagonal elements of A are stored in the DiagA.
|
|
//--- Diagonal of DenseA and uppper triangle are used as temporaries.
|
|
//--- Why such complex storage? Because it:
|
|
//--- 1. allows us to easily recover from exceptions (lower triangle
|
|
//--- is unmodified during execution as well as DiagA,and on entry
|
|
//--- we will always find unmodified matrix)
|
|
//--- 2. allows us to make Cholesky decomposition in the upper triangle
|
|
//--- of DenseA or to do other SPD-related operations.
|
|
n=state.m_n;
|
|
state.m_akind=0;
|
|
//--- function call
|
|
CApServ::RMatrixSetLengthAtLeast(state.m_densea,n,n);
|
|
//--- function call
|
|
CApServ::RVectorSetLengthAtLeast(state.m_diaga,n);
|
|
//--- check
|
|
if(isupper)
|
|
{
|
|
for(k=0;k<=n-2;k++)
|
|
{
|
|
//--- change values
|
|
state.m_diaga[k]=a[k][k]+s;
|
|
for(i_=k+1;i_<=n-1;i_++)
|
|
state.m_densea[i_].Set(k,a[k][i_]);
|
|
}
|
|
state.m_diaga[n-1]=a[n-1][n-1]+s;
|
|
}
|
|
else
|
|
{
|
|
state.m_diaga[0]=a[0][0]+s;
|
|
for(k=1;k<=n-1;k++)
|
|
{
|
|
//--- change values
|
|
for(i_=0;i_<=k-1;i_++)
|
|
state.m_densea[k].Set(i_,a[k][i_]);
|
|
state.m_diaga[k]=a[k][k]+s;
|
|
}
|
|
}
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Interna lfunction which allows to rewrite diagonal of quadratic |
|
|
//| term. For internal use only. |
|
|
//| This function can be used only when you have dense A and already |
|
|
//| made MinQPSetQuadraticTerm(Fast) call. |
|
|
//+------------------------------------------------------------------+
|
|
static void CMinQP::MinQPRewriteDiagonal(CMinQPState &state,double &s[])
|
|
{
|
|
//--- create variables
|
|
int k=0;
|
|
int n=0;
|
|
//--- check
|
|
if(!CAp::Assert(state.m_akind==0,__FUNCTION__+": internal error (AKind<>0)"))
|
|
return;
|
|
//--- initialization
|
|
n=state.m_n;
|
|
for(k=0;k<=n-1;k++)
|
|
state.m_diaga[k]=s[k];
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Fast version of MinQPSetStartingPoint(), which doesn't check its |
|
|
//| arguments. For internal use only. |
|
|
//+------------------------------------------------------------------+
|
|
static void CMinQP::MinQPSetStartingPointFast(CMinQPState &state,double &x[])
|
|
{
|
|
//--- create variables
|
|
int n=0;
|
|
int i_=0;
|
|
//--- initialization
|
|
n=state.m_n;
|
|
for(i_=0;i_<=n-1;i_++)
|
|
state.m_startx[i_]=x[i_];
|
|
state.m_havex=true;
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Fast version of MinQPSetOrigin(), which doesn't check its |
|
|
//| arguments. For internal use only. |
|
|
//+------------------------------------------------------------------+
|
|
static void CMinQP::MinQPSetOriginFast(CMinQPState &state,double &xorigin[])
|
|
{
|
|
//--- create variables
|
|
int n=0;
|
|
int i_=0;
|
|
//--- initialization
|
|
n=state.m_n;
|
|
for(i_=0;i_<=n-1;i_++)
|
|
state.m_xorigin[i_]=xorigin[i_];
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| This function calculates gradient of quadratic function at XC and|
|
|
//| stores it in the GC. |
|
|
//+------------------------------------------------------------------+
|
|
static void CMinQP::MinQPGrad(CMinQPState &state)
|
|
{
|
|
//--- create variables
|
|
int n=0;
|
|
int i=0;
|
|
double v=0;
|
|
int i_=0;
|
|
//--- initialization
|
|
n=state.m_n;
|
|
//--- check
|
|
if(!CAp::Assert(state.m_akind==-1||state.m_akind==0,__FUNCTION__+": internal error"))
|
|
return;
|
|
//--- zero A
|
|
if(state.m_akind==-1)
|
|
{
|
|
for(i_=0;i_<=n-1;i_++)
|
|
state.m_gc[i_]=state.m_b[i_];
|
|
//--- exit the function
|
|
return;
|
|
}
|
|
//--- dense A
|
|
if(state.m_akind==0)
|
|
{
|
|
for(i_=0;i_<=n-1;i_++)
|
|
state.m_gc[i_]=state.m_b[i_];
|
|
state.m_gc[0]=state.m_gc[0]+state.m_diaga[0]*state.m_xc[0];
|
|
//--- calculation
|
|
for(i=1;i<=n-1;i++)
|
|
{
|
|
v=0.0;
|
|
for(i_=0;i_<=i-1;i_++)
|
|
v+=state.m_densea[i][i_]*state.m_xc[i_];
|
|
state.m_gc[i]=state.m_gc[i]+v+state.m_diaga[i]*state.m_xc[i];
|
|
v=state.m_xc[i];
|
|
//--- change values
|
|
for(i_=0;i_<=i-1;i_++)
|
|
state.m_gc[i_]=state.m_gc[i_]+v*state.m_densea[i][i_];
|
|
}
|
|
//--- exit the function
|
|
return;
|
|
}
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| This function calculates x'*A*x for given X. |
|
|
//+------------------------------------------------------------------+
|
|
static double CMinQP::MinQPXTAX(CMinQPState &state,double &x[])
|
|
{
|
|
//--- create variables
|
|
double result=0;
|
|
int n=0;
|
|
int i=0;
|
|
int j=0;
|
|
//--- initialization
|
|
n=state.m_n;
|
|
//--- check
|
|
if(!CAp::Assert(state.m_akind==-1 || state.m_akind==0,__FUNCTION__+": internal error"))
|
|
return(EMPTY_VALUE);
|
|
result=0;
|
|
//--- zero A
|
|
if(state.m_akind==-1)
|
|
return(0.0);
|
|
//--- dense A
|
|
if(state.m_akind==0)
|
|
{
|
|
result=0;
|
|
for(i=0;i<=n-1;i++)
|
|
{
|
|
for(j=0;j<=i-1;j++)
|
|
result=result+state.m_densea[i][j]*x[i]*x[j];
|
|
//--- get result
|
|
result=result+0.5*state.m_diaga[i]*CMath::Sqr(x[i]);
|
|
}
|
|
//--- return result
|
|
return(result);
|
|
}
|
|
//--- return result
|
|
return(result);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Levenberg-Marquardt optimizer. |
|
|
//| This structure should be created using one of the |
|
|
//| MinLMCreate() functions. You should not access its fields |
|
|
//| directly; use ALGLIB functions to work with it. |
|
|
//+------------------------------------------------------------------+
|
|
class CMinLMState
|
|
{
|
|
public:
|
|
//--- variables
|
|
int m_n;
|
|
int m_m;
|
|
double m_diffstep;
|
|
double m_epsg;
|
|
double m_epsf;
|
|
double m_epsx;
|
|
int m_maxits;
|
|
bool m_xrep;
|
|
double m_stpmax;
|
|
int m_maxmodelage;
|
|
bool m_makeadditers;
|
|
double m_f;
|
|
bool m_needf;
|
|
bool m_needfg;
|
|
bool m_needfgh;
|
|
bool m_needfij;
|
|
bool m_needfi;
|
|
bool m_xupdated;
|
|
int m_algomode;
|
|
bool m_hasf;
|
|
bool m_hasfi;
|
|
bool m_hasg;
|
|
double m_fbase;
|
|
double m_lambdav;
|
|
double m_nu;
|
|
int m_modelage;
|
|
bool m_deltaxready;
|
|
bool m_deltafready;
|
|
int m_repiterationscount;
|
|
int m_repterminationtype;
|
|
int m_repnfunc;
|
|
int m_repnjac;
|
|
int m_repngrad;
|
|
int m_repnhess;
|
|
int m_repncholesky;
|
|
RCommState m_rstate;
|
|
double m_actualdecrease;
|
|
double m_predicteddecrease;
|
|
double m_xm1;
|
|
double m_xp1;
|
|
CMinLBFGSState m_internalstate;
|
|
CMinLBFGSReport m_internalrep;
|
|
CMinQPState m_qpstate;
|
|
CMinQPReport m_qprep;
|
|
//--- arrays
|
|
double m_x[];
|
|
double m_fi[];
|
|
double m_g[];
|
|
double m_xbase[];
|
|
double m_fibase[];
|
|
double m_gbase[];
|
|
double m_bndl[];
|
|
double m_bndu[];
|
|
bool m_havebndl[];
|
|
bool m_havebndu[];
|
|
double m_s[];
|
|
double m_xdir[];
|
|
double m_deltax[];
|
|
double m_deltaf[];
|
|
double m_choleskybuf[];
|
|
double m_tmp0[];
|
|
double m_fm1[];
|
|
double m_fp1[];
|
|
//--- matrix
|
|
CMatrixDouble m_j;
|
|
CMatrixDouble m_h;
|
|
CMatrixDouble m_quadraticmodel;
|
|
//--- constructor, destructor
|
|
CMinLMState(void);
|
|
~CMinLMState(void);
|
|
//--- copy
|
|
void Copy(CMinLMState &obj);
|
|
};
|
|
//+------------------------------------------------------------------+
|
|
//| Constructor without parameters |
|
|
//+------------------------------------------------------------------+
|
|
CMinLMState::CMinLMState(void)
|
|
{
|
|
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Destructor |
|
|
//+------------------------------------------------------------------+
|
|
CMinLMState::~CMinLMState(void)
|
|
{
|
|
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Copy |
|
|
//+------------------------------------------------------------------+
|
|
void CMinLMState::Copy(CMinLMState &obj)
|
|
{
|
|
//--- copy variables
|
|
m_n=obj.m_n;
|
|
m_m=obj.m_m;
|
|
m_diffstep=obj.m_diffstep;
|
|
m_epsg=obj.m_epsg;
|
|
m_epsf=obj.m_epsf;
|
|
m_epsx=obj.m_epsx;
|
|
m_maxits=obj.m_maxits;
|
|
m_xrep=obj.m_xrep;
|
|
m_stpmax=obj.m_stpmax;
|
|
m_maxmodelage=obj.m_maxmodelage;
|
|
m_makeadditers=obj.m_makeadditers;
|
|
m_f=obj.m_f;
|
|
m_needf=obj.m_needf;
|
|
m_needfg=obj.m_needfg;
|
|
m_needfgh=obj.m_needfgh;
|
|
m_needfij=obj.m_needfij;
|
|
m_needfi=obj.m_needfi;
|
|
m_xupdated=obj.m_xupdated;
|
|
m_algomode=obj.m_algomode;
|
|
m_hasf=obj.m_hasf;
|
|
m_hasfi=obj.m_hasfi;
|
|
m_hasg=obj.m_hasg;
|
|
m_fbase=obj.m_fbase;
|
|
m_lambdav=obj.m_lambdav;
|
|
m_nu=obj.m_nu;
|
|
m_modelage=obj.m_modelage;
|
|
m_deltaxready=obj.m_deltaxready;
|
|
m_deltafready=obj.m_deltafready;
|
|
m_repiterationscount=obj.m_repiterationscount;
|
|
m_repterminationtype=obj.m_repterminationtype;
|
|
m_repnfunc=obj.m_repnfunc;
|
|
m_repnjac=obj.m_repnjac;
|
|
m_repngrad=obj.m_repngrad;
|
|
m_repnhess=obj.m_repnhess;
|
|
m_repncholesky=obj.m_repncholesky;
|
|
m_actualdecrease=obj.m_actualdecrease;
|
|
m_predicteddecrease=obj.m_predicteddecrease;
|
|
m_xm1=obj.m_xm1;
|
|
m_xp1=obj.m_xp1;
|
|
m_rstate.Copy(obj.m_rstate);
|
|
m_internalstate.Copy(obj.m_internalstate);
|
|
m_internalrep.Copy(obj.m_internalrep);
|
|
m_qpstate.Copy(obj.m_qpstate);
|
|
m_qprep.Copy(obj.m_qprep);
|
|
//--- copy arrays
|
|
ArrayCopy(m_x,obj.m_x);
|
|
ArrayCopy(m_fi,obj.m_fi);
|
|
ArrayCopy(m_g,obj.m_g);
|
|
ArrayCopy(m_xbase,obj.m_xbase);
|
|
ArrayCopy(m_fibase,obj.m_fibase);
|
|
ArrayCopy(m_gbase,obj.m_gbase);
|
|
ArrayCopy(m_bndl,obj.m_bndl);
|
|
ArrayCopy(m_bndu,obj.m_bndu);
|
|
ArrayCopy(m_havebndl,obj.m_havebndl);
|
|
ArrayCopy(m_havebndu,obj.m_havebndu);
|
|
ArrayCopy(m_s,obj.m_s);
|
|
ArrayCopy(m_xdir,obj.m_xdir);
|
|
ArrayCopy(m_deltax,obj.m_deltax);
|
|
ArrayCopy(m_deltaf,obj.m_deltaf);
|
|
ArrayCopy(m_choleskybuf,obj.m_choleskybuf);
|
|
ArrayCopy(m_tmp0,obj.m_tmp0);
|
|
ArrayCopy(m_fm1,obj.m_fm1);
|
|
ArrayCopy(m_fp1,obj.m_fp1);
|
|
//--- copy matrix
|
|
m_j=obj.m_j;
|
|
m_h=obj.m_h;
|
|
m_quadraticmodel=obj.m_quadraticmodel;
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Levenberg-Marquardt optimizer. |
|
|
//| This structure should be created using one of the |
|
|
//| MinLMCreate() functions. You should not access its fields |
|
|
//| directly; use ALGLIB functions to work with it. |
|
|
//+------------------------------------------------------------------+
|
|
class CMinLMStateShell
|
|
{
|
|
private:
|
|
CMinLMState m_innerobj;
|
|
public:
|
|
//--- constructors, destructor
|
|
CMinLMStateShell(void);
|
|
CMinLMStateShell(CMinLMState &obj);
|
|
~CMinLMStateShell(void);
|
|
//--- methods
|
|
bool GetNeedF(void);
|
|
void SetNeedF(const bool b);
|
|
bool GetNeedFG(void);
|
|
void SetNeedFG(const bool b);
|
|
bool GetNeedFGH(void);
|
|
void SetNeedFGH(const bool b);
|
|
bool GetNeedFI(void);
|
|
void SetNeedFI(const bool b);
|
|
bool GetNeedFIJ(void);
|
|
void SetNeedFIJ(const bool b);
|
|
bool GetXUpdated(void);
|
|
void SetXUpdated(const bool b);
|
|
double GetF(void);
|
|
void SetF(const double d);
|
|
CMinLMState *GetInnerObj(void);
|
|
};
|
|
//+------------------------------------------------------------------+
|
|
//| Constructor without parameters |
|
|
//+------------------------------------------------------------------+
|
|
CMinLMStateShell::CMinLMStateShell(void)
|
|
{
|
|
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Copy constructor |
|
|
//+------------------------------------------------------------------+
|
|
CMinLMStateShell::CMinLMStateShell(CMinLMState &obj)
|
|
{
|
|
//--- copy
|
|
m_innerobj.Copy(obj);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Destructor |
|
|
//+------------------------------------------------------------------+
|
|
CMinLMStateShell::~CMinLMStateShell(void)
|
|
{
|
|
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Returns the value of the variable needf |
|
|
//+------------------------------------------------------------------+
|
|
bool CMinLMStateShell::GetNeedF(void)
|
|
{
|
|
//--- return result
|
|
return(m_innerobj.m_needf);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Changing the value of the variable needf |
|
|
//+------------------------------------------------------------------+
|
|
void CMinLMStateShell::SetNeedF(const bool b)
|
|
{
|
|
//--- change value
|
|
m_innerobj.m_needf=b;
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Returns the value of the variable needfg |
|
|
//+------------------------------------------------------------------+
|
|
bool CMinLMStateShell::GetNeedFG(void)
|
|
{
|
|
//--- return result
|
|
return(m_innerobj.m_needfg);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Changing the value of the variable needfg |
|
|
//+------------------------------------------------------------------+
|
|
void CMinLMStateShell::SetNeedFG(const bool b)
|
|
{
|
|
//--- change value
|
|
m_innerobj.m_needfg=b;
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Returns the value of the variable needfgh |
|
|
//+------------------------------------------------------------------+
|
|
bool CMinLMStateShell::GetNeedFGH(void)
|
|
{
|
|
//--- return result
|
|
return(m_innerobj.m_needfgh);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Changing the value of the variable needfgh |
|
|
//+------------------------------------------------------------------+
|
|
void CMinLMStateShell::SetNeedFGH(const bool b)
|
|
{
|
|
//--- change value
|
|
m_innerobj.m_needfgh=b;
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Returns the value of the variable needfi |
|
|
//+------------------------------------------------------------------+
|
|
bool CMinLMStateShell::GetNeedFI(void)
|
|
{
|
|
//--- return result
|
|
return(m_innerobj.m_needfi);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Changing the value of the variable needfi |
|
|
//+------------------------------------------------------------------+
|
|
void CMinLMStateShell::SetNeedFI(const bool b)
|
|
{
|
|
//--- change value
|
|
m_innerobj.m_needfi=b;
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Returns the value of the variable needfij |
|
|
//+------------------------------------------------------------------+
|
|
bool CMinLMStateShell::GetNeedFIJ(void)
|
|
{
|
|
//--- return result
|
|
return(m_innerobj.m_needfij);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Changing the value of the variable needfij |
|
|
//+------------------------------------------------------------------+
|
|
void CMinLMStateShell::SetNeedFIJ(const bool b)
|
|
{
|
|
//--- change value
|
|
m_innerobj.m_needfij=b;
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Returns the value of the variable xupdated |
|
|
//+------------------------------------------------------------------+
|
|
bool CMinLMStateShell::GetXUpdated(void)
|
|
{
|
|
//--- return result
|
|
return(m_innerobj.m_xupdated);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Changing the value of the variable xupdated |
|
|
//+------------------------------------------------------------------+
|
|
void CMinLMStateShell::SetXUpdated(const bool b)
|
|
{
|
|
//--- change value
|
|
m_innerobj.m_xupdated=b;
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Returns the value of the variable f |
|
|
//+------------------------------------------------------------------+
|
|
double CMinLMStateShell::GetF(void)
|
|
{
|
|
//--- return result
|
|
return(m_innerobj.m_f);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Changing the value of the variable f |
|
|
//+------------------------------------------------------------------+
|
|
void CMinLMStateShell::SetF(const double d)
|
|
{
|
|
//--- change value
|
|
m_innerobj.m_f=d;
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Return object of class |
|
|
//+------------------------------------------------------------------+
|
|
CMinLMState *CMinLMStateShell::GetInnerObj(void)
|
|
{
|
|
//--- return result
|
|
return(GetPointer(m_innerobj));
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Optimization report, filled by MinLMResults() function |
|
|
//| FIELDS: |
|
|
//| * TerminationType, completetion code: |
|
|
//| * -9 derivative correctness check failed; |
|
|
//| see Rep.WrongNum, Rep.WrongI, Rep.WrongJ for |
|
|
//| more information. |
|
|
//| * 1 relative function improvement is no more than |
|
|
//| EpsF. |
|
|
//| * 2 relative step is no more than EpsX. |
|
|
//| * 4 gradient is no more than EpsG. |
|
|
//| * 5 MaxIts steps was taken |
|
|
//| * 7 stopping conditions are too stringent, |
|
|
//| further improvement is impossible |
|
|
//| * IterationsCount, contains iterations count |
|
|
//| * NFunc, number of function calculations |
|
|
//| * NJac, number of Jacobi matrix calculations |
|
|
//| * NGrad, number of gradient calculations |
|
|
//| * NHess, number of Hessian calculations |
|
|
//| * NCholesky, number of Cholesky decomposition calculations |
|
|
//+------------------------------------------------------------------+
|
|
class CMinLMReport
|
|
{
|
|
public:
|
|
//--- variables
|
|
int m_iterationscount;
|
|
int m_terminationtype;
|
|
int m_nfunc;
|
|
int m_njac;
|
|
int m_ngrad;
|
|
int m_nhess;
|
|
int m_ncholesky;
|
|
//--- constructor, destructor
|
|
CMinLMReport(void);
|
|
~CMinLMReport(void);
|
|
//--- copy
|
|
void Copy(CMinLMReport &obj);
|
|
};
|
|
//+------------------------------------------------------------------+
|
|
//| Constructor without parameters |
|
|
//+------------------------------------------------------------------+
|
|
CMinLMReport::CMinLMReport(void)
|
|
{
|
|
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Destructor |
|
|
//+------------------------------------------------------------------+
|
|
CMinLMReport::~CMinLMReport(void)
|
|
{
|
|
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Copy |
|
|
//+------------------------------------------------------------------+
|
|
void CMinLMReport::Copy(CMinLMReport &obj)
|
|
{
|
|
//--- copy variables
|
|
m_iterationscount=obj.m_iterationscount;
|
|
m_terminationtype=obj.m_terminationtype;
|
|
m_nfunc=obj.m_nfunc;
|
|
m_njac=obj.m_njac;
|
|
m_ngrad=obj.m_ngrad;
|
|
m_nhess=obj.m_nhess;
|
|
m_ncholesky=obj.m_ncholesky;
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Optimization report, filled by MinLMResults() function |
|
|
//| FIELDS: |
|
|
//| * TerminationType, completetion code: |
|
|
//| * -9 derivative correctness check failed; |
|
|
//| see Rep.WrongNum, Rep.WrongI, Rep.WrongJ for |
|
|
//| more information. |
|
|
//| * 1 relative function improvement is no more than |
|
|
//| EpsF. |
|
|
//| * 2 relative step is no more than EpsX. |
|
|
//| * 4 gradient is no more than EpsG. |
|
|
//| * 5 MaxIts steps was taken |
|
|
//| * 7 stopping conditions are too stringent, |
|
|
//| further improvement is impossible |
|
|
//| * IterationsCount, contains iterations count |
|
|
//| * NFunc, number of function calculations |
|
|
//| * NJac, number of Jacobi matrix calculations |
|
|
//| * NGrad, number of gradient calculations |
|
|
//| * NHess, number of Hessian calculations |
|
|
//| * NCholesky, number of Cholesky decomposition calculations |
|
|
//+------------------------------------------------------------------+
|
|
class CMinLMReportShell
|
|
{
|
|
private:
|
|
CMinLMReport m_innerobj;
|
|
public:
|
|
//--- constructors, destructor
|
|
CMinLMReportShell(void);
|
|
CMinLMReportShell(CMinLMReport &obj);
|
|
~CMinLMReportShell(void);
|
|
//--- methods
|
|
int GetIterationsCount(void);
|
|
void SetIterationsCount(const int i);
|
|
int GetTerminationType(void);
|
|
void SetTerminationType(const int i);
|
|
int GetNFunc(void);
|
|
void SetNFunc(const int i);
|
|
int GetNJAC(void);
|
|
void SetNJAC(const int i);
|
|
int GetNGrad(void);
|
|
void SetNGrad(const int i);
|
|
int GetNHess(void);
|
|
void SetNHess(const int i);
|
|
int GetNCholesky(void);
|
|
void SetNCholesky(const int i);
|
|
CMinLMReport *GetInnerObj(void);
|
|
};
|
|
//+------------------------------------------------------------------+
|
|
//| Constructor without parameters |
|
|
//+------------------------------------------------------------------+
|
|
CMinLMReportShell::CMinLMReportShell(void)
|
|
{
|
|
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Copy constructor |
|
|
//+------------------------------------------------------------------+
|
|
CMinLMReportShell::CMinLMReportShell(CMinLMReport &obj)
|
|
{
|
|
//--- copy
|
|
m_innerobj.Copy(obj);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Destructor |
|
|
//+------------------------------------------------------------------+
|
|
CMinLMReportShell::~CMinLMReportShell(void)
|
|
{
|
|
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Returns the value of the variable iterationscount |
|
|
//+------------------------------------------------------------------+
|
|
int CMinLMReportShell::GetIterationsCount(void)
|
|
{
|
|
//--- return result
|
|
return(m_innerobj.m_iterationscount);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Changing the value of the variable iterationscount |
|
|
//+------------------------------------------------------------------+
|
|
void CMinLMReportShell::SetIterationsCount(const int i)
|
|
{
|
|
//--- change value
|
|
m_innerobj.m_iterationscount=i;
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Returns the value of the variable terminationtype |
|
|
//+------------------------------------------------------------------+
|
|
int CMinLMReportShell::GetTerminationType(void)
|
|
{
|
|
//--- return result
|
|
return(m_innerobj.m_terminationtype);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Changing the value of the variable terminationtype |
|
|
//+------------------------------------------------------------------+
|
|
void CMinLMReportShell::SetTerminationType(const int i)
|
|
{
|
|
//--- change value
|
|
m_innerobj.m_terminationtype=i;
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Returns the value of the variable nfunc |
|
|
//+------------------------------------------------------------------+
|
|
int CMinLMReportShell::GetNFunc(void)
|
|
{
|
|
//--- return result
|
|
return(m_innerobj.m_nfunc);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Changing the value of the variable nfunc |
|
|
//+------------------------------------------------------------------+
|
|
void CMinLMReportShell::SetNFunc(const int i)
|
|
{
|
|
//--- change value
|
|
m_innerobj.m_nfunc=i;
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Returns the value of the variable njac |
|
|
//+------------------------------------------------------------------+
|
|
int CMinLMReportShell::GetNJAC(void)
|
|
{
|
|
//--- return result
|
|
return(m_innerobj.m_njac);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Changing the value of the variable njac |
|
|
//+------------------------------------------------------------------+
|
|
void CMinLMReportShell::SetNJAC(const int i)
|
|
{
|
|
//--- change value
|
|
m_innerobj.m_njac=i;
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Returns the value of the variable ngrad |
|
|
//+------------------------------------------------------------------+
|
|
int CMinLMReportShell::GetNGrad(void)
|
|
{
|
|
//--- return result
|
|
return(m_innerobj.m_ngrad);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Changing the value of the variable ngrad |
|
|
//+------------------------------------------------------------------+
|
|
void CMinLMReportShell::SetNGrad(const int i)
|
|
{
|
|
//--- change value
|
|
m_innerobj.m_ngrad=i;
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Returns the value of the variable nhess |
|
|
//+------------------------------------------------------------------+
|
|
int CMinLMReportShell::GetNHess(void)
|
|
{
|
|
//--- return result
|
|
return(m_innerobj.m_nhess);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Changing the value of the variable nhess |
|
|
//+------------------------------------------------------------------+
|
|
void CMinLMReportShell::SetNHess(const int i)
|
|
{
|
|
//--- change value
|
|
m_innerobj.m_nhess=i;
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Returns the value of the variable ncholesky |
|
|
//+------------------------------------------------------------------+
|
|
int CMinLMReportShell::GetNCholesky(void)
|
|
{
|
|
//--- return result
|
|
return(m_innerobj.m_ncholesky);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Changing the value of the variable ncholesky |
|
|
//+------------------------------------------------------------------+
|
|
void CMinLMReportShell::SetNCholesky(const int i)
|
|
{
|
|
//--- change value
|
|
m_innerobj.m_ncholesky=i;
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Return object of class |
|
|
//+------------------------------------------------------------------+
|
|
CMinLMReport *CMinLMReportShell::GetInnerObj(void)
|
|
{
|
|
//--- return result
|
|
return(GetPointer(m_innerobj));
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Levenberg-Marquardt method |
|
|
//+------------------------------------------------------------------+
|
|
class CMinLM
|
|
{
|
|
private:
|
|
//--- private methods
|
|
static void LMPRepare(const int n,const int m,bool havegrad,CMinLMState &state);
|
|
static void ClearRequestFields(CMinLMState &state);
|
|
static bool IncreaseLambda(double &lambdav,double &nu);
|
|
static void DecreaseLambda(double &lambdav,double &nu);
|
|
static double BoundedScaledAntigradNorm(CMinLMState &state,double &x[],double &g[]);
|
|
//--- auxiliary functions for MinLMIteration
|
|
static void Func_lbl_rcomm(CMinLMState &state,int n,int m,int iflag,int i,int k,bool bflag,double v,double s,double t);
|
|
static bool Func_lbl_16(CMinLMState &state,int &n,int &m,int &iflag,int &i,int &k,bool &bflag,double &v,double &s,double &t);
|
|
static bool Func_lbl_19(CMinLMState &state,int &n,int &m,int &iflag,int &i,int &k,bool &bflag,double &v,double &s,double &t);
|
|
static bool Func_lbl_20(CMinLMState &state,int &n,int &m,int &iflag,int &i,int &k,bool &bflag,double &v,double &s,double &t);
|
|
static bool Func_lbl_21(CMinLMState &state,int &n,int &m,int &iflag,int &i,int &k,bool &bflag,double &v,double &s,double &t);
|
|
static bool Func_lbl_22(CMinLMState &state,int &n,int &m,int &iflag,int &i,int &k,bool &bflag,double &v,double &s,double &t);
|
|
static bool Func_lbl_24(CMinLMState &state,int &n,int &m,int &iflag,int &i,int &k,bool &bflag,double &v,double &s,double &t);
|
|
static bool Func_lbl_25(CMinLMState &state,int &n,int &m,int &iflag,int &i,int &k,bool &bflag,double &v,double &s,double &t);
|
|
static bool Func_lbl_28(CMinLMState &state,int &n,int &m,int &iflag,int &i,int &k,bool &bflag,double &v,double &s,double &t);
|
|
static bool Func_lbl_31(CMinLMState &state,int &n,int &m,int &iflag,int &i,int &k,bool &bflag,double &v,double &s,double &t);
|
|
static bool Func_lbl_39(CMinLMState &state,int &n,int &m,int &iflag,int &i,int &k,bool &bflag,double &v,double &s,double &t);
|
|
static bool Func_lbl_40(CMinLMState &state,int &n,int &m,int &iflag,int &i,int &k,bool &bflag,double &v,double &s,double &t);
|
|
static bool Func_lbl_41(CMinLMState &state,int &n,int &m,int &iflag,int &i,int &k,bool &bflag,double &v,double &s,double &t);
|
|
static bool Func_lbl_48(CMinLMState &state,int &n,int &m,int &iflag,int &i,int &k,bool &bflag,double &v,double &s,double &t);
|
|
static bool Func_lbl_49(CMinLMState &state,int &n,int &m,int &iflag,int &i,int &k,bool &bflag,double &v,double &s,double &t);
|
|
static bool Func_lbl_55(CMinLMState &state,int &n,int &m,int &iflag,int &i,int &k,bool &bflag,double &v,double &s,double &t);
|
|
public:
|
|
//--- class constants
|
|
static const int m_lmmodefj;
|
|
static const int m_lmmodefgj;
|
|
static const int m_lmmodefgh;
|
|
static const int m_lmflagnopreLBFGS;
|
|
static const int m_lmflagnointLBFGS;
|
|
static const int m_lmpreLBFGSm;
|
|
static const int m_lmintLBFGSits;
|
|
static const int m_lbfgsnorealloc;
|
|
static const double m_lambdaup;
|
|
static const double m_lambdadown;
|
|
static const double m_suspiciousnu;
|
|
static const int m_smallmodelage;
|
|
static const int m_additers;
|
|
//--- constructor, destructor
|
|
CMinLM(void);
|
|
~CMinLM(void);
|
|
//--- public methods
|
|
static void MinLMCreateVJ(const int n,const int m,double &x[],CMinLMState &state);
|
|
static void MinLMCreateV(const int n,const int m,double &x[],const double diffstep,CMinLMState &state);
|
|
static void MinLMCreateFGH(const int n,double &x[],CMinLMState &state);
|
|
static void MinLMSetCond(CMinLMState &state,const double epsg,const double epsf,double epsx,const int maxits);
|
|
static void MinLMSetXRep(CMinLMState &state,const bool needxrep);
|
|
static void MinLMSetStpMax(CMinLMState &state,const double stpmax);
|
|
static void MinLMSetScale(CMinLMState &state,double &s[]);
|
|
static void MinLMSetBC(CMinLMState &state,double &bndl[],double &bndu[]);
|
|
static void MinLMSetAccType(CMinLMState &state,int acctype);
|
|
static void MinLMResults(CMinLMState &state,double &x[],CMinLMReport &rep);
|
|
static void MinLMResultsBuf(CMinLMState &state,double &x[],CMinLMReport &rep);
|
|
static void MinLMRestartFrom(CMinLMState &state,double &x[]);
|
|
static void MinLMCreateVGJ(const int n,const int m,double &x[],CMinLMState &state);
|
|
static void MinLMCreateFGJ(const int n,const int m,double &x[],CMinLMState &state);
|
|
static void MinLMCreateFJ(const int n,const int m,double &x[],CMinLMState &state);
|
|
static bool MinLMIteration(CMinLMState &state);
|
|
};
|
|
//+------------------------------------------------------------------+
|
|
//| Initialize constants |
|
|
//+------------------------------------------------------------------+
|
|
const int CMinLM::m_lmmodefj=0;
|
|
const int CMinLM::m_lmmodefgj=1;
|
|
const int CMinLM::m_lmmodefgh=2;
|
|
const int CMinLM::m_lmflagnopreLBFGS=1;
|
|
const int CMinLM::m_lmflagnointLBFGS=2;
|
|
const int CMinLM::m_lmpreLBFGSm=5;
|
|
const int CMinLM::m_lmintLBFGSits=5;
|
|
const int CMinLM::m_lbfgsnorealloc=1;
|
|
const double CMinLM::m_lambdaup=2.0;
|
|
const double CMinLM::m_lambdadown=0.33;
|
|
const double CMinLM::m_suspiciousnu=16;
|
|
const int CMinLM::m_smallmodelage=3;
|
|
const int CMinLM::m_additers=5;
|
|
//+------------------------------------------------------------------+
|
|
//| Constructor without parameters |
|
|
//+------------------------------------------------------------------+
|
|
CMinLM::CMinLM(void)
|
|
{
|
|
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Destructor |
|
|
//+------------------------------------------------------------------+
|
|
CMinLM::~CMinLM(void)
|
|
{
|
|
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| IMPROVED LEVENBERG-MARQUARDT METHOD FOR |
|
|
//| NON-LINEAR LEAST SQUARES OPTIMIZATION |
|
|
//| DESCRIPTION: |
|
|
//| This function is used to find minimum of function which is |
|
|
//| represented as sum of squares: |
|
|
//| F(x) = f[0]^2(x[0],...,x[n-1]) + ... + |
|
|
//| + f[m-1]^2(x[0],...,x[n-1]) |
|
|
//| using value of function vector f[] and Jacobian of f[]. |
|
|
//| REQUIREMENTS: |
|
|
//| This algorithm will request following information during its |
|
|
//| operation: |
|
|
//| * function vector f[] at given point X |
|
|
//| * function vector f[] and Jacobian of f[] (simultaneously) at |
|
|
//| given point |
|
|
//| There are several overloaded versions of MinLMOptimize() |
|
|
//| function which correspond to different LM-like optimization |
|
|
//| algorithms provided by this unit. You should choose version which|
|
|
//| accepts fvec() and jac() callbacks. First one is used to |
|
|
//| calculate f[] at given point, second one calculates f[] and |
|
|
//| Jacobian df[i]/dx[j]. |
|
|
//| You can try to initialize MinLMState structure with VJ function |
|
|
//| and then use incorrect version of MinLMOptimize() (for example,|
|
|
//| version which works with general form function and does not |
|
|
//| provide Jacobian), but it will lead to exception being thrown |
|
|
//| after first attempt to calculate Jacobian. |
|
|
//| USAGE: |
|
|
//| 1. User initializes algorithm state with MinLMCreateVJ() call |
|
|
//| 2. User tunes solver parameters with MinLMSetCond(), |
|
|
//| MinLMSetStpMax() and other functions |
|
|
//| 3. User calls MinLMOptimize() function which takes algorithm |
|
|
//| state and callback functions. |
|
|
//| 4. User calls MinLMResults() to get solution |
|
|
//| 5. Optionally, user may call MinLMRestartFrom() to solve another |
|
|
//| problem with same N/M but another starting point and/or |
|
|
//| another function. MinLMRestartFrom() allows to reuse already |
|
|
//| initialized structure. |
|
|
//| INPUT PARAMETERS: |
|
|
//| N - dimension, N>1 |
|
|
//| * if given, only leading N elements of X are |
|
|
//| used |
|
|
//| * if not given, automatically determined from |
|
|
//| size of X |
|
|
//| M - number of functions f[i] |
|
|
//| X - initial solution, array[0..N-1] |
|
|
//| OUTPUT PARAMETERS: |
|
|
//| State - structure which stores algorithm state |
|
|
//| NOTES: |
|
|
//| 1. you may tune stopping conditions with MinLMSetCond() function |
|
|
//| 2. if target function contains exp() or other fast growing |
|
|
//| functions, and optimization algorithm makes too large steps |
|
|
//| which leads to overflow, use MinLMSetStpMax() function to |
|
|
//| bound algorithm's steps. |
|
|
//+------------------------------------------------------------------+
|
|
static void CMinLM::MinLMCreateVJ(const int n,const int m,double &x[],
|
|
CMinLMState &state)
|
|
{
|
|
//--- check
|
|
if(!CAp::Assert(n>=1,__FUNCTION__+": N<1!"))
|
|
return;
|
|
//--- check
|
|
if(!CAp::Assert(m>=1,__FUNCTION__+": M<1!"))
|
|
return;
|
|
//--- check
|
|
if(!CAp::Assert(CAp::Len(x)>=n,__FUNCTION__+": Length(X)<N!"))
|
|
return;
|
|
//--- check
|
|
if(!CAp::Assert(CApServ::IsFiniteVector(x,n),__FUNCTION__+": X contains infinite or NaN values!"))
|
|
return;
|
|
//--- initialize,check parameters
|
|
state.m_n=n;
|
|
state.m_m=m;
|
|
state.m_algomode=1;
|
|
state.m_hasf=false;
|
|
state.m_hasfi=true;
|
|
state.m_hasg=false;
|
|
//--- second stage of initialization
|
|
LMPRepare(n,m,false,state);
|
|
MinLMSetAccType(state,0);
|
|
MinLMSetCond(state,0,0,0,0);
|
|
MinLMSetXRep(state,false);
|
|
MinLMSetStpMax(state,0);
|
|
MinLMRestartFrom(state,x);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| IMPROVED LEVENBERG-MARQUARDT METHOD FOR |
|
|
//| NON-LINEAR LEAST SQUARES OPTIMIZATION |
|
|
//| DESCRIPTION: |
|
|
//| This function is used to find minimum of function which is |
|
|
//| represented as sum of squares: |
|
|
//| F(x) = f[0]^2(x[0],...,x[n-1]) + ... + |
|
|
//| + f[m-1]^2(x[0],...,x[n-1]) |
|
|
//| using value of function vector f[] only. Finite differences are |
|
|
//| used to calculate Jacobian. |
|
|
//| REQUIREMENTS: |
|
|
//| This algorithm will request following information during its |
|
|
//| operation: |
|
|
//| * function vector f[] at given point X |
|
|
//| There are several overloaded versions of MinLMOptimize() function|
|
|
//| which correspond to different LM-like optimization algorithms |
|
|
//| provided by this unit. You should choose version which accepts |
|
|
//| fvec() callback. |
|
|
//| You can try to initialize MinLMState structure with VJ function |
|
|
//| and then use incorrect version of MinLMOptimize() (for example, |
|
|
//| version which works with general form function and does not |
|
|
//| accept function vector), but it will lead to exception being |
|
|
//| thrown after first attempt to calculate Jacobian. |
|
|
//| USAGE: |
|
|
//| 1. User initializes algorithm state with MinLMCreateV() call |
|
|
//| 2. User tunes solver parameters with MinLMSetCond(), |
|
|
//| MinLMSetStpMax() and other functions |
|
|
//| 3. User calls MinLMOptimize() function which takes algorithm |
|
|
//| state and callback functions. |
|
|
//| 4. User calls MinLMResults() to get solution |
|
|
//| 5. Optionally, user may call MinLMRestartFrom() to solve another |
|
|
//| problem with same N/M but another starting point and/or |
|
|
//| another function. MinLMRestartFrom() allows to reuse already |
|
|
//| initialized structure. |
|
|
//| INPUT PARAMETERS: |
|
|
//| N - dimension, N>1 |
|
|
//| * if given, only leading N elements of X are |
|
|
//| used |
|
|
//| * if not given, automatically determined from |
|
|
//| size of X |
|
|
//| M - number of functions f[i] |
|
|
//| X - initial solution, array[0..N-1] |
|
|
//| DiffStep- differentiation step, >0 |
|
|
//| OUTPUT PARAMETERS: |
|
|
//| State - structure which stores algorithm state |
|
|
//| See also MinLMIteration, MinLMResults. |
|
|
//| NOTES: |
|
|
//| 1. you may tune stopping conditions with MinLMSetCond() function |
|
|
//| 2. if target function contains exp() or other fast growing |
|
|
//| functions, and optimization algorithm makes too large steps |
|
|
//| which leads to overflow, use MinLMSetStpMax() function to |
|
|
//| bound algorithm's steps. |
|
|
//+------------------------------------------------------------------+
|
|
static void CMinLM::MinLMCreateV(const int n,const int m,double &x[],
|
|
const double diffstep,CMinLMState &state)
|
|
{
|
|
//--- check
|
|
if(!CAp::Assert(CMath::IsFinite(diffstep),__FUNCTION__+": DiffStep is not finite!"))
|
|
return;
|
|
//--- check
|
|
if(!CAp::Assert(diffstep>0.0,__FUNCTION__+": DiffStep<=0!"))
|
|
return;
|
|
//--- check
|
|
if(!CAp::Assert(n>=1,__FUNCTION__+": N<1!"))
|
|
return;
|
|
//--- check
|
|
if(!CAp::Assert(m>=1,__FUNCTION__+": M<1!"))
|
|
return;
|
|
//--- check
|
|
if(!CAp::Assert(CAp::Len(x)>=n,__FUNCTION__+": Length(X)<N!"))
|
|
return;
|
|
//--- check
|
|
if(!CAp::Assert(CApServ::IsFiniteVector(x,n),__FUNCTION__+": X contains infinite or NaN values!"))
|
|
return;
|
|
//--- initialize
|
|
state.m_n=n;
|
|
state.m_m=m;
|
|
state.m_algomode=0;
|
|
state.m_hasf=false;
|
|
state.m_hasfi=true;
|
|
state.m_hasg=false;
|
|
state.m_diffstep=diffstep;
|
|
//--- second stage of initialization
|
|
LMPRepare(n,m,false,state);
|
|
MinLMSetAccType(state,1);
|
|
MinLMSetCond(state,0,0,0,0);
|
|
MinLMSetXRep(state,false);
|
|
MinLMSetStpMax(state,0);
|
|
MinLMRestartFrom(state,x);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| LEVENBERG-MARQUARDT-LIKE METHOD FOR NON-LINEAR OPTIMIZATION |
|
|
//| DESCRIPTION: |
|
|
//| This function is used to find minimum of general form (not |
|
|
//| "sum-of-squares") function |
|
|
//| F = F(x[0], ..., x[n-1]) |
|
|
//| using its gradient and Hessian. Levenberg-Marquardt modification |
|
|
//| with L-BFGS pre-optimization and internal pre-conditioned L-BFGS |
|
|
//| optimization after each Levenberg-Marquardt step is used. |
|
|
//| REQUIREMENTS: |
|
|
//| This algorithm will request following information during its |
|
|
//| operation: |
|
|
//| * function value F at given point X |
|
|
//| * F and gradient G (simultaneously) at given point X |
|
|
//| * F, G and Hessian H (simultaneously) at given point X |
|
|
//| There are several overloaded versions of MinLMOptimize() |
|
|
//| function which correspond to different LM-like optimization |
|
|
//| algorithms provided by this unit. You should choose version which|
|
|
//| accepts func(), grad() and hess() function pointers. First |
|
|
//| pointer is used to calculate F at given point, second one |
|
|
//| calculates F(x) and grad F(x), third one calculates F(x), grad |
|
|
//| F(x), hess F(x). |
|
|
//| You can try to initialize MinLMState structure with FGH-function |
|
|
//| and then use incorrect version of MinLMOptimize() (for example, |
|
|
//| version which does not provide Hessian matrix), but it will lead |
|
|
//| to exception being thrown after first attempt to calculate |
|
|
//| Hessian. |
|
|
//| USAGE: |
|
|
//| 1. User initializes algorithm state with MinLMCreateFGH() call |
|
|
//| 2. User tunes solver parameters with MinLMSetCond(), |
|
|
//| MinLMSetStpMax() and other functions |
|
|
//| 3. User calls MinLMOptimize() function which takes algorithm |
|
|
//| state and pointers (delegates, etc.) to callback functions. |
|
|
//| 4. User calls MinLMResults() to get solution |
|
|
//| 5. Optionally, user may call MinLMRestartFrom() to solve another |
|
|
//| problem with same N but another starting point and/or another |
|
|
//| function. MinLMRestartFrom() allows to reuse already |
|
|
//| initialized structure. |
|
|
//| INPUT PARAMETERS: |
|
|
//| N - dimension, N>1 |
|
|
//| * if given, only leading N elements of X are |
|
|
//| used |
|
|
//| * if not given, automatically determined from |
|
|
//| size of X |
|
|
//| X - initial solution, array[0..N-1] |
|
|
//| OUTPUT PARAMETERS: |
|
|
//| State - structure which stores algorithm state |
|
|
//| NOTES: |
|
|
//| 1. you may tune stopping conditions with MinLMSetCond() function |
|
|
//| 2. if target function contains exp() or other fast growing |
|
|
//| functions, and optimization algorithm makes too large steps |
|
|
//| which leads to overflow, use MinLMSetStpMax() function to |
|
|
//| bound algorithm's steps. |
|
|
//+------------------------------------------------------------------+
|
|
static void CMinLM::MinLMCreateFGH(const int n,double &x[],CMinLMState &state)
|
|
{
|
|
//--- check
|
|
if(!CAp::Assert(n>=1,__FUNCTION__+": N<1!"))
|
|
return;
|
|
//--- check
|
|
if(!CAp::Assert(CAp::Len(x)>=n,__FUNCTION__+": Length(X)<N!"))
|
|
return;
|
|
//--- check
|
|
if(!CAp::Assert(CApServ::IsFiniteVector(x,n),__FUNCTION__+": X contains infinite or NaN values!"))
|
|
return;
|
|
//--- initialize
|
|
state.m_n=n;
|
|
state.m_m=0;
|
|
state.m_algomode=2;
|
|
state.m_hasf=true;
|
|
state.m_hasfi=false;
|
|
state.m_hasg=true;
|
|
//--- init2
|
|
LMPRepare(n,0,true,state);
|
|
MinLMSetAccType(state,2);
|
|
MinLMSetCond(state,0,0,0,0);
|
|
MinLMSetXRep(state,false);
|
|
MinLMSetStpMax(state,0);
|
|
MinLMRestartFrom(state,x);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| This function sets stopping conditions for Levenberg-Marquardt |
|
|
//| optimization algorithm. |
|
|
//| INPUT PARAMETERS: |
|
|
//| State - structure which stores algorithm state |
|
|
//| EpsG - >=0 |
|
|
//| The subroutine finishes its work if the |
|
|
//| condition |v|<EpsG is satisfied, where: |
|
|
//| * |.| means Euclidian norm |
|
|
//| * v - scaled gradient vector, v[i]=g[i]*s[i] |
|
|
//| * g - gradient |
|
|
//| * s - scaling coefficients set by MinLMSetScale()|
|
|
//| EpsF - >=0 |
|
|
//| The subroutine finishes its work if on k+1-th |
|
|
//| iteration the condition |F(k+1)-F(k)| <= |
|
|
//| <= EpsF*max{|F(k)|,|F(k+1)|,1} is satisfied. |
|
|
//| EpsX - >=0 |
|
|
//| The subroutine finishes its work if on k+1-th |
|
|
//| iteration the condition |v|<=EpsX is fulfilled, |
|
|
//| where: |
|
|
//| * |.| means Euclidian norm |
|
|
//| * v - scaled step vector, v[i]=dx[i]/s[i] |
|
|
//| * dx - ste pvector, dx=X(k+1)-X(k) |
|
|
//| * s - scaling coefficients set by MinLMSetScale()|
|
|
//| MaxIts - maximum number of iterations. If MaxIts=0, the |
|
|
//| number of iterations is unlimited. Only |
|
|
//| Levenberg-Marquardt iterations are counted |
|
|
//| (L-BFGS/CG iterations are NOT counted because |
|
|
//| their cost is very low compared to that of LM). |
|
|
//| Passing EpsG=0, EpsF=0, EpsX=0 and MaxIts=0 (simultaneously) will|
|
|
//| lead to automatic stopping criterion selection (small EpsX). |
|
|
//+------------------------------------------------------------------+
|
|
static void CMinLM::MinLMSetCond(CMinLMState &state,const double epsg,
|
|
const double epsf,double epsx,
|
|
const int maxits)
|
|
{
|
|
//--- check
|
|
if(!CAp::Assert(CMath::IsFinite(epsg),__FUNCTION__+": EpsG is not finite number!"))
|
|
return;
|
|
//--- check
|
|
if(!CAp::Assert(epsg>=0.0,__FUNCTION__+": negative EpsG!"))
|
|
return;
|
|
//--- check
|
|
if(!CAp::Assert(CMath::IsFinite(epsf),__FUNCTION__+": EpsF is not finite number!"))
|
|
return;
|
|
//--- check
|
|
if(!CAp::Assert(epsf>=0.0,__FUNCTION__+": negative EpsF!"))
|
|
return;
|
|
//--- check
|
|
if(!CAp::Assert(CMath::IsFinite(epsx),__FUNCTION__+": EpsX is not finite number!"))
|
|
return;
|
|
//--- check
|
|
if(!CAp::Assert(epsx>=0.0,__FUNCTION__+": negative EpsX!"))
|
|
return;
|
|
//--- check
|
|
if(!CAp::Assert(maxits>=0,__FUNCTION__+": negative MaxIts!"))
|
|
return;
|
|
//--- check
|
|
if(((epsg==0.0 && epsf==0.0) && epsx==0.0) && maxits==0)
|
|
epsx=1.0E-6;
|
|
//--- change values
|
|
state.m_epsg=epsg;
|
|
state.m_epsf=epsf;
|
|
state.m_epsx=epsx;
|
|
state.m_maxits=maxits;
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| This function turns on/off reporting. |
|
|
//| INPUT PARAMETERS: |
|
|
//| State - structure which stores algorithm state |
|
|
//| NeedXRep- whether iteration reports are needed or not |
|
|
//| If NeedXRep is True, algorithm will call rep() callback function |
|
|
//| if it is provided to MinLMOptimize(). Both Levenberg-Marquardt |
|
|
//| and internal L-BFGS iterations are reported. |
|
|
//+------------------------------------------------------------------+
|
|
static void CMinLM::MinLMSetXRep(CMinLMState &state,const bool needxrep)
|
|
{
|
|
//--- change value
|
|
state.m_xrep=needxrep;
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| This function sets maximum step length |
|
|
//| INPUT PARAMETERS: |
|
|
//| State - structure which stores algorithm state |
|
|
//| StpMax - maximum step length, >=0. Set StpMax to 0.0, if |
|
|
//| you don't want to limit step length. |
|
|
//| Use this subroutine when you optimize target function which |
|
|
//| contains exp() or other fast growing functions, and optimization |
|
|
//| algorithm makes too large steps which leads to overflow. This |
|
|
//| function allows us to reject steps that are too large (and |
|
|
//| therefore expose us to the possible overflow) without actually |
|
|
//| calculating function value at the x+stp*d. |
|
|
//| NOTE: non-zero StpMax leads to moderate performance degradation |
|
|
//| because intermediate step of preconditioned L-BFGS optimization |
|
|
//| is incompatible with limits on step size. |
|
|
//+------------------------------------------------------------------+
|
|
static void CMinLM::MinLMSetStpMax(CMinLMState &state,const double stpmax)
|
|
{
|
|
//--- check
|
|
if(!CAp::Assert(CMath::IsFinite(stpmax),__FUNCTION__+": StpMax is not finite!"))
|
|
return;
|
|
//--- check
|
|
if(!CAp::Assert(stpmax>=0.0,__FUNCTION__+": StpMax<0!"))
|
|
return;
|
|
//--- change value
|
|
state.m_stpmax=stpmax;
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| This function sets scaling coefficients for LM optimizer. |
|
|
//| ALGLIB optimizers use scaling matrices to test stopping |
|
|
//| conditions (step size and gradient are scaled before comparison |
|
|
//| with tolerances). Scale of the I-th variable is a translation |
|
|
//| invariant measure of: |
|
|
//| a) "how large" the variable is |
|
|
//| b) how large the step should be to make significant changes in |
|
|
//| the function |
|
|
//| Generally, scale is NOT considered to be a form of |
|
|
//| preconditioner. But LM optimizer is unique in that it uses |
|
|
//| scaling matrix both in the stopping condition tests and as |
|
|
//| Marquardt damping factor. |
|
|
//| Proper scaling is very important for the algorithm performance. |
|
|
//| It is less important for the quality of results, but still has |
|
|
//| some influence (it is easier to converge when variables are |
|
|
//| properly scaled, so premature stopping is possible when very |
|
|
//| badly scalled variables are combined with relaxed stopping |
|
|
//| conditions). |
|
|
//| INPUT PARAMETERS: |
|
|
//| State - structure stores algorithm state |
|
|
//| S - array[N], non-zero scaling coefficients |
|
|
//| S[i] may be negative, sign doesn't matter. |
|
|
//+------------------------------------------------------------------+
|
|
static void CMinLM::MinLMSetScale(CMinLMState &state,double &s[])
|
|
{
|
|
//--- create a variable
|
|
int i=0;
|
|
//--- check
|
|
if(!CAp::Assert(CAp::Len(s)>=state.m_n,__FUNCTION__+": Length(S)<N"))
|
|
return;
|
|
for(i=0;i<=state.m_n-1;i++)
|
|
{
|
|
//--- check
|
|
if(!CAp::Assert(CMath::IsFinite(s[i]),__FUNCTION__+": S contains infinite or NAN elements"))
|
|
return;
|
|
//--- check
|
|
if(!CAp::Assert(s[i]!=0.0,__FUNCTION__+": S contains zero elements"))
|
|
return;
|
|
//--- change value
|
|
state.m_s[i]=MathAbs(s[i]);
|
|
}
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| This function sets boundary constraints for LM optimizer |
|
|
//| Boundary constraints are inactive by default (after initial |
|
|
//| creation). They are preserved until explicitly turned off with |
|
|
//| another SetBC() call. |
|
|
//| INPUT PARAMETERS: |
|
|
//| State - structure stores algorithm state |
|
|
//| BndL - lower bounds, array[N]. |
|
|
//| If some (all) variables are unbounded, you may |
|
|
//| specify very small number or -INF (latter is |
|
|
//| recommended because it will allow solver to use |
|
|
//| better algorithm). |
|
|
//| BndU - upper bounds, array[N]. |
|
|
//| If some (all) variables are unbounded, you may |
|
|
//| specify very large number or +INF (latter is |
|
|
//| recommended because it will allow solver to use |
|
|
//| better algorithm). |
|
|
//| NOTE 1: it is possible to specify BndL[i]=BndU[i]. In this case |
|
|
//| I-th variable will be "frozen" at X[i]=BndL[i]=BndU[i]. |
|
|
//| NOTE 2: this solver has following useful properties: |
|
|
//| * bound constraints are always satisfied exactly |
|
|
//| * function is evaluated only INSIDE area specified by bound |
|
|
//| constraints or at its boundary |
|
|
//+------------------------------------------------------------------+
|
|
static void CMinLM::MinLMSetBC(CMinLMState &state,double &bndl[],double &bndu[])
|
|
{
|
|
//--- create variables
|
|
int i=0;
|
|
int n=0;
|
|
//--- initialization
|
|
n=state.m_n;
|
|
//--- check
|
|
if(!CAp::Assert(CAp::Len(bndl)>=n,__FUNCTION__+": Length(BndL)<N"))
|
|
return;
|
|
//--- check
|
|
if(!CAp::Assert(CAp::Len(bndu)>=n,__FUNCTION__+": Length(BndU)<N"))
|
|
return;
|
|
for(i=0;i<=n-1;i++)
|
|
{
|
|
//--- check
|
|
if(!CAp::Assert(CMath::IsFinite(bndl[i]) || CInfOrNaN::IsNegativeInfinity(bndl[i]),"MinLMSetBC: BndL contains NAN or +INF"))
|
|
return;
|
|
//--- check
|
|
if(!CAp::Assert(CMath::IsFinite(bndu[i]) || CInfOrNaN::IsPositiveInfinity(bndu[i]),"MinLMSetBC: BndU contains NAN or -INF"))
|
|
return;
|
|
//--- change values
|
|
state.m_bndl[i]=bndl[i];
|
|
state.m_havebndl[i]=CMath::IsFinite(bndl[i]);
|
|
state.m_bndu[i]=bndu[i];
|
|
state.m_havebndu[i]=CMath::IsFinite(bndu[i]);
|
|
}
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| This function is used to change acceleration settings |
|
|
//| You can choose between three acceleration strategies: |
|
|
//| * AccType=0, no acceleration. |
|
|
//| * AccType=1, secant updates are used to update quadratic model |
|
|
//| after each iteration. After fixed number of iterations (or |
|
|
//| after model breakdown) we recalculate quadratic model using |
|
|
//| analytic Jacobian or finite differences. Number of secant-based|
|
|
//| iterations depends on optimization settings: about 3 |
|
|
//| iterations - when we have analytic Jacobian, up to 2*N |
|
|
//| iterations - when we use finite differences to calculate |
|
|
//| Jacobian. |
|
|
//| AccType=1 is recommended when Jacobian calculation cost is |
|
|
//| prohibitive high (several Mx1 function vector calculations |
|
|
//| followed by several NxN Cholesky factorizations are faster than |
|
|
//| calculation of one M*N Jacobian). It should also be used when we|
|
|
//| have no Jacobian, because finite difference approximation takes |
|
|
//| too much time to compute. |
|
|
//| Table below list optimization protocols (XYZ protocol corresponds|
|
|
//| to MinLMCreateXYZ) and acceleration types they support (and use |
|
|
//| by default). |
|
|
//| ACCELERATION TYPES SUPPORTED BY OPTIMIZATION PROTOCOLS: |
|
|
//| protocol 0 1 comment |
|
|
//| V + + |
|
|
//| VJ + + |
|
|
//| FGH + |
|
|
//| DAFAULT VALUES: |
|
|
//| protocol 0 1 comment |
|
|
//| V x without acceleration it is so slooooooooow |
|
|
//| VJ x |
|
|
//| FGH x |
|
|
//| NOTE: this function should be called before optimization. |
|
|
//| Attempt to call it during algorithm iterations may result in |
|
|
//| unexpected behavior. |
|
|
//| NOTE: attempt to call this function with unsupported |
|
|
//| protocol/acceleration combination will result in exception being |
|
|
//| thrown. |
|
|
//+------------------------------------------------------------------+
|
|
static void CMinLM::MinLMSetAccType(CMinLMState &state,int acctype)
|
|
{
|
|
//--- check
|
|
if(!CAp::Assert((acctype==0||acctype==1)||acctype==2,__FUNCTION__+": incorrect AccType!"))
|
|
return;
|
|
//--- check
|
|
if(acctype==2)
|
|
acctype=0;
|
|
//--- check
|
|
if(acctype==0)
|
|
{
|
|
state.m_maxmodelage=0;
|
|
state.m_makeadditers=false;
|
|
//--- exit the function
|
|
return;
|
|
}
|
|
//--- check
|
|
if(acctype==1)
|
|
{
|
|
//--- check
|
|
if(!CAp::Assert(state.m_hasfi,__FUNCTION__+": AccType=1 is incompatible with current protocol!"))
|
|
return;
|
|
//--- check
|
|
if(state.m_algomode==0)
|
|
state.m_maxmodelage=2*state.m_n;
|
|
else
|
|
state.m_maxmodelage=m_smallmodelage;
|
|
state.m_makeadditers=false;
|
|
//--- exit the function
|
|
return;
|
|
}
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Levenberg-Marquardt algorithm results |
|
|
//| INPUT PARAMETERS: |
|
|
//| State - algorithm state |
|
|
//| OUTPUT PARAMETERS: |
|
|
//| X - array[0..N-1], solution |
|
|
//| Rep - optimization report; |
|
|
//| see comments for this structure for more info. |
|
|
//+------------------------------------------------------------------+
|
|
static void CMinLM::MinLMResults(CMinLMState &state,double &x[],
|
|
CMinLMReport &rep)
|
|
{
|
|
//--- reset memory
|
|
ArrayResizeAL(x,0);
|
|
//--- function call
|
|
MinLMResultsBuf(state,x,rep);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Levenberg-Marquardt algorithm results |
|
|
//| Buffered implementation of MinLMResults(), which uses |
|
|
//| pre-allocated buffer to store X[]. If buffer size is too small, |
|
|
//| it resizes buffer. It is intended to be used in the inner cycles |
|
|
//| of performance critical algorithms where array reallocation |
|
|
//| penalty is too large to be ignored. |
|
|
//+------------------------------------------------------------------+
|
|
static void CMinLM::MinLMResultsBuf(CMinLMState &state,double &x[],
|
|
CMinLMReport &rep)
|
|
{
|
|
//--- create a variable
|
|
int i_=0;
|
|
//--- check
|
|
if(CAp::Len(x)<state.m_n)
|
|
ArrayResizeAL(x,state.m_n);
|
|
//--- copy
|
|
for(i_=0;i_<=state.m_n-1;i_++)
|
|
x[i_]=state.m_x[i_];
|
|
//--- change values
|
|
rep.m_iterationscount=state.m_repiterationscount;
|
|
rep.m_terminationtype=state.m_repterminationtype;
|
|
rep.m_nfunc=state.m_repnfunc;
|
|
rep.m_njac=state.m_repnjac;
|
|
rep.m_ngrad=state.m_repngrad;
|
|
rep.m_nhess=state.m_repnhess;
|
|
rep.m_ncholesky=state.m_repncholesky;
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| This subroutine restarts LM algorithm from new point. All |
|
|
//| optimization parameters are left unchanged. |
|
|
//| This function allows to solve multiple optimization problems |
|
|
//| (which must have same number of dimensions) without object |
|
|
//| reallocation penalty. |
|
|
//| INPUT PARAMETERS: |
|
|
//| State - structure used for reverse communication |
|
|
//| previously allocated with MinLMCreateXXX call. |
|
|
//| X - new starting point. |
|
|
//+------------------------------------------------------------------+
|
|
static void CMinLM::MinLMRestartFrom(CMinLMState &state,double &x[])
|
|
{
|
|
//--- create a variable
|
|
int i_=0;
|
|
//--- check
|
|
if(!CAp::Assert(CAp::Len(x)>=state.m_n,__FUNCTION__+": Length(X)<N!"))
|
|
return;
|
|
//--- check
|
|
if(!CAp::Assert(CApServ::IsFiniteVector(x,state.m_n),__FUNCTION__+": X contains infinite or NaN values!"))
|
|
return;
|
|
//--- copy
|
|
for(i_=0;i_<=state.m_n-1;i_++)
|
|
state.m_xbase[i_]=x[i_];
|
|
//--- allocation
|
|
ArrayResizeAL(state.m_rstate.ia,5);
|
|
ArrayResizeAL(state.m_rstate.ba,1);
|
|
ArrayResizeAL(state.m_rstate.ra,3);
|
|
state.m_rstate.stage=-1;
|
|
//--- function call
|
|
ClearRequestFields(state);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| This is obsolete function. |
|
|
//| Since ALGLIB 3.3 it is equivalent to MinLMCreateVJ(). |
|
|
//+------------------------------------------------------------------+
|
|
static void CMinLM::MinLMCreateVGJ(const int n,const int m,double &x[],
|
|
CMinLMState &state)
|
|
{
|
|
//--- function call
|
|
MinLMCreateVJ(n,m,x,state);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| This is obsolete function. |
|
|
//| Since ALGLIB 3.3 it is equivalent to MinLMCreateFJ(). |
|
|
//+------------------------------------------------------------------+
|
|
static void CMinLM::MinLMCreateFGJ(const int n,const int m,double &x[],
|
|
CMinLMState &state)
|
|
{
|
|
//--- function call
|
|
MinLMCreateFJ(n,m,x,state);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| This function is considered obsolete since ALGLIB 3.1.0 and is |
|
|
//| present for backward compatibility only. We recommend to use |
|
|
//| MinLMCreateVJ, which provides similar, but more consistent and |
|
|
//| feature-rich interface. |
|
|
//+------------------------------------------------------------------+
|
|
static void CMinLM::MinLMCreateFJ(const int n,const int m,double &x[],
|
|
CMinLMState &state)
|
|
{
|
|
//--- check
|
|
if(!CAp::Assert(n>=1,__FUNCTION__+": N<1!"))
|
|
return;
|
|
//--- check
|
|
if(!CAp::Assert(m>=1,__FUNCTION__+": M<1!"))
|
|
return;
|
|
//--- check
|
|
if(!CAp::Assert(CAp::Len(x)>=n,__FUNCTION__+": Length(X)<N!"))
|
|
return;
|
|
//--- check
|
|
if(!CAp::Assert(CApServ::IsFiniteVector(x,n),__FUNCTION__+": X contains infinite or NaN values!"))
|
|
return;
|
|
//--- initialize
|
|
state.m_n=n;
|
|
state.m_m=m;
|
|
state.m_algomode=1;
|
|
state.m_hasf=true;
|
|
state.m_hasfi=false;
|
|
state.m_hasg=false;
|
|
//--- init 2
|
|
LMPRepare(n,m,true,state);
|
|
//--- function call
|
|
MinLMSetAccType(state,0);
|
|
//--- function call
|
|
MinLMSetCond(state,0,0,0,0);
|
|
//--- function call
|
|
MinLMSetXRep(state,false);
|
|
//--- function call
|
|
MinLMSetStpMax(state,0);
|
|
//--- function call
|
|
MinLMRestartFrom(state,x);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Prepare internal structures (except for RComm). |
|
|
//| Note: M must be zero for FGH mode, non-zero for V/VJ/FJ/FGJ mode.|
|
|
//+------------------------------------------------------------------+
|
|
static void CMinLM::LMPRepare(const int n,const int m,bool havegrad,
|
|
CMinLMState &state)
|
|
{
|
|
//--- create a variable
|
|
int i=0;
|
|
//--- check
|
|
if(n<=0 || m<0)
|
|
return;
|
|
//--- check
|
|
if(havegrad)
|
|
ArrayResizeAL(state.m_g,n);
|
|
//--- check
|
|
if(m!=0)
|
|
{
|
|
//--- allocation
|
|
state.m_j.Resize(m,n);
|
|
ArrayResizeAL(state.m_fi,m);
|
|
ArrayResizeAL(state.m_fibase,m);
|
|
ArrayResizeAL(state.m_deltaf,m);
|
|
ArrayResizeAL(state.m_fm1,m);
|
|
ArrayResizeAL(state.m_fp1,m);
|
|
}
|
|
else
|
|
state.m_h.Resize(n,n);
|
|
//--- allocation
|
|
ArrayResizeAL(state.m_x,n);
|
|
ArrayResizeAL(state.m_deltax,n);
|
|
state.m_quadraticmodel.Resize(n,n);
|
|
ArrayResizeAL(state.m_xbase,n);
|
|
ArrayResizeAL(state.m_gbase,n);
|
|
ArrayResizeAL(state.m_xdir,n);
|
|
ArrayResizeAL(state.m_tmp0,n);
|
|
//--- prepare internal L-BFGS
|
|
for(i=0;i<=n-1;i++)
|
|
state.m_x[i]=0;
|
|
//--- function call
|
|
CMinLBFGS::MinLBFGSCreate(n,MathMin(m_additers,n),state.m_x,state.m_internalstate);
|
|
//--- function call
|
|
CMinLBFGS::MinLBFGSSetCond(state.m_internalstate,0.0,0.0,0.0,MathMin(m_additers,n));
|
|
//--- Prepare internal QP solver
|
|
CMinQP::MinQPCreate(n,state.m_qpstate);
|
|
//--- function call
|
|
CMinQP::MinQPSetAlgoCholesky(state.m_qpstate);
|
|
//--- Prepare boundary constraints
|
|
ArrayResizeAL(state.m_bndl,n);
|
|
ArrayResizeAL(state.m_bndu,n);
|
|
ArrayResizeAL(state.m_havebndl,n);
|
|
ArrayResizeAL(state.m_havebndu,n);
|
|
for(i=0;i<=n-1;i++)
|
|
{
|
|
//--- change values
|
|
state.m_bndl[i]=CInfOrNaN::NegativeInfinity();
|
|
state.m_havebndl[i]=false;
|
|
state.m_bndu[i]=CInfOrNaN::PositiveInfinity();
|
|
state.m_havebndu[i]=false;
|
|
}
|
|
//--- Prepare scaling matrix
|
|
ArrayResizeAL(state.m_s,n);
|
|
for(i=0;i<=n-1;i++)
|
|
state.m_s[i]=1.0;
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Clears request fileds (to be sure that we don't forgot to clear |
|
|
//| something) |
|
|
//+------------------------------------------------------------------+
|
|
static void CMinLM::ClearRequestFields(CMinLMState &state)
|
|
{
|
|
//--- change values
|
|
state.m_needf=false;
|
|
state.m_needfg=false;
|
|
state.m_needfgh=false;
|
|
state.m_needfij=false;
|
|
state.m_needfi=false;
|
|
state.m_xupdated=false;
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Increases lambda, returns False when there is a danger of |
|
|
//| overflow |
|
|
//+------------------------------------------------------------------+
|
|
static bool CMinLM::IncreaseLambda(double &lambdav,double &nu)
|
|
{
|
|
//--- create variables
|
|
bool result;
|
|
double lnlambda=0;
|
|
double lnnu=0;
|
|
double lnlambdaup=0;
|
|
double lnmax=0;
|
|
//--- initialization
|
|
result=false;
|
|
lnlambda=MathLog(lambdav);
|
|
lnlambdaup=MathLog(m_lambdaup);
|
|
lnnu=MathLog(nu);
|
|
lnmax=MathLog(CMath::m_maxrealnumber);
|
|
//--- check
|
|
if(lnlambda+lnlambdaup+lnnu>0.25*lnmax)
|
|
{
|
|
//--- return result
|
|
return(result);
|
|
}
|
|
//--- check
|
|
if(lnnu+MathLog(2)>lnmax)
|
|
return(result);
|
|
//--- change values
|
|
lambdav=lambdav*m_lambdaup*nu;
|
|
nu=nu*2;
|
|
result=true;
|
|
//--- return result
|
|
return(result);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Decreases lambda, but leaves it unchanged when there is danger of|
|
|
//| underflow. |
|
|
//+------------------------------------------------------------------+
|
|
static void CMinLM::DecreaseLambda(double &lambdav,double &nu)
|
|
{
|
|
//--- initialization
|
|
nu=1;
|
|
//--- check
|
|
if(MathLog(lambdav)+MathLog(m_lambdadown)<MathLog(CMath::m_minrealnumber))
|
|
lambdav=CMath::m_minrealnumber;
|
|
else
|
|
lambdav=lambdav*m_lambdadown;
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Returns norm of bounded scaled anti-gradient. |
|
|
//| Bounded antigradient is a vector obtained from anti-gradient by |
|
|
//| zeroing components which point outwards: |
|
|
//| result = norm(v) |
|
|
//| v[i]=0 if ((-g[i]<0)and(x[i]=bndl[i])) or |
|
|
//| ((-g[i]>0)and(x[i]=bndu[i])) |
|
|
//| v[i]=-g[i]*s[i] otherwise, where s[i] is a scale for I-th |
|
|
//| variable |
|
|
//| This function may be used to check a stopping criterion. |
|
|
//+------------------------------------------------------------------+
|
|
static double CMinLM::BoundedScaledAntigradNorm(CMinLMState &state,
|
|
double &x[],double &g[])
|
|
{
|
|
//--- create variables
|
|
double result=0;
|
|
int n=0;
|
|
int i=0;
|
|
double v=0;
|
|
//--- initialization
|
|
result=0;
|
|
n=state.m_n;
|
|
for(i=0;i<=n-1;i++)
|
|
{
|
|
v=-(g[i]*state.m_s[i]);
|
|
//--- check
|
|
if(state.m_havebndl[i])
|
|
{
|
|
//--- check
|
|
if(x[i]<=state.m_bndl[i] && (double)(-g[i])<0.0)
|
|
v=0;
|
|
}
|
|
//--- check
|
|
if(state.m_havebndu[i])
|
|
{
|
|
//--- check
|
|
if(x[i]>=state.m_bndu[i] && (double)(-g[i])>0.0)
|
|
v=0;
|
|
}
|
|
result=result+CMath::Sqr(v);
|
|
}
|
|
//--- return result
|
|
return(MathSqrt(result));
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| NOTES: |
|
|
//| 1. Depending on function used to create state structure, this |
|
|
//| algorithm may accept Jacobian and/or Hessian and/or gradient. |
|
|
//| According to the said above, there ase several versions of |
|
|
//| this function, which accept different sets of callbacks. |
|
|
//| This flexibility opens way to subtle errors - you may create |
|
|
//| state with MinLMCreateFGH() (optimization using Hessian), but |
|
|
//| call function which does not accept Hessian. So when |
|
|
//| algorithm will request Hessian, there will be no callback to |
|
|
//| call. In this case exception will be thrown. |
|
|
//| Be careful to avoid such errors because there is no way to |
|
|
//| find them at compile time - you can see them at runtime only. |
|
|
//+------------------------------------------------------------------+
|
|
static bool CMinLM::MinLMIteration(CMinLMState &state)
|
|
{
|
|
//--- create variables
|
|
int n=0;
|
|
int m=0;
|
|
bool bflag;
|
|
int iflag=0;
|
|
double v=0;
|
|
double s=0;
|
|
double t=0;
|
|
int i=0;
|
|
int k=0;
|
|
int i_=0;
|
|
//--- This code initializes locals by:
|
|
//--- * random values determined during code
|
|
//--- generation - on first subroutine call
|
|
//--- * values from previous call - on subsequent calls
|
|
if(state.m_rstate.stage>=0)
|
|
{
|
|
//--- initialization
|
|
n=state.m_rstate.ia[0];
|
|
m=state.m_rstate.ia[1];
|
|
iflag=state.m_rstate.ia[2];
|
|
i=state.m_rstate.ia[3];
|
|
k=state.m_rstate.ia[4];
|
|
bflag=state.m_rstate.ba[0];
|
|
v=state.m_rstate.ra[0];
|
|
s=state.m_rstate.ra[1];
|
|
t=state.m_rstate.ra[2];
|
|
}
|
|
else
|
|
{
|
|
//--- initialization
|
|
n=-983;
|
|
m=-989;
|
|
iflag=-834;
|
|
i=900;
|
|
k=-287;
|
|
bflag=false;
|
|
v=214;
|
|
s=-338;
|
|
t=-686;
|
|
}
|
|
//--- check
|
|
if(state.m_rstate.stage==0)
|
|
{
|
|
//--- change value
|
|
state.m_needf=false;
|
|
//--- function call, return result
|
|
return(Func_lbl_19(state,n,m,iflag,i,k,bflag,v,s,t));
|
|
}
|
|
//--- check
|
|
if(state.m_rstate.stage==1)
|
|
{
|
|
//--- change values
|
|
state.m_needfi=false;
|
|
v=0.0;
|
|
for(i_=0;i_<=m-1;i_++)
|
|
v+=state.m_fi[i_]*state.m_fi[i_];
|
|
state.m_f=v;
|
|
//--- function call, return result
|
|
return(Func_lbl_19(state,n,m,iflag,i,k,bflag,v,s,t));
|
|
}
|
|
//--- check
|
|
if(state.m_rstate.stage==2)
|
|
{
|
|
//--- change value
|
|
state.m_xupdated=false;
|
|
//--- function call, return result
|
|
return(Func_lbl_16(state,n,m,iflag,i,k,bflag,v,s,t));
|
|
}
|
|
//--- check
|
|
if(state.m_rstate.stage==3)
|
|
{
|
|
//--- change value
|
|
state.m_repnfunc=state.m_repnfunc+1;
|
|
//--- copy
|
|
for(i_=0;i_<=m-1;i_++)
|
|
state.m_fm1[i_]=state.m_fi[i_];
|
|
for(i_=0;i_<=n-1;i_++)
|
|
state.m_x[i_]=state.m_xbase[i_];
|
|
state.m_x[k]=state.m_x[k]+state.m_s[k]*state.m_diffstep;
|
|
//--- check
|
|
if(state.m_havebndl[k])
|
|
state.m_x[k]=MathMax(state.m_x[k],state.m_bndl[k]);
|
|
//--- check
|
|
if(state.m_havebndu[k])
|
|
state.m_x[k]=MathMin(state.m_x[k],state.m_bndu[k]);
|
|
state.m_xp1=state.m_x[k];
|
|
//--- function call
|
|
ClearRequestFields(state);
|
|
state.m_needfi=true;
|
|
state.m_rstate.stage=4;
|
|
//--- Saving state
|
|
Func_lbl_rcomm(state,n,m,iflag,i,k,bflag,v,s,t);
|
|
//--- return result
|
|
return(true);
|
|
}
|
|
//--- check
|
|
if(state.m_rstate.stage==4)
|
|
{
|
|
//--- change value
|
|
state.m_repnfunc=state.m_repnfunc+1;
|
|
//--- copy
|
|
for(i_=0;i_<=m-1;i_++)
|
|
state.m_fp1[i_]=state.m_fi[i_];
|
|
v=state.m_xp1-state.m_xm1;
|
|
//--- check
|
|
if(v!=0.0)
|
|
{
|
|
v=1/v;
|
|
for(i_=0;i_<=m-1;i_++)
|
|
state.m_j[i_].Set(k,v*state.m_fp1[i_]);
|
|
for(i_=0;i_<=m-1;i_++)
|
|
state.m_j[i_].Set(k,state.m_j[i_][k]-v*state.m_fm1[i_]);
|
|
}
|
|
else
|
|
{
|
|
for(i=0;i<=m-1;i++)
|
|
state.m_j[i].Set(k,0);
|
|
}
|
|
k=k+1;
|
|
//--- function call, return result
|
|
return(Func_lbl_28(state,n,m,iflag,i,k,bflag,v,s,t));
|
|
}
|
|
//--- check
|
|
if(state.m_rstate.stage==5)
|
|
{
|
|
//--- change values
|
|
state.m_needfi=false;
|
|
state.m_repnfunc=state.m_repnfunc+1;
|
|
state.m_repnjac=state.m_repnjac+1;
|
|
//--- New model
|
|
state.m_modelage=0;
|
|
//--- function call, return result
|
|
return(Func_lbl_25(state,n,m,iflag,i,k,bflag,v,s,t));
|
|
}
|
|
//--- check
|
|
if(state.m_rstate.stage==6)
|
|
{
|
|
//--- change values
|
|
state.m_needfij=false;
|
|
state.m_repnfunc=state.m_repnfunc+1;
|
|
state.m_repnjac=state.m_repnjac+1;
|
|
//--- New model
|
|
state.m_modelage=0;
|
|
//--- function call, return result
|
|
return(Func_lbl_25(state,n,m,iflag,i,k,bflag,v,s,t));
|
|
}
|
|
//--- check
|
|
if(state.m_rstate.stage==7)
|
|
{
|
|
//--- change values
|
|
state.m_needfgh=false;
|
|
state.m_repnfunc=state.m_repnfunc+1;
|
|
state.m_repngrad=state.m_repngrad+1;
|
|
state.m_repnhess=state.m_repnhess+1;
|
|
//--- function call
|
|
CAblas::RMatrixCopy(n,n,state.m_h,0,0,state.m_quadraticmodel,0,0);
|
|
for(i_=0;i_<=n-1;i_++)
|
|
state.m_gbase[i_]=state.m_g[i_];
|
|
state.m_fbase=state.m_f;
|
|
//--- set control variables
|
|
bflag=true;
|
|
state.m_modelage=0;
|
|
//--- function call, return result
|
|
return(Func_lbl_31(state,n,m,iflag,i,k,bflag,v,s,t));
|
|
}
|
|
//--- check
|
|
if(state.m_rstate.stage==8)
|
|
{
|
|
//--- change value
|
|
state.m_xupdated=false;
|
|
//--- return result
|
|
return(false);
|
|
}
|
|
//--- check
|
|
if(state.m_rstate.stage==9)
|
|
{
|
|
//--- change value
|
|
state.m_xupdated=false;
|
|
//--- return result
|
|
return(false);
|
|
}
|
|
//--- check
|
|
if(state.m_rstate.stage==10)
|
|
{
|
|
//--- change values
|
|
state.m_needfi=false;
|
|
v=0.0;
|
|
for(i_=0;i_<=m-1;i_++)
|
|
v+=state.m_fi[i_]*state.m_fi[i_];
|
|
state.m_f=v;
|
|
//--- copy
|
|
for(i_=0;i_<=m-1;i_++)
|
|
state.m_deltaf[i_]=state.m_fi[i_];
|
|
for(i_=0;i_<=m-1;i_++)
|
|
state.m_deltaf[i_]=state.m_deltaf[i_]-state.m_fibase[i_];
|
|
state.m_deltafready=true;
|
|
//--- function call, return result
|
|
return(Func_lbl_48(state,n,m,iflag,i,k,bflag,v,s,t));
|
|
}
|
|
//--- check
|
|
if(state.m_rstate.stage==11)
|
|
{
|
|
//--- change value
|
|
state.m_needf=false;
|
|
//--- function call, return result
|
|
return(Func_lbl_48(state,n,m,iflag,i,k,bflag,v,s,t));
|
|
}
|
|
//--- check
|
|
if(state.m_rstate.stage==12)
|
|
{
|
|
//--- change value
|
|
state.m_xupdated=false;
|
|
//--- return result
|
|
return(false);
|
|
}
|
|
//--- check
|
|
if(state.m_rstate.stage==13)
|
|
{
|
|
//--- change value
|
|
state.m_xupdated=false;
|
|
//--- function call, return result
|
|
return(Func_lbl_55(state,n,m,iflag,i,k,bflag,v,s,t));
|
|
}
|
|
//--- check
|
|
if(state.m_rstate.stage==14)
|
|
{
|
|
//--- change value
|
|
state.m_xupdated=false;
|
|
//--- return result
|
|
return(false);
|
|
}
|
|
//--- check
|
|
if(state.m_rstate.stage==15)
|
|
{
|
|
//--- change value
|
|
state.m_xupdated=false;
|
|
//--- return result
|
|
return(false);
|
|
}
|
|
//--- Routine body
|
|
//--- prepare
|
|
n=state.m_n;
|
|
m=state.m_m;
|
|
state.m_repiterationscount=0;
|
|
state.m_repterminationtype=0;
|
|
state.m_repnfunc=0;
|
|
state.m_repnjac=0;
|
|
state.m_repngrad=0;
|
|
state.m_repnhess=0;
|
|
state.m_repncholesky=0;
|
|
//--- check consistency of constraints
|
|
//--- set constraints
|
|
for(i=0;i<=n-1;i++)
|
|
{
|
|
//--- check
|
|
if(state.m_havebndl[i] && state.m_havebndu[i])
|
|
{
|
|
//--- check
|
|
if(state.m_bndl[i]>state.m_bndu[i])
|
|
{
|
|
state.m_repterminationtype=-3;
|
|
//--- return result
|
|
return(false);
|
|
}
|
|
}
|
|
}
|
|
//--- function call
|
|
CMinQP::MinQPSetBC(state.m_qpstate,state.m_bndl,state.m_bndu);
|
|
//--- Initial report of current point
|
|
//--- Note 1: we rewrite State.X twice because
|
|
//--- user may accidentally change it after first call.
|
|
//--- Note 2: we set NeedF or NeedFI depending on what
|
|
//--- information about function we have.
|
|
if(!state.m_xrep)
|
|
return(Func_lbl_16(state,n,m,iflag,i,k,bflag,v,s,t));
|
|
for(i_=0;i_<=n-1;i_++)
|
|
state.m_x[i_]=state.m_xbase[i_];
|
|
//--- function call
|
|
ClearRequestFields(state);
|
|
//--- check
|
|
if(!state.m_hasf)
|
|
{
|
|
//--- check
|
|
if(!CAp::Assert(state.m_hasfi,"MinLM: internal error 2!"))
|
|
return(false);
|
|
//--- change values
|
|
state.m_needfi=true;
|
|
state.m_rstate.stage=1;
|
|
//--- Saving state
|
|
Func_lbl_rcomm(state,n,m,iflag,i,k,bflag,v,s,t);
|
|
//--- return result
|
|
return(true);
|
|
}
|
|
//--- change values
|
|
state.m_needf=true;
|
|
state.m_rstate.stage=0;
|
|
//--- Saving state
|
|
Func_lbl_rcomm(state,n,m,iflag,i,k,bflag,v,s,t);
|
|
//--- return result
|
|
return(true);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Auxiliary function for MinLMIteration. Is a product to get rid of|
|
|
//| the operator unconditional jump goto. |
|
|
//+------------------------------------------------------------------+
|
|
static void CMinLM::Func_lbl_rcomm(CMinLMState &state,int n,int m,int iflag,
|
|
int i,int k,bool bflag,double v,
|
|
double s,double t)
|
|
{
|
|
//--- save
|
|
state.m_rstate.ia[0]=n;
|
|
state.m_rstate.ia[1]=m;
|
|
state.m_rstate.ia[2]=iflag;
|
|
state.m_rstate.ia[3]=i;
|
|
state.m_rstate.ia[4]=k;
|
|
state.m_rstate.ba[0]=bflag;
|
|
state.m_rstate.ra[0]=v;
|
|
state.m_rstate.ra[1]=s;
|
|
state.m_rstate.ra[2]=t;
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Auxiliary function for MinLMIteration. Is a product to get rid of|
|
|
//| the operator unconditional jump goto. |
|
|
//+------------------------------------------------------------------+
|
|
static bool CMinLM::Func_lbl_16(CMinLMState &state,int &n,int &m,int &iflag,
|
|
int &i,int &k,bool &bflag,double &v,
|
|
double &s,double &t)
|
|
{
|
|
//--- Prepare control variables
|
|
state.m_nu=1;
|
|
state.m_lambdav=-CMath::m_maxrealnumber;
|
|
state.m_modelage=state.m_maxmodelage+1;
|
|
state.m_deltaxready=false;
|
|
state.m_deltafready=false;
|
|
//--- Main cycle.
|
|
//--- We move through it until either:
|
|
//--- * one of the stopping conditions is met
|
|
//--- * we decide that stopping conditions are too stringent
|
|
//--- and break from cycle
|
|
return(Func_lbl_20(state,n,m,iflag,i,k,bflag,v,s,t));
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Auxiliary function for MinLMIteration. Is a product to get rid of|
|
|
//| the operator unconditional jump goto. |
|
|
//+------------------------------------------------------------------+
|
|
static bool CMinLM::Func_lbl_19(CMinLMState &state,int &n,int &m,int &iflag,
|
|
int &i,int &k,bool &bflag,double &v,
|
|
double &s,double &t)
|
|
{
|
|
//--- change value
|
|
state.m_repnfunc=state.m_repnfunc+1;
|
|
//--- copy
|
|
for(int i_=0;i_<=n-1;i_++)
|
|
state.m_x[i_]=state.m_xbase[i_];
|
|
//--- function call
|
|
ClearRequestFields(state);
|
|
//--- change values
|
|
state.m_xupdated=true;
|
|
state.m_rstate.stage=2;
|
|
//--- Saving state
|
|
Func_lbl_rcomm(state,n,m,iflag,i,k,bflag,v,s,t);
|
|
//--- return result
|
|
return(true);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Auxiliary function for MinLMIteration. Is a product to get rid of|
|
|
//| the operator unconditional jump goto. |
|
|
//+------------------------------------------------------------------+
|
|
static bool CMinLM::Func_lbl_20(CMinLMState &state,int &n,int &m,int &iflag,
|
|
int &i,int &k,bool &bflag,double &v,
|
|
double &s,double &t)
|
|
{
|
|
//--- First,we have to prepare quadratic model for our function.
|
|
//--- We use BFlag to ensure that model is prepared;
|
|
//--- if it is false at the end of this block,something went wrong.
|
|
//--- We may either calculate brand new model or update old one.
|
|
//--- Before this block we have:
|
|
//--- * State.XBase - current position.
|
|
//--- * State.DeltaX - if DeltaXReady is True
|
|
//--- * State.DeltaF - if DeltaFReady is True
|
|
//--- After this block is over,we will have:
|
|
//--- * State.XBase - base point (unchanged)
|
|
//--- * State.FBase - F(XBase)
|
|
//--- * State.GBase - linear term
|
|
//--- * State.QuadraticModel - quadratic term
|
|
//--- * State.LambdaV - current estimate for lambda
|
|
//--- We also clear DeltaXReady/DeltaFReady flags
|
|
//--- after initialization is done.
|
|
bflag=false;
|
|
if(!(state.m_algomode==0 || state.m_algomode==1))
|
|
return(Func_lbl_22(state,n,m,iflag,i,k,bflag,v,s,t));
|
|
//--- Calculate f[] and Jacobian
|
|
if(!(state.m_modelage>state.m_maxmodelage||!(state.m_deltaxready &state.m_deltafready)))
|
|
return(Func_lbl_24(state,n,m,iflag,i,k,bflag,v,s,t));
|
|
//--- Refresh model (using either finite differences or analytic Jacobian)
|
|
if(state.m_algomode!=0)
|
|
{
|
|
//--- Obtain f[] and Jacobian
|
|
for(int i_=0;i_<=n-1;i_++)
|
|
state.m_x[i_]=state.m_xbase[i_];
|
|
//--- function call
|
|
ClearRequestFields(state);
|
|
//--- change values
|
|
state.m_needfij=true;
|
|
state.m_rstate.stage=6;
|
|
//--- Saving state
|
|
Func_lbl_rcomm(state,n,m,iflag,i,k,bflag,v,s,t);
|
|
//--- return result
|
|
return(true);
|
|
}
|
|
//--- Optimization using F values only.
|
|
//--- Use finite differences to estimate Jacobian.
|
|
if(!CAp::Assert(state.m_hasfi,"MinLMIteration: internal error when estimating Jacobian (no f[])"))
|
|
return(false);
|
|
k=0;
|
|
//--- function call, return result
|
|
return(Func_lbl_28(state,n,m,iflag,i,k,bflag,v,s,t));
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Auxiliary function for MinLMIteration. Is a product to get rid of|
|
|
//| the operator unconditional jump goto. |
|
|
//+------------------------------------------------------------------+
|
|
static bool CMinLM::Func_lbl_21(CMinLMState &state,int &n,int &m,int &iflag,
|
|
int &i,int &k,bool &bflag,double &v,
|
|
double &s,double &t)
|
|
{
|
|
//--- Lambda is too large,we have to break iterations.
|
|
state.m_repterminationtype=7;
|
|
if(!state.m_xrep)
|
|
return(false);
|
|
for(int i_=0;i_<=n-1;i_++)
|
|
state.m_x[i_]=state.m_xbase[i_];
|
|
state.m_f=state.m_fbase;
|
|
//--- function call
|
|
ClearRequestFields(state);
|
|
//--- change values
|
|
state.m_xupdated=true;
|
|
state.m_rstate.stage=15;
|
|
//--- Saving state
|
|
Func_lbl_rcomm(state,n,m,iflag,i,k,bflag,v,s,t);
|
|
//--- return result
|
|
return(true);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Auxiliary function for MinLMIteration. Is a product to get rid of|
|
|
//| the operator unconditional jump goto. |
|
|
//+------------------------------------------------------------------+
|
|
static bool CMinLM::Func_lbl_22(CMinLMState &state,int &n,int &m,int &iflag,
|
|
int &i,int &k,bool &bflag,double &v,
|
|
double &s,double &t)
|
|
{
|
|
//--- check
|
|
if(state.m_algomode!=2)
|
|
return(Func_lbl_31(state,n,m,iflag,i,k,bflag,v,s,t));
|
|
//--- check
|
|
if(!CAp::Assert(!state.m_hasfi,"MinLMIteration: internal error (HasFI is True in Hessian-based mode)"))
|
|
return(false);
|
|
//--- Obtain F,G,H
|
|
for(int i_=0;i_<=n-1;i_++)
|
|
state.m_x[i_]=state.m_xbase[i_];
|
|
//--- function call
|
|
ClearRequestFields(state);
|
|
//--- change values
|
|
state.m_needfgh=true;
|
|
state.m_rstate.stage=7;
|
|
//--- Saving state
|
|
Func_lbl_rcomm(state,n,m,iflag,i,k,bflag,v,s,t);
|
|
//--- return result
|
|
return(true);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Auxiliary function for MinLMIteration. Is a product to get rid of|
|
|
//| the operator unconditional jump goto. |
|
|
//+------------------------------------------------------------------+
|
|
static bool CMinLM::Func_lbl_24(CMinLMState &state,int &n,int &m,int &iflag,
|
|
int &i,int &k,bool &bflag,double &v,
|
|
double &s,double &t)
|
|
{
|
|
//--- State.J contains Jacobian or its current approximation;
|
|
//--- refresh it using secant updates:
|
|
//--- f(x0+dx)=f(x0) + J*dx,
|
|
//--- J_new=J_old + u*h'
|
|
//--- h=x_new-x_old
|
|
//--- u=(f_new - f_old - J_old*h)/(h'h)
|
|
//--- We can explicitly generate h and u,but it is
|
|
//--- preferential to do in-place calculations. Only
|
|
//--- I-th row of J_old is needed to calculate u[I],
|
|
//--- so we can update J row by row in one pass.
|
|
//--- NOTE: we expect that State.XBase contains new point,
|
|
//--- State.FBase contains old point,State.DeltaX and
|
|
//--- State.DeltaY contain updates from last step.
|
|
if(!CAp::Assert(state.m_deltaxready && state.m_deltafready,"MinLMIteration: uninitialized DeltaX/DeltaF"))
|
|
return(false);
|
|
t=0.0;
|
|
for(int i_=0;i_<=n-1;i_++)
|
|
t+=state.m_deltax[i_]*state.m_deltax[i_];
|
|
//--- check
|
|
if(!CAp::Assert(t!=0.0,"MinLM: internal error (T=0)"))
|
|
return(false);
|
|
for(i=0;i<=m-1;i++)
|
|
{
|
|
//--- change value
|
|
v=0.0;
|
|
for(int i_=0;i_<=n-1;i_++)
|
|
v+=state.m_j[i][i_]*state.m_deltax[i_];
|
|
v=(state.m_deltaf[i]-v)/t;
|
|
for(int i_=0;i_<=n-1;i_++)
|
|
state.m_j[i].Set(i_,state.m_j[i][i_]+v*state.m_deltax[i_]);
|
|
}
|
|
for(int i_=0;i_<=m-1;i_++)
|
|
state.m_fi[i_]=state.m_fibase[i_];
|
|
for(int i_=0;i_<=m-1;i_++)
|
|
state.m_fi[i_]=state.m_fi[i_]+state.m_deltaf[i_];
|
|
//--- Increase model age
|
|
state.m_modelage=state.m_modelage+1;
|
|
//--- function call, return result
|
|
return(Func_lbl_25(state,n,m,iflag,i,k,bflag,v,s,t));
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Auxiliary function for MinLMIteration. Is a product to get rid of|
|
|
//| the operator unconditional jump goto. |
|
|
//+------------------------------------------------------------------+
|
|
static bool CMinLM::Func_lbl_25(CMinLMState &state,int &n,int &m,int &iflag,
|
|
int &i,int &k,bool &bflag,double &v,
|
|
double &s,double &t)
|
|
{
|
|
//--- Generate quadratic model:
|
|
//--- f(xbase+dx)=
|
|
//--- =(f0 + J*dx)'(f0 + J*dx)
|
|
//--- =f0^2 + dx'J'f0 + f0*J*dx + dx'J'J*dx
|
|
//--- =f0^2 + 2*f0*J*dx + dx'J'J*dx
|
|
//--- Note that we calculate 2*(J'J) instead of J'J because
|
|
//--- our quadratic model is based on Tailor decomposition,
|
|
//--- i.m_e. it has 0.5 before quadratic term.
|
|
CAblas::RMatrixGemm(n,n,m,2.0,state.m_j,0,0,1,state.m_j,0,0,0,0.0,state.m_quadraticmodel,0,0);
|
|
CAblas::RMatrixMVect(n,m,state.m_j,0,0,1,state.m_fi,0,state.m_gbase,0);
|
|
for(int i_=0;i_<=n-1;i_++)
|
|
state.m_gbase[i_]=2*state.m_gbase[i_];
|
|
//--- change value
|
|
v=0.0;
|
|
for(int i_=0;i_<=m-1;i_++)
|
|
v+=state.m_fi[i_]*state.m_fi[i_];
|
|
state.m_fbase=v;
|
|
//--- copy
|
|
for(int i_=0;i_<=m-1;i_++)
|
|
state.m_fibase[i_]=state.m_fi[i_];
|
|
//--- set control variables
|
|
bflag=true;
|
|
//--- function call, return result
|
|
return(Func_lbl_22(state,n,m,iflag,i,k,bflag,v,s,t));
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Auxiliary function for MinLMIteration. Is a product to get rid of|
|
|
//| the operator unconditional jump goto. |
|
|
//+------------------------------------------------------------------+
|
|
static bool CMinLM::Func_lbl_28(CMinLMState &state,int &n,int &m,int &iflag,
|
|
int &i,int &k,bool &bflag,double &v,
|
|
double &s,double &t)
|
|
{
|
|
//--- check
|
|
if(k>n-1)
|
|
{
|
|
//--- Calculate F(XBase)
|
|
for(int i_=0;i_<=n-1;i_++)
|
|
state.m_x[i_]=state.m_xbase[i_];
|
|
//--- function call
|
|
ClearRequestFields(state);
|
|
//--- change values
|
|
state.m_needfi=true;
|
|
state.m_rstate.stage=5;
|
|
//--- Saving state
|
|
Func_lbl_rcomm(state,n,m,iflag,i,k,bflag,v,s,t);
|
|
//--- return result
|
|
return(true);
|
|
}
|
|
//--- We guard X[k] from leaving [BndL,BndU].
|
|
//--- In case BndL=BndU,we assume that derivative in this direction is zero.
|
|
for(int i_=0;i_<=n-1;i_++)
|
|
state.m_x[i_]=state.m_xbase[i_];
|
|
state.m_x[k]=state.m_x[k]-state.m_s[k]*state.m_diffstep;
|
|
//--- check
|
|
if(state.m_havebndl[k])
|
|
state.m_x[k]=MathMax(state.m_x[k],state.m_bndl[k]);
|
|
//--- check
|
|
if(state.m_havebndu[k])
|
|
state.m_x[k]=MathMin(state.m_x[k],state.m_bndu[k]);
|
|
state.m_xm1=state.m_x[k];
|
|
//--- function call
|
|
ClearRequestFields(state);
|
|
//--- change values
|
|
state.m_needfi=true;
|
|
state.m_rstate.stage=3;
|
|
//--- Saving state
|
|
Func_lbl_rcomm(state,n,m,iflag,i,k,bflag,v,s,t);
|
|
//--- return result
|
|
return(true);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Auxiliary function for MinLMIteration. Is a product to get rid of|
|
|
//| the operator unconditional jump goto. |
|
|
//+------------------------------------------------------------------+
|
|
static bool CMinLM::Func_lbl_31(CMinLMState &state,int &n,int &m,int &iflag,
|
|
int &i,int &k,bool &bflag,double &v,
|
|
double &s,double &t)
|
|
{
|
|
//--- check
|
|
if(!CAp::Assert(bflag,"MinLM: internal integrity check failed!"))
|
|
return(false);
|
|
//--- change values
|
|
state.m_deltaxready=false;
|
|
state.m_deltafready=false;
|
|
//--- If Lambda is not initialized,initialize it using quadratic model
|
|
if(state.m_lambdav<0.0)
|
|
{
|
|
state.m_lambdav=0;
|
|
for(i=0;i<=n-1;i++)
|
|
state.m_lambdav=MathMax(state.m_lambdav,MathAbs(state.m_quadraticmodel[i][i])*CMath::Sqr(state.m_s[i]));
|
|
state.m_lambdav=0.001*state.m_lambdav;
|
|
//--- check
|
|
if(state.m_lambdav==0.0)
|
|
state.m_lambdav=1;
|
|
}
|
|
//--- Test stopping conditions for function gradient
|
|
if(BoundedScaledAntigradNorm(state,state.m_xbase,state.m_gbase)>state.m_epsg)
|
|
{
|
|
//--- Find value of Levenberg-Marquardt damping parameter which:
|
|
//--- * leads to positive definite damped model
|
|
//--- * within bounds specified by StpMax
|
|
//--- * generates step which decreases function value
|
|
//--- After this block IFlag is set to:
|
|
//--- * -3,if constraints are infeasible
|
|
//--- * -2,if model update is needed (either Lambda growth is too large
|
|
//--- or step is too short,but we can't rely on model and stop iterations)
|
|
//--- * -1,if model is fresh,Lambda have grown too large,termination is needed
|
|
//--- * 0,if everything is OK,continue iterations
|
|
//--- State.Nu can have any value on enter,but after exit it is set to 1.0
|
|
iflag=-99;
|
|
//--- function call, return result
|
|
return(Func_lbl_39(state,n,m,iflag,i,k,bflag,v,s,t));
|
|
}
|
|
//--- check
|
|
if(state.m_modelage!=0)
|
|
{
|
|
//--- Model is not fresh,we should refresh it and test
|
|
//--- conditions once more
|
|
state.m_modelage=state.m_maxmodelage+1;
|
|
//--- function call, return result
|
|
return(Func_lbl_20(state,n,m,iflag,i,k,bflag,v,s,t));
|
|
}
|
|
//--- Model is fresh,we can rely on it and terminate algorithm
|
|
state.m_repterminationtype=4;
|
|
//--- check
|
|
if(!state.m_xrep)
|
|
return(false);
|
|
for(int i_=0;i_<=n-1;i_++)
|
|
state.m_x[i_]=state.m_xbase[i_];
|
|
state.m_f=state.m_fbase;
|
|
//--- function call
|
|
ClearRequestFields(state);
|
|
//--- change values
|
|
state.m_xupdated=true;
|
|
state.m_rstate.stage=8;
|
|
//--- Saving state
|
|
Func_lbl_rcomm(state,n,m,iflag,i,k,bflag,v,s,t);
|
|
//--- return result
|
|
return(true);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Auxiliary function for MinLMIteration. Is a product to get rid of|
|
|
//| the operator unconditional jump goto. |
|
|
//+------------------------------------------------------------------+
|
|
static bool CMinLM::Func_lbl_39(CMinLMState &state,int &n,int &m,int &iflag,
|
|
int &i,int &k,bool &bflag,double &v,
|
|
double &s,double &t)
|
|
{
|
|
//--- Do we need model update?
|
|
if(state.m_modelage>0 && state.m_nu>=m_suspiciousnu)
|
|
{
|
|
iflag=-2;
|
|
//--- function call, return result
|
|
return(Func_lbl_40(state,n,m,iflag,i,k,bflag,v,s,t));
|
|
}
|
|
//--- Setup quadratic solver and solve quadratic programming problem.
|
|
//--- After problem is solved we'll try to bound step by StpMax
|
|
//--- (Lambda will be increased if step size is too large).
|
|
//--- We use BFlag variable to indicate that we have to increase Lambda.
|
|
//--- If it is False,we will try to increase Lambda and move to new iteration.
|
|
bflag=true;
|
|
//--- function call
|
|
CMinQP::MinQPSetStartingPointFast(state.m_qpstate,state.m_xbase);
|
|
//--- function call
|
|
CMinQP::MinQPSetOriginFast(state.m_qpstate,state.m_xbase);
|
|
//--- function call
|
|
CMinQP::MinQPSetLinearTermFast(state.m_qpstate,state.m_gbase);
|
|
//--- function call
|
|
CMinQP::MinQPSetQuadraticTermFast(state.m_qpstate,state.m_quadraticmodel,true,0.0);
|
|
for(i=0;i<=n-1;i++)
|
|
state.m_tmp0[i]=state.m_quadraticmodel[i][i]+state.m_lambdav/CMath::Sqr(state.m_s[i]);
|
|
//--- function call
|
|
CMinQP::MinQPRewriteDiagonal(state.m_qpstate,state.m_tmp0);
|
|
//--- function call
|
|
CMinQP::MinQPOptimize(state.m_qpstate);
|
|
//--- function call
|
|
CMinQP::MinQPResultsBuf(state.m_qpstate,state.m_xdir,state.m_qprep);
|
|
//--- check
|
|
if(state.m_qprep.m_terminationtype>0)
|
|
{
|
|
//--- successful solution of QP problem
|
|
for(int i_=0;i_<=n-1;i_++)
|
|
state.m_xdir[i_]=state.m_xdir[i_]-state.m_xbase[i_];
|
|
v=0.0;
|
|
for(int i_=0;i_<=n-1;i_++)
|
|
v+=state.m_xdir[i_]*state.m_xdir[i_];
|
|
//--- check
|
|
if(CMath::IsFinite(v))
|
|
{
|
|
v=MathSqrt(v);
|
|
//--- check
|
|
if((state.m_stpmax>0.0)&&(v>state.m_stpmax))
|
|
bflag=false;
|
|
}
|
|
else
|
|
bflag=false;
|
|
}
|
|
else
|
|
{
|
|
//--- Either problem is non-convex (increase LambdaV) or constraints are inconsistent
|
|
if(!CAp::Assert(state.m_qprep.m_terminationtype==-3 || state.m_qprep.m_terminationtype==-5,"MinLM: unexpected completion code from QP solver"))
|
|
return(false);
|
|
//--- check
|
|
if(state.m_qprep.m_terminationtype==-3)
|
|
{
|
|
iflag=-3;
|
|
//--- function call, return result
|
|
return(Func_lbl_40(state,n,m,iflag,i,k,bflag,v,s,t));
|
|
}
|
|
bflag=false;
|
|
}
|
|
//--- check
|
|
if(!bflag)
|
|
{
|
|
//--- Solution failed:
|
|
//--- try to increase lambda to make matrix positive definite and continue.
|
|
if(!IncreaseLambda(state.m_lambdav,state.m_nu))
|
|
{
|
|
iflag=-1;
|
|
//--- function call, return result
|
|
return(Func_lbl_40(state,n,m,iflag,i,k,bflag,v,s,t));
|
|
}
|
|
//--- function call, return result
|
|
return(Func_lbl_39(state,n,m,iflag,i,k,bflag,v,s,t));
|
|
}
|
|
//--- Step in State.XDir and it is bounded by StpMax.
|
|
//--- We should check stopping conditions on step size here.
|
|
//--- DeltaX,which is used for secant updates,is initialized here.
|
|
//--- This code is a bit tricky because sometimes XDir<>0,but
|
|
//--- it is so small that XDir+XBase==XBase (in finite precision
|
|
//--- arithmetics). So we set DeltaX to XBase,then
|
|
//--- add XDir,and then subtract XBase to get exact value of
|
|
//--- DeltaX.
|
|
//--- Step length is estimated using DeltaX.
|
|
//--- NOTE: stopping conditions are tested
|
|
//--- for fresh models only (ModelAge=0)
|
|
for(int i_=0;i_<=n-1;i_++)
|
|
state.m_deltax[i_]=state.m_xbase[i_];
|
|
for(int i_=0;i_<=n-1;i_++)
|
|
state.m_deltax[i_]=state.m_deltax[i_]+state.m_xdir[i_];
|
|
for(int i_=0;i_<=n-1;i_++)
|
|
state.m_deltax[i_]=state.m_deltax[i_]-state.m_xbase[i_];
|
|
state.m_deltaxready=true;
|
|
//--- change value
|
|
v=0.0;
|
|
for(i=0;i<=n-1;i++)
|
|
v=v+CMath::Sqr(state.m_deltax[i]/state.m_s[i]);
|
|
v=MathSqrt(v);
|
|
//--- check
|
|
if(v>state.m_epsx)
|
|
return(Func_lbl_41(state,n,m,iflag,i,k,bflag,v,s,t));
|
|
//--- check
|
|
if(state.m_modelage!=0)
|
|
{
|
|
//--- Step is suspiciously short,but model is not fresh
|
|
//--- and we can't rely on it.
|
|
iflag=-2;
|
|
//--- function call, return result
|
|
return(Func_lbl_40(state,n,m,iflag,i,k,bflag,v,s,t));
|
|
}
|
|
//--- Step is too short,model is fresh and we can rely on it.
|
|
//--- Terminating.
|
|
state.m_repterminationtype=2;
|
|
if(!state.m_xrep)
|
|
return(false);
|
|
//--- copy
|
|
for(int i_=0;i_<=n-1;i_++)
|
|
state.m_x[i_]=state.m_xbase[i_];
|
|
state.m_f=state.m_fbase;
|
|
//--- function call
|
|
ClearRequestFields(state);
|
|
//--- change values
|
|
state.m_xupdated=true;
|
|
state.m_rstate.stage=9;
|
|
//--- Saving state
|
|
Func_lbl_rcomm(state,n,m,iflag,i,k,bflag,v,s,t);
|
|
//--- return result
|
|
return(true);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Auxiliary function for MinLMIteration. Is a product to get rid of|
|
|
//| the operator unconditional jump goto. |
|
|
//+------------------------------------------------------------------+
|
|
static bool CMinLM::Func_lbl_40(CMinLMState &state,int &n,int &m,int &iflag,
|
|
int &i,int &k,bool &bflag,double &v,
|
|
double &s,double &t)
|
|
{
|
|
state.m_nu=1;
|
|
//--- check
|
|
if(!CAp::Assert(iflag>=-3 && iflag<=0,"MinLM: internal integrity check failed!"))
|
|
return(false);
|
|
//--- check
|
|
if(iflag==-3)
|
|
{
|
|
state.m_repterminationtype=-3;
|
|
//--- return result
|
|
return(false);
|
|
}
|
|
//--- check
|
|
if(iflag==-2)
|
|
{
|
|
state.m_modelage=state.m_maxmodelage+1;
|
|
//--- function call, return result
|
|
return(Func_lbl_20(state,n,m,iflag,i,k,bflag,v,s,t));
|
|
}
|
|
//--- check
|
|
if(iflag==-1)
|
|
return(Func_lbl_21(state,n,m,iflag,i,k,bflag,v,s,t));
|
|
//--- Levenberg-Marquardt step is ready.
|
|
//--- Compare predicted vs. actual decrease and decide what to do with lambda.
|
|
//--- NOTE: we expect that State.DeltaX contains direction of step,
|
|
//--- State.F contains function value at new point.
|
|
if(!CAp::Assert(state.m_deltaxready,"MinLM: deltaX is not ready"))
|
|
return(false);
|
|
t=0;
|
|
for(i=0;i<=n-1;i++)
|
|
{
|
|
//--- change values
|
|
v=0.0;
|
|
for(int i_=0;i_<=n-1;i_++)
|
|
v+=state.m_quadraticmodel[i][i_]*state.m_deltax[i_];
|
|
t=t+state.m_deltax[i]*state.m_gbase[i]+0.5*state.m_deltax[i]*v;
|
|
}
|
|
//--- change values
|
|
state.m_predicteddecrease=-t;
|
|
state.m_actualdecrease=-(state.m_f-state.m_fbase);
|
|
//--- check
|
|
if(state.m_predicteddecrease<=0.0)
|
|
return(Func_lbl_21(state,n,m,iflag,i,k,bflag,v,s,t));
|
|
v=state.m_actualdecrease/state.m_predicteddecrease;
|
|
//--- check
|
|
if(v>=0.1)
|
|
return(Func_lbl_49(state,n,m,iflag,i,k,bflag,v,s,t));
|
|
//--- check
|
|
if(IncreaseLambda(state.m_lambdav,state.m_nu))
|
|
return(Func_lbl_49(state,n,m,iflag,i,k,bflag,v,s,t));
|
|
//--- Lambda is too large,we have to break iterations.
|
|
state.m_repterminationtype=7;
|
|
//--- check
|
|
if(!state.m_xrep)
|
|
return(false);
|
|
for(int i_=0;i_<=n-1;i_++)
|
|
state.m_x[i_]=state.m_xbase[i_];
|
|
state.m_f=state.m_fbase;
|
|
//--- function call
|
|
ClearRequestFields(state);
|
|
//--- change values
|
|
state.m_xupdated=true;
|
|
state.m_rstate.stage=12;
|
|
//--- Saving state
|
|
Func_lbl_rcomm(state,n,m,iflag,i,k,bflag,v,s,t);
|
|
//--- return result
|
|
return(true);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Auxiliary function for MinLMIteration. Is a product to get rid of|
|
|
//| the operator unconditional jump goto. |
|
|
//+------------------------------------------------------------------+
|
|
static bool CMinLM::Func_lbl_41(CMinLMState &state,int &n,int &m,int &iflag,
|
|
int &i,int &k,bool &bflag,double &v,
|
|
double &s,double &t)
|
|
{
|
|
//--- Let's evaluate new step:
|
|
//--- a) if we have Fi vector,we evaluate it using rcomm,and
|
|
//--- then we manually calculate State.F as sum of squares of Fi[]
|
|
//--- b) if we have F value,we just evaluate it through rcomm interface
|
|
//--- We prefer (a) because we may need Fi vector for additional
|
|
//--- iterations
|
|
if(!CAp::Assert(state.m_hasfi|state.m_hasf,"MinLM: internal error 2!"))
|
|
return(false);
|
|
for(int i_=0;i_<=n-1;i_++)
|
|
state.m_x[i_]=state.m_xbase[i_];
|
|
for(int i_=0;i_<=n-1;i_++)
|
|
state.m_x[i_]=state.m_x[i_]+state.m_xdir[i_];
|
|
//--- function call
|
|
ClearRequestFields(state);
|
|
//--- check
|
|
if(!state.m_hasfi)
|
|
{
|
|
//--- change values
|
|
state.m_needf=true;
|
|
state.m_rstate.stage=11;
|
|
//--- Saving state
|
|
Func_lbl_rcomm(state,n,m,iflag,i,k,bflag,v,s,t);
|
|
//--- return result
|
|
return(true);
|
|
}
|
|
//--- change values
|
|
state.m_needfi=true;
|
|
state.m_rstate.stage=10;
|
|
//--- Saving state
|
|
Func_lbl_rcomm(state,n,m,iflag,i,k,bflag,v,s,t);
|
|
//--- return result
|
|
return(true);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Auxiliary function for MinLMIteration. Is a product to get rid of|
|
|
//| the operator unconditional jump goto. |
|
|
//+------------------------------------------------------------------+
|
|
static bool CMinLM::Func_lbl_48(CMinLMState &state,int &n,int &m,int &iflag,
|
|
int &i,int &k,bool &bflag,double &v,
|
|
double &s,double &t)
|
|
{
|
|
state.m_repnfunc=state.m_repnfunc+1;
|
|
//--- check
|
|
if(state.m_f>=state.m_fbase)
|
|
{
|
|
//--- Increase lambda and continue
|
|
if(!IncreaseLambda(state.m_lambdav,state.m_nu))
|
|
{
|
|
iflag=-1;
|
|
//--- function call, return result
|
|
return(Func_lbl_40(state,n,m,iflag,i,k,bflag,v,s,t));
|
|
}
|
|
//--- function call, return result
|
|
return(Func_lbl_39(state,n,m,iflag,i,k,bflag,v,s,t));
|
|
}
|
|
//--- We've found our step!
|
|
iflag=0;
|
|
//--- function call, return result
|
|
return(Func_lbl_40(state,n,m,iflag,i,k,bflag,v,s,t));
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Auxiliary function for MinLMIteration. Is a product to get rid of|
|
|
//| the operator unconditional jump goto. |
|
|
//+------------------------------------------------------------------+
|
|
static bool CMinLM::Func_lbl_49(CMinLMState &state,int &n,int &m,int &iflag,
|
|
int &i,int &k,bool &bflag,double &v,
|
|
double &s,double &t)
|
|
{
|
|
//--- check
|
|
if(v>0.5)
|
|
DecreaseLambda(state.m_lambdav,state.m_nu);
|
|
//--- Accept step,report it and
|
|
//--- test stopping conditions on iterations count and function decrease.
|
|
//--- NOTE: we expect that State.DeltaX contains direction of step,
|
|
//--- State.F contains function value at new point.
|
|
//--- NOTE2: we should update XBase ONLY. In the beginning of the next
|
|
//--- iteration we expect that State.FIBase is NOT updated and
|
|
//--- contains old value of a function vector.
|
|
for(int i_=0;i_<=n-1;i_++)
|
|
state.m_xbase[i_]=state.m_xbase[i_]+state.m_deltax[i_];
|
|
//--- check
|
|
if(!state.m_xrep)
|
|
return(Func_lbl_55(state,n,m,iflag,i,k,bflag,v,s,t));
|
|
for(int i_=0;i_<=n-1;i_++)
|
|
state.m_x[i_]=state.m_xbase[i_];
|
|
//--- function call
|
|
ClearRequestFields(state);
|
|
//--- change values
|
|
state.m_xupdated=true;
|
|
state.m_rstate.stage=13;
|
|
//--- Saving state
|
|
Func_lbl_rcomm(state,n,m,iflag,i,k,bflag,v,s,t);
|
|
//--- return result
|
|
return(true);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Auxiliary function for MinLMIteration. Is a product to get rid of|
|
|
//| the operator unconditional jump goto. |
|
|
//+------------------------------------------------------------------+
|
|
static bool CMinLM::Func_lbl_55(CMinLMState &state,int &n,int &m,int &iflag,
|
|
int &i,int &k,bool &bflag,double &v,
|
|
double &s,double &t)
|
|
{
|
|
state.m_repiterationscount=state.m_repiterationscount+1;
|
|
//--- check
|
|
if(state.m_repiterationscount>=state.m_maxits&&state.m_maxits>0)
|
|
state.m_repterminationtype=5;
|
|
//--- check
|
|
if(state.m_modelage==0)
|
|
{
|
|
//--- check
|
|
if(MathAbs(state.m_f-state.m_fbase)<=state.m_epsf*MathMax(1,MathMax(MathAbs(state.m_f),MathAbs(state.m_fbase))))
|
|
state.m_repterminationtype=1;
|
|
}
|
|
//--- check
|
|
if(state.m_repterminationtype<=0)
|
|
{
|
|
state.m_modelage=state.m_modelage+1;
|
|
//--- function call, return result
|
|
return(Func_lbl_20(state,n,m,iflag,i,k,bflag,v,s,t));
|
|
}
|
|
//--- check
|
|
if(!state.m_xrep)
|
|
return(false);
|
|
//--- Report: XBase contains new point,F contains function value at new point
|
|
for(int i_=0;i_<=n-1;i_++)
|
|
state.m_x[i_]=state.m_xbase[i_];
|
|
//--- function call
|
|
ClearRequestFields(state);
|
|
//--- change values
|
|
state.m_xupdated=true;
|
|
state.m_rstate.stage=14;
|
|
//--- Saving state
|
|
Func_lbl_rcomm(state,n,m,iflag,i,k,bflag,v,s,t);
|
|
//--- return result
|
|
return(true);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Auxiliary class for CMinComp |
|
|
//+------------------------------------------------------------------+
|
|
class CMinASAState
|
|
{
|
|
public:
|
|
//--- variables
|
|
int m_n;
|
|
double m_epsg;
|
|
double m_epsf;
|
|
double m_epsx;
|
|
int m_maxits;
|
|
bool m_xrep;
|
|
double m_stpmax;
|
|
int m_cgtype;
|
|
int m_k;
|
|
int m_nfev;
|
|
int m_mcstage;
|
|
int m_curalgo;
|
|
int m_acount;
|
|
double m_mu;
|
|
double m_finit;
|
|
double m_dginit;
|
|
double m_fold;
|
|
double m_stp;
|
|
double m_laststep;
|
|
double m_f;
|
|
bool m_needfg;
|
|
bool m_xupdated;
|
|
RCommState m_rstate;
|
|
int m_repiterationscount;
|
|
int m_repnfev;
|
|
int m_repterminationtype;
|
|
int m_debugrestartscount;
|
|
CLinMinState m_lstate;
|
|
double m_betahs;
|
|
double m_betady;
|
|
//--- arrays
|
|
double m_bndl[];
|
|
double m_bndu[];
|
|
double m_ak[];
|
|
double m_xk[];
|
|
double m_dk[];
|
|
double m_an[];
|
|
double m_xn[];
|
|
double m_dn[];
|
|
double m_d[];
|
|
double m_work[];
|
|
double m_yk[];
|
|
double m_gc[];
|
|
double m_x[];
|
|
double m_g[];
|
|
//--- constructor, destructor
|
|
CMinASAState(void);
|
|
~CMinASAState(void);
|
|
//--- copy
|
|
void Copy(CMinASAState &obj);
|
|
};
|
|
//+------------------------------------------------------------------+
|
|
//| Constructor without parameters |
|
|
//+------------------------------------------------------------------+
|
|
CMinASAState::CMinASAState(void)
|
|
{
|
|
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Destructor |
|
|
//+------------------------------------------------------------------+
|
|
CMinASAState::~CMinASAState(void)
|
|
{
|
|
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Copy |
|
|
//+------------------------------------------------------------------+
|
|
void CMinASAState::Copy(CMinASAState &obj)
|
|
{
|
|
//--- copy variables
|
|
m_n=obj.m_n;
|
|
m_epsg=obj.m_epsg;
|
|
m_epsf=obj.m_epsf;
|
|
m_epsx=obj.m_epsx;
|
|
m_maxits=obj.m_maxits;
|
|
m_xrep=obj.m_xrep;
|
|
m_stpmax=obj.m_stpmax;
|
|
m_cgtype=obj.m_cgtype;
|
|
m_k=obj.m_k;
|
|
m_nfev=obj.m_nfev;
|
|
m_mcstage=obj.m_mcstage;
|
|
m_curalgo=obj.m_curalgo;
|
|
m_acount=obj.m_acount;
|
|
m_mu=obj.m_mu;
|
|
m_finit=obj.m_finit;
|
|
m_dginit=obj.m_dginit;
|
|
m_fold=obj.m_fold;
|
|
m_stp=obj.m_stp;
|
|
m_laststep=obj.m_laststep;
|
|
m_f=obj.m_f;
|
|
m_needfg=obj.m_needfg;
|
|
m_xupdated=obj.m_xupdated;
|
|
m_repiterationscount=obj.m_repiterationscount;
|
|
m_repnfev=obj.m_repnfev;
|
|
m_repterminationtype=obj.m_repterminationtype;
|
|
m_debugrestartscount=obj.m_debugrestartscount;
|
|
m_betahs=obj.m_betahs;
|
|
m_betady=obj.m_betady;
|
|
m_rstate.Copy(obj.m_rstate);
|
|
m_lstate.Copy(obj.m_lstate);
|
|
//--- copy arrays
|
|
ArrayCopy(m_bndl,obj.m_bndl);
|
|
ArrayCopy(m_bndu,obj.m_bndu);
|
|
ArrayCopy(m_ak,obj.m_ak);
|
|
ArrayCopy(m_xk,obj.m_xk);
|
|
ArrayCopy(m_dk,obj.m_dk);
|
|
ArrayCopy(m_an,obj.m_an);
|
|
ArrayCopy(m_xn,obj.m_xn);
|
|
ArrayCopy(m_dn,obj.m_dn);
|
|
ArrayCopy(m_d,obj.m_d);
|
|
ArrayCopy(m_work,obj.m_work);
|
|
ArrayCopy(m_yk,obj.m_yk);
|
|
ArrayCopy(m_gc,obj.m_gc);
|
|
ArrayCopy(m_x,obj.m_x);
|
|
ArrayCopy(m_g,obj.m_g);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| This class is a shell for class CMinASAState |
|
|
//+------------------------------------------------------------------+
|
|
class CMinASAStateShell
|
|
{
|
|
private:
|
|
CMinASAState m_innerobj;
|
|
public:
|
|
//--- constructors, destructor
|
|
CMinASAStateShell(void);
|
|
CMinASAStateShell(CMinASAState &obj);
|
|
~CMinASAStateShell(void);
|
|
//--- methods
|
|
bool GetNeedFG(void);
|
|
void SetNeedFG(const bool b);
|
|
bool GetXUpdated(void);
|
|
void SetXUpdated(const bool b);
|
|
double GetF(void);
|
|
void SetF(const double d);
|
|
CMinASAState *GetInnerObj(void);
|
|
};
|
|
//+------------------------------------------------------------------+
|
|
//| Constructor without parameters |
|
|
//+------------------------------------------------------------------+
|
|
CMinASAStateShell::CMinASAStateShell(void)
|
|
{
|
|
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Copy constructor |
|
|
//+------------------------------------------------------------------+
|
|
CMinASAStateShell::CMinASAStateShell(CMinASAState &obj)
|
|
{
|
|
//--- copy
|
|
m_innerobj.Copy(obj);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Destructor |
|
|
//+------------------------------------------------------------------+
|
|
CMinASAStateShell::~CMinASAStateShell(void)
|
|
{
|
|
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Returns the value of the variable needfg |
|
|
//+------------------------------------------------------------------+
|
|
bool CMinASAStateShell::GetNeedFG(void)
|
|
{
|
|
//--- return result
|
|
return(m_innerobj.m_needfg);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Changing the value of the variable needfg |
|
|
//+------------------------------------------------------------------+
|
|
void CMinASAStateShell::SetNeedFG(const bool b)
|
|
{
|
|
//--- change value
|
|
m_innerobj.m_needfg=b;
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Returns the value of the variable xupdated |
|
|
//+------------------------------------------------------------------+
|
|
bool CMinASAStateShell::GetXUpdated(void)
|
|
{
|
|
//--- return result
|
|
return(m_innerobj.m_xupdated);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Changing the value of the variable xupdated |
|
|
//+------------------------------------------------------------------+
|
|
void CMinASAStateShell::SetXUpdated(const bool b)
|
|
{
|
|
//--- change value
|
|
m_innerobj.m_xupdated=b;
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Returns the value of the variable f |
|
|
//+------------------------------------------------------------------+
|
|
double CMinASAStateShell::GetF(void)
|
|
{
|
|
//--- return result
|
|
return(m_innerobj.m_f);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Changing the value of the variable f |
|
|
//+------------------------------------------------------------------+
|
|
void CMinASAStateShell::SetF(const double d)
|
|
{
|
|
//--- change value
|
|
m_innerobj.m_f=d;
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Return object of class |
|
|
//+------------------------------------------------------------------+
|
|
CMinASAState *CMinASAStateShell::GetInnerObj(void)
|
|
{
|
|
//--- return result
|
|
return(GetPointer(m_innerobj));
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Auxiliary class for CMinComp |
|
|
//+------------------------------------------------------------------+
|
|
class CMinASAReport
|
|
{
|
|
public:
|
|
//--- variables
|
|
int m_iterationscount;
|
|
int m_nfev;
|
|
int m_terminationtype;
|
|
int m_activeconstraints;
|
|
//--- constructor, destructor
|
|
CMinASAReport(void);
|
|
~CMinASAReport(void);
|
|
//--- copy
|
|
void Copy(CMinASAReport &obj);
|
|
};
|
|
//+------------------------------------------------------------------+
|
|
//| Constructor without parameters |
|
|
//+------------------------------------------------------------------+
|
|
CMinASAReport::CMinASAReport(void)
|
|
{
|
|
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Destructor |
|
|
//+------------------------------------------------------------------+
|
|
CMinASAReport::~CMinASAReport(void)
|
|
{
|
|
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Copy |
|
|
//+------------------------------------------------------------------+
|
|
void CMinASAReport::Copy(CMinASAReport &obj)
|
|
{
|
|
//--- copy variables
|
|
m_iterationscount=obj.m_iterationscount;
|
|
m_nfev=obj.m_nfev;
|
|
m_terminationtype=obj.m_terminationtype;
|
|
m_activeconstraints=obj.m_activeconstraints;
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| This class is a shell for class CMinASAReport |
|
|
//+------------------------------------------------------------------+
|
|
class CMinASAReportShell
|
|
{
|
|
private:
|
|
CMinASAReport m_innerobj;
|
|
public:
|
|
//--- constructors, destructor
|
|
CMinASAReportShell(void);
|
|
CMinASAReportShell(CMinASAReport &obj);
|
|
~CMinASAReportShell(void);
|
|
//--- methods
|
|
int GetIterationsCount(void);
|
|
void SetIterationsCount(const int i);
|
|
int GetNFev(void);
|
|
void SetNFev(const int i);
|
|
int GetTerminationType(void);
|
|
void SetTerminationType(const int i);
|
|
int GetActiveConstraints(void);
|
|
void SetActiveConstraints(const int i);
|
|
CMinASAReport *GetInnerObj(void);
|
|
};
|
|
//+------------------------------------------------------------------+
|
|
//| Constructor without parameters |
|
|
//+------------------------------------------------------------------+
|
|
CMinASAReportShell::CMinASAReportShell(void)
|
|
{
|
|
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Copy constructor |
|
|
//+------------------------------------------------------------------+
|
|
CMinASAReportShell::CMinASAReportShell(CMinASAReport &obj)
|
|
{
|
|
//--- copy
|
|
m_innerobj.Copy(obj);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Destructor |
|
|
//+------------------------------------------------------------------+
|
|
CMinASAReportShell::~CMinASAReportShell(void)
|
|
{
|
|
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Returns the value of the variable iterationscount |
|
|
//+------------------------------------------------------------------+
|
|
int CMinASAReportShell::GetIterationsCount(void)
|
|
{
|
|
//--- return result
|
|
return(m_innerobj.m_iterationscount);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Changing the value of the variable iterationscount |
|
|
//+------------------------------------------------------------------+
|
|
void CMinASAReportShell::SetIterationsCount(const int i)
|
|
{
|
|
//--- change value
|
|
m_innerobj.m_iterationscount=i;
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Returns the value of the variable nfev |
|
|
//+------------------------------------------------------------------+
|
|
int CMinASAReportShell::GetNFev(void)
|
|
{
|
|
//--- return result
|
|
return(m_innerobj.m_nfev);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Changing the value of the variable nfev |
|
|
//+------------------------------------------------------------------+
|
|
void CMinASAReportShell::SetNFev(const int i)
|
|
{
|
|
//--- change value
|
|
m_innerobj.m_nfev=i;
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Returns the value of the variable terminationtype |
|
|
//+------------------------------------------------------------------+
|
|
int CMinASAReportShell::GetTerminationType(void)
|
|
{
|
|
//--- return result
|
|
return(m_innerobj.m_terminationtype);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Changing the value of the variable terminationtype |
|
|
//+------------------------------------------------------------------+
|
|
void CMinASAReportShell::SetTerminationType(const int i)
|
|
{
|
|
//--- change value
|
|
m_innerobj.m_terminationtype=i;
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Returns the value of the variable activeconstraints |
|
|
//+------------------------------------------------------------------+
|
|
int CMinASAReportShell::GetActiveConstraints(void)
|
|
{
|
|
//--- return result
|
|
return(m_innerobj.m_activeconstraints);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Changing the value of the variable activeconstraints |
|
|
//+------------------------------------------------------------------+
|
|
void CMinASAReportShell::SetActiveConstraints(const int i)
|
|
{
|
|
//--- change value
|
|
m_innerobj.m_activeconstraints=i;
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Return object of class |
|
|
//+------------------------------------------------------------------+
|
|
CMinASAReport *CMinASAReportShell::GetInnerObj(void)
|
|
{
|
|
//--- return result
|
|
return(GetPointer(m_innerobj));
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Backward compatibility functions |
|
|
//+------------------------------------------------------------------+
|
|
class CMinComp
|
|
{
|
|
private:
|
|
//--- private methods
|
|
static double ASABoundedAntigradNorm(CMinASAState &state);
|
|
static double ASAGINorm(CMinASAState &state);
|
|
static double ASAD1Norm(CMinASAState &state);
|
|
static bool ASAUIsEmpty(CMinASAState &state);
|
|
static void ClearRequestFields(CMinASAState &state);
|
|
//--- auxiliary functions for MinASAIteration
|
|
static void Func_lbl_rcomm(CMinASAState &state,int n,int i,int mcinfo,int diffcnt,bool b,bool stepfound,double betak,double v,double vv);
|
|
static bool Func_lbl_15(CMinASAState &state,int &n,int &i,int &mcinfo,int &diffcnt,bool &b,bool &stepfound,double &betak,double &v,double &vv);
|
|
static bool Func_lbl_17(CMinASAState &state,int &n,int &i,int &mcinfo,int &diffcnt,bool &b,bool &stepfound,double &betak,double &v,double &vv);
|
|
static bool Func_lbl_19(CMinASAState &state,int &n,int &i,int &mcinfo,int &diffcnt,bool &b,bool &stepfound,double &betak,double &v,double &vv);
|
|
static bool Func_lbl_21(CMinASAState &state,int &n,int &i,int &mcinfo,int &diffcnt,bool &b,bool &stepfound,double &betak,double &v,double &vv);
|
|
static bool Func_lbl_24(CMinASAState &state,int &n,int &i,int &mcinfo,int &diffcnt,bool &b,bool &stepfound,double &betak,double &v,double &vv);
|
|
static bool Func_lbl_26(CMinASAState &state,int &n,int &i,int &mcinfo,int &diffcnt,bool &b,bool &stepfound,double &betak,double &v,double &vv);
|
|
static bool Func_lbl_27(CMinASAState &state,int &n,int &i,int &mcinfo,int &diffcnt,bool &b,bool &stepfound,double &betak,double &v,double &vv);
|
|
static bool Func_lbl_29(CMinASAState &state,int &n,int &i,int &mcinfo,int &diffcnt,bool &b,bool &stepfound,double &betak,double &v,double &vv);
|
|
static bool Func_lbl_31(CMinASAState &state,int &n,int &i,int &mcinfo,int &diffcnt,bool &b,bool &stepfound,double &betak,double &v,double &vv);
|
|
static bool Func_lbl_35(CMinASAState &state,int &n,int &i,int &mcinfo,int &diffcnt,bool &b,bool &stepfound,double &betak,double &v,double &vv);
|
|
static bool Func_lbl_39(CMinASAState &state,int &n,int &i,int &mcinfo,int &diffcnt,bool &b,bool &stepfound,double &betak,double &v,double &vv);
|
|
static bool Func_lbl_43(CMinASAState &state,int &n,int &i,int &mcinfo,int &diffcnt,bool &b,bool &stepfound,double &betak,double &v,double &vv);
|
|
static bool Func_lbl_49(CMinASAState &state,int &n,int &i,int &mcinfo,int &diffcnt,bool &b,bool &stepfound,double &betak,double &v,double &vv);
|
|
static bool Func_lbl_51(CMinASAState &state,int &n,int &i,int &mcinfo,int &diffcnt,bool &b,bool &stepfound,double &betak,double &v,double &vv);
|
|
static bool Func_lbl_52(CMinASAState &state,int &n,int &i,int &mcinfo,int &diffcnt,bool &b,bool &stepfound,double &betak,double &v,double &vv);
|
|
static bool Func_lbl_53(CMinASAState &state,int &n,int &i,int &mcinfo,int &diffcnt,bool &b,bool &stepfound,double &betak,double &v,double &vv);
|
|
static bool Func_lbl_55(CMinASAState &state,int &n,int &i,int &mcinfo,int &diffcnt,bool &b,bool &stepfound,double &betak,double &v,double &vv);
|
|
static bool Func_lbl_59(CMinASAState &state,int &n,int &i,int &mcinfo,int &diffcnt,bool &b,bool &stepfound,double &betak,double &v,double &vv);
|
|
static bool Func_lbl_63(CMinASAState &state,int &n,int &i,int &mcinfo,int &diffcnt,bool &b,bool &stepfound,double &betak,double &v,double &vv);
|
|
static bool Func_lbl_65(CMinASAState &state,int &n,int &i,int &mcinfo,int &diffcnt,bool &b,bool &stepfound,double &betak,double &v,double &vv);
|
|
public:
|
|
//--- class constants
|
|
static const int m_n1;
|
|
static const int m_n2;
|
|
static const double m_stpmin;
|
|
static const double m_gtol;
|
|
static const double m_gpaftol;
|
|
static const double m_gpadecay;
|
|
static const double m_asarho;
|
|
//--- constructor, destructor
|
|
CMinComp(void);
|
|
~CMinComp(void);
|
|
//--- public methods
|
|
static void MinLBFGSSetDefaultPreconditioner(CMinLBFGSState &state);
|
|
static void MinLBFGSSetCholeskyPreconditioner(CMinLBFGSState &state,CMatrixDouble &p,const bool isupper);
|
|
static void MinBLEICSetBarrierWidth(CMinBLEICState &state,const double mu);
|
|
static void MinBLEICSetBarrierDecay(CMinBLEICState &state,const double mudecay);
|
|
static void MinASACreate(const int n,double &x[],double &bndl[],double &bndu[],CMinASAState &state);
|
|
static void MinASASetCond(CMinASAState &state,const double epsg,const double epsf,double epsx,const int maxits);
|
|
static void MinASASetXRep(CMinASAState &state,const bool needxrep);
|
|
static void MinASASetAlgorithm(CMinASAState &state,int algotype);
|
|
static void MinASASetStpMax(CMinASAState &state,const double stpmax);
|
|
static void MinASAResults(CMinASAState &state,double &x[],CMinASAReport &rep);
|
|
static void MinASAResultsBuf(CMinASAState &state,double &x[],CMinASAReport &rep);
|
|
static void MinASARestartFrom(CMinASAState &state,double &x[],double &bndl[],double &bndu[]);
|
|
static bool MinASAIteration(CMinASAState &state);
|
|
};
|
|
//+------------------------------------------------------------------+
|
|
//| Initialize constants |
|
|
//+------------------------------------------------------------------+
|
|
const int CMinComp::m_n1=2;
|
|
const int CMinComp::m_n2=2;
|
|
const double CMinComp::m_stpmin=1.0E-300;
|
|
const double CMinComp::m_gtol=0.3;
|
|
const double CMinComp::m_gpaftol=0.0001;
|
|
const double CMinComp::m_gpadecay=0.5;
|
|
const double CMinComp::m_asarho=0.5;
|
|
//+------------------------------------------------------------------+
|
|
//| Constructor without parameters |
|
|
//+------------------------------------------------------------------+
|
|
CMinComp::CMinComp(void)
|
|
{
|
|
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Destructor |
|
|
//+------------------------------------------------------------------+
|
|
CMinComp::~CMinComp(void)
|
|
{
|
|
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Obsolete function, use MinLBFGSSetPrecDefault() instead. |
|
|
//+------------------------------------------------------------------+
|
|
static void CMinComp::MinLBFGSSetDefaultPreconditioner(CMinLBFGSState &state)
|
|
{
|
|
//--- function call
|
|
CMinLBFGS::MinLBFGSSetPrecDefault(state);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Obsolete function, use MinLBFGSSetCholeskyPreconditioner() |
|
|
//| instead. |
|
|
//+------------------------------------------------------------------+
|
|
static void CMinComp::MinLBFGSSetCholeskyPreconditioner(CMinLBFGSState &state,
|
|
CMatrixDouble &p,
|
|
const bool isupper)
|
|
{
|
|
//--- function call
|
|
CMinLBFGS::MinLBFGSSetPrecCholesky(state,p,isupper);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| This is obsolete function which was used by previous version of |
|
|
//| the BLEIC optimizer. It does nothing in the current version of |
|
|
//| BLEIC. |
|
|
//+------------------------------------------------------------------+
|
|
static void CMinComp::MinBLEICSetBarrierWidth(CMinBLEICState &state,
|
|
const double mu)
|
|
{
|
|
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| This is obsolete function which was used by previous version of |
|
|
//| the BLEIC optimizer. It does nothing in the current version of |
|
|
//| BLEIC. |
|
|
//+------------------------------------------------------------------+
|
|
static void CMinComp::MinBLEICSetBarrierDecay(CMinBLEICState &state,
|
|
const double mudecay)
|
|
{
|
|
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Obsolete optimization algorithm. |
|
|
//| Was replaced by MinBLEIC subpackage. |
|
|
//+------------------------------------------------------------------+
|
|
static void CMinComp::MinASACreate(const int n,double &x[],double &bndl[],
|
|
double &bndu[],CMinASAState &state)
|
|
{
|
|
//--- create a variable
|
|
int i=0;
|
|
//--- check
|
|
if(!CAp::Assert(n>=1,__FUNCTION__+": N too small!"))
|
|
return;
|
|
//--- check
|
|
if(!CAp::Assert(CAp::Len(x)>=n,__FUNCTION__+": Length(X)<N!"))
|
|
return;
|
|
//--- check
|
|
if(!CAp::Assert(CApServ::IsFiniteVector(x,n),__FUNCTION__+": X contains infinite or NaN values!"))
|
|
return;
|
|
//--- check
|
|
if(!CAp::Assert(CAp::Len(bndl)>=n,__FUNCTION__+": Length(BndL)<N!"))
|
|
return;
|
|
//--- check
|
|
if(!CAp::Assert(CApServ::IsFiniteVector(bndl,n),__FUNCTION__+": BndL contains infinite or NaN values!"))
|
|
return;
|
|
//--- check
|
|
if(!CAp::Assert(CAp::Len(bndu)>=n,__FUNCTION__+": Length(BndU)<N!"))
|
|
return;
|
|
//--- check
|
|
if(!CAp::Assert(CApServ::IsFiniteVector(bndu,n),__FUNCTION__+": BndU contains infinite or NaN values!"))
|
|
return;
|
|
for(i=0;i<=n-1;i++)
|
|
{
|
|
//--- check
|
|
if(!CAp::Assert((double)(bndl[i])<=(double)(bndu[i]),__FUNCTION__+": inconsistent bounds!"))
|
|
return;
|
|
//--- check
|
|
if(!CAp::Assert((double)(bndl[i])<=x[i],__FUNCTION__+": infeasible X!"))
|
|
return;
|
|
//--- check
|
|
if(!CAp::Assert(x[i]<=(double)(bndu[i]),__FUNCTION__+": infeasible X!"))
|
|
return;
|
|
}
|
|
//--- Initialize
|
|
state.m_n=n;
|
|
MinASASetCond(state,0,0,0,0);
|
|
MinASASetXRep(state,false);
|
|
MinASASetStpMax(state,0);
|
|
MinASASetAlgorithm(state,-1);
|
|
//--- allocation
|
|
ArrayResizeAL(state.m_bndl,n);
|
|
ArrayResizeAL(state.m_bndu,n);
|
|
ArrayResizeAL(state.m_ak,n);
|
|
ArrayResizeAL(state.m_xk,n);
|
|
ArrayResizeAL(state.m_dk,n);
|
|
ArrayResizeAL(state.m_an,n);
|
|
ArrayResizeAL(state.m_xn,n);
|
|
ArrayResizeAL(state.m_dn,n);
|
|
ArrayResizeAL(state.m_x,n);
|
|
ArrayResizeAL(state.m_d,n);
|
|
ArrayResizeAL(state.m_g,n);
|
|
ArrayResizeAL(state.m_gc,n);
|
|
ArrayResizeAL(state.m_work,n);
|
|
ArrayResizeAL(state.m_yk,n);
|
|
//--- function call
|
|
MinASARestartFrom(state,x,bndl,bndu);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Obsolete optimization algorithm. |
|
|
//| Was replaced by MinBLEIC subpackage. |
|
|
//+------------------------------------------------------------------+
|
|
static void CMinComp::MinASASetCond(CMinASAState &state,const double epsg,
|
|
const double epsf,double epsx,
|
|
const int maxits)
|
|
{
|
|
//--- check
|
|
if(!CAp::Assert(CMath::IsFinite(epsg),__FUNCTION__+": EpsG is not finite number!"))
|
|
return;
|
|
//--- check
|
|
if(!CAp::Assert(epsg>=0.0,__FUNCTION__+": negative EpsG!"))
|
|
return;
|
|
//--- check
|
|
if(!CAp::Assert(CMath::IsFinite(epsf),__FUNCTION__+": EpsF is not finite number!"))
|
|
return;
|
|
//--- check
|
|
if(!CAp::Assert(epsf>=0.0,__FUNCTION__+": negative EpsF!"))
|
|
return;
|
|
//--- check
|
|
if(!CAp::Assert(CMath::IsFinite(epsx),__FUNCTION__+": EpsX is not finite number!"))
|
|
return;
|
|
//--- check
|
|
if(!CAp::Assert(epsx>=0.0,__FUNCTION__+": negative EpsX!"))
|
|
return;
|
|
//--- check
|
|
if(!CAp::Assert(maxits>=0,__FUNCTION__+": negative MaxIts!"))
|
|
return;
|
|
//--- check
|
|
if(((epsg==0.0 && epsf==0.0) && epsx==0.0) && maxits==0)
|
|
epsx=1.0E-6;
|
|
//--- change values
|
|
state.m_epsg=epsg;
|
|
state.m_epsf=epsf;
|
|
state.m_epsx=epsx;
|
|
state.m_maxits=maxits;
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Obsolete optimization algorithm. |
|
|
//| Was replaced by MinBLEIC subpackage. |
|
|
//+------------------------------------------------------------------+
|
|
static void CMinComp::MinASASetXRep(CMinASAState &state,const bool needxrep)
|
|
{
|
|
//--- change value
|
|
state.m_xrep=needxrep;
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Obsolete optimization algorithm. |
|
|
//| Was replaced by MinBLEIC subpackage. |
|
|
//+------------------------------------------------------------------+
|
|
static void CMinComp::MinASASetAlgorithm(CMinASAState &state,int algotype)
|
|
{
|
|
//--- check
|
|
if(!CAp::Assert(algotype>=-1 && algotype<=1,__FUNCTION__+": incorrect AlgoType!"))
|
|
return;
|
|
//--- check
|
|
if(algotype==-1)
|
|
algotype=1;
|
|
//--- change value
|
|
state.m_cgtype=algotype;
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Obsolete optimization algorithm. |
|
|
//| Was replaced by MinBLEIC subpackage. |
|
|
//+------------------------------------------------------------------+
|
|
static void CMinComp::MinASASetStpMax(CMinASAState &state,const double stpmax)
|
|
{
|
|
//--- check
|
|
if(!CAp::Assert(CMath::IsFinite(stpmax),__FUNCTION__+": StpMax is not finite!"))
|
|
return;
|
|
//--- check
|
|
if(!CAp::Assert(stpmax>=0.0,__FUNCTION__+": StpMax<0!"))
|
|
return;
|
|
//--- change value
|
|
state.m_stpmax=stpmax;
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Obsolete optimization algorithm. |
|
|
//| Was replaced by MinBLEIC subpackage. |
|
|
//+------------------------------------------------------------------+
|
|
static void CMinComp::MinASAResults(CMinASAState &state,double &x[],CMinASAReport &rep)
|
|
{
|
|
//--- reset memory
|
|
ArrayResizeAL(x,0);
|
|
//--- function call
|
|
MinASAResultsBuf(state,x,rep);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Obsolete optimization algorithm. |
|
|
//| Was replaced by MinBLEIC subpackage. |
|
|
//+------------------------------------------------------------------+
|
|
static void CMinComp::MinASAResultsBuf(CMinASAState &state,double &x[],
|
|
CMinASAReport &rep)
|
|
{
|
|
//--- create variables
|
|
int i=0;
|
|
int i_=0;
|
|
//--- check
|
|
if(CAp::Len(x)<state.m_n)
|
|
ArrayResizeAL(x,state.m_n);
|
|
//--- copy
|
|
for(i_=0;i_<=state.m_n-1;i_++)
|
|
x[i_]=state.m_x[i_];
|
|
//--- change values
|
|
rep.m_iterationscount=state.m_repiterationscount;
|
|
rep.m_nfev=state.m_repnfev;
|
|
rep.m_terminationtype=state.m_repterminationtype;
|
|
rep.m_activeconstraints=0;
|
|
for(i=0;i<=state.m_n-1;i++)
|
|
{
|
|
//--- check
|
|
if(state.m_ak[i]==0.0)
|
|
rep.m_activeconstraints=rep.m_activeconstraints+1;
|
|
}
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Obsolete optimization algorithm. |
|
|
//| Was replaced by MinBLEIC subpackage. |
|
|
//+------------------------------------------------------------------+
|
|
static void CMinComp::MinASARestartFrom(CMinASAState &state,double &x[],double &bndl[],double &bndu[])
|
|
{
|
|
//--- create a variable
|
|
int i_=0;
|
|
//--- check
|
|
if(!CAp::Assert(CAp::Len(x)>=state.m_n,__FUNCTION__+": Length(X)<N!"))
|
|
return;
|
|
//--- check
|
|
if(!CAp::Assert(CApServ::IsFiniteVector(x,state.m_n),__FUNCTION__+": X contains infinite or NaN values!"))
|
|
return;
|
|
//--- check
|
|
if(!CAp::Assert(CAp::Len(bndl)>=state.m_n,__FUNCTION__+": Length(BndL)<N!"))
|
|
return;
|
|
//--- check
|
|
if(!CAp::Assert(CApServ::IsFiniteVector(bndl,state.m_n),__FUNCTION__+": BndL contains infinite or NaN values!"))
|
|
return;
|
|
//--- check
|
|
if(!CAp::Assert(CAp::Len(bndu)>=state.m_n,__FUNCTION__+": Length(BndU)<N!"))
|
|
return;
|
|
//--- check
|
|
if(!CAp::Assert(CApServ::IsFiniteVector(bndu,state.m_n),__FUNCTION__+": BndU contains infinite or NaN values!"))
|
|
return;
|
|
//--- copy
|
|
for(i_=0;i_<=state.m_n-1;i_++)
|
|
state.m_x[i_]=x[i_];
|
|
for(i_=0;i_<=state.m_n-1;i_++)
|
|
state.m_bndl[i_]=bndl[i_];
|
|
for(i_=0;i_<=state.m_n-1;i_++)
|
|
state.m_bndu[i_]=bndu[i_];
|
|
state.m_laststep=0;
|
|
//--- allocation
|
|
ArrayResizeAL(state.m_rstate.ia,4);
|
|
ArrayResizeAL(state.m_rstate.ba,2);
|
|
ArrayResizeAL(state.m_rstate.ra,3);
|
|
state.m_rstate.stage=-1;
|
|
//--- function call
|
|
ClearRequestFields(state);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Returns norm of bounded anti-gradient. |
|
|
//| Bounded antigradient is a vector obtained from anti-gradient by |
|
|
//| zeroing components which point outwards: |
|
|
//| result = norm(v) |
|
|
//| v[i]=0 if ((-g[i]<0)and(x[i]=bndl[i])) or |
|
|
//| ((-g[i]>0)and(x[i]=bndu[i])) |
|
|
//| v[i]=-g[i] otherwise |
|
|
//| This function may be used to check a stopping criterion. |
|
|
//+------------------------------------------------------------------+
|
|
static double CMinComp::ASABoundedAntigradNorm(CMinASAState &state)
|
|
{
|
|
//--- create variables
|
|
double result=0;
|
|
int i=0;
|
|
double v=0;
|
|
//--- initialization
|
|
result=0;
|
|
for(i=0;i<=state.m_n-1;i++)
|
|
{
|
|
v=-state.m_g[i];
|
|
//--- check
|
|
if(state.m_x[i]==state.m_bndl[i] && -state.m_g[i]<0.0)
|
|
v=0;
|
|
//--- check
|
|
if(state.m_x[i]==state.m_bndu[i] && -state.m_g[i]>0.0)
|
|
v=0;
|
|
result=result+CMath::Sqr(v);
|
|
}
|
|
//--- return result
|
|
return(MathSqrt(result));
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Returns norm of GI(x). |
|
|
//| GI(x) is a gradient vector whose components associated with |
|
|
//| active constraints are zeroed. It differs from bounded |
|
|
//| anti-gradient because components of GI(x) are zeroed |
|
|
//| independently of sign(g[i]), and anti-gradient's components are |
|
|
//| zeroed with respect to both constraint and sign. |
|
|
//+------------------------------------------------------------------+
|
|
static double CMinComp::ASAGINorm(CMinASAState &state)
|
|
{
|
|
//--- create variables
|
|
double result=0;
|
|
int i=0;
|
|
//--- initialization
|
|
result=0;
|
|
for(i=0;i<=state.m_n-1;i++)
|
|
{
|
|
//--- check
|
|
if(state.m_x[i]!=state.m_bndl[i] && state.m_x[i]!=state.m_bndu[i])
|
|
result=result+CMath::Sqr(state.m_g[i]);
|
|
}
|
|
//--- return result
|
|
return(MathSqrt(result));
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Returns norm(D1(State.X)) |
|
|
//| For a meaning of D1 see 'NEW ACTIVE SET ALGORITHM FOR BOX |
|
|
//| CONSTRAINED OPTIMIZATION' by WILLIAM W. HAGER AND HONGCHAO ZHANG.|
|
|
//+------------------------------------------------------------------+
|
|
static double CMinComp::ASAD1Norm(CMinASAState &state)
|
|
{
|
|
//--- create variables
|
|
double result=0;
|
|
int i=0;
|
|
//--- initialization
|
|
result=0;
|
|
for(i=0;i<=state.m_n-1;i++)
|
|
result=result+CMath::Sqr(CApServ::BoundVal(state.m_x[i]-state.m_g[i],state.m_bndl[i],state.m_bndu[i])-state.m_x[i]);
|
|
//--- return result
|
|
return(MathSqrt(result));
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Returns True, if U set is empty. |
|
|
//| * State.X is used as point, |
|
|
//| * State.G - as gradient, |
|
|
//| * D is calculated within function (because State.D may have |
|
|
//| different meaning depending on current optimization algorithm) |
|
|
//| For a meaning of U see 'NEW ACTIVE SET ALGORITHM FOR BOX |
|
|
//| CONSTRAINED OPTIMIZATION' by WILLIAM W. HAGER AND HONGCHAO ZHANG.|
|
|
//+------------------------------------------------------------------+
|
|
static bool CMinComp::ASAUIsEmpty(CMinASAState &state)
|
|
{
|
|
//--- create variables
|
|
int i=0;
|
|
double d=0;
|
|
double d2=0;
|
|
double d32=0;
|
|
//--- initialization
|
|
d=ASAD1Norm(state);
|
|
d2=MathSqrt(d);
|
|
d32=d*d2;
|
|
for(i=0;i<=state.m_n-1;i++)
|
|
{
|
|
//--- check
|
|
if(MathAbs(state.m_g[i])>=d2 && MathMin(state.m_x[i]-state.m_bndl[i],state.m_bndu[i]-state.m_x[i])>=d32)
|
|
return(false);
|
|
}
|
|
//--- return result
|
|
return(true);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Clears request fileds (to be sure that we don't forgot to clear |
|
|
//| something) |
|
|
//+------------------------------------------------------------------+
|
|
static void CMinComp::ClearRequestFields(CMinASAState &state)
|
|
{
|
|
//--- change values
|
|
state.m_needfg=false;
|
|
state.m_xupdated=false;
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| |
|
|
//+------------------------------------------------------------------+
|
|
static bool CMinComp::MinASAIteration(CMinASAState &state)
|
|
{
|
|
//--- create variables
|
|
int n=0;
|
|
int i=0;
|
|
double betak=0;
|
|
double v=0;
|
|
double vv=0;
|
|
int mcinfo=0;
|
|
bool b;
|
|
bool stepfound;
|
|
int diffcnt=0;
|
|
int i_=0;
|
|
//--- This code initializes locals by:
|
|
//--- * random values determined during code
|
|
//--- generation - on first subroutine call
|
|
//--- * values from previous call - on subsequent calls
|
|
if(state.m_rstate.stage>=0)
|
|
{
|
|
//--- initialization
|
|
n=state.m_rstate.ia[0];
|
|
i=state.m_rstate.ia[1];
|
|
mcinfo=state.m_rstate.ia[2];
|
|
diffcnt=state.m_rstate.ia[3];
|
|
b=state.m_rstate.ba[0];
|
|
stepfound=state.m_rstate.ba[1];
|
|
betak=state.m_rstate.ra[0];
|
|
v=state.m_rstate.ra[1];
|
|
vv=state.m_rstate.ra[2];
|
|
}
|
|
else
|
|
{
|
|
//--- initialization
|
|
n=-983;
|
|
i=-989;
|
|
mcinfo=-834;
|
|
diffcnt=900;
|
|
b=true;
|
|
stepfound=false;
|
|
betak=214;
|
|
v=-338;
|
|
vv=-686;
|
|
}
|
|
//--- check
|
|
if(state.m_rstate.stage==0)
|
|
{
|
|
//--- change value
|
|
state.m_needfg=false;
|
|
//--- check
|
|
if(!state.m_xrep)
|
|
return(Func_lbl_15(state,n,i,mcinfo,diffcnt,b,stepfound,betak,v,vv));
|
|
//--- progress report
|
|
ClearRequestFields(state);
|
|
state.m_xupdated=true;
|
|
state.m_rstate.stage=1;
|
|
//--- Saving state
|
|
Func_lbl_rcomm(state,n,i,mcinfo,diffcnt,b,stepfound,betak,v,vv);
|
|
//--- return result
|
|
return(true);
|
|
}
|
|
//--- check
|
|
if(state.m_rstate.stage==1)
|
|
{
|
|
//--- change value
|
|
state.m_xupdated=false;
|
|
//--- function call, return result
|
|
return(Func_lbl_15(state,n,i,mcinfo,diffcnt,b,stepfound,betak,v,vv));
|
|
}
|
|
//--- check
|
|
if(state.m_rstate.stage==2)
|
|
{
|
|
//--- change values
|
|
state.m_needfg=false;
|
|
state.m_repnfev=state.m_repnfev+1;
|
|
stepfound=state.m_f<=state.m_finit+m_gpaftol*state.m_dginit;
|
|
//--- function call, return result
|
|
return(Func_lbl_24(state,n,i,mcinfo,diffcnt,b,stepfound,betak,v,vv));
|
|
}
|
|
//--- check
|
|
if(state.m_rstate.stage==3)
|
|
{
|
|
//--- change values
|
|
state.m_needfg=false;
|
|
state.m_repnfev=state.m_repnfev+1;
|
|
//--- check
|
|
if(state.m_stp<=m_stpmin)
|
|
{
|
|
for(i_=0;i_<=n-1;i_++)
|
|
state.m_xn[i_]=state.m_x[i_];
|
|
//--- function call, return result
|
|
return(Func_lbl_26(state,n,i,mcinfo,diffcnt,b,stepfound,betak,v,vv));
|
|
}
|
|
//--- check
|
|
if(state.m_f<=state.m_finit+state.m_stp*m_gpaftol*state.m_dginit)
|
|
{
|
|
//--- copy
|
|
for(i_=0;i_<=n-1;i_++)
|
|
state.m_xn[i_]=state.m_x[i_];
|
|
//--- function call, return result
|
|
return(Func_lbl_26(state,n,i,mcinfo,diffcnt,b,stepfound,betak,v,vv));
|
|
}
|
|
//--- change value
|
|
state.m_stp=state.m_stp*m_gpadecay;
|
|
//--- function call, return result
|
|
return(Func_lbl_27(state,n,i,mcinfo,diffcnt,b,stepfound,betak,v,vv));
|
|
}
|
|
//--- check
|
|
if(state.m_rstate.stage==4)
|
|
{
|
|
//--- change value
|
|
state.m_xupdated=false;
|
|
//--- function call, return result
|
|
return(Func_lbl_29(state,n,i,mcinfo,diffcnt,b,stepfound,betak,v,vv));
|
|
}
|
|
//--- check
|
|
if(state.m_rstate.stage==5)
|
|
{
|
|
//--- change value
|
|
state.m_xupdated=false;
|
|
//--- return result
|
|
return(false);
|
|
}
|
|
//--- check
|
|
if(state.m_rstate.stage==6)
|
|
{
|
|
//--- change value
|
|
state.m_xupdated=false;
|
|
//--- return result
|
|
return(false);
|
|
}
|
|
//--- check
|
|
if(state.m_rstate.stage==7)
|
|
{
|
|
//--- change value
|
|
state.m_xupdated=false;
|
|
//--- return result
|
|
return(false);
|
|
}
|
|
//--- check
|
|
if(state.m_rstate.stage==8)
|
|
{
|
|
//--- change value
|
|
state.m_xupdated=false;
|
|
//--- return result
|
|
return(false);
|
|
}
|
|
//--- check
|
|
if(state.m_rstate.stage==9)
|
|
{
|
|
//--- change value
|
|
state.m_needfg=false;
|
|
//--- postprocess data: zero components of G corresponding to
|
|
//--- the active constraints
|
|
for(i=0;i<=n-1;i++)
|
|
{
|
|
//--- check
|
|
if(state.m_x[i]==state.m_bndl[i] || state.m_x[i]==state.m_bndu[i])
|
|
state.m_gc[i]=0;
|
|
else
|
|
state.m_gc[i]=state.m_g[i];
|
|
}
|
|
CLinMin::MCSrch(n,state.m_xn,state.m_f,state.m_gc,state.m_d,state.m_stp,state.m_stpmax,m_gtol,mcinfo,state.m_nfev,state.m_work,state.m_lstate,state.m_mcstage);
|
|
//--- function call, return result
|
|
return(Func_lbl_51(state,n,i,mcinfo,diffcnt,b,stepfound,betak,v,vv));
|
|
}
|
|
//--- check
|
|
if(state.m_rstate.stage==10)
|
|
{
|
|
//--- change value
|
|
state.m_xupdated=false;
|
|
//--- function call, return result
|
|
return(Func_lbl_53(state,n,i,mcinfo,diffcnt,b,stepfound,betak,v,vv));
|
|
}
|
|
//--- check
|
|
if(state.m_rstate.stage==11)
|
|
{
|
|
//--- change value
|
|
state.m_xupdated=false;
|
|
//--- return result
|
|
return(false);
|
|
}
|
|
//--- check
|
|
if(state.m_rstate.stage==12)
|
|
{
|
|
//--- change value
|
|
state.m_xupdated=false;
|
|
//--- return result
|
|
return(false);
|
|
}
|
|
//--- check
|
|
if(state.m_rstate.stage==13)
|
|
{
|
|
//--- change value
|
|
state.m_xupdated=false;
|
|
//--- return result
|
|
return(false);
|
|
}
|
|
//--- check
|
|
if(state.m_rstate.stage==14)
|
|
{
|
|
//--- change value
|
|
state.m_xupdated=false;
|
|
//--- return result
|
|
return(false);
|
|
}
|
|
//--- Routine body
|
|
//--- Prepare
|
|
n=state.m_n;
|
|
state.m_repterminationtype=0;
|
|
state.m_repiterationscount=0;
|
|
state.m_repnfev=0;
|
|
state.m_debugrestartscount=0;
|
|
state.m_cgtype=1;
|
|
//--- copy
|
|
for(i_=0;i_<=n-1;i_++)
|
|
state.m_xk[i_]=state.m_x[i_];
|
|
for(i=0;i<=n-1;i++)
|
|
{
|
|
//--- check
|
|
if(state.m_xk[i]==state.m_bndl[i] || state.m_xk[i]==state.m_bndu[i])
|
|
state.m_ak[i]=0;
|
|
else
|
|
state.m_ak[i]=1;
|
|
}
|
|
//--- change values
|
|
state.m_mu=0.1;
|
|
state.m_curalgo=0;
|
|
//--- Calculate F/G,initialize algorithm
|
|
ClearRequestFields(state);
|
|
//--- change values
|
|
state.m_needfg=true;
|
|
state.m_rstate.stage=0;
|
|
//--- Saving state
|
|
Func_lbl_rcomm(state,n,i,mcinfo,diffcnt,b,stepfound,betak,v,vv);
|
|
//--- return result
|
|
return(true);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Auxiliary function for MinASAIteration. Is a product to get rid |
|
|
//| of the operator unconditional jump goto. |
|
|
//+------------------------------------------------------------------+
|
|
static void CMinComp::Func_lbl_rcomm(CMinASAState &state,int n,int i,
|
|
int mcinfo,int diffcnt,bool b,
|
|
bool stepfound,double betak,
|
|
double v,double vv)
|
|
{
|
|
//--- save
|
|
state.m_rstate.ia[0]=n;
|
|
state.m_rstate.ia[1]=i;
|
|
state.m_rstate.ia[2]=mcinfo;
|
|
state.m_rstate.ia[3]=diffcnt;
|
|
state.m_rstate.ba[0]=b;
|
|
state.m_rstate.ba[1]=stepfound;
|
|
state.m_rstate.ra[0]=betak;
|
|
state.m_rstate.ra[1]=v;
|
|
state.m_rstate.ra[2]=vv;
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Auxiliary function for MinASAIteration. Is a product to get rid |
|
|
//| of the operator unconditional jump goto. |
|
|
//+------------------------------------------------------------------+
|
|
static bool CMinComp::Func_lbl_15(CMinASAState &state,int &n,int &i,
|
|
int &mcinfo,int &diffcnt,bool &b,
|
|
bool &stepfound,double &betak,
|
|
double &v,double &vv)
|
|
{
|
|
//--- check
|
|
if(ASABoundedAntigradNorm(state)<=state.m_epsg)
|
|
{
|
|
state.m_repterminationtype=4;
|
|
//--- return result
|
|
return(false);
|
|
}
|
|
state.m_repnfev=state.m_repnfev+1;
|
|
//--- Main cycle
|
|
//--- At the beginning of new iteration:
|
|
//--- * CurAlgo stores current algorithm selector
|
|
//--- * State.XK,State.F and State.G store current X/F/G
|
|
//--- * State.AK stores current set of active constraints
|
|
return(Func_lbl_17(state,n,i,mcinfo,diffcnt,b,stepfound,betak,v,vv));
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Auxiliary function for MinASAIteration. Is a product to get rid |
|
|
//| of the operator unconditional jump goto. |
|
|
//+------------------------------------------------------------------+
|
|
static bool CMinComp::Func_lbl_17(CMinASAState &state,int &n,int &i,
|
|
int &mcinfo,int &diffcnt,bool &b,
|
|
bool &stepfound,double &betak,
|
|
double &v,double &vv)
|
|
{
|
|
//--- GPA algorithm
|
|
if(state.m_curalgo!=0)
|
|
return(Func_lbl_19(state,n,i,mcinfo,diffcnt,b,stepfound,betak,v,vv));
|
|
//--- change values
|
|
state.m_k=0;
|
|
state.m_acount=0;
|
|
//--- function call, return result
|
|
return(Func_lbl_21(state,n,i,mcinfo,diffcnt,b,stepfound,betak,v,vv));
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Auxiliary function for MinASAIteration. Is a product to get rid |
|
|
//| of the operator unconditional jump goto. |
|
|
//+------------------------------------------------------------------+
|
|
static bool CMinComp::Func_lbl_19(CMinASAState &state,int &n,int &i,
|
|
int &mcinfo,int &diffcnt,bool &b,
|
|
bool &stepfound,double &betak,
|
|
double &v,double &vv)
|
|
{
|
|
//--- CG algorithm
|
|
if(state.m_curalgo!=1)
|
|
return(Func_lbl_17(state,n,i,mcinfo,diffcnt,b,stepfound,betak,v,vv));
|
|
//--- first,check that there are non-active constraints.
|
|
//--- move to GPA algorithm,if all constraints are active
|
|
b=true;
|
|
for(i=0;i<=n-1;i++)
|
|
{
|
|
//--- check
|
|
if(state.m_ak[i]!=0.0)
|
|
{
|
|
b=false;
|
|
break;
|
|
}
|
|
}
|
|
//--- check
|
|
if(b)
|
|
{
|
|
state.m_curalgo=0;
|
|
//--- function call, return result
|
|
return(Func_lbl_17(state,n,i,mcinfo,diffcnt,b,stepfound,betak,v,vv));
|
|
}
|
|
//--- CG iterations
|
|
state.m_fold=state.m_f;
|
|
for(int i_=0;i_<=n-1;i_++)
|
|
state.m_xk[i_]=state.m_x[i_];
|
|
for(i=0;i<=n-1;i++)
|
|
{
|
|
//--- change values
|
|
state.m_dk[i]=-(state.m_g[i]*state.m_ak[i]);
|
|
state.m_gc[i]=state.m_g[i]*state.m_ak[i];
|
|
}
|
|
//--- function call, return result
|
|
return(Func_lbl_49(state,n,i,mcinfo,diffcnt,b,stepfound,betak,v,vv));
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Auxiliary function for MinASAIteration. Is a product to get rid |
|
|
//| of the operator unconditional jump goto. |
|
|
//+------------------------------------------------------------------+
|
|
static bool CMinComp::Func_lbl_21(CMinASAState &state,int &n,int &i,
|
|
int &mcinfo,int &diffcnt,bool &b,
|
|
bool &stepfound,double &betak,
|
|
double &v,double &vv)
|
|
{
|
|
//--- Determine Dk=proj(xk - gk)-xk
|
|
for(i=0;i<=n-1;i++)
|
|
state.m_d[i]=CApServ::BoundVal(state.m_xk[i]-state.m_g[i],state.m_bndl[i],state.m_bndu[i])-state.m_xk[i];
|
|
//--- Armijo line search.
|
|
//--- * exact search with alpha=1 is tried first,
|
|
//--- 'exact' means that we evaluate f() EXACTLY at
|
|
//--- bound(x-g,bndl,bndu),without intermediate floating
|
|
//--- point operations.
|
|
//--- * alpha<1 are tried if explicit search wasn't successful
|
|
//--- Result is placed into XN.
|
|
//--- Two types of search are needed because we can't
|
|
//--- just use second type with alpha=1 because in finite
|
|
//--- precision arithmetics (x1-x0)+x0 may differ from x1.
|
|
//--- So while x1 is correctly bounded (it lie EXACTLY on
|
|
//--- boundary,if it is active),(x1-x0)+x0 may be
|
|
//--- not bounded.
|
|
v=0.0;
|
|
for(int i_=0;i_<=n-1;i_++)
|
|
v+=state.m_d[i_]*state.m_g[i_];
|
|
//--- change values
|
|
state.m_dginit=v;
|
|
state.m_finit=state.m_f;
|
|
//--- check
|
|
if(!(ASAD1Norm(state)<=state.m_stpmax || state.m_stpmax==0.0))
|
|
{
|
|
stepfound=false;
|
|
//--- function call, return result
|
|
return(Func_lbl_24(state,n,i,mcinfo,diffcnt,b,stepfound,betak,v,vv));
|
|
}
|
|
//--- Try alpha=1 step first
|
|
for(i=0;i<=n-1;i++)
|
|
state.m_x[i]=CApServ::BoundVal(state.m_xk[i]-state.m_g[i],state.m_bndl[i],state.m_bndu[i]);
|
|
//--- function call
|
|
ClearRequestFields(state);
|
|
//--- change values
|
|
state.m_needfg=true;
|
|
state.m_rstate.stage=2;
|
|
//--- Saving state
|
|
Func_lbl_rcomm(state,n,i,mcinfo,diffcnt,b,stepfound,betak,v,vv);
|
|
//--- return result
|
|
return(true);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Auxiliary function for MinASAIteration. Is a product to get rid |
|
|
//| of the operator unconditional jump goto. |
|
|
//+------------------------------------------------------------------+
|
|
static bool CMinComp::Func_lbl_24(CMinASAState &state,int &n,int &i,
|
|
int &mcinfo,int &diffcnt,bool &b,
|
|
bool &stepfound,double &betak,
|
|
double &v,double &vv)
|
|
{
|
|
//--- check
|
|
if(!stepfound)
|
|
{
|
|
//--- alpha=1 is too large,try smaller values
|
|
state.m_stp=1;
|
|
//--- function call
|
|
CLinMin::LinMinNormalized(state.m_d,state.m_stp,n);
|
|
//--- change values
|
|
state.m_dginit=state.m_dginit/state.m_stp;
|
|
state.m_stp=m_gpadecay*state.m_stp;
|
|
//--- check
|
|
if(state.m_stpmax>0.0)
|
|
state.m_stp=MathMin(state.m_stp,state.m_stpmax);
|
|
//--- function call, return result
|
|
return(Func_lbl_27(state,n,i,mcinfo,diffcnt,b,stepfound,betak,v,vv));
|
|
}
|
|
//--- we are at the boundary(ies)
|
|
for(int i_=0;i_<=n-1;i_++)
|
|
state.m_xn[i_]=state.m_x[i_];
|
|
state.m_stp=1;
|
|
//--- function call, return result
|
|
return(Func_lbl_26(state,n,i,mcinfo,diffcnt,b,stepfound,betak,v,vv));
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Auxiliary function for MinASAIteration. Is a product to get rid |
|
|
//| of the operator unconditional jump goto. |
|
|
//+------------------------------------------------------------------+
|
|
static bool CMinComp::Func_lbl_26(CMinASAState &state,int &n,int &i,
|
|
int &mcinfo,int &diffcnt,bool &b,
|
|
bool &stepfound,double &betak,
|
|
double &v,double &vv)
|
|
{
|
|
state.m_repiterationscount=state.m_repiterationscount+1;
|
|
//--- check
|
|
if(!state.m_xrep)
|
|
return(Func_lbl_29(state,n,i,mcinfo,diffcnt,b,stepfound,betak,v,vv));
|
|
//--- progress report
|
|
ClearRequestFields(state);
|
|
//--- change values
|
|
state.m_xupdated=true;
|
|
state.m_rstate.stage=4;
|
|
//--- Saving state
|
|
Func_lbl_rcomm(state,n,i,mcinfo,diffcnt,b,stepfound,betak,v,vv);
|
|
//--- return result
|
|
return(true);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Auxiliary function for MinASAIteration. Is a product to get rid |
|
|
//| of the operator unconditional jump goto. |
|
|
//+------------------------------------------------------------------+
|
|
static bool CMinComp::Func_lbl_27(CMinASAState &state,int &n,int &i,
|
|
int &mcinfo,int &diffcnt,bool &b,
|
|
bool &stepfound,double &betak,
|
|
double &v,double &vv)
|
|
{
|
|
v=state.m_stp;
|
|
//--- copy
|
|
for(int i_=0;i_<=n-1;i_++)
|
|
state.m_x[i_]=state.m_xk[i_];
|
|
for(int i_=0;i_<=n-1;i_++)
|
|
state.m_x[i_]=state.m_x[i_]+v*state.m_d[i_];
|
|
ClearRequestFields(state);
|
|
//--- change values
|
|
state.m_needfg=true;
|
|
state.m_rstate.stage=3;
|
|
//--- Saving state
|
|
Func_lbl_rcomm(state,n,i,mcinfo,diffcnt,b,stepfound,betak,v,vv);
|
|
//--- return result
|
|
return(true);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Auxiliary function for MinASAIteration. Is a product to get rid |
|
|
//| of the operator unconditional jump goto. |
|
|
//+------------------------------------------------------------------+
|
|
static bool CMinComp::Func_lbl_29(CMinASAState &state,int &n,int &i,
|
|
int &mcinfo,int &diffcnt,bool &b,
|
|
bool &stepfound,double &betak,
|
|
double &v,double &vv)
|
|
{
|
|
//--- Calculate new set of active constraints.
|
|
//--- Reset counter if active set was changed.
|
|
//--- Prepare for the new iteration
|
|
for(i=0;i<=n-1;i++)
|
|
{
|
|
//--- check
|
|
if(state.m_xn[i]==state.m_bndl[i] || state.m_xn[i]==state.m_bndu[i])
|
|
state.m_an[i]=0;
|
|
else
|
|
state.m_an[i]=1;
|
|
}
|
|
for(i=0;i<=n-1;i++)
|
|
{
|
|
//--- check
|
|
if(state.m_ak[i]!=state.m_an[i])
|
|
{
|
|
state.m_acount=-1;
|
|
break;
|
|
}
|
|
}
|
|
state.m_acount=state.m_acount+1;
|
|
//--- copy
|
|
for(int i_=0;i_<=n-1;i_++)
|
|
state.m_xk[i_]=state.m_xn[i_];
|
|
for(int i_=0;i_<=n-1;i_++)
|
|
state.m_ak[i_]=state.m_an[i_];
|
|
//--- Stopping conditions
|
|
if(!(state.m_repiterationscount>=state.m_maxits&&state.m_maxits>0))
|
|
return(Func_lbl_31(state,n,i,mcinfo,diffcnt,b,stepfound,betak,v,vv));
|
|
//--- Too many iterations
|
|
state.m_repterminationtype=5;
|
|
//--- check
|
|
if(!state.m_xrep)
|
|
return(false);
|
|
//--- function call
|
|
ClearRequestFields(state);
|
|
//--- change values
|
|
state.m_xupdated=true;
|
|
state.m_rstate.stage=5;
|
|
//--- Saving state
|
|
Func_lbl_rcomm(state,n,i,mcinfo,diffcnt,b,stepfound,betak,v,vv);
|
|
//--- return result
|
|
return(true);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Auxiliary function for MinASAIteration. Is a product to get rid |
|
|
//| of the operator unconditional jump goto. |
|
|
//+------------------------------------------------------------------+
|
|
static bool CMinComp::Func_lbl_31(CMinASAState &state,int &n,int &i,
|
|
int &mcinfo,int &diffcnt,bool &b,
|
|
bool &stepfound,double &betak,
|
|
double &v,double &vv)
|
|
{
|
|
//--- check
|
|
if(ASABoundedAntigradNorm(state)>state.m_epsg)
|
|
return(Func_lbl_35(state,n,i,mcinfo,diffcnt,b,stepfound,betak,v,vv));
|
|
//--- Gradient is small enough
|
|
state.m_repterminationtype=4;
|
|
//--- check
|
|
if(!state.m_xrep)
|
|
return(false);
|
|
//--- function call
|
|
ClearRequestFields(state);
|
|
//--- change values
|
|
state.m_xupdated=true;
|
|
state.m_rstate.stage=6;
|
|
//--- Saving state
|
|
Func_lbl_rcomm(state,n,i,mcinfo,diffcnt,b,stepfound,betak,v,vv);
|
|
//--- return result
|
|
return(true);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Auxiliary function for MinASAIteration. Is a product to get rid |
|
|
//| of the operator unconditional jump goto. |
|
|
//+------------------------------------------------------------------+
|
|
static bool CMinComp::Func_lbl_35(CMinASAState &state,int &n,int &i,
|
|
int &mcinfo,int &diffcnt,bool &b,
|
|
bool &stepfound,double &betak,
|
|
double &v,double &vv)
|
|
{
|
|
//--- change value
|
|
v=0.0;
|
|
for(int i_=0;i_<=n-1;i_++)
|
|
v+=state.m_d[i_]*state.m_d[i_];
|
|
//--- check
|
|
if(MathSqrt(v)*state.m_stp>state.m_epsx)
|
|
return(Func_lbl_39(state,n,i,mcinfo,diffcnt,b,stepfound,betak,v,vv));
|
|
//--- Step size is too small,no further improvement is
|
|
//--- possible
|
|
state.m_repterminationtype=2;
|
|
//--- check
|
|
if(!state.m_xrep)
|
|
return(false);
|
|
//--- function call
|
|
ClearRequestFields(state);
|
|
//--- change values
|
|
state.m_xupdated=true;
|
|
state.m_rstate.stage=7;
|
|
//--- Saving state
|
|
Func_lbl_rcomm(state,n,i,mcinfo,diffcnt,b,stepfound,betak,v,vv);
|
|
//--- return result
|
|
return(true);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Auxiliary function for MinASAIteration. Is a product to get rid |
|
|
//| of the operator unconditional jump goto. |
|
|
//+------------------------------------------------------------------+
|
|
static bool CMinComp::Func_lbl_39(CMinASAState &state,int &n,int &i,
|
|
int &mcinfo,int &diffcnt,bool &b,
|
|
bool &stepfound,double &betak,
|
|
double &v,double &vv)
|
|
{
|
|
//--- check
|
|
if(state.m_finit-state.m_f>state.m_epsf*MathMax(MathAbs(state.m_finit),MathMax(MathAbs(state.m_f),1.0)))
|
|
return(Func_lbl_43(state,n,i,mcinfo,diffcnt,b,stepfound,betak,v,vv));
|
|
//--- F(k+1)-F(k) is small enough
|
|
state.m_repterminationtype=1;
|
|
//--- check
|
|
if(!state.m_xrep)
|
|
return(false);
|
|
ClearRequestFields(state);
|
|
//--- change values
|
|
state.m_xupdated=true;
|
|
state.m_rstate.stage=8;
|
|
//--- Saving state
|
|
Func_lbl_rcomm(state,n,i,mcinfo,diffcnt,b,stepfound,betak,v,vv);
|
|
//--- return result
|
|
return(true);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Auxiliary function for MinASAIteration. Is a product to get rid |
|
|
//| of the operator unconditional jump goto. |
|
|
//+------------------------------------------------------------------+
|
|
static bool CMinComp::Func_lbl_43(CMinASAState &state,int &n,int &i,
|
|
int &mcinfo,int &diffcnt,bool &b,
|
|
bool &stepfound,double &betak,
|
|
double &v,double &vv)
|
|
{
|
|
//--- Decide - should we switch algorithm or not
|
|
if(ASAUIsEmpty(state))
|
|
{
|
|
//--- check
|
|
if(ASAGINorm(state)>=state.m_mu*ASAD1Norm(state))
|
|
{
|
|
state.m_curalgo=1;
|
|
//--- function call, return result
|
|
return(Func_lbl_19(state,n,i,mcinfo,diffcnt,b,stepfound,betak,v,vv));
|
|
}
|
|
else
|
|
state.m_mu=state.m_mu*m_asarho;
|
|
}
|
|
else
|
|
{
|
|
//--- check
|
|
if(state.m_acount==m_n1)
|
|
{
|
|
//--- check
|
|
if(ASAGINorm(state)>=state.m_mu*ASAD1Norm(state))
|
|
{
|
|
state.m_curalgo=1;
|
|
//--- function call, return result
|
|
return(Func_lbl_19(state,n,i,mcinfo,diffcnt,b,stepfound,betak,v,vv));
|
|
}
|
|
}
|
|
}
|
|
//--- Next iteration
|
|
state.m_k=state.m_k+1;
|
|
//--- function call, return result
|
|
return(Func_lbl_21(state,n,i,mcinfo,diffcnt,b,stepfound,betak,v,vv));
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Auxiliary function for MinASAIteration. Is a product to get rid |
|
|
//| of the operator unconditional jump goto. |
|
|
//+------------------------------------------------------------------+
|
|
static bool CMinComp::Func_lbl_49(CMinASAState &state,int &n,int &i,
|
|
int &mcinfo,int &diffcnt,bool &b,
|
|
bool &stepfound,double &betak,
|
|
double &v,double &vv)
|
|
{
|
|
//--- Store G[k] for later calculation of Y[k]
|
|
for(i=0;i<=n-1;i++)
|
|
state.m_yk[i]=-state.m_gc[i];
|
|
//--- Make a CG step in direction given by DK[]:
|
|
//--- * calculate step. Step projection into feasible set
|
|
//--- is used. It has several benefits: a) step may be
|
|
//--- found with usual line search,b) multiple constraints
|
|
//--- may be activated with one step,c) activated constraints
|
|
//--- are detected in a natural way - just compare x[i] with
|
|
//--- bounds
|
|
//--- * update active set,set B to True,if there
|
|
//--- were changes in the set.
|
|
for(int i_=0;i_<=n-1;i_++)
|
|
state.m_d[i_]=state.m_dk[i_];
|
|
for(int i_=0;i_<=n-1;i_++)
|
|
state.m_xn[i_]=state.m_xk[i_];
|
|
//--- change values
|
|
state.m_mcstage=0;
|
|
state.m_stp=1;
|
|
//--- function call
|
|
CLinMin::LinMinNormalized(state.m_d,state.m_stp,n);
|
|
//--- check
|
|
if(state.m_laststep!=0.0)
|
|
state.m_stp=state.m_laststep;
|
|
//--- function call
|
|
CLinMin::MCSrch(n,state.m_xn,state.m_f,state.m_gc,state.m_d,state.m_stp,state.m_stpmax,m_gtol,mcinfo,state.m_nfev,state.m_work,state.m_lstate,state.m_mcstage);
|
|
//--- function call, return result
|
|
return(Func_lbl_51(state,n,i,mcinfo,diffcnt,b,stepfound,betak,v,vv));
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Auxiliary function for MinASAIteration. Is a product to get rid |
|
|
//| of the operator unconditional jump goto. |
|
|
//+------------------------------------------------------------------+
|
|
static bool CMinComp::Func_lbl_51(CMinASAState &state,int &n,int &i,
|
|
int &mcinfo,int &diffcnt,bool &b,
|
|
bool &stepfound,double &betak,
|
|
double &v,double &vv)
|
|
{
|
|
//--- check
|
|
if(state.m_mcstage==0)
|
|
return(Func_lbl_52(state,n,i,mcinfo,diffcnt,b,stepfound,betak,v,vv));
|
|
//--- preprocess data: bound State.XN so it belongs to the
|
|
//--- feasible set and store it in the State.X
|
|
for(i=0;i<=n-1;i++)
|
|
state.m_x[i]=CApServ::BoundVal(state.m_xn[i],state.m_bndl[i],state.m_bndu[i]);
|
|
//--- RComm
|
|
ClearRequestFields(state);
|
|
//--- change values
|
|
state.m_needfg=true;
|
|
state.m_rstate.stage=9;
|
|
//--- Saving state
|
|
Func_lbl_rcomm(state,n,i,mcinfo,diffcnt,b,stepfound,betak,v,vv);
|
|
//--- return result
|
|
return(true);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Auxiliary function for MinASAIteration. Is a product to get rid |
|
|
//| of the operator unconditional jump goto. |
|
|
//+------------------------------------------------------------------+
|
|
static bool CMinComp::Func_lbl_52(CMinASAState &state,int &n,int &i,
|
|
int &mcinfo,int &diffcnt,bool &b,
|
|
bool &stepfound,double &betak,
|
|
double &v,double &vv)
|
|
{
|
|
diffcnt=0;
|
|
for(i=0;i<=n-1;i++)
|
|
{
|
|
//--- XN contains unprojected result,project it,
|
|
//--- save copy to X (will be used for progress reporting)
|
|
state.m_xn[i]=CApServ::BoundVal(state.m_xn[i],state.m_bndl[i],state.m_bndu[i]);
|
|
//--- update active set
|
|
if(state.m_xn[i]==state.m_bndl[i] || state.m_xn[i]==state.m_bndu[i])
|
|
state.m_an[i]=0;
|
|
else
|
|
state.m_an[i]=1;
|
|
//--- check
|
|
if(state.m_an[i]!=state.m_ak[i])
|
|
diffcnt=diffcnt+1;
|
|
state.m_ak[i]=state.m_an[i];
|
|
}
|
|
for(int i_=0;i_<=n-1;i_++)
|
|
state.m_xk[i_]=state.m_xn[i_];
|
|
//--- change values
|
|
state.m_repnfev=state.m_repnfev+state.m_nfev;
|
|
state.m_repiterationscount=state.m_repiterationscount+1;
|
|
//--- check
|
|
if(!state.m_xrep)
|
|
return(Func_lbl_53(state,n,i,mcinfo,diffcnt,b,stepfound,betak,v,vv));
|
|
//--- progress report
|
|
ClearRequestFields(state);
|
|
//--- change values
|
|
state.m_xupdated=true;
|
|
state.m_rstate.stage=10;
|
|
//--- Saving state
|
|
Func_lbl_rcomm(state,n,i,mcinfo,diffcnt,b,stepfound,betak,v,vv);
|
|
//--- return result
|
|
return(true);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Auxiliary function for MinASAIteration. Is a product to get rid |
|
|
//| of the operator unconditional jump goto. |
|
|
//+------------------------------------------------------------------+
|
|
static bool CMinComp::Func_lbl_53(CMinASAState &state,int &n,int &i,
|
|
int &mcinfo,int &diffcnt,bool &b,
|
|
bool &stepfound,double &betak,
|
|
double &v,double &vv)
|
|
{
|
|
//--- Update info about step length
|
|
v=0.0;
|
|
for(int i_=0;i_<=n-1;i_++)
|
|
v+=state.m_d[i_]*state.m_d[i_];
|
|
state.m_laststep=MathSqrt(v)*state.m_stp;
|
|
//--- Check stopping conditions.
|
|
if(ASABoundedAntigradNorm(state)>state.m_epsg)
|
|
return(Func_lbl_55(state,n,i,mcinfo,diffcnt,b,stepfound,betak,v,vv));
|
|
//--- Gradient is small enough
|
|
state.m_repterminationtype=4;
|
|
//--- check
|
|
if(!state.m_xrep)
|
|
return(false);
|
|
ClearRequestFields(state);
|
|
//--- change values
|
|
state.m_xupdated=true;
|
|
state.m_rstate.stage=11;
|
|
//--- Saving state
|
|
Func_lbl_rcomm(state,n,i,mcinfo,diffcnt,b,stepfound,betak,v,vv);
|
|
//--- return result
|
|
return(true);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Auxiliary function for MinASAIteration. Is a product to get rid |
|
|
//| of the operator unconditional jump goto. |
|
|
//+------------------------------------------------------------------+
|
|
static bool CMinComp::Func_lbl_55(CMinASAState &state,int &n,int &i,
|
|
int &mcinfo,int &diffcnt,bool &b,
|
|
bool &stepfound,double &betak,
|
|
double &v,double &vv)
|
|
{
|
|
//--- check
|
|
if(!(state.m_repiterationscount>=state.m_maxits && state.m_maxits>0))
|
|
return(Func_lbl_59(state,n,i,mcinfo,diffcnt,b,stepfound,betak,v,vv));
|
|
//--- Too many iterations
|
|
state.m_repterminationtype=5;
|
|
//--- check
|
|
if(!state.m_xrep)
|
|
return(false);
|
|
ClearRequestFields(state);
|
|
//--- change values
|
|
state.m_xupdated=true;
|
|
state.m_rstate.stage=12;
|
|
//--- Saving state
|
|
Func_lbl_rcomm(state,n,i,mcinfo,diffcnt,b,stepfound,betak,v,vv);
|
|
//--- return result
|
|
return(true);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Auxiliary function for MinASAIteration. Is a product to get rid |
|
|
//| of the operator unconditional jump goto. |
|
|
//+------------------------------------------------------------------+
|
|
static bool CMinComp::Func_lbl_59(CMinASAState &state,int &n,int &i,
|
|
int &mcinfo,int &diffcnt,bool &b,
|
|
bool &stepfound,double &betak,
|
|
double &v,double &vv)
|
|
{
|
|
//--- check
|
|
if(!(ASAGINorm(state)>=state.m_mu*ASAD1Norm(state) && diffcnt==0))
|
|
return(Func_lbl_63(state,n,i,mcinfo,diffcnt,b,stepfound,betak,v,vv));
|
|
//--- These conditions (EpsF/EpsX) are explicitly or implicitly
|
|
//--- related to the current step size and influenced
|
|
//--- by changes in the active constraints.
|
|
//--- For these reasons they are checked only when we don't
|
|
//--- want to 'unstick' at the end of the iteration and there
|
|
//--- were no changes in the active set.
|
|
//--- NOTE: consition |G|>=Mu*|D1| must be exactly opposite
|
|
//--- to the condition used to switch back to GPA. At least
|
|
//--- one inequality must be strict,otherwise infinite cycle
|
|
//--- may occur when |G|=Mu*|D1| (we DON'T test stopping
|
|
//--- conditions and we DON'T switch to GPA,so we cycle
|
|
//--- indefinitely).
|
|
if(state.m_fold-state.m_f>state.m_epsf*MathMax(MathAbs(state.m_fold),MathMax(MathAbs(state.m_f),1.0)))
|
|
return(Func_lbl_65(state,n,i,mcinfo,diffcnt,b,stepfound,betak,v,vv));
|
|
//--- F(k+1)-F(k) is small enough
|
|
state.m_repterminationtype=1;
|
|
if(!state.m_xrep)
|
|
return(false);
|
|
//--- function call
|
|
ClearRequestFields(state);
|
|
//--- change values
|
|
state.m_xupdated=true;
|
|
state.m_rstate.stage=13;
|
|
//--- Saving state
|
|
Func_lbl_rcomm(state,n,i,mcinfo,diffcnt,b,stepfound,betak,v,vv);
|
|
//--- return result
|
|
return(true);
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Auxiliary function for MinASAIteration. Is a product to get rid |
|
|
//| of the operator unconditional jump goto. |
|
|
//+------------------------------------------------------------------+
|
|
static bool CMinComp::Func_lbl_63(CMinASAState &state,int &n,int &i,
|
|
int &mcinfo,int &diffcnt,bool &b,
|
|
bool &stepfound,double &betak,
|
|
double &v,double &vv)
|
|
{
|
|
//--- Check conditions for switching
|
|
if(ASAGINorm(state)<state.m_mu*ASAD1Norm(state))
|
|
{
|
|
state.m_curalgo=0;
|
|
//--- function call, return result
|
|
return(Func_lbl_17(state,n,i,mcinfo,diffcnt,b,stepfound,betak,v,vv));
|
|
}
|
|
//--- check
|
|
if(diffcnt>0)
|
|
{
|
|
//--- check
|
|
if(ASAUIsEmpty(state) || diffcnt>=m_n2)
|
|
state.m_curalgo=1;
|
|
else
|
|
state.m_curalgo=0;
|
|
//--- function call, return result
|
|
return(Func_lbl_17(state,n,i,mcinfo,diffcnt,b,stepfound,betak,v,vv));
|
|
}
|
|
//--- Calculate D(k+1)
|
|
//--- Line search may result in:
|
|
//--- * maximum feasible step being taken (already processed)
|
|
//--- * point satisfying Wolfe conditions
|
|
//--- * some kind of error (CG is restarted by assigning 0.0 to Beta)
|
|
if(mcinfo==1)
|
|
{
|
|
//--- Standard Wolfe conditions are satisfied:
|
|
//--- * calculate Y[K] and BetaK
|
|
for(int i_=0;i_<=n-1;i_++)
|
|
state.m_yk[i_]=state.m_yk[i_]+state.m_gc[i_];
|
|
//--- change value
|
|
vv=0.0;
|
|
for(int i_=0;i_<=n-1;i_++)
|
|
vv+=state.m_yk[i_]*state.m_dk[i_];
|
|
//--- change value
|
|
v=0.0;
|
|
for(int i_=0;i_<=n-1;i_++)
|
|
v+=state.m_gc[i_]*state.m_gc[i_];
|
|
state.m_betady=v/vv;
|
|
//--- change value
|
|
v=0.0;
|
|
for(int i_=0;i_<=n-1;i_++)
|
|
v+=state.m_gc[i_]*state.m_yk[i_];
|
|
state.m_betahs=v/vv;
|
|
//--- check
|
|
if(state.m_cgtype==0)
|
|
betak=state.m_betady;
|
|
//--- check
|
|
if(state.m_cgtype==1)
|
|
betak=MathMax(0,MathMin(state.m_betady,state.m_betahs));
|
|
}
|
|
else
|
|
{
|
|
//--- Something is wrong (may be function is too wild or too flat).
|
|
//--- We'll set BetaK=0,which will restart CG algorithm.
|
|
//--- We can stop later (during normal checks) if stopping conditions are met.
|
|
betak=0;
|
|
state.m_debugrestartscount=state.m_debugrestartscount+1;
|
|
}
|
|
//--- change values
|
|
for(int i_=0;i_<=n-1;i_++)
|
|
state.m_dn[i_]=-state.m_gc[i_];
|
|
for(int i_=0;i_<=n-1;i_++)
|
|
state.m_dn[i_]=state.m_dn[i_]+betak*state.m_dk[i_];
|
|
for(int i_=0;i_<=n-1;i_++)
|
|
state.m_dk[i_]=state.m_dn[i_];
|
|
//--- update other information
|
|
state.m_fold=state.m_f;
|
|
state.m_k=state.m_k+1;
|
|
//--- function call, return result
|
|
return(Func_lbl_49(state,n,i,mcinfo,diffcnt,b,stepfound,betak,v,vv));
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
//| Auxiliary function for MinASAIteration. Is a product to get rid |
|
|
//| of the operator unconditional jump goto. |
|
|
//+------------------------------------------------------------------+
|
|
static bool CMinComp::Func_lbl_65(CMinASAState &state,int &n,int &i,
|
|
int &mcinfo,int &diffcnt,bool &b,
|
|
bool &stepfound,double &betak,
|
|
double &v,double &vv)
|
|
{
|
|
//--- check
|
|
if(state.m_laststep>state.m_epsx)
|
|
return(Func_lbl_63(state,n,i,mcinfo,diffcnt,b,stepfound,betak,v,vv));
|
|
//--- X(k+1)-X(k) is small enough
|
|
state.m_repterminationtype=2;
|
|
//--- check
|
|
if(!state.m_xrep)
|
|
return(false);
|
|
//--- function call
|
|
ClearRequestFields(state);
|
|
//--- change values
|
|
state.m_xupdated=true;
|
|
state.m_rstate.stage=14;
|
|
//--- Saving state
|
|
Func_lbl_rcomm(state,n,i,mcinfo,diffcnt,b,stepfound,betak,v,vv);
|
|
//--- return result
|
|
return(true);
|
|
}
|
|
//+------------------------------------------------------------------+
|