如何将Eigen库objective仿函数改造为支持scalar入参的p_objective用于线搜索
实现方案
核心思路
你需要的p_objective本质是对原目标函数、初始点、搜索方向的轻量封装,所有入参为alpha的方法,内部先计算x = x0 + alpha * p,再转发调用原目标函数的对应方法即可。我们可以用泛型实现,不仅支持你当前的objective结构体,也可兼容其他符合接口规范的目标函数。
完整实现代码
#ifndef P_OBJECTIVE_HPP #define P_OBJECTIVE_HPP #include "EigenDataTypes.hpp" // 泛型版本,适配Eigen多维度目标函数 template <int Nx, typename ObjectiveT = objective<Nx>> struct p_objective { private: const ObjectiveT& m_f; // 原目标函数引用,避免拷贝大稀疏矩阵 const Vec<Nx>& m_x0; // 初始点引用 const Vec<Nx>& m_p; // 搜索方向引用 public: // 构造函数,传入原目标函数、初始点、搜索方向 p_objective(const ObjectiveT& f, const Vec<Nx>& x0, const Vec<Nx>& p) : m_f(f), m_x0(x0), m_p(p) {} // 计算目标值,等价于 f(x0 + alpha*p) inline scalar operator()(double alpha) const { Vec<Nx> x = m_x0 + alpha * m_p; return m_f(x); } // 计算梯度,按需求等价于 f.Eval_grad(x0 + alpha*p) // 注:如果需要标准线搜索的一维phi导数,这里改为返回 m_p.dot(m_f.Eval_grad(x)) 即可 inline Vec<Nx> Eval_grad(double alpha) const { Vec<Nx> x = m_x0 + alpha * m_p; return m_f.Eval_grad(x); } // 计算海森矩阵,等价于 f.Eval_hessian(x0 + alpha*p) inline Mat<Nx, Nx> Eval_hessian(double alpha) const { Vec<Nx> x = m_x0 + alpha * m_p; return m_f.Eval_hessian(x); } }; // 1D无Eigen版本特化适配 template <> struct p_objective<1, objective_1D> { private: const objective_1D& m_f; const double& m_x0; const double& m_p; public: p_objective(const objective_1D& f, const double& x0, const double& p) : m_f(f), m_x0(x0), m_p(p) {} double operator()(double alpha) const { double x = m_x0 + alpha * m_p; return m_f(x); } double Eval_grad(double alpha) const { double x = m_x0 + alpha * m_p; return m_f.Eval_grad(x); } double Eval_hessian(double alpha) const { double x = m_x0 + alpha * m_p; return m_f.Eval_hessian(x); } }; #endif
使用示例
完全符合你要求的调用形式:
// 2D Eigen版本调用示例 objective<2> f(Q, c); Vec<2> x0{0, 1}; Vec<2> p{1, 0}; // 自定义搜索方向 p_objective<2> Phi(f, x0, p); double alpha = 0.9; // 调用方式和你要求完全一致 scalar obj_val = Phi(alpha); Vec<2> grad = Phi.Eval_grad(alpha); Mat<2,2> hess = Phi.Eval_hessian(alpha); // 1D版本调用示例 objective_1D f1d(2.0, 1.0); double x0_1d = 0.0; double p_1d = 1.0; p_objective<1, objective_1D> Phi_1d(f1d, x0_1d, p_1d); double val_1d = Phi_1d(0.9);
可选优化点
如果担心每次调用都构造x向量的开销,可以把x作为缓存成员变量,用Eigen的noalias避免临时变量开销:
// 优化版本示例 template <int Nx, typename ObjectiveT = objective<Nx>> struct p_objective { private: const ObjectiveT& m_f; const Vec<Nx>& m_x0; const Vec<Nx>& m_p; mutable Vec<Nx> m_x_cache; // mutable允许const方法修改缓存 public: p_objective(const ObjectiveT& f, const Vec<Nx>& x0, const Vec<Nx>& p) : m_f(f), m_x0(x0), m_p(p), m_x_cache(x0) {} inline scalar operator()(double alpha) const { m_x_cache.noalias() = m_x0 + alpha * m_p; return m_f(m_x_cache); } // 其余方法同理复用m_x_cache即可 };
注意:上述实现默认用引用存储外部对象,需要保证
f、x0、p的生命周期长于p_objective实例,如果无法保证可以把成员变量改为值存储,牺牲少量拷贝成本保证安全。
内容的提问来源于stack exchange,提问作者Jonas Hjulstad
相关产品推荐
相关产品推荐

