Program Listing for File base_potential.hpp#

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

#ifndef PYGMIN_POTENTIAL_H
#define PYGMIN_POTENTIAL_H

#include <assert.h>
#include <cstddef>
#include <cstdint>
#include <iostream>
#include <stdexcept>
#include <vector>

#include "array.hpp"

extern "C" {
#include "xsum.h"
}

namespace pele {

class BasePotential {
 public:
  virtual ~BasePotential() {}

  virtual double get_energy(Array<double> const &x) {
    throw std::runtime_error("BasePotential::get_energy must be overloaded");
  }

  virtual double get_energy_change(Array<double> const &old_coords,
                                   Array<double> const &new_coords,
                                   std::vector<size_t> const &) {
    return get_energy(new_coords) - get_energy(old_coords);
  }

  virtual size_t get_ndim() const {
    throw std::runtime_error("BasePotential::get_ndim must be overloaded");
  }

  virtual bool find_rattlers(pele::Array<double> const &minimum_coords,
                             Array<uint8_t> not_rattlers, bool &jammed) {
    throw std::runtime_error("BasePotential::find_rattlers must be overloaded");
  }
  virtual double add_energy_gradient(Array<double> const &x,
                                     Array<double> &grad) {
    throw std::runtime_error(
        "BasePotential::add_energy_gradient must be overloaded");
  }

  virtual double get_energy_gradient(Array<double> const &x,
                                     Array<double> &grad) {
    std::cout << "BasePotential::get_energy_gradient must be overloaded"
              << std::endl;
    double energy = get_energy(x);
    numerical_gradient(x, grad);
    return energy;
  }

  virtual double get_energy_gradient(
      Array<double> const &x, std::vector<xsum_small_accumulator> &exact_grad) {
    throw std::runtime_error(
        "A numerical version for the exact gradient has not been written");
  }

  virtual double add_energy_gradient_hessian(Array<double> const &x,
                                             Array<double> &grad,
                                             Array<double> &hess) {
    throw std::runtime_error(
        "BasePotential::add_energy_gradient_hessian must be overloaded");
  }

  virtual void add_hessian(Array<double> const &x, Array<double> &hess) {
    throw std::runtime_error("BasePotential::add_hessian must be overloaded");
  }
  virtual double get_energy_gradient_hessian(Array<double> const &x,
                                             Array<double> &grad,
                                             Array<double> &hess) {
    double energy = get_energy_gradient(x, grad);
    numerical_hessian(x, hess);
    return energy;
  }

  virtual double get_energy_gradient_hessian_rattlers(
      Array<double> const &x, Array<double> &grad, Array<double> &hess,
      Array<uint8_t> not_rattlers) {
    throw std::runtime_error(
        "BasePotential::get_energy_gradient_hessian with "
        "rattlers must be overloaded");
  }

  virtual double get_energy_gradient(Array<double> const &x,
                                     Array<double> &grad,
                                     Array<uint8_t> not_rattlers) {
    throw std::runtime_error(
        "BasePotential::get_energy_gradient with rattlers must be overloaded");
  }
  virtual void numerical_gradient(Array<double> const &x, Array<double> &grad,
                                  double eps = 1e-6) {
    if (x.size() != grad.size()) {
      throw std::invalid_argument("grad.size() be the same as x.size()");
    }

    Array<double> xnew(x.copy());
    for (size_t i = 0; i < xnew.size(); ++i) {
      xnew[i] -= eps;
      double eminus = get_energy(xnew);
      xnew[i] += 2. * eps;
      double eplus = get_energy(xnew);
      grad[i] = (eplus - eminus) / (2. * eps);
      xnew[i] = x[i];
    }
  }

  virtual void get_hessian(Array<double> const &x, Array<double> &hess) {
    Array<double> grad(x.size());
    get_energy_gradient_hessian(x, grad, hess);
  }

  virtual void numerical_hessian(Array<double> const &x, Array<double> &hess,
                                 double eps = 1e-6) {
    if (hess.size() != x.size() * x.size()) {
      throw std::invalid_argument(
          "hess.size() be the same as x.size()*x.size()");
    }
    size_t const N = x.size();

    Array<double> gplus(x.size());
    Array<double> gminus(x.size());

    Array<double> xnew(x.copy());
    for (size_t i = 0; i < xnew.size(); ++i) {
      double xbackup = xnew[i];
      xnew[i] -= eps;
      get_energy_gradient(xnew, gminus);
      xnew[i] += 2. * eps;
      get_energy_gradient(xnew, gplus);
      xnew[i] = xbackup;

      for (size_t j = 0; j < xnew.size(); ++j) {
        hess[N * i + j] = (gplus[j] - gminus[j]) / (2. * eps);
      }
    }
    /*//print hessian
    std::cout<<""<<std::endl;
    std::cout<<""<<std::endl;
    for (size_t i=0; i<hess.size(); ++i){
        std::cout<<hess[i]<<" ";
        int j = i+1;
        if (j % (int) (3*sqrt(hess.size())) == 0)
            std::cout<<"\n"<<std::endl;
        else if (j % (int) sqrt(hess.size()) == 0)
            std::cout<<""<<std::endl;
        else if (j % 3 == 0){
            std::cout<<"  ";
        }
    }*/
  }

  virtual double compute_norm(pele::Array<double> const &x) { return norm(x); }
};
}  // namespace pele

#endif