Changeset f433ec for src/Dynamics


Ignore:
Timestamp:
Apr 10, 2018, 6:43:30 AM (7 years ago)
Author:
Frederik Heber <frederik.heber@…>
Branches:
AutomationFragmentation_failures, Candidate_v1.6.1, ChemicalSpaceEvaluator, Exclude_Hydrogens_annealWithBondGraph, ForceAnnealing_with_BondGraph, ForceAnnealing_with_BondGraph_contraction-expansion, Gui_displays_atomic_force_velocity, PythonUI_with_named_parameters, StoppableMakroAction, TremoloParser_IncreasedPrecision
Children:
07d4b1
Parents:
7b4e67
git-author:
Frederik Heber <frederik.heber@…> (07/18/17 22:24:12)
git-committer:
Frederik Heber <frederik.heber@…> (04/10/18 06:43:30)
Message:

We now obtain weights via levmar minimization.

  • this should yield the best possible weights within the interval of [1/n,1.].
  • note that we cannot always get an exact solution because of this constraint.
Location:
src/Dynamics
Files:
5 edited

Legend:

Unmodified
Added
Removed
  • src/Dynamics/BondVectors.cpp

    r7b4e67 rf433ec  
    4545#include "CodePatterns/Assert.hpp"
    4646#include "CodePatterns/Log.hpp"
     47
     48#include <levmar.h>
    4749
    4850#include "Atom/atom.hpp"
     
    111113}
    112114
     115struct WeightsMinimization {
     116
     117  static void evaluate(double *p, double *x, int m, int n, void *data)
     118  {
     119    // current weights in p, output to x
     120    double *matrix = static_cast<double*>(data);
     121    for(size_t i=0;i<(size_t)n;++i) {
     122      x[i] = 0.;
     123      for(size_t j=0;j<(size_t)n;++j)
     124        x[i] += p[j]*matrix[i*n+j];
     125//      x[i] = .5*x[i]*x[i];
     126    }
     127  }
     128
     129  static void evaluate_derivative(double *p, double *jac, int m, int n, void *data)
     130  {
     131    // current weights in p, output to x
     132    double *matrix = static_cast<double*>(data);
     133//    // tmp = (Bx - 1)
     134//    double *tmp = new double[n];
     135//    for(size_t i=0;i<(size_t)n;++i) {
     136//      tmp[i] = -1.;
     137//      for(size_t j=0;j<(size_t)n;++j)
     138//        tmp[i] += p[j]*matrix[i*n+j];
     139//    }
     140//    // tmp(i) * B_(ij)
     141//    for(size_t i=0;i<(size_t)n;++i)
     142//      for(size_t j=0;j<(size_t)n;++j)
     143//        jac[i*n+j] = tmp[i]*matrix[i*n+j];
     144//    delete[] tmp ;
     145    for(size_t i=0;i<(size_t)n;++i)
     146      for(size_t j=0;j<(size_t)n;++j)
     147        jac[i*n+j] = matrix[i*n+j];
     148  }
     149
     150  static double* getMatrixFromBondVectors(
     151      const std::vector<Vector> &_bondvectors
     152      )
     153  {
     154    const size_t n = _bondvectors.size();
     155    double *matrix = new double[n*n];
     156    size_t i=0;
     157    for (std::vector<Vector>::const_iterator iter = _bondvectors.begin();
     158        iter != _bondvectors.end(); ++iter, ++i) {
     159      size_t j=0;
     160      for (std::vector<Vector>::const_iterator otheriter = _bondvectors.begin();
     161          otheriter != _bondvectors.end(); ++otheriter, ++j) {
     162        // only magnitude is important not the sign
     163        matrix[i*n+j] = fabs((*iter).ScalarProduct(*otheriter));
     164      }
     165    }
     166
     167    return matrix;
     168  }
     169};
     170
     171
     172BondVectors::weights_t BondVectors::getWeightsForAtomAtStep(
     173    const atom &_walker,
     174    const std::vector<Vector> &_bondvectors,
     175    const size_t &_step) const
     176{
     177  // let levmar optimize
     178  register int i, j;
     179  int ret;
     180  double *p;
     181  double *x;
     182  int n=_bondvectors.size();
     183  double opts[LM_OPTS_SZ], info[LM_INFO_SZ];
     184
     185  double *matrix = WeightsMinimization::getMatrixFromBondVectors(_bondvectors);
     186
     187  weights_t weights(n, 0.);
     188
     189  // minim. options [\tau, \epsilon1, \epsilon2, \epsilon3]. Respectively the scale factor for initial \mu,
     190  // * stopping thresholds for ||J^T e||_inf, ||Dp||_2 and ||e||_2.
     191  opts[0]=LM_INIT_MU; opts[1]=1E-15; opts[2]=1E-15; opts[3]=1E-20;
     192  opts[4]= LM_DIFF_DELTA; // relevant only if the Jacobian is approximated using finite differences; specifies forward differencing
     193  //opts[4]=-LM_DIFF_DELTA; // specifies central differencing to approximate Jacobian; more accurate but more expensive to compute!
     194
     195  // prepare initial values for weights
     196  p = new double[n];
     197  for (i=0;i<n;++i)
     198    p[i] = 1.;
     199  // prepare output value, i.e. the row sums
     200  x = new double[n];
     201  for (i=0;i<n;++i)
     202    x[i] = 1.;
     203
     204  {
     205    double *work, *covar;
     206    work=(double *)malloc((LM_DIF_WORKSZ(n, n)+n*n)*sizeof(double));
     207    if(!work){
     208      ELOG(0, "BondVectors::getWeightsForAtomAtStep() - memory allocation request failed.");
     209      return weights;
     210    }
     211    covar=work+LM_DIF_WORKSZ(n, n);
     212
     213    // give this pointer as additional data to construct function pointer in
     214    // LevMarCallback and call
     215    double *lb = new double[n];
     216    double *ub = new double[n];
     217    for (i=0;i<n;++i) {
     218      lb[i] = 1./(double)n;
     219      ub[i] = 1.;
     220    }
     221    ret=dlevmar_bc_der(
     222        &WeightsMinimization::evaluate,
     223        &WeightsMinimization::evaluate_derivative,
     224        p, x, n, n, lb, ub, NULL, 1000, opts, info, work, covar, matrix); // no Jacobian, caller allocates work memory, covariance estimated
     225    delete[] lb;
     226    delete[] ub;
     227
     228    if (0)
     229    {
     230      std::stringstream covar_msg;
     231      covar_msg << "Covariance of the fit:\n";
     232      for(i=0; i<n; ++i){
     233        for(j=0; j<n; ++j)
     234          covar_msg << covar[i*n+j] << " ";
     235        covar_msg << std::endl;
     236      }
     237      covar_msg << std::endl;
     238      LOG(1, "INFO: " << covar_msg.str());
     239    }
     240
     241    free(work);
     242  }
     243
     244  if (DoLog(4)) {
     245   std::stringstream result_msg;
     246   result_msg << "Levenberg-Marquardt returned " << ret << " in " << info[5] << " iter, reason " << info[6] << "\nSolution: ";
     247   for(i=0; i<n; ++i)
     248     result_msg << p[i] << " ";
     249   result_msg << "\n\nMinimization info:\n";
     250   std::vector<std::string> infonames(LM_INFO_SZ);
     251   infonames[0] = std::string("||e||_2 at initial p");
     252   infonames[1] = std::string("||e||_2");
     253   infonames[2] = std::string("||J^T e||_inf");
     254   infonames[3] = std::string("||Dp||_2");
     255   infonames[4] = std::string("mu/max[J^T J]_ii");
     256   infonames[5] = std::string("# iterations");
     257   infonames[6] = std::string("reason for termination");
     258   infonames[7] = std::string(" # function evaluations");
     259   infonames[8] = std::string(" # Jacobian evaluations");
     260   infonames[9] = std::string(" # linear systems solved");
     261   for(i=0; i<LM_INFO_SZ; ++i)
     262      result_msg << infonames[i] << ": " << info[i] << " ";
     263    result_msg << std::endl;
     264    LOG(4, "DEBUG: " << result_msg.str());
     265  }
     266
     267  std::copy(p, p+n, weights.begin());
     268  LOG(4, "DEBUG: Weights for atom #" << _walker.getId() << ": " << weights);
     269
     270  delete[] p;
     271  delete[] x;
     272  delete[] matrix;
     273
     274  return weights;
     275}
     276
    113277BondVectors::weights_t BondVectors::getWeightsForAtomAtStep(
    114278    const atom &_walker,
     
    118282      getAtomsBondVectorsAtStep(_walker, _step);
    119283
    120   weights_t weights;
    121   for (std::vector<Vector>::const_iterator iter = BondVectors.begin();
    122       iter != BondVectors.end(); ++iter) {
     284  return getWeightsForAtomAtStep(_walker, BondVectors, _step);
     285}
     286
     287Vector BondVectors::getRemnantGradientForAtomAtStep(
     288    const atom &_walker,
     289    const std::vector<Vector> _BondVectors,
     290    const BondVectors::weights_t &_weights,
     291    const size_t &_step,
     292    forcestore_t _forcestore) const
     293{
     294  const Vector &walkerGradient = _walker.getAtomicForceAtStep(_step);
     295  BondVectors::weights_t::const_iterator weightiter = _weights.begin();
     296  std::vector<Vector>::const_iterator vectoriter = _BondVectors.begin();
     297  const BondList& ListOfBonds = _walker.getListOfBonds();
     298
     299  Vector forcesum;
     300  for(BondList::const_iterator bonditer = ListOfBonds.begin();
     301      bonditer != ListOfBonds.end(); ++bonditer, ++weightiter, ++vectoriter) {
     302    const bond::ptr &current_bond = *bonditer;
     303    const Vector &BondVector = *vectoriter;
     304
     305    const double temp = (*weightiter)*walkerGradient.ScalarProduct(BondVector);
     306    _forcestore(_walker, current_bond, _step, temp);
     307    LOG(4, "DEBUG: BondVector " << BondVector << " receives projected force of "
     308        << temp);
     309    forcesum += temp * BondVector;
     310  }
     311  ASSERT( weightiter == _weights.end(),
     312      "BondVectors::getRemnantGradientForAtomAtStep() - weightiter is not at end when it should be.");
     313  ASSERT( vectoriter == _BondVectors.end(),
     314      "BondVectors::getRemnantGradientForAtomAtStep() - vectoriter is not at end when it should be.");
     315
     316  return walkerGradient-forcesum;
     317}
     318
     319bool BondVectors::getCheckWeightSumForAtomAtStep(
     320    const atom &_walker,
     321    const std::vector<Vector> _BondVectors,
     322    const BondVectors::weights_t &_weights,
     323    const size_t &_step) const
     324{
     325  bool status = true;
     326  for (std::vector<Vector>::const_iterator iter = _BondVectors.begin();
     327      iter != _BondVectors.end(); ++iter) {
    123328    std::vector<double> scps;
    124     scps.reserve(BondVectors.size());
     329    scps.reserve(_BondVectors.size());
    125330    std::transform(
    126         BondVectors.begin(), BondVectors.end(),
    127         std::back_inserter(scps),
    128         boost::bind(static_cast< double (*)(double) >(&fabs),
    129             boost::bind(&Vector::ScalarProduct, boost::cref(*iter), _1))
    130         );
    131     const double scp_sum = std::accumulate(scps.begin(), scps.end(), 0.);
    132     ASSERT( (scp_sum-1.) > -MYEPSILON,
    133         "ForceAnnealing() - sum of weights must be equal or larger one but is "
    134         +toString(scp_sum));
    135     weights.push_back( 1./scp_sum );
    136   }
    137   LOG(4, "DEBUG: Weights for atom #" << _walker.getId() << ": " << weights);
    138 
    139   // for testing we check whether all weighted scalar products now come out as 1.
    140 #ifndef NDEBUG
    141   for (std::vector<Vector>::const_iterator iter = BondVectors.begin();
    142       iter != BondVectors.end(); ++iter) {
    143     std::vector<double> scps;
    144     scps.reserve(BondVectors.size());
    145     std::transform(
    146         BondVectors.begin(), BondVectors.end(),
    147         weights.begin(),
     331        _BondVectors.begin(), _BondVectors.end(),
     332        _weights.begin(),
    148333        std::back_inserter(scps),
    149334        boost::bind(static_cast< double (*)(double) >(&fabs),
     
    153338        );
    154339    const double scp_sum = std::accumulate(scps.begin(), scps.end(), 0.);
    155     ASSERT( fabs(scp_sum - 1.) < MYEPSILON,
    156         "ForceAnnealing::operator() - for BondVector "+toString(*iter)
    157         +" we have weighted scalar product of "+toString(scp_sum)+" != 1.");
    158   }
    159 #endif
    160   return weights;
    161 }
     340    if (fabs(scp_sum -1.) > MYEPSILON)
     341      status = false;
     342  }
     343
     344  return status;
     345}
  • src/Dynamics/BondVectors.hpp

    r7b4e67 rf433ec  
    1717#include <map>
    1818#include <vector>
     19
     20#include <boost/function.hpp>
    1921
    2022#include "CodePatterns/Assert.hpp"
     
    9395   *
    9496   * \param _walker atom to get BondVectors for
     97   * \param _bondvectors precalculated bond vectors for given \a _walker
     98   * \param _step time step for which the bond vector is request
     99   */
     100  weights_t getWeightsForAtomAtStep(
     101      const atom &_walker,
     102      const std::vector<Vector> &_bondvectors,
     103      const size_t &_step) const;
     104
     105  /** Calculates the weights for a frame where each Bondvector of the
     106   * given atom is a vector of the frame.
     107   *
     108   * The idea is that we can represent any vector by appropriate weights such
     109   * that is still sums up to one.
     110   *
     111   * \param _walker atom to get BondVectors for
    95112   * \param _step time step for which the bond vector is request
    96113   */
     
    98115      const atom &_walker,
    99116      const size_t &_step) const;
     117
     118  /** Function typedef to store the bond gradient into a specific container
     119   * depending on the atom, its current bond and the time step.
     120   */
     121  typedef boost::function<void (
     122      const atom &,
     123      const bond::ptr &,
     124      const size_t &,
     125      const double)> forcestore_t;
     126
     127  /** Function calculates the remaining part of the atomic gradient that is
     128   * not captured by the sum of the force along the Bond Vectors.
     129   *
     130   * \param _walker atom to get BondVectors for
     131   * \param _BondVectors precalculated bond vectors for given \a _walker
     132   * \param _weights weight per bond vector (as it is a frame, not a basis)
     133   * \param _step time step for which the bond vector is request
     134   * \param _forcestore additional function which may be used to store each
     135   *        calculated bond force in a bound container
     136   */
     137  Vector getRemnantGradientForAtomAtStep(
     138      const atom &_walker,
     139      const std::vector<Vector> _BondVectors,
     140      const BondVectors::weights_t &_weights,
     141      const size_t &_step,
     142      forcestore_t _forcestore) const;
    100143
    101144private:
     
    105148   */
    106149  void recalculateBondVectorsAtStep(const size_t &_step) const;
     150
     151  /** Helper function to check whether weights sum up to one for each
     152   * Bond Vector.
     153   *
     154   * \param _walker atom to get BondVectors for
     155   * \param _BondVectors precalculated bond vectors for given \a _walker
     156   * \param _weights weight per bond vector (as it is a frame, not a basis)
     157   * \param _step time step for which the bond vector is request
     158   */
     159  bool getCheckWeightSumForAtomAtStep(
     160      const atom &_walker,
     161      const std::vector<Vector> _BondVectors,
     162      const BondVectors::weights_t &_weights,
     163      const size_t &_step) const;
    107164
    108165private:
  • src/Dynamics/ForceAnnealing.hpp

    r7b4e67 rf433ec  
    119119      LOG(2, "DEBUG: current step is #" << currentStep);
    120120
     121      // bond graph annealing is always followed by a normal annealing
    121122      if (_UseBondgraph)
    122123        annealWithBondGraph(_CurrentTimeStep, _offset, maxComponents);
    123       else
    124         anneal(_CurrentTimeStep, _offset, maxComponents);
     124      anneal(_CurrentTimeStep, _offset, maxComponents);
    125125    }
    126126
     
    217217  }
    218218
     219  // knowing the number of bonds in total, we can setup the storage for the
     220  // projected forces
     221  enum whichatom_t {
     222    leftside=0,
     223    rightside=1,
     224    MAX_sides
     225  };
     226
     227  /** Helper function to put bond force into a container.
     228   *
     229   * \param _walker atom
     230   * \param _current_bond current bond of \a _walker
     231   * \param _timestep time step
     232   * \param _force calculated bond force
     233   * \param _bv bondvectors for obtaining the correct index
     234   * \param _projected_forces container
     235   */
     236  static void ForceStore(
     237      const atom &_walker,
     238      const bond::ptr &_current_bond,
     239      const size_t &_timestep,
     240      const double _force,
     241      const BondVectors &_bv,
     242      std::vector< // time step
     243            std::vector< // which bond side
     244              std::vector<double> > // over all bonds
     245                > &_projected_forces)
     246  {
     247    std::vector<double> &forcelist = (&_walker == _current_bond->leftatom) ?
     248        _projected_forces[_timestep][leftside] : _projected_forces[_timestep][rightside];
     249    const size_t index = _bv.getIndexForBond(_current_bond);
     250    ASSERT( index != (size_t)-1,
     251        "ForceAnnealing() - could not find bond "+toString(*_current_bond)
     252        +" in bondvectors");
     253    forcelist[index] = _force;
     254  }
     255
    219256  /** Performs Gradient optimization on the bonds.
    220257   *
     
    274311    const BondVectors::container_t &sorted_bonds = bv.getSorted();
    275312
    276     // knowing the number of bonds in total, we can setup the storage for the
    277     // projected forces
    278     enum whichatom_t {
    279       leftside=0,
    280       rightside=1,
    281       MAX_sides
    282     };
    283313    std::vector< // time step
    284314      std::vector< // which bond side
     
    294324    typedef std::map<atomId_t, BondVectors::weights_t > weights_per_atom_t;
    295325    std::vector<weights_per_atom_t> weights_per_atom(2);
     326    typedef std::map<atomId_t, Vector> RemnantGradient_per_atom_t;
     327    RemnantGradient_per_atom_t RemnantGradient_per_atom;
    296328    for (size_t timestep = 0; timestep <= 1; ++timestep) {
    297329      const size_t CurrentStep = CurrentTimeStep-timestep-1 >= 0 ? CurrentTimeStep-timestep-1 : 0;
     
    320352              weights_per_atom[timestep].insert(
    321353                  std::make_pair(walker.getId(),
    322                   bv.getWeightsForAtomAtStep(walker, CurrentStep)) );
     354                  bv.getWeightsForAtomAtStep(walker, BondVectors, CurrentStep)) );
    323355          ASSERT( inserter.second,
    324356              "ForceAnnealing::operator() - weight map for atom "+toString(walker)
     
    332364          // projected gradient over all bonds and place in one of projected_forces
    333365          // using the obtained weights
    334           {
    335             BondVectors::weights_t::const_iterator weightiter = weights.begin();
    336             std::vector<Vector>::const_iterator vectoriter = BondVectors.begin();
    337             Vector forcesum_check;
    338             for(BondList::const_iterator bonditer = ListOfBonds.begin();
    339                 bonditer != ListOfBonds.end(); ++bonditer, ++weightiter, ++vectoriter) {
    340               const bond::ptr &current_bond = *bonditer;
    341               const Vector &BondVector = *vectoriter;
    342 
    343               std::vector<double> &forcelist = (&walker == current_bond->leftatom) ?
    344                   projected_forces[timestep][leftside] : projected_forces[timestep][rightside];
    345               const size_t index = bv.getIndexForBond(current_bond);
    346               ASSERT( index != (size_t)-1,
    347                   "ForceAnnealing() - could not find bond "+toString(*current_bond)
    348                   +" in bondvectors");
    349               forcelist[index] = (*weightiter)*walkerGradient.ScalarProduct(BondVector);
    350               LOG(4, "DEBUG: BondVector " << BondVector << " receives projected force of "
    351                   << forcelist[index]);
    352               forcesum_check += forcelist[index] * BondVector;
    353             }
    354             ASSERT( weightiter == weights.end(),
    355                 "ForceAnnealing - weightiter is not at end when it should be.");
    356             ASSERT( vectoriter == BondVectors.end(),
    357                 "ForceAnnealing - vectoriter is not at end when it should be.");
    358             LOG(3, "DEBUG: sum of projected forces is " << forcesum_check);
    359           }
    360 
     366          BondVectors::forcestore_t forcestoring =
     367              boost::bind(&ForceAnnealing::ForceStore, _1, _2, _3, _4,
     368                  boost::cref(bv), boost::ref(projected_forces));
     369          const Vector RemnantGradient = bv.getRemnantGradientForAtomAtStep(
     370              walker, BondVectors, weights, timestep, forcestoring
     371          );
     372          RemnantGradient_per_atom.insert( std::make_pair(walker.getId(), RemnantGradient) );
    361373        } else {
    362374          LOG(2, "DEBUG: Gradient is " << walkerGradient << " less than "
     
    487499    }
    488500
    489     // apply the gathered updates
     501    // apply the gathered updates and set remnant gradients for atomic annealing
    490502    for (std::map<atomId_t, Vector>::const_iterator iter = GatheredUpdates.begin();
    491503        iter != GatheredUpdates.end(); ++iter) {
     
    501513          + update);
    502514      walker->setAtomicVelocity(update);
    503 //      walker->setAtomicForce( RemnantGradient_per_atom[walker->getId()] );
     515      walker->setAtomicForce( RemnantGradient_per_atom[walker->getId()] );
    504516    }
    505517  }
  • src/Dynamics/unittests/BondVectorsUnitTest.cpp

    r7b4e67 rf433ec  
    197197  CPPUNIT_ASSERT( fabs(weight_sum - 1.) < MYEPSILON );
    198198  // check weight
    199   CPPUNIT_ASSERT_EQUAL( weights[0], 1. );
     199  CPPUNIT_ASSERT( fabs(weight_sum - 1.) < 1e-10 );
    200200}
    201201
     
    289289  // check number of weights
    290290  CPPUNIT_ASSERT_EQUAL( weights.size(), (size_t)5 );
    291   // check sum of weights: one linear independent, two dependent vectors = 1 + 2*0.5
    292   const double weight_sum = std::accumulate(weights.begin(), weights.end(), 0.);
    293   CPPUNIT_ASSERT( fabs(weight_sum - 2.) < 1e-10 );
    294 }
     291  // check sum of weights
     292  const double weight_sum = std::accumulate(weights.begin(), weights.end(), 0.);
     293  CPPUNIT_ASSERT( fabs(weights[0] - .372244) < 1e-6 );
     294  CPPUNIT_ASSERT( fabs(weights[1] - .529694) < 1e-6 );
     295  CPPUNIT_ASSERT( fabs(weights[2] - .2) < 1e-6 );
     296  CPPUNIT_ASSERT( fabs(weights[3] - .248464) < 1e-6 );
     297  CPPUNIT_ASSERT( fabs(weights[4] - .248464) < 1e-6 );
     298}
  • src/Dynamics/unittests/Makefile.am

    r7b4e67 rf433ec  
    1111  BondVectorsUnitTest
    1212
    13 XFAIL_TESTS += BondVectorsUnitTest
    14  
    1513TESTS += $(DYNAMICSTESTS)
    1614check_PROGRAMS += $(DYNAMICSTESTS)
Note: See TracChangeset for help on using the changeset viewer.