6#ifndef NURI_ALGO_OPTIM_H_
7#define NURI_ALGO_OPTIM_H_
16#include <absl/log/absl_check.h>
40 constexpr double kEpsMach = 2.220446049250313e-16;
41 constexpr double kSqrtEpsMach =
42 1.4901161193847655978721599999999997530663236602513281125266e-8;
44 enum class DcsrchStatus : std::uint8_t {
52 constexpr static double kStepMin = 0, kStepMax = 1e+10;
54 Dcsrch(
double f0,
double g0,
double step0,
double stepmin = kStepMin,
55 double stepmax = kStepMax,
double ftol = 1e-3,
double gtol = 0.9,
56 double xtol = 0.1) noexcept;
58 DcsrchStatus operator()(
double f,
double g);
60 double step()
const {
return step_; }
62 double finit()
const {
return finit_; }
64 double ginit()
const {
return ginit_; }
67 constexpr static double kXTrapL = 1.1, kXTrapU = 4;
69 double finit_, ginit_;
70 double stepmin_, stepmax_;
71 double gtest_, gtol_, xtol_;
81 double stx_ = 0, fx_, gx_;
82 double sty_ = 0, fy_, gy_;
83 double stmin_ = 0, stmax_;
84 double width_, width1_;
86 bool brackt_ =
false, bisect_ =
false;
91 LBfgsBase(MutRef<ArrayXd> x,
int m);
95 int n()
const {
return static_cast<int>(x_.size()); }
97 int m()
const {
return static_cast<int>(ws_.cols()); }
99 auto &x() {
return x_; }
101 const auto &x()
const {
return x_; }
103 auto wnt() {
return wnt_.topLeftCorner(2 * col_, 2 * col_); }
104 auto &wnt_raw() {
return wnt_; }
106 auto ws() {
return ws_.leftCols(col_); }
108 auto wy() {
return wy_.leftCols(col_); }
110 auto ss() {
return ss_.topLeftCorner(col_, col_); }
112 auto sy() {
return sy_.topLeftCorner(col_, col_); }
114 auto wtt() {
return wtt_.topLeftCorner(col_, col_); }
116 auto p() {
return p_.head(2 * col_); }
118 auto v() {
return v_.head(2 * col_); }
120 auto c() {
return c_.head(2 * col_); }
122 auto wbp() {
return wbp_.head(2 * col_); }
124 auto smul() {
return smul_.head(nonnegative(col_ - 1)); }
126 auto &z() {
return z_; }
127 auto &xcp() {
return z_; }
129 auto &r() {
return r_; }
131 auto &t() {
return t_; }
132 auto &xp() {
return t_; }
134 auto &d() {
return d_; }
136 double d_dot(
const ArrayXd &gx)
const {
137 return gx.matrix().dot(d_.matrix());
140 double theta()
const {
return theta_; }
142 int col()
const {
return col_; }
144 int prev_col()
const {
return prev_col_; }
146 bool updated()
const {
return updated_; }
150 void update_col(
int col) { col_ = col; }
152 void update_theta(
double theta) { theta_ = theta; }
155 bool prepare_next_iter(
double gd,
double ginit,
double step,
double dtd);
157 void reset_memory() {
159 col_ = prev_col_ = 0;
172 MatrixXd ss_, sy_, wtt_;
174 VectorXd p_, v_, c_, wbp_;
176 ArrayXd z_, r_, t_, d_;
185 extern bool lbfgs_bmv(MutVecBlock<VectorXd> p, MutVecBlock<ArrayXd> smul,
186 ConstRef<VectorXd> v, ConstRef<MatrixXd> sy,
187 ConstRef<MatrixXd> wtt);
243class LBfgs:
public internal::LBfgsBase {
253 LBfgs(MutRef<ArrayXd> x, Impl &&impl,
const int m = 10)
254 : internal::LBfgsBase(x, m), impl_(std::move(impl)) { }
271 template <
class FuncGrad>
273 int maxiter = 15000,
int maxls = 20);
276 Impl &impl() {
return impl_; }
283template <
class FuncGrad>
285 const double pgtol,
const int maxiter,
287 const double tol = factr * internal::kEpsMach;
290 double fx = fg(gx, x());
292 internal::AllowEigenMallocScoped<false> ems;
294 double sbgnrm = impl_.projgr(x(), gx);
302 for (; iter < maxiter; ++iter) {
303 if (!impl_.prepare_lnsrch(*
this, gx, sbgnrm, iter)) {
310 double gd = d_dot(gx);
322 auto lnsrl = impl_.lnsrch(x(), t(), z(), d(), fx, gd, iter);
323 bool converged =
false;
324 for (
int i = 0; !converged && i < maxls; ++i) {
325 NURI_EIGEN_ALLOW_MALLOC(
true);
327 NURI_EIGEN_ALLOW_MALLOC(
false);
330 converged = lnsrl.search(fx, gd);
345 sbgnrm = impl_.projgr(x(), gx);
349 double ddum = std::max({ std::abs(lnsrl.finit()), std::abs(fx), 1.0 });
350 if (lnsrl.finit() - fx <= tol * ddum)
354 if (!prepare_next_iter(gd, lnsrl.ginit(), lnsrl.step(), lnsrl.dtd()))
369 LbfgsbBounds(
const ArrayXi &nbd,
const Array2Xd &bounds)
370 : nbd_(&nbd), bds_(&bounds) {
374 LbfgsbBounds(ArrayXi &&nbd, Array2Xd &&bounds) =
delete;
376 bool has_bound(
int i)
const {
return nbd()[i] != 0; }
378 bool has_lb(
int i)
const {
return (nbd()[i] & 0x1) != 0; }
380 bool has_ub(
int i)
const {
return (nbd()[i] & 0x2) != 0; }
382 bool has_both(
int i)
const {
return nbd()[i] == (0x1 | 0x2); }
384 int raw_nbd(
int i)
const {
return nbd()[i]; }
386 double lb(
int i)
const {
387 ABSL_DCHECK(has_lb(i));
391 double ub(
int i)
const {
392 ABSL_DCHECK(has_ub(i));
397 const ArrayXi &nbd()
const {
return *nbd_; }
399 const Array2Xd &bds()
const {
return *bds_; }
401 void check_sizes()
const {
402 ABSL_DCHECK(nbd().size() == bds().cols());
403 ABSL_DCHECK(((nbd() >= 0) && (nbd() <= 3)).all());
405 ((nbd() != 3).transpose() || bds().row(0) <= bds().row(1)).all());
409 const Array2Xd *bds_;
414 LbfgsbLnsrch(MutRef<ArrayXd> &x,
const ArrayXd &t,
const ArrayXd &z,
415 const ArrayXd &d,
const LbfgsbBounds &bounds,
double f0,
416 double g0,
int iter,
bool constrained,
bool boxed,
417 double ftol = 1e-3,
double gtol = 0.9,
418 double xtol = 0.1) noexcept;
420 bool search(
double f,
double g);
422 double dtd()
const {
return dtd_; }
424 double step()
const {
return dcsrch_.step(); }
426 double xstep()
const {
return step() * dnorm_; }
428 double finit()
const {
return dcsrch_.finit(); }
430 double ginit()
const {
return dcsrch_.ginit(); }
433 MutRef<ArrayXd> &x() {
return *x_; }
435 const ArrayXd &t()
const {
return *t_; }
437 const ArrayXd &z()
const {
return *z_; }
439 const ArrayXd &d()
const {
return *d_; }
441 const LbfgsbBounds &bounds()
const {
return *bounds_; }
446 const ArrayXd *t_, *z_, *d_;
447 const LbfgsbBounds *bounds_;
458 inline bool operator>(CauchyBrkpt lhs, CauchyBrkpt rhs) {
459 return lhs.tj > rhs.tj;
464 LBfgsBImpl(MutRef<ArrayXd> x, LbfgsbBounds bounds,
int m);
466 LbfgsbLnsrch lnsrch(MutRef<ArrayXd> &x,
const ArrayXd &t,
const ArrayXd &z,
467 const ArrayXd &d,
double f0,
double g0,
int iter,
468 double ftol = 1e-3,
double gtol = 0.9,
469 double xtol = 0.1) const noexcept;
473 bool prepare_lnsrch(LBfgsBase &lbfgsb, const ArrayXd &gx,
double sbgnrm,
476 double projgr(ConstRef<ArrayXd> x, const ArrayXd &gx);
480 MatrixXd &wn1() {
return wn1_; }
482 int n()
const {
return static_cast<int>(iwhere_.size()); }
484 const LbfgsbBounds &bounds()
const {
return bounds_; }
486 int nfree()
const {
return nfree_; }
488 bool constrained()
const {
return constrained_; }
490 bool boxed()
const {
return boxed_; }
492 auto &iwhere() {
return iwhere_; }
494 auto free()
const {
return free_bound_.head(nfree_); }
496 auto bound()
const {
return free_bound_.tail(n() - nfree_); }
498 auto enter()
const {
return enter_leave_.head(nenter_); }
500 auto leave()
const {
return enter_leave_.tail(nleave_); }
502 auto &brks() {
return brks_; }
504 ArrayXi &free_bound() {
return free_bound_; }
506 void update_nfree(
int nfree) { nfree_ = nfree; }
509 bool freev(
int iter);
515 LbfgsbBounds bounds_;
518 ArrayXi iwhere_, free_bound_, enter_leave_;
520 ClearablePQ<CauchyBrkpt, std::greater<>> brks_;
522 int nfree_, nenter_, nleave_;
523 bool constrained_, boxed_;
526 extern bool lbfgsb_errclb(
const ArrayXd &x,
const ArrayXi &nbd,
527 const Array2Xd &bounds,
int m,
double factr);
529 extern bool lbfgsb_cauchy(LBfgsBase &lbfgsb, LBfgsBImpl &impl,
530 const ArrayXd &gx,
double sbgnrm);
532 extern bool lbfgsb_subsm(LBfgsBase &lbfgsb, LBfgsBImpl &impl,
609template <
class FuncGrad>
611 const Array2Xd &bounds,
const int m = 10,
612 const double factr = 1e+7,
const double pgtol = 1e-5,
613 const int maxiter = 15000,
const int maxls = 20) {
614 bool args_ok = internal::lbfgsb_errclb(x, nbd, bounds, m, factr);
619 x, internal::LBfgsBImpl(x, { nbd, bounds }, m), m);
620 return lbfgsb.
minimize(std::forward<FuncGrad>(fg), factr, pgtol, maxiter,
627 LbfgsLnsrch(MutRef<ArrayXd> &x,
const ArrayXd &t,
const ArrayXd &z,
628 const ArrayXd &d,
double f0,
double g0,
int iter,
629 double ftol = 1e-3,
double gtol = 0.9,
630 double xtol = 0.1) noexcept;
632 bool search(
double f,
double g);
634 double dtd()
const {
return dtd_; }
636 double step()
const {
return dcsrch_.step(); }
638 double xstep()
const {
return step() * dnorm_; }
640 double finit()
const {
return dcsrch_.finit(); }
642 double ginit()
const {
return dcsrch_.ginit(); }
645 MutRef<ArrayXd> &x() {
return *x_; }
647 const ArrayXd &t()
const {
return *t_; }
649 const ArrayXd &z()
const {
return *z_; }
651 const ArrayXd &d()
const {
return *d_; }
656 const ArrayXd *t_, *z_, *d_;
664 LBfgsImpl(): LBfgsImpl(10) { }
666 explicit LBfgsImpl(
int m);
668 static LbfgsLnsrch lnsrch(MutRef<ArrayXd> &x,
const ArrayXd &t,
669 const ArrayXd &z,
const ArrayXd &d,
double f0,
670 double g0,
int iter,
double ftol = 1e-3,
671 double gtol = 0.9,
double xtol = 0.1) noexcept;
673 static
void reset() { }
675 bool prepare_lnsrch(LBfgsBase &lbfgs,
const ArrayXd &gx,
double sbgnrm,
678 static double projgr(ConstRef<ArrayXd> x,
const ArrayXd &gx);
680 MatrixXd &wn1() {
return wn1_; }
687 extern bool lbfgs_errclb(
const ArrayXd &x,
int m,
double factr);
759template <
class FuncGrad>
761 const double factr = 1e+7,
const double pgtol = 1e-5,
762 const int maxiter = 15000,
const int maxls = 20) {
763 bool args_ok = internal::lbfgs_errclb(x, m, factr);
768 return lbfgsb.
minimize(std::forward<FuncGrad>(fg), factr, pgtol, maxiter,
853 template <
class FuncGrad>
855 const double xrtol = 0,
int maxiter = -1,
856 const int maxls = 100,
const double ftol = 1e-4,
857 const double gtol = 0.9,
const double xtol = 1e-14) {
858 using internal::Dcsrch;
859 using internal::DcsrchStatus;
862 maxiter = 200 *
static_cast<int>(x().size());
866 ArrayXd gfk(x().size());
868 const double f0 = fg(gfk, x());
869 double gnorm = gfk.abs().maxCoeff();
874 double fk = f0, fkm1 = fk + gfk.matrix().norm() * 0.5;
875 for (; k < maxiter; ++k) {
876 Dcsrch dcsrch = prepare_lnsrch(gfk, fk, fkm1, ftol, gtol, xtol);
879 bool success =
false;
880 for (
int iter = 0; iter < maxls; ++iter) {
881 xk() = x() + dcsrch.step() * pk().array();
882 fk = fg(gfkp1(), xk());
884 auto status = dcsrch(fk, gfkp1().matrix().dot(pk()));
885 if (status == DcsrchStatus::kContinue)
894 bool converged = prepare_next_iter(gfk, dcsrch.step(), pgtol, xrtol);
903 internal::Dcsrch prepare_lnsrch(
const ArrayXd &gfk,
double fk,
double fkm1,
904 double ftol,
double gtol,
double xtol);
906 bool prepare_next_iter(ArrayXd &gfk,
double step,
double pgtol,
double xrtol);
908 MutRef<ArrayXd> &x() {
return x_; }
911 Eigen::SelfAdjointView<MatrixXd, Eigen::Upper> Hk() {
912 return Hk_.selfadjointView<Eigen::Upper>();
915 VectorXd &pk() {
return pk_; }
917 ArrayXd &xk() {
return xk_; }
918 Eigen::MatrixWrapper<ArrayXd> sk() {
return xk_.matrix(); }
920 VectorXd &yk() {
return yk_; }
922 ArrayXd &gfkp1() {
return gfkp1_; }
924 Eigen::MatrixWrapper<ArrayXd> Hk_yk() {
return gfkp1_.matrix(); }
998template <
class FuncGrad>
1000 const double pgtol = 1e-5,
const double xrtol = 0,
1001 int maxiter = -1,
const int maxls = 100,
1002 const double ftol = 1e-4,
const double gtol = 0.9,
1003 const double xtol = 1e-14) {
1005 return bfgs.minimize(std::forward<FuncGrad>(fg), pgtol, xrtol, maxiter, maxls,
1037 auto n()
const {
return data_.cols() - 1; }
1040 int argmax()
const {
return static_cast<int>(
n()); }
1049 double max2f()
const {
return data_(
n(),
n() - 1); }
1070 template <
class Func>
1072 const double alpha = 1,
const double gamma = 2,
1073 const double rho = 0.5,
const double sigma = 0.5) {
1077 maxiter =
static_cast<int>(N) * 200;
1079 auto eval_update = [&](
auto &&simplexf) ->
double {
1080 return simplexf[N] = f(simplexf.head(N));
1083 for (
int i = 0; i < data_.cols(); ++i)
1084 eval_update(data_.col(i));
1086 for (
int iter = 0; iter < maxiter; ++iter) {
1087 partiton_min1_max2();
1095 double fr = eval_update(r_);
1098 expansion(ets_, gamma);
1099 const double fe = eval_update(ets_);
1101 max() = (fr < fe) ? r_ : ets_;
1114 contraction(ets_, rho);
1115 const double ft = eval_update(ets_);
1123 for (
int i = 1; i < data_.cols(); ++i)
1124 eval_update(data_.col(i));
1131 void partiton_min1_max2();
1135 void reflection(
double alpha);
1137 void expansion(ArrayXd &e,
double gamma)
const;
1139 void contraction(ArrayXd &t,
double rho)
const;
1141 void shrink(
double sigma);
1144 MutRef<ArrayXXd> data_;
1146 ArrayXd c_, r_, ets_;
1150 extern bool nm_check_input(ConstRef<ArrayXXd> data,
double alpha,
1151 double gamma,
double rho,
double sigma);
1186template <
class Func>
1188 const double ftol = 1e-6,
const double alpha = 1,
1189 const double gamma = 2,
const double rho = 0.5,
1190 const double sigma = 0.5) {
1191 if (!internal::nm_check_input(data, alpha, gamma, rho, sigma))
1195 return nm.
minimize(std::forward<Func>(f), maxiter, ftol, alpha, gamma, rho,
BFGS minimizer.
Definition optim.h:826
Bfgs(MutRef< ArrayXd > x)
Prepare BFGS minimization algorithm.
BfgsResult minimize(FuncGrad fg, const double pgtol=1e-5, const double xrtol=0, int maxiter=-1, const int maxls=100, const double ftol=1e-4, const double gtol=0.9, const double xtol=1e-14)
Minimize a function using BFGS algorithm.
Definition optim.h:854
L-BFGS(-B) minimizer.
Definition optim.h:243
LbfgsResult minimize(FuncGrad fg, double factr=1e+7, double pgtol=1e-5, int maxiter=15000, int maxls=20)
Minimize a function using L-BFGS(-B) algorithm.
Definition optim.h:284
LBfgs(MutRef< ArrayXd > x, Impl &&impl, const int m=10)
Prepare L-BFGS(-B) minimization algorithm.
Definition optim.h:253
Nelder-Mead simplex algorithm for function minimization.
Definition optim.h:1025
static constexpr int argmin()
Definition optim.h:1039
auto max()
Definition optim.h:1045
NelderMead(MutRef< ArrayXXd > data)
Prepare Nelder-Mead simplex algorithm.
NMResult minimize(Func f, int maxiter=-1, const double ftol=1e-6, const double alpha=1, const double gamma=2, const double rho=0.5, const double sigma=0.5)
Minimize a function using Nelder-Mead simplex algorithm.
Definition optim.h:1071
auto min() const
Definition optim.h:1042
double maxf() const
Definition optim.h:1047
int argmax() const
Definition optim.h:1040
auto n() const
Definition optim.h:1037
double minf() const
Definition optim.h:1043
double max2f() const
Definition optim.h:1049
auto max() const
Definition optim.h:1046
ArrayXXd nm_prepare_simplex(ConstRef< ArrayXd > x0, double eps=1e-6)
Prepare the initial simplex for Nelder-Mead algorithm.
BfgsResult bfgs(FuncGrad &&fg, MutRef< ArrayXd > x, const double pgtol=1e-5, const double xrtol=0, int maxiter=-1, const int maxls=100, const double ftol=1e-4, const double gtol=0.9, const double xtol=1e-14)
Minimize a function using BFGS algorithm.
Definition optim.h:999
OptimResultCode
Definition optim.h:25
@ kMaxIterReached
Definition optim.h:27
@ kInvalidInput
Definition optim.h:28
@ kAbnormalTerm
Definition optim.h:29
@ kSuccess
Definition optim.h:26
LbfgsResult l_bfgs_b(FuncGrad &&fg, MutRef< ArrayXd > x, const ArrayXi &nbd, const Array2Xd &bounds, const int m=10, const double factr=1e+7, const double pgtol=1e-5, const int maxiter=15000, const int maxls=20)
Minimize a function using L-BFGS-B algorithm.
Definition optim.h:610
NMResult nelder_mead(Func &&f, MutRef< ArrayXXd > data, int maxiter=-1, const double ftol=1e-6, const double alpha=1, const double gamma=2, const double rho=0.5, const double sigma=0.5)
Minimize a function using Nelder-Mead simplex algorithm.
Definition optim.h:1187
LbfgsResult l_bfgs(FuncGrad &&fg, MutRef< ArrayXd > x, const int m=10, const double factr=1e+7, const double pgtol=1e-5, const int maxiter=15000, const int maxls=20)
Minimize a function using L-BFGS algorithm.
Definition optim.h:760
double fx
Definition optim.h:775
OptimResultCode code
Definition optim.h:773
int niter
Definition optim.h:774
ArrayXd gx
Definition optim.h:776
ArrayXd gx
Definition optim.h:36
int niter
Definition optim.h:34
OptimResultCode code
Definition optim.h:33
double fx
Definition optim.h:35
int argmin
Definition optim.h:1011
OptimResultCode code
Definition optim.h:1010