Program Listing for File cosine_gradient_descent.hpp#

↰ Return to documentation for file (source/pele/cosine_gradient_descent.hpp)

#ifndef _PELE_COS_GD_H__
#define _PELE_COS_GD_H__

#include <memory>
#include <pele/vecn.hpp>
#include <vector>

#include "array.hpp"
#include "base_potential.hpp"
#include "optimizer.hpp"

namespace pele {

class CosineGradientDescent : public ODEBasedOptimizer {
 private:
  Array<double> xold;
  Array<double> gold;
  Array<double> step;
  double cos_sim_tol_;
  double dt_;
  double dt_initial_;
  int back_track_n_;
                      //  if step keeps satisfying the condition for
                      //  back_track_n_ times,
                      // i.e we're not making progress, multiply the step by
                      // back_track_n_
  const int max_back_track = 100;
  int n_success_without_backtrack = 0;

 public:
  CosineGradientDescent(std::shared_ptr<pele::BasePotential> potential,
                        const pele::Array<double> x0, double tol = 1e-4,
                        double cos_sim_tol = 1e-2, double dt_initial = 1e-5,
                        int back_track_n = 5, bool save_trajectory = true,
                        int iterations_before_save = 1)
      : ODEBasedOptimizer(potential, x0, tol, save_trajectory,
                          iterations_before_save),
        xold(x_.size(), 0),
        gold(x_.size(), 0),
        step(x_.size(), 0),
        cos_sim_tol_(cos_sim_tol),
        dt_(dt_initial),
        dt_initial_(dt_initial),
        back_track_n_(back_track_n) {
    std::cout << "cosine gradient descent initialized" << std::endl;
    std::cout << "with parameters: " << std::endl;
    std::cout << "tol: " << tol << std::endl;
    std::cout << "cos_sim_tol: " << cos_sim_tol << std::endl;
    std::cout << "dt_initial: " << dt_initial << std::endl;
    std::cout << "back_track_n: " << back_track_n << std::endl;
    std::cout << "save_trajectory: " << save_trajectory << std::endl;
    std::cout << "iterations_before_save: " << iterations_before_save
              << std::endl;
    if (back_track_n_ < 1) {
      throw std::invalid_argument(
          "back_track_n must be greater than or equal to 1");
    }
  }

  virtual ~CosineGradientDescent() {}

  void one_iteration() {
    if (!func_initialized_) {
      initialize_func_gradient();
    }
    // make a copy of the position and gradient
    xold.assign(x_);
    //  really need to refactor and clean this up
    gold.assign(g_);
    // Gradient defines step direction
#if OPTIMIZER_DEBUG_LEVEL > 2
    std::cout << "cosine gradient descent: " << iter_number_ << " E " << f_
              << " rms " << gradient_norm_ << " nfev " << nfev_ << std::endl;
#endif
#if OPTIMIZER_DEBUG_LEVEL > 3
    std::cout << "x" << x_ << std::endl;
    std::cout << g_ << std::endl;
#endif
    // if the cosine similarity is less than the tolerance, reset the step size

    for (int i = 0; i < max_back_track; i++) {
      x_.assign(xold - dt_ * g_);
      f_ = potential_->get_energy_gradient(x_, g_);
      nfev_ += 1;
      double cos_sim_eps = abs(1 - dot(gold, g_) / (norm(gold) * norm(g_)));
#if OPTIMIZER_DEBUG_LEVEL > 2
      std::cout << "cosine similarity: " << cos_sim_eps << std::endl;
#endif
      if (cos_sim_eps < cos_sim_tol_) {
        if (i == 0) {
          n_success_without_backtrack += 1;
        }
        break;
      }
      // backtracking failed
      n_success_without_backtrack = 0;
      dt_ = dt_ / back_track_n_;
    }
    time_ += dt_;
    step.assign(x_ - xold);
    if (n_success_without_backtrack == back_track_n_) {
      dt_ = dt_ * back_track_n_;
      n_success_without_backtrack = 0;
    }
    step_norm_ = norm(step);
    gradient_norm_ = norm(g_);
    // print some status information
    if ((iprint_ > 0) && (iter_number_ % iprint_ == 0)) {
      std::cout << "steepest descent: " << iter_number_ << " E " << f_
                << " rms " << gradient_norm_ << " nfev " << nfev_
                << " step norm " << step_norm_ << std::endl;
    }
    iter_number_ += 1;
  }
  virtual void reset(pele::Array<double> &x0) {
    if (x0.size() != x_.size()) {
      throw std::invalid_argument(
          "The number of degrees of freedom (x0.size()) cannot change when "
          "calling reset()");
    }
    iter_number_ = 0;
    nfev_ = 0;
    n_success_without_backtrack = 0;
    x_.assign(x0);
    dt_ = dt_initial_;
    initialize_func_gradient();
    time_ = 0;
#if OPTIMIZER_DEBUG_LEVEL > 2
    std::cout << "resetting optimizer" << std::endl;
#endif
  }
};
}  // namespace pele

#endif