Changeset b3aaf4 for src/Dynamics


Ignore:
Timestamp:
Jun 20, 2018, 8:21:13 AM (7 years ago)
Author:
Frederik Heber <frederik.heber@…>
Branches:
Candidate_v1.6.1, ChemicalSpaceEvaluator, Exclude_Hydrogens_annealWithBondGraph, ForceAnnealing_with_BondGraph_contraction-expansion
Children:
31baf0
Parents:
883d98
git-author:
Frederik Heber <frederik.heber@…> (12/05/17 09:10:11)
git-committer:
Frederik Heber <frederik.heber@…> (06/20/18 08:21:13)
Message:

Preventing cancelling updates for optimization with bond graph.

  • we check norm of single updates against gathered update. If the latter is very small, we perform a regular optimization step.
  • extracted method annealAtom_BB() to this end.
  • Moreover, we cap the maximum BB update at 0.5 as it tends to overshoot.
  • TESTS: This fixes the issue with 2-body Ising model Python regression test where this issue occurs.
File:
1 edited

Legend:

Unmodified
Added
Removed
  • src/Dynamics/ForceAnnealing.hpp

    r883d98 rb3aaf4  
    1717#include <functional>
    1818#include <iterator>
     19#include <math.h>
    1920
    2021#include <boost/bind.hpp>
     
    167168  {
    168169    double stepwidth = 0.;
    169     if (_GradientDifference.NormSquared() > MYEPSILON)
     170    if (_GradientDifference.Norm() > MYEPSILON)
    170171      stepwidth = fabs(_PositionDifference.ScalarProduct(_GradientDifference))/
    171172          _GradientDifference.NormSquared();
     
    200201        iter != AtomicForceManipulator<T>::atoms.end(); ++iter) {
    201202      // atom's force vector gives steepest descent direction
    202       const Vector currentPosition = (*iter)->getPositionAtStep(CurrentTimeStep);
    203       const Vector currentGradient = (*iter)->getAtomicForceAtStep(CurrentTimeStep);
     203      const Vector &currentPosition = (*iter)->getPositionAtStep(CurrentTimeStep);
     204      const Vector &currentGradient = (*iter)->getAtomicForceAtStep(CurrentTimeStep);
    204205      LOG(4, "DEBUG: currentPosition for atom #" << (*iter)->getId() << " is " << currentPosition);
    205206      LOG(4, "DEBUG: currentGradient for atom #" << (*iter)->getId() << " is " << currentGradient);
     
    248249  }
    249250
     251  /** Performs Gradient optimization on a single atom using BarzilaiBorwein step width.
     252   *
     253   * \param _atom atom to anneal
     254   * \param OldTimeStep old time step
     255   * \param CurrentTimeStep current time step whose gradient we've just calculated
     256   * \param TimeStepToSet time step to update (i.e. \f$ t + \Delta t \f$ in the sense of the velocity verlet)
     257   */
     258  void annealAtom_BarzilaiBorwein(
     259      atom * const _atom,
     260      const int &OldTimeStep,
     261      const int &CurrentTimeStep,
     262      const int &TimeStepToSet
     263      )
     264  {
     265    // atom's force vector gives steepest descent direction
     266    const Vector &oldPosition = _atom->getPositionAtStep(OldTimeStep);
     267    const Vector &currentPosition = _atom->getPositionAtStep(CurrentTimeStep);
     268    const Vector &oldGradient = _atom->getAtomicForceAtStep(OldTimeStep);
     269    const Vector &currentGradient = _atom->getAtomicForceAtStep(CurrentTimeStep);
     270    LOG(4, "DEBUG: oldPosition for atom #" << _atom->getId() << " is " << oldPosition);
     271    LOG(4, "DEBUG: currentPosition for atom #" << _atom->getId() << " is " << currentPosition);
     272    LOG(4, "DEBUG: oldGradient for atom #" << _atom->getId() << " is " << oldGradient);
     273    LOG(4, "DEBUG: currentGradient for atom #" << _atom->getId() << " is " << currentGradient);
     274//      LOG(4, "DEBUG: Force for atom #" << _atom->getId() << " is " << currentGradient);
     275
     276    // we use Barzilai-Borwein update with position reversed to get descent
     277    const Vector PositionDifference = currentPosition - oldPosition;
     278    const Vector GradientDifference = (currentGradient - oldGradient);
     279    double stepwidth = getBarzilaiBorweinStepwidth(PositionDifference, GradientDifference);
     280    Vector PositionUpdate = stepwidth * currentGradient;
     281    LOG(3, "DEBUG: Update would be " << stepwidth << "*" << currentGradient << " = " << PositionUpdate);
     282
     283    // finally set new values
     284    _atom->setPositionAtStep(TimeStepToSet, currentPosition + PositionUpdate);
     285  }
     286
    250287  /** Performs Gradient optimization on the atoms using BarzilaiBorwein step width.
    251288   *
    252289   * \note this can only be called when there are at least two optimization
    253    * time steps present, i.e. this must be preceeded by a simple anneal().
     290   * time steps present, i.e. this must be preceded by a simple anneal().
    254291   *
    255292   * We assume that forces have just been calculated.
     
    274311    for(typename AtomSetMixin<T>::iterator iter = AtomicForceManipulator<T>::atoms.begin();
    275312        iter != AtomicForceManipulator<T>::atoms.end(); ++iter) {
    276       // atom's force vector gives steepest descent direction
    277       const Vector &oldPosition = (*iter)->getPositionAtStep(OldTimeStep);
    278       const Vector &currentPosition = (*iter)->getPositionAtStep(CurrentTimeStep);
    279       const Vector &oldGradient = (*iter)->getAtomicForceAtStep(OldTimeStep);
     313
     314      annealAtom_BarzilaiBorwein(*iter, OldTimeStep, CurrentTimeStep, _TimeStep);
     315
     316      // extract largest components for showing progress of annealing
    280317      const Vector &currentGradient = (*iter)->getAtomicForceAtStep(CurrentTimeStep);
    281       LOG(4, "DEBUG: oldPosition for atom #" << (*iter)->getId() << " is " << oldPosition);
    282       LOG(4, "DEBUG: currentPosition for atom #" << (*iter)->getId() << " is " << currentPosition);
    283       LOG(4, "DEBUG: oldGradient for atom #" << (*iter)->getId() << " is " << oldGradient);
    284       LOG(4, "DEBUG: currentGradient for atom #" << (*iter)->getId() << " is " << currentGradient);
    285 //      LOG(4, "DEBUG: Force for atom #" << (*iter)->getId() << " is " << currentGradient);
    286 
    287       // we use Barzilai-Borwein update with position reversed to get descent
    288       const Vector PositionDifference = currentPosition - oldPosition;
    289       const Vector GradientDifference = (currentGradient - oldGradient);
    290       double stepwidth = getBarzilaiBorweinStepwidth(PositionDifference, GradientDifference);
    291       Vector PositionUpdate = stepwidth * currentGradient;
    292       LOG(3, "DEBUG: Update would be " << stepwidth << "*" << currentGradient << " = " << PositionUpdate);
    293 
    294       // extract largest components for showing progress of annealing
    295318      for(size_t i=0;i<NDIM;++i)
    296319        maxComponents[i] = std::max(maxComponents[i], fabs(currentGradient[i]));
    297 
    298       // finally set new values
    299       (*iter)->setPositionAtStep(_TimeStep, currentPosition + PositionUpdate);
    300320    }
    301321
     
    399419
    400420    std::map<atomId_t, Vector> GatheredUpdates; //!< gathers all updates which are applied at the end
     421    std::map<atomId_t, double> LargestUpdate_per_Atom; //!< check whether updates cancelled each other
    401422    for(typename AtomSetMixin<T>::iterator iter = AtomicForceManipulator<T>::atoms.begin();
    402423        iter != AtomicForceManipulator<T>::atoms.end(); ++iter) {
     
    428449      }
    429450      Vector PositionUpdate = stepwidth * currentGradient;
     451      // cap updates (if non-zero) at 0.2 angstroem. BB tends to overshoot.
     452      for (size_t i=0;i<NDIM;++i)
     453        if (fabs(PositionUpdate[i]) > MYEPSILON)
     454          PositionUpdate[i] = std::min(0.5, fabs(PositionUpdate[i]))*PositionUpdate[i]/fabs(PositionUpdate[i]);
    430455      LOG(3, "DEBUG: Update would be " << stepwidth << "*" << currentGradient << " = " << PositionUpdate);
    431456
     
    558583        // hence, the decision is only based on sides once we have picked a side
    559584        // depending on all bonds associated with have same good type.
    560         const double sign = sides[first_per_side[sideno]];
     585
     586        // away from gradient (minus) and contracting
     587        // or towards gradient (plus) and expanding
     588        // gather all on same side and remove
     589        const double sign =
     590            (sides[first_per_side[sideno]] == types[first_per_side[sideno]])
     591            ? sides[first_per_side[sideno]] : -1.*sides[first_per_side[sideno]];
     592
    561593        LOG(4, "DEBUG: Removing edges from side with sign " << sign);
    562594        BondList::const_iterator bonditer = ListOfBonds.begin();
     
    603635          if (GatheredUpdates.count((*setiter))) {
    604636            GatheredUpdates[(*setiter)] += factor*PositionUpdate;
     637            LargestUpdate_per_Atom[(*setiter)] =
     638                std::max(LargestUpdate_per_Atom[(*setiter)], factor*PositionUpdate.Norm());
    605639          } else {
    606640            GatheredUpdates.insert(
     
    608642                    (*setiter),
    609643                    factor*PositionUpdate) );
     644            LargestUpdate_per_Atom.insert(
     645                std::make_pair(
     646                    (*setiter),
     647                    factor*PositionUpdate.Norm()) );
    610648          }
    611649        }
     
    617655                walker.getId(),
    618656                PositionUpdate) );
     657        LargestUpdate_per_Atom.insert(
     658            std::make_pair(
     659                walker.getId(),
     660                PositionUpdate.Norm()) );
    619661      }
    620662    }
     
    624666      atom &walker = *(*iter);
    625667      // extract largest components for showing progress of annealing
    626       const Vector currentGradient = walker.getAtomicForceAtStep(CurrentTimeStep);
     668      const Vector &currentGradient = walker.getAtomicForceAtStep(CurrentTimeStep);
    627669      for(size_t i=0;i<NDIM;++i)
    628670        maxComponents[i] = std::max(maxComponents[i], fabs(currentGradient[i]));
     
    653695      for (size_t i=0;i<NDIM;++i)
    654696        LargestUpdate[i] = std::max(LargestUpdate[i], fabs(update[i]));
    655       walker->setPositionAtStep(_TimeStep,
    656           walker->getPositionAtStep(CurrentTimeStep) + update); // - CommonTranslation);
     697
     698      std::map<atomId_t, double>::const_iterator largestiter = LargestUpdate_per_Atom.find(atomid);
     699      ASSERT( largestiter != LargestUpdate_per_Atom.end(),
     700          "ForceAnnealing() - walker with id "+toString(atomid)+" not in LargestUpdates.");
     701      // if we had large updates but their sum is very small
     702      if (update.Norm()/largestiter->second > MYEPSILON) {
     703        walker->setPositionAtStep(_TimeStep,
     704            walker->getPositionAtStep(CurrentTimeStep) + update); // - CommonTranslation);
     705      } else {
     706        // then recalc update with simple anneal
     707        LOG(2, "WARNING: Updates on atom " << *iter << " cancel themselves, performing simple anneal step.");
     708        annealAtom_BarzilaiBorwein(walker, OldTimeStep, CurrentTimeStep, _TimeStep);
     709      }
    657710    }
    658711    LOG(1, "STATUS: Largest absolute update components are " << LargestUpdate);
Note: See TracChangeset for help on using the changeset viewer.