// Copyright (C) 2016-2019 Yixuan Qiu & Dirk Toewe // Under MIT license #ifndef LINE_SEARCH_BRACKETING_H #define LINE_SEARCH_BRACKETING_H #include #include // std::runtime_error namespace LBFGSpp { /// /// The bracketing line search algorithm for LBFGS. Mainly for internal use. /// template class LineSearchBracketing { private: typedef Eigen::Matrix Vector; public: /// /// Line search by bracketing. Similar to the backtracking line search /// except that it actively maintains an upper and lower bound of the /// current search range. /// /// \param f A function object such that `f(x, grad)` returns the /// objective function value at `x`, and overwrites `grad` with /// the gradient. /// \param fx In: The objective function value at the current point. /// Out: The function value at the new point. /// \param x Out: The new point moved to. /// \param grad In: The current gradient vector. Out: The gradient at the /// new point. /// \param step In: The initial step length. Out: The calculated step length. /// \param drt The current moving direction. /// \param xp The current point. /// \param param Parameters for the LBFGS algorithm /// template static void LineSearch(Foo&& f, Scalar& fx, Eigen::Ref x, Vector& grad, Scalar& step, const Vector& drt, const Vector& xp, const LBFGSParam& param) { // Check the value of step if (step <= Scalar(0)) std::invalid_argument("'step' must be positive"); // Save the function value at the current x const Scalar fx_init = fx; // Projection of gradient on the search direction const Scalar dg_init = grad.dot(drt); // Make sure d points to a descent direction if (dg_init > 0) std::logic_error("the moving direction increases the objective function value"); const Scalar dg_test = param.ftol * dg_init; // Upper and lower end of the current line search range Scalar step_lo = 0, step_hi = std::numeric_limits::infinity(); for (int iter = 0; iter < param.max_linesearch; iter++) { // x_{k+1} = x_k + step * d_k x.noalias() = xp + step * drt; // Evaluate this candidate fx = f(x, grad); if (fx > fx_init + step * dg_test) { step_hi = step; } else { // Armijo condition is met if (param.linesearch == LBFGS_LINESEARCH_BACKTRACKING_ARMIJO) break; const Scalar dg = grad.dot(drt); if (dg < param.wolfe * dg_init) { step_lo = step; } else { // Regular Wolfe condition is met if (param.linesearch == LBFGS_LINESEARCH_BACKTRACKING_WOLFE) break; if (dg > -param.wolfe * dg_init) { step_hi = step; } else { // Strong Wolfe condition is met break; } } } assert(step_lo < step_hi); if (iter >= param.max_linesearch) throw std::runtime_error("the line search routine reached the maximum number of iterations"); if (step < param.min_step) throw std::runtime_error("the line search step became smaller than the minimum value allowed"); if (step > param.max_step) throw std::runtime_error("the line search step became larger than the maximum value allowed"); // continue search in mid of current search range step = std::isinf(step_hi) ? 2 * step : step_lo / 2 + step_hi / 2; } } }; } // namespace LBFGSpp #endif // LINE_SEARCH_BRACKETING_H