/** \file num_diff.hpp */ #ifndef NUMDIFF_HPP #define NUMDIFF_HPP #include #include #include #include #include namespace opt_utilities { /** \brief differentiable function \tparam rT the return type \tparam the parameter type */ template class dfunc_obj :public func_obj { private: virtual pT do_diff(const pT& p)=0; public: /** calculate the differentiation \param p the self-var \return the gradient at p */ pT diff(const pT& p) { return do_diff(p); } }; /** When trying to diff an object function, when it is not differentiable, this exception will be thrown. */ class underivable :public opt_exception { public: underivable() :opt_exception("underivable") {} }; /** calculate the numerical differential of a func_obj */ template pT numdiff(func_obj& f,const pT& p) { rT ep=std::sqrt(std::numeric_limits::epsilon()); pT result; resize(result,get_size(p)); pT p2; resize(p2,get_size(p)); pT p1; resize(p1,get_size(p)); for(size_t i=0;i::element_type h= std::max(get_element(p,i),rT(1))*ep; set_element(p2,i,get_element(p,i)+h); set_element(p1,i,get_element(p,i)-h); rT v2=f(p2); rT v1=f(p1); set_element(result,i, (v2-v1)/h/2 ); set_element(p2,i,get_element(p,i)); set_element(p1,i,get_element(p,i)); } return result; } /** Help function to calculate the gradient of an objection function func_obj, whether it is differentiable or not. If it is differentiable, the gradient will be calculated by calling the diff member in the func_obj, or a numerical calculation will be performed. */ template pT diff(func_obj& f,const pT& p) { dfunc_obj* pdf=dynamic_cast*>(&f); if(pdf) { return pdf->diff(p); } return numdiff(f,p); } } #endif