//+------------------------------------------------------------------+ //| num_diff.mqh | //| Copyright 2025, MetaQuotes Ltd. | //| https://www.mql5.com | //+------------------------------------------------------------------+ #property copyright "Copyright 2025, MetaQuotes Ltd." #property link "https://www.mql5.com" //+------------------------------------------------------------------+ //| directional scheme options | //+------------------------------------------------------------------+ enum ENUM_SCHEME_DIRECTION { SCHEME_1=0,//1 sided SCHEME_2//2 sided }; //+------------------------------------------------------------------+ //| num points of evaluation | //+------------------------------------------------------------------+ enum ENUM_DIFF_POINTS { GRAD_POINT_2=0,//2-point GRAD_POINT_3,//3-point GRAD_POINT_CS,//complex GRAD_POINT_CALLABLE//callable }; //+------------------------------------------------------------------+ //| num points of evaluation | //+------------------------------------------------------------------+ enum ENUM_HESS_DIFF_POINTS { HESS_POINT_2=0,//2-point HESS_POINT_3,//3-point HESS_POINT_CS,//complex HESS_POINT_HESS_STRATEGY,//hessian update strategy HESS_POINT_CALLABLE//callable }; //+------------------------------------------------------------------+ //|struct objective function return | //+------------------------------------------------------------------+ struct ObjReturn { double f; vector g; vector mf; matrix mg; ObjReturn(void) { f = double(0); g = vector::Zeros(0); mf = vector::Zeros(0); mg = matrix::Zeros(0,0); } ObjReturn(ObjReturn& other) { f = other.f; g = other.g; mf = other.mf; mg = other.mg; } void operator=(ObjReturn& other) { f = other.f; g = other.g; mf = other.mf; mg = other.mg; } }; //+------------------------------------------------------------------+ //|IObjective provides the base interface for any function | //|that will be provided to a minimizer routine | //+------------------------------------------------------------------+ interface IObjective { //---the objective function vector objective_function(vector& x); ObjReturn fun_and_grad(vector& x); }; //+------------------------------------------------------------------+ //|differentiation options | //+------------------------------------------------------------------+ struct GradDiffOptions { ENUM_DIFF_POINTS method; vector rel_step; vector abs_step; matrix bounds; GradDiffOptions(void) { method = WRONG_VALUE; rel_step = abs_step = vector::Zeros(0); bounds = matrix::Zeros(0,0); } GradDiffOptions(ENUM_DIFF_POINTS m, vector& relstep, vector& absstep, matrix& bnds) { method = m; rel_step = relstep; abs_step = absstep; bounds = bnds; } GradDiffOptions(GradDiffOptions& other) { method = other.method; rel_step = other.rel_step; abs_step = other.abs_step; bounds = other.bounds; } void operator=(GradDiffOptions& other) { method = other.method; rel_step = other.rel_step; abs_step = other.abs_step; bounds = other.bounds; } }; //+------------------------------------------------------------------+ //|differentiation options | //+------------------------------------------------------------------+ struct HessDiffOptions { bool as_linear_operator; ENUM_HESS_DIFF_POINTS method; vector rel_step; vector abs_step; HessDiffOptions(void) { method = WRONG_VALUE; as_linear_operator = false; rel_step = abs_step = vector::Zeros(0); } HessDiffOptions(ENUM_HESS_DIFF_POINTS m, vector& relstep, vector& absstep, bool aslinearoperator) { method = m; rel_step = relstep; abs_step = absstep; as_linear_operator = aslinearoperator; } HessDiffOptions(HessDiffOptions& other) { method = other.method; rel_step = other.rel_step; abs_step = other.abs_step; as_linear_operator = other.as_linear_operator; } void operator=(HessDiffOptions& other) { method = other.method; rel_step = other.rel_step; abs_step = other.abs_step; as_linear_operator = other.as_linear_operator; } }; //+---------------------------------------------------------------------------+ //|function objective representing the objective function and its derivatives.| //+---------------------------------------------------------------------------+ class CFunctor:public IObjective { protected: vector m_xp; vector m_x; ulong m_n; matrix m_H; int m_nfev,m_ngev,m_nhev; bool m_fupdated,m_gupdated,m_hupdated; double m_lowest_f,m_f; vector m_lowest_x,m_g; bool m_clip; GradDiffOptions m_grad_options; HessDiffOptions m_hess_options; void update_fun(void) { if(!m_fupdated) { double fx = wrapped_fun(m_x); if(fxbnds[i,1]) x[i] = bnds[i,1]; } } vector copy = x; return orig_fun(copy); } vector objective_function(vector& x) { vector r(1); r[0] = wrapped_fun(x); return r; } vector wrapped_grad(vector& x,vector& f0) { m_ngev += 1; if(m_clip) { matrix bnds = m_grad_options.bounds; for(ulong i = 0; ibnds[i,1]) x[i] = bnds[i,1]; } } vector copy = x; if(m_grad_options.method == GRAD_POINT_CALLABLE) return grad_fun(copy); IObjective* objective = GetPointer(this); matrix ad = approx_derivative(objective,copy,f0,m_grad_options.method,m_grad_options.rel_step,m_grad_options.abs_step,m_grad_options.bounds); return ad.Row(0); } matrix wrapped_hess(vector& x, vector& f0) { m_nhev += 1; if(m_clip) { matrix bnds = m_grad_options.bounds; for(ulong i = 0; ibnds[i,1]) x[i] = bnds[i,1]; } } vector copy = x; if(m_hess_options.method == HESS_POINT_CALLABLE) return hess_fun(copy); IObjective* objective = GetPointer(this); return approx_derivative(objective,x,f0,m_grad_options.method,m_grad_options.rel_step,m_grad_options.abs_step,m_grad_options.bounds); } vector lower_bounds(void) { return m_grad_options.bounds.Col(0); } vector upper_bounds(void) { return m_grad_options.bounds.Col(1); } vector initial_params(void) { return m_xp; } virtual double orig_fun(vector& x) { return double("nan"); } virtual vector grad_fun(vector& x) { return vector::Zeros(0); } virtual matrix hess_fun(vector& x) { return matrix::Zeros(0,0); } ObjReturn fun_and_grad(vector& x) { vector dif = MathAbs(m_x - x); if(dif.Sum() >= DBL_EPSILON || dif.HasNan()) update_x(x); update_fun(); update_grad(); ObjReturn out; out.f = m_f; out.g = m_g; return out; } }; //+------------------------------------------------------------------+ //|base class representing function and its derivative | //+------------------------------------------------------------------+ class CConstraints:public IObjective { protected: int m_n,m_m; bool m_clip,m_initialized; GradDiffOptions m_options; vector wrapped_func(vector& x) { if(!m_initialized) return vector::Zeros(0); if(m_clip) { matrix bnds = m_options.bounds; for(ulong i = 0; ibnds[i,1]) x[i] = bnds[i,1]; } } vector copy = x; return orig_fun(copy); } matrix wrapped_grad(vector& x,vector& f0) { if(!m_initialized) return matrix::Zeros(0,0); if(m_clip) { matrix bnds = m_options.bounds; for(ulong i = 0; ibnds[i,1]) x[i] = bnds[i,1]; } } vector copy = x; if(m_options.method == GRAD_POINT_CALLABLE) return orig_grad(copy); IObjective* fun = (IObjective*)GetPointer(this); matrix ad = approx_derivative(fun,copy,f0,m_options.method,m_options.rel_step,m_options.abs_step,m_options.bounds); return ad; } public: CConstraints(void) { m_n = m_m = 0; m_clip = true; m_initialized = false; m_options.method = GRAD_POINT_2; } ~CConstraints(void) { } bool initialize(vector& x) { if((m_m == 0 && m_n) || (int)x.Size()!=m_n) { Print(__FUNCTION__," ","Invalid property dimensions.", " n ", m_n, " vs ", x.Size()); return false; } vector check = orig_fun(x); if(!check.Size() || check.HasNan() || MathClassify(fabs(check.Max())) == FP_INFINITE) { Print(__FUNCTION__," ","Check the implementation of the constraints function."); return false; } if(m_options.method == GRAD_POINT_CALLABLE) { matrix a = orig_grad(x); if(a.Rows()!=m_m || a.HasNan() || MathClassify(fabs(a.Max())) == FP_INFINITE) { Print(__FUNCTION__," ","Check the implementation of the overriden constraints gradient function."); return false; } } if(m_options.bounds.Rows()!=ulong(m_n)) { m_options.bounds.Resize(m_n,2); m_options.bounds.Fill(double("inf")); for(int i = 0; i= 0.) signx0[i]=1.*2-1; else signx0[i] = 0.0*2-1; } double rstep = eps_for_method(method); if(rel_step.Size()==0) for(ulong i = 0; iub[i]); fitting = int(fabs(h_total[i])<=MathMax(lower_dist[i],upper_dist[i])); if(bool(violated & fitting)) h_adjusted[i]*=-1.; forward = int((upper_dist[i] >= lower_dist[i]) & ~fitting); if(forward) h_adjusted[i] = upper_dist[i]/double(num_steps); backward = int((upper_dist[i]=h_total[i]) & (upper_dist[i] >= h_total[i]))); forward = int(((upper_dist[i]>=lower_dist[i]) & ~central)); if(forward) { h_adjusted[i] = MathMin(h[i],0.5*upper_dist[i]/double(num_steps)); one_sided[i] = 1.; } backward = int(((upper_dist[i]ub[i]) { Print(__FUNCTION__, " x0 violates bound constraints "); return matrix::Zeros(0,0); } vector h; if(!abs_step.Size()) h = compute_absolute_step(rel_step,x0,f0,method); else { h = abs_step; vector signx0 = vector::Zeros(x0.Size()); for(ulong i = 0; i=0.0) signx0[i] = 1.0*2.-1.; else signx0[i] = 0.0*2.-1.; if(((x0[i]+h[i]) - x0[i]) == 0.0) h[i] = eps_for_method(method)*signx0[i]*MathMax(1.,fabs(x0[i])); } } vector use_one_sided; switch(method) { case GRAD_POINT_2: h = adjust_scheme_to_bounds(x0,h,1,SCHEME_1,lb,ub,use_one_sided); break; case GRAD_POINT_3: h = adjust_scheme_to_bounds(x0,h,1,SCHEME_2,lb,ub,use_one_sided); break; case GRAD_POINT_CS: use_one_sided = vector::Zeros(x0.Size()); break; } return dense_difference(fun,x0,f0,h,use_one_sided,method); } //+------------------------------------------------------------------+