def __init__(self, coords, potential, eigenvec0=None, rotational_steps=20, translational_steps=10, maxiter=500, leig_kwargs=None, translator_kwargs=None, dimer=True, ): coords = coords.copy() self.rotational_steps = rotational_steps self.translational_steps = translational_steps self.maxiter = maxiter self.iter_number = 0 # check the keyword dictionaries if translator_kwargs is None: translator_kwargs = {} if leig_kwargs is None: leig_kwargs = {} # set up the initial guess for the eigenvector if eigenvec0 is None: eigenvec0 = rotations.vec_random_ndim(coords.shape) eigenvec0 /= np.linalg.norm(eigenvec0) assert coords.shape == eigenvec0.shape # set up the object that will maintain the rotation of the dimer self.rotator = FindLowestEigenVector(coords, potential, eigenvec0=eigenvec0, **leig_kwargs) # set up the object that will translate the dimer if dimer: self.translator = _DimerTranslator(coords, potential, eigenvec0, **translator_kwargs) else: self.translator = _HybridEigenvectorWalker(coords, potential, eigenvec0, **translator_kwargs)
def _getLowestEigenVector(self, coords, i, gradient=None): """compute the lowest eigenvector at position coords Parameters ---------- coords : the current position i : the iteration number gradient : the gradient at coords """ if "nsteps" in self.lowestEigenvectorQuenchParams: niter = self.lowestEigenvectorQuenchParams["nsteps"] else: niter = 100 if self.verbosity > 3: print "Using default of", niter, "steps for finding lowest eigenvalue" optimizer = FindLowestEigenVector(coords, self.pot, # H0=self.H0_leig, eigenvec0=self.eigenvec, orthogZeroEigs=self.orthogZeroEigs, gradient=gradient, **self.lowestEigenvectorQuenchParams) res = optimizer.run(niter) if res.nsteps == 0: if self.verbosity > 2: print "eigenvector converged, but doing one iteration anyway" optimizer.one_iteration() res = optimizer.get_result() self.leig_result = res self.nfev += res.nfev # self.H0_leig = res.H0 self.eigenvec = res.eigenvec self.eigenval = res.eigenval if i > 0: overlap = np.dot(self.oldeigenvec, res.eigenvec) if overlap < 0.5 and self.verbosity > 2: logger.info("warning: the new eigenvector has low overlap with previous %s %s", overlap, self.eigenval) else: overlap = 0. if res.success: self.nfail = 0 else: self.nfail += 1 self.oldeigenvec = self.eigenvec.copy() return overlap
def _get_lowest_eigenvector_RR(self, coords, gradient=None): """get the lowest eigenvector using Reyleigh Ritz minimization""" if "nsteps" in self.lowestEigenvectorQuenchParams: niter = self.lowestEigenvectorQuenchParams["nsteps"] else: niter = 100 if self.verbosity > 3: print("Using default of", niter, "steps for finding lowest eigenvalue") optimizer = FindLowestEigenVector(coords, self.pot, eigenvec0=self.eigenvec, orthogZeroEigs=self.orthogZeroEigs, gradient=gradient, **self.lowestEigenvectorQuenchParams) # H0=self.H0_leig, res = optimizer.run(niter) if res.nsteps == 0: if self.verbosity > 2: print("eigenvector converged, but doing one iteration anyway") optimizer.one_iteration() res = optimizer.get_result() self.H0_leig = res.H0 return res
def _get_lowest_eigenvector_RR(self, coords, gradient=None): """get the lowest eigenvector using Reyleigh Ritz minimization""" if "nsteps" in self.lowestEigenvectorQuenchParams: niter = self.lowestEigenvectorQuenchParams["nsteps"] else: niter = 100 if self.verbosity > 3: print "Using default of", niter, "steps for finding lowest eigenvalue" optimizer = FindLowestEigenVector(coords, self.pot, eigenvec0=self.eigenvec, orthogZeroEigs=self.orthogZeroEigs, gradient=gradient, **self.lowestEigenvectorQuenchParams) # H0=self.H0_leig, res = optimizer.run(niter) if res.nsteps == 0: if self.verbosity > 2: print "eigenvector converged, but doing one iteration anyway" optimizer.one_iteration() res = optimizer.get_result() self.H0_leig = res.H0 return res
class GeneralizedDimer(object): """Use the generalized dimer method to find a saddle point This should be considered experimental. It works, but I haven't spent enough time on it to make it very robust and efficient. I would recommend using FindTransitionState instead. Parameters ---------- coords : array The starting point for the optimization potential : the potential object eigenvec0 : array An initial guess for the smallest eigenvector (dimer direction) rotational_steps : int The number of iterations for optimizing the smallest eigenvector (rotating the dimer) between translational moves translational_steps : int The number of translational steps before re-optimizing the smallest eigenvector maxiter : int the maximum number of iterations minimizer_kwargs : dict these keyword arguments are passed to the minimizer that does the translational steps leig_kwargs : dict these keyword arguments are passed to the class for optimizing the smallest eigenvector Notes ----- The dimer method defines a way of walking along an energy surface towards a saddle point. The dimer is defined by a location and a direction, vec. vec will point along the direction of largest negative curvature, which corresponds to the eigenvector of the Hessian matrix with the smallest eigenvalue. The eigenvalue is the curvature. The eigenvector can be computed analytically if the Hessian is available, but this is often computationally expensive normally you would use gradients and an optimization function to converge towards the direction of largest curvature. If the gradient is inverted along the direction of the largest negative curvature then following that gradient will lead you towards a saddle point. As you walk towards the saddle point the direction of largest negative curvature will change, so it will need to be periodically re-optimized. Thus the main iteration loop is 1. Optimize the rotation of the dimer so that it is aligned with the direction of largest negative curvature. 2. Translate the dimer uphill following the direction of largest negative curvature while simultaneously walking downhill in all perpendicular directions. 3. If converged, then end, else go to 1. """ def __init__(self, coords, potential, eigenvec0=None, rotational_steps=20, translational_steps=10, maxiter=500, leig_kwargs=None, translator_kwargs=None, dimer=True, ): coords = coords.copy() self.rotational_steps = rotational_steps self.translational_steps = translational_steps self.maxiter = maxiter self.iter_number = 0 # check the keyword dictionaries if translator_kwargs is None: translator_kwargs = {} if leig_kwargs is None: leig_kwargs = {} # set up the initial guess for the eigenvector if eigenvec0 is None: eigenvec0 = rotations.vec_random_ndim(coords.shape) eigenvec0 /= np.linalg.norm(eigenvec0) assert coords.shape == eigenvec0.shape # set up the object that will maintain the rotation of the dimer self.rotator = FindLowestEigenVector(coords, potential, eigenvec0=eigenvec0, **leig_kwargs) # set up the object that will translate the dimer if dimer: self.translator = _DimerTranslator(coords, potential, eigenvec0, **translator_kwargs) else: self.translator = _HybridEigenvectorWalker(coords, potential, eigenvec0, **translator_kwargs) def get_true_energy_gradient(self, coords): """return the true energy and gradient""" return self.translator.get_true_energy_gradient(coords) # def get_true_energy(self): # """return the true energy""" # return self.translator.get_energy() # # def get_true_gradient(self): # """return the true gradient""" # # these are stored in dimer_potential # return self.translator.get_gradient() def get_coords(self): """return the current location of the dimer""" mret = self.translator.get_result() return mret.coords def stop_criterion_satisfied(self): """return True if the stop criterion is satisfied""" return self.translator.stop_criterion_satisfied() and self.rotator.stop_criterion_satisfied() def one_iteration(self): """do one iteration""" # update the eigenvector (rotate the dimer) # print "rotating dimer" energy, gradient = self.get_true_energy_gradient(self.get_coords()) self.rotator.update_coords(self.get_coords(), gradient=gradient) ret = self.rotator.run(self.rotational_steps) # update the eigenvector and eigenvalue in the dimer_potential self.translator.update_eigenvec(ret.eigenvec, ret.eigenval) # translate the dimer # print "translating dimer" self.translator.run(self.translational_steps) self.iter_number += 1 def run(self): """the main iteration loop""" while not self.stop_criterion_satisfied() and self.iter_number < self.maxiter: self.one_iteration() return self.get_result() def get_result(self): """return a results object""" trans_res = self.translator.get_result() rot_result = self.rotator.get_result() res = Result() res.eigenval = rot_result.eigenval res.eigenvec = rot_result.eigenvec res.coords = trans_res.coords res.energy, res.grad = self.get_true_energy_gradient(res.coords) res.rms = trans_res.rms res.nfev = rot_result.nfev + trans_res.nfev res.nsteps = self.iter_number res.success = self.stop_criterion_satisfied() if res.eigenval > 0: res.success = False return res