Ejemplo n.º 1
0
def lbfgs_scipy(coords, pot, iprint=-1, tol=1e-3, nsteps=15000):
    """
    a wrapper function for lbfgs routine in scipy
    
    .. warn::
        the scipy version of lbfgs uses linesearch based only on energy
        which can make the minimization stop early.  When the step size
        is so small that the energy doesn't change to within machine precision (times the
        parameter `factr`) the routine declares success and stops.  This sounds fine, but
        if the gradient is analytical the gradient can still be not converged.  This is
        because in the vicinity of the minimum the gradient changes much more rapidly then
        the energy.  Thus we want to make factr as small as possible.  Unfortunately,
        if we make it too small the routine realizes that the linesearch routine
        isn't working and declares failure and exits.
        
        So long story short, if your tolerance is very small (< 1e-6) this routine
        will probably stop before truly reaching that tolerance.  If you reduce `factr` 
        too much to mitigate this lbfgs will stop anyway, but declare failure misleadingly.  
    """
    import scipy.optimize

    res = Result()
    res.coords, res.energy, dictionary = scipy.optimize.fmin_l_bfgs_b(
        pot.getEnergyGradient,
        coords,
        iprint=iprint,
        pgtol=tol,
        maxfun=nsteps,
        factr=10.)
    res.grad = dictionary["grad"]
    res.nfev = dictionary["funcalls"]
    warnflag = dictionary['warnflag']
    # res.nsteps = dictionary['nit'] #  new in scipy version 0.12
    res.nsteps = res.nfev
    res.message = dictionary['task']
    res.success = True
    if warnflag > 0:
        print("warning: problem with quench: ", end=' ')
        res.success = False
        if warnflag == 1:
            res.message = "too many function evaluations"
        else:
            res.message = str(dictionary['task'])
        print(res.message)
    # note: if the linesearch fails the lbfgs may fail without setting warnflag.  Check
    # tolerance exactly
    if False:
        if res.success:
            maxV = np.max(np.abs(res.grad))
            if maxV > tol:
                print(
                    "warning: gradient seems too large", maxV, "tol =", tol,
                    ". This is a known, but not understood issue of scipy_lbfgs"
                )
                print(res.message)
    res.rms = res.grad.std()
    return res
Ejemplo n.º 2
0
def ode_julia_naive(coords,
                    pot,
                    tol=1e-4,
                    nsteps=2000,
                    convergence_check=None,
                    solver_type=de.ROCK2(),
                    reltol=1e-5,
                    abstol=1e-5,
                    **kwargs):
    class feval_pot:
        """ wrapper class that makes sure the function is right
        """
        def __init__(self):
            self.nfev = 0

        def get_negative_grad(self, x, p, t):
            self.nfev += 1
            return -pot.getEnergyGradient(x.copy())[1]

        def get_energy_gradient(self, x):
            self.nfev += 1
            return pot.getEnergyGradient(x.copy())

    function_evaluate_pot = feval_pot()
    converged = False
    n = 0
    if convergence_check == None:
        convergence_check = lambda g: np.max(g) < tol

    # initialize ode problem
    tspan = (0.0, 10.0)
    # tspan
    # tspan = (0.0, 10.0)
    prob = de.ODEProblem(function_evaluate_pot.get_negative_grad, coords,
                         tspan)
    integrator = de.init(prob, solver_type, reltol=reltol, abstol=abstol)
    x_ = np.full(len(coords), np.nan)
    while not converged and n < nsteps:
        xold = x_
        de.step_b(integrator)
        x_ = integrator.u
        n += 1
        converged = convergence_check(de.get_du(integrator))
        if converged:
            print(de.get_du(integrator))
    res = Result()
    res.coords = x_
    res.energy = pot.getEnergy(x_)
    res.rms = 0
    res.grad = 0
    res.nfev = function_evaluate_pot.nfev
    res.nsteps = n
    res.success = converged
    return res
Ejemplo n.º 3
0
def lbfgs_scipy(coords, pot, iprint=-1, tol=1e-3, nsteps=15000):
    """
    a wrapper function for lbfgs routine in scipy
    
    .. warn::
        the scipy version of lbfgs uses linesearch based only on energy
        which can make the minimization stop early.  When the step size
        is so small that the energy doesn't change to within machine precision (times the
        parameter `factr`) the routine declares success and stops.  This sounds fine, but
        if the gradient is analytical the gradient can still be not converged.  This is
        because in the vicinity of the minimum the gradient changes much more rapidly then
        the energy.  Thus we want to make factr as small as possible.  Unfortunately,
        if we make it too small the routine realizes that the linesearch routine
        isn't working and declares failure and exits.
        
        So long story short, if your tolerance is very small (< 1e-6) this routine
        will probably stop before truly reaching that tolerance.  If you reduce `factr` 
        too much to mitigate this lbfgs will stop anyway, but declare failure misleadingly.  
    """
    if not hasattr(pot, "getEnergyGradient"):
        # for compatibility with old quenchers.
        # assume pot is a getEnergyGradient function
        pot = _getEnergyGradientWrapper(pot)
    import scipy.optimize
    res = Result()
    res.coords, res.energy, dictionary = scipy.optimize.fmin_l_bfgs_b(pot.getEnergyGradient, 
            coords, iprint=iprint, pgtol=tol, maxfun=nsteps, factr=10.)
    res.grad = dictionary["grad"]
    res.nfev = dictionary["funcalls"]
    warnflag = dictionary['warnflag']
    #res.nsteps = dictionary['nit'] #  new in scipy version 0.12
    res.nsteps = res.nfev
    res.message = dictionary['task']
    res.success = True
    if warnflag > 0:
        print "warning: problem with quench: ",
        res.success = False
        if warnflag == 1:
            res.message = "too many function evaluations"
        else:
            res.message = str(dictionary['task'])
        print res.message
    #note: if the linesearch fails the lbfgs may fail without setting warnflag.  Check
    #tolerance exactly
    if False:
        if res.success:
            maxV = np.max( np.abs(res.grad) )
            if maxV > tol:
                print "warning: gradient seems too large", maxV, "tol =", tol, ". This is a known, but not understood issue of scipy_lbfgs"
                print res.message
    res.rms = res.grad.std()
    return res
Ejemplo n.º 4
0
 def get_results(self):
     res = Result()
     with self.g.as_default(), self.g.device(self.device):
         with self.session.as_default():
             g, x = self.compute_gradients(self.model.gloss,
                                           var_list=[self.model.x])[0]
             g, x = self.process_grad(g, x)
             res.energy = self.model.gloss.eval()
             res.coords = x.eval()
             res.grad = g.eval()
             res.nfev = self.nfev.eval()
             res.rms = self.rms.eval()
             res.success = self.success.eval()
             res.nsteps = self.neval
     return res
Ejemplo n.º 5
0
def steepest_descent(x0,
                     pot,
                     iprint=-1,
                     dx=1e-4,
                     nsteps=100000,
                     tol=1e-3,
                     maxstep=-1.,
                     events=None):
    """steepest descent minimization
    
    Notes
    -----
    this should never be used except for testing purposes.  It is a bad implementation
    of a terrible minimization routine.  It will be very slow.
    """
    N = len(x0)
    x = x0.copy()
    E, V = pot.getEnergyGradient(x)
    funcalls = 1
    for k in xrange(nsteps):
        stp = -V * dx
        if maxstep > 0:
            stpsize = np.max(np.abs(V))
            if stpsize > maxstep:
                stp *= maxstep / stpsize
        x += stp
        E, V = pot.getEnergyGradient(x)
        funcalls += 1
        rms = np.linalg.norm(V) / np.sqrt(N)
        if iprint > 0:
            if funcalls % iprint == 0:
                print "step %8d energy %20.12g rms gradient %20.12g" % (
                    funcalls, E, rms)
        if events is not None:
            for event in events:
                event(energy=E, coords=x, rms=rms)
        if rms < tol:
            break
    res = Result()
    res.coords = x
    res.energy = E
    res.rms = rms
    res.grad = V
    res.nfev = funcalls
    res.nsteps = k
    res.success = res.rms <= tol
    return res
Ejemplo n.º 6
0
def bfgs_scipy(coords, pot, iprint=-1, tol=1e-3, nsteps=5000, **kwargs):
    """
    a wrapper function for the scipy BFGS algorithm
    """
    import scipy.optimize
    ret = scipy.optimize.fmin_bfgs(pot.getEnergy, coords, fprime=pot.getGradient,
                                   gtol=tol, full_output=True, disp=iprint>0, 
                                   maxiter=nsteps, **kwargs)
    res = Result()
    res.coords = ret[0]
    res.energy = ret[1]
    res.grad = ret[2]
    res.rms = np.linalg.norm(res.grad) / np.sqrt(len(res.grad))
    res.nfev = ret[4] + ret[5]
    res.nsteps = res.nfev #  not correct, but no better information
    res.success = np.max(np.abs(res.grad)) < tol
    return res
Ejemplo n.º 7
0
    def run(self, fmax=1e-3, steps=100000):
        """Run structure optimization algorithm.

        This method will return when the forces on all individual
        atoms are less than *fmax* or when the number of steps exceeds
        *steps*."""

        self.fmax = fmax
        step = 0
        res = Result()
        res.success = False
        while step < steps:
            E, f = self.potential.getEnergyGradient(self.coords)
            self.nfev += 1
            #self.call_observers()
            #print E
            if self.alternate_stop_criterion is None:
                i_am_done = self.converged(f)
            else:
                i_am_done = self.alternate_stop_criterion(energy=E, gradient=f, 
                                                          tol=self.fmax)                
            if i_am_done:
                res.success = True
                break
            self.step(-f)
            self.nsteps += 1
            rms = np.linalg.norm(f)/np.sqrt(len(f))
            if self.iprint > 0:
                if step % self.iprint == 0:
                    self.logger.info("fire: %s E %s rms %s", step, E, rms)
            for event in self.events:
                event(coords=self.coords, energy=E, rms=rms)

            step += 1
            
        res.nsteps = step
        res.nfev = self.nfev
        res.coords = self.coords
        res.energy = E
        res.grad = -f
        res.rms = np.linalg.norm(res.grad)/np.sqrt(len(res.grad))
        self.result = res
        return res
Ejemplo n.º 8
0
    def run(self, fmax=1e-3, steps=100000):
        """Run structure optimization algorithm.

        This method will return when the forces on all individual
        atoms are less than *fmax* or when the number of steps exceeds
        *steps*.
        """
        self.fmax = fmax
        step = 0
        res = Result()
        res.success = False
        while step < steps:
            E, f = self.potential.getEnergyGradient(self.coords)
            self.nfev += 1
            if self.alternate_stop_criterion is None:
                i_am_done = self.converged(f)
            else:
                i_am_done = self.alternate_stop_criterion(energy=E,
                                                          gradient=f,
                                                          tol=self.fmax)
            if i_am_done:
                res.success = True
                break
            self.step(-f)
            self.nsteps += 1
            rms = np.linalg.norm(f) / np.sqrt(len(f))
            if self.iprint > 0:
                if step % self.iprint == 0:
                    self.logger.info("fire: %s E %s rms %s", step, E, rms)
            for event in self.events:
                event(coords=self.coords, energy=E, rms=rms)

            step += 1

        res.nsteps = step
        res.nfev = self.nfev
        res.coords = self.coords
        res.energy = E
        res.grad = -f
        res.rms = np.linalg.norm(res.grad) / np.sqrt(len(res.grad))
        self.result = res
        return res
Ejemplo n.º 9
0
def steepest_descent(x0, pot, iprint=-1, dx=1e-4, nsteps=100000,
                     tol=1e-3, maxstep=-1., events=None):
    """steepest descent minimization
    
    Notes
    -----
    this should never be used except for testing purposes.  It is a bad implementation
    of a terrible minimization routine.  It will be very slow.
    """
    N = len(x0)
    x = x0.copy()
    E, V = pot.getEnergyGradient(x)
    funcalls = 1
    for k in range(nsteps):
        stp = -V * dx
        if maxstep > 0:
            stpsize = np.max(np.abs(V))
            if stpsize > maxstep:
                stp *= maxstep / stpsize
        x += stp
        E, V = pot.getEnergyGradient(x)
        funcalls += 1
        rms = np.linalg.norm(V) / np.sqrt(N)
        if iprint > 0:
            if funcalls % iprint == 0:
                print("step %8d energy %20.12g rms gradient %20.12g" % (funcalls, E, rms))
        if events is not None:
            for event in events:
                event(energy=E, coords=x, rms=rms)
        if rms < tol:
            break
    res = Result()
    res.coords = x
    res.energy = E
    res.rms = rms
    res.grad = V
    res.nfev = funcalls
    res.nsteps = k
    res.success = res.rms <= tol
    return res
Ejemplo n.º 10
0
def bfgs_scipy(coords, pot, iprint=-1, tol=1e-3, nsteps=5000, **kwargs):
    """
    a wrapper function for the scipy BFGS algorithm
    """
    import scipy.optimize

    ret = scipy.optimize.fmin_bfgs(pot.getEnergy,
                                   coords,
                                   fprime=pot.getGradient,
                                   gtol=tol,
                                   full_output=True,
                                   disp=iprint > 0,
                                   maxiter=nsteps,
                                   **kwargs)
    res = Result()
    res.coords = ret[0]
    res.energy = ret[1]
    res.grad = ret[2]
    res.rms = np.linalg.norm(res.grad) / np.sqrt(len(res.grad))
    res.nfev = ret[4] + ret[5]
    res.nsteps = res.nfev  # not correct, but no better information
    res.success = np.max(np.abs(res.grad)) < tol
    return res
Ejemplo n.º 11
0
def steepest_descent(x0, pot, iprint=-1, dx=1e-4, nsteps=100000,
                      tol=1e-3, maxstep=-1., events=None):
    if not hasattr(pot, "getEnergyGradient"):
        # for compatibility with old quenchers.
        # assume pot is a getEnergyGradient function
        pot = _getEnergyGradientWrapper(pot)
    N = len(x0)
    x=x0.copy()
    E, V = pot.getEnergyGradient(x)
    funcalls = 1
    for k in xrange(nsteps):
        stp = -V * dx
        if maxstep > 0:
            stpsize = np.max(np.abs(V))
            if stpsize > maxstep:
                stp *= maxstep / stpsize                
        x += stp
        E, V = pot.getEnergyGradient(x)
        funcalls += 1
        rms = np.linalg.norm(V)/np.sqrt(N)
        if iprint > 0:
            if funcalls % iprint == 0: 
                print "step %8d energy %20.12g rms gradient %20.12g" % (funcalls, E, rms)
        if events is not None:
            for event in events:
                event(energy=E, coords=x, rms=rms)
        if rms < tol:
            break
    res = Result()
    res.coords = x
    res.energy = E
    res.rms = rms
    res.grad = V
    res.nfev = funcalls
    res.nsteps = k
    res.success = res.rms <= tol 
    return res
Ejemplo n.º 12
0
def ode_julia_naive(coords,
                    pot,
                    tol=1e-4,
                    nsteps=20000,
                    convergence_check=None,
                    solver_type=de.CVODE_BDF(),
                    rtol=1e-4,
                    atol=1e-4,
                    **kwargs):
    class feval_pot:
        """ wrapper class that interfaces base potential to functions for ode solver
        """
        def __init__(self):
            self.nfev = 0
            self.nhev = 0

        def get_negative_grad(self, x, p, t):
            """
            negative grad is f(u, p, t)
            """
            self.nfev += 1
            return -pot.getEnergyGradient(x.copy())[1]

        def get_energy_gradient(self, x):
            self.nfev += 1
            return pot.getEnergyGradient(x.copy())

        def get_jacobian(self, x, p, t):
            self.nhev += 1
            return -pot.getEnergyGradientHessian(x.copy())[2]

    function_evaluate_pot = feval_pot()
    converged = False
    n = 0
    if convergence_check == None:
        convergence_check = lambda g: np.linalg.norm(g) < tol
    # odefunc = de.ODEFunction(function_evaluate_pot.get_negative_grad, function_evaluate_pot.get_jacobian)
    # initialize ode problem
    tspan = (0, 10000.0)
    f_bound = de.ODEFunction(function_evaluate_pot.get_negative_grad)
    # f_free = de.ODEFunction(get_negative_grad,jac = get_jacobian)
    prob = de.ODEProblem(f_bound, coords, tspan)
    solver = Main.eval("CVODE_BDF(linear_solver=:GMRES)")
    integrator = de.init(prob, solver, reltol=rtol, abstol=atol)
    x_ = np.full(len(coords), np.nan)
    while not converged and n < nsteps:
        xold = x_
        de.step_b(integrator)
        x_ = integrator.u
        n += 1
        converged = convergence_check(de.get_du(integrator))
    res = Result()
    res.coords = x_
    res.energy = pot.getEnergy(x_)
    res.grad = 0
    res.nfev = function_evaluate_pot.nfev
    res.nsteps = n
    res.nhev = function_evaluate_pot.nhev
    res.success = converged
    # res.nhev = function_evaluate_pot.nhev
    return res
Ejemplo n.º 13
0
    def run(self):
        """The main loop of the algorithm"""
        coords = np.copy(self.coords)
        res = Result()  # return object
        res.message = []

        self._compute_gradients(coords)
        iend = 0
        for i in xrange(self.nsteps):
            iend = i
            # get the lowest eigenvalue and eigenvector
            self.overlap = self._getLowestEigenVector(coords, i)
            overlap = self.overlap

            if self.eigenval < 0:
                self.negatives_before_check -= 1

            # determine whether everything looks OK.
            all_ok = self.eigenval < 0 or not self.check_negative
            if not all_ok:
                if i == 0:
                    # we need to accept because we haven't saved the state yet
                    # Also, demand_initial_negative_vec will stop later if needed
                    all_ok = True
            if not all_ok:
                if self.negatives_before_check > 0 and not self.demand_initial_negative_vec:
                    print "  positive before check. setting all ok"
                    all_ok = True

            # if everything is OK, then continue, else revert the step
            if all_ok:
                self._saveState(coords)
                self.reduce_step = 0
            else:
                self.npositive += 1
                if self.npositive > self.npositive_max:
                    logger.warning(
                        "positive eigenvalue found too many times. ending %s",
                        self.npositive)
                    res.message.append(
                        "positive eigenvalue found too many times %d" %
                        self.npositive)
                    break
                if self.verbosity > 2:
                    logger.info(
                        "the eigenvalue turned positive. %s %s", self.eigenval,
                        "Resetting last good values and taking smaller steps")
                coords = self._resetState()
                self.reduce_step += 1

            # step uphill along the direction of the lowest eigenvector
            coords = self._stepUphill(coords)

            # minimize the coordinates in the space perpendicular to the lowest eigenvector
            tangent_ret = self._minimizeTangentSpace(
                coords, energy=self.get_energy(), gradient=self.get_gradient())
            coords = tangent_ret.coords
            tangentrms = tangent_ret.rms

            # check if we are done and print some stuff
            # self._compute_gradients(coords) # this is unnecessary
            E = self.get_energy()
            grad = self.get_gradient()
            rms = np.linalg.norm(grad) * self.rmsnorm
            gradpar = np.dot(grad, self.eigenvec) / np.linalg.norm(
                self.eigenvec)

            if self.iprint > 0:
                if (i + 1) % self.iprint == 0:
                    ostring = "findTS: %3d E %9g rms %8g eigenvalue %9g rms perp %8g grad par %9g overlap %g" % (
                        i, E, rms, self.eigenval, tangentrms, gradpar, overlap)
                    extra = "  Evec search: %d rms %g" % (
                        self.leig_result.nfev, self.leig_result.rms)
                    extra += "  Tverse search: %d step %g" % (
                        self.tangent_result.nfev, self.tangent_move_step)
                    extra += "  Uphill step:%g" % (self.uphill_step_size, )
                    logger.info("%s %s", ostring, extra)

            if callable(self.event):
                self.event(energy=E,
                           coords=coords,
                           rms=rms,
                           eigenval=self.eigenval,
                           stepnum=i)
            if rms < self.tol:
                break
            if self.nfail >= self.nfail_max:
                logger.warning(
                    "stopping findTransitionState.  too many failures in eigenvector search %s",
                    self.nfail)
                res.message.append(
                    "too many failures in eigenvector search %d" % self.nfail)
                break

            if i == 0 and self.eigenval > 0.:
                if self.verbosity > 1:
                    logger.warning(
                        "initial eigenvalue is positive - increase NEB spring constant?"
                    )
                if self.demand_initial_negative_vec:
                    logger.warning(
                        "            aborting transition state search")
                    res.message.append("initial eigenvalue is positive %f" %
                                       self.eigenval)
                    break

        # done.  do one last eigenvector search because coords may have changed
        self._getLowestEigenVector(coords, iend)

        # print some data
        if self.verbosity > 0 or self.iprint > 0:
            logger.info("findTransitionState done: %s %s %s %s %s", iend, E,
                        rms, "eigenvalue", self.eigenval)

        success = True
        # check if results make sense
        if self.eigenval >= 0.:
            if self.verbosity > 2:
                logger.info(
                    "warning: transition state is ending with positive eigenvalue %s",
                    self.eigenval)
            success = False
        if rms > self.tol:
            if self.verbosity > 2:
                logger.info(
                    "warning: transition state search appears to have failed: rms %s",
                    rms)
            success = False
        if iend >= self.nsteps:
            res.message.append("maximum iterations reached %d" % iend)

        # update nfev with the number of calls from the transverse walker
        if self._transverse_walker is not None:
            twres = self._transverse_walker.get_result()
            self.nfev += twres.nfev

        res.coords = coords
        res.energy = E
        res.eigenval = self.eigenval
        res.eigenvec = self.eigenvec
        res.grad = grad
        res.rms = rms
        res.nsteps = iend
        res.success = success
        res.nfev = self.nfev
        return res
    def run(self):
        """The main loop of the algorithm"""
        coords = np.copy(self.coords)
        res = Result() #  return object
        res.message = []
        
        # if starting with positive curvature, disable negative eigenvalue check
        # this will be reenabled as soon as the eigenvector becomes negative
        negative_before_check =  10

        self._compute_gradients(coords)
        for i in xrange(self.nsteps):
            
            # get the lowest eigenvalue and eigenvector
            self.overlap = self._getLowestEigenVector(coords, i)
            overlap = self.overlap
            #print self.eigenval
            
            if self.eigenval < 0:
                negative_before_check -= 1
            
            # check to make sure the eigenvector is ok
            if (i == 0 or self.eigenval <= 0 or not self.check_negative or 
                (negative_before_check > 0 and not self.demand_initial_negative_vec)):
                self._saveState(coords)
                self.reduce_step = 0
            else:
                self.npositive += 1
                if self.npositive > self.npositive_max:
                    logger.warning( "positive eigenvalue found too many times. ending %s", self.npositive)
                    res.message.append( "positive eigenvalue found too many times %d" % self.npositive )
                    break
                if self.verbosity > 2:
                    logger.info("the eigenvalue turned positive. %s %s", self.eigenval, "Resetting last good values and taking smaller steps")
                coords = self._resetState()
                self.reduce_step += 1
            
            # step uphill along the direction of the lowest eigenvector
            coords = self._stepUphill(coords)

            # minimize the coordinates in the space perpendicular to the lowest eigenvector
            tangent_ret = self._minimizeTangentSpace(coords, energy=self.get_energy(), gradient=self.get_gradient())
            coords = tangent_ret.coords
            tangentrms = tangent_ret.rms


            # check if we are done and print some stuff
#            self._compute_gradients(coords) # this is unnecessary
            E = self.get_energy()
            grad = self.get_gradient()
            rms = np.linalg.norm(grad) * self.rmsnorm
            gradpar = np.dot(grad, self.eigenvec) / np.linalg.norm(self.eigenvec)
            
            if self.iprint > 0:
                if (i+1) % self.iprint == 0:
                    ostring = "findTS: %3d E %9g rms %8g eigenvalue %9g rms perp %8g grad par %9g overlap %g" % (
                                    i, E, rms, self.eigenval, tangentrms, gradpar, overlap)
                    extra = "  Evec search: %d rms %g" % (self.leig_result.nfev, self.leig_result.rms)
                    extra += "  Tverse search: %d step %g" % (self.tangent_result.nfev, 
                                                                    self.tangent_move_step)
                    extra += "  Uphill step:%g" % (self.uphill_step_size,)
                    logger.info("%s %s", ostring, extra)
            
            if callable(self.event):
                self.event(energy=E, coords=coords, rms=rms, eigenval=self.eigenval, stepnum=i)
            if rms < self.tol:
                break
            if self.nfail >= self.nfail_max:
                logger.warning("stopping findTransitionState.  too many failures in eigenvector search %s", self.nfail)
                res.message.append( "too many failures in eigenvector search %d" % self.nfail )
                break

            if i == 0 and self.eigenval > 0.:
                if self.verbosity > 1:
                    logger.warning("initial eigenvalue is positive - increase NEB spring constant?")
                if self.demand_initial_negative_vec:
                    logger.warning("            aborting transition state search")
                    res.message.append( "initial eigenvalue is positive %f" % self.eigenval )
                    break

        # done.  do one last eigenvector search because coords may have changed
        self._getLowestEigenVector(coords, i)

        # print some data
        if self.verbosity > 0 or self.iprint > 0:
            logger.info("findTransitionState done: %s %s %s %s %s", i, E, rms, "eigenvalue", self.eigenval)
    
        success = True
        # check if results make sense
        if self.eigenval >= 0.:
            if self.verbosity > 2:
                logger.info( "warning: transition state is ending with positive eigenvalue %s", self.eigenval)
            success = False
        if rms > self.tol:
            if self.verbosity > 2:
                logger.info("warning: transition state search appears to have failed: rms %s", rms)
            success = False
        if i >= self.nsteps:
            res.message.append( "maximum iterations reached %d" % i )
            
        # update nfev with the number of calls from the transverse walker
        if self._transverse_walker is not None:
            twres = self._transverse_walker.get_result()
            self.nfev += twres.nfev 

        #return results
        res.coords = coords
        res.energy = E
        res.eigenval = self.eigenval
        res.eigenvec = self.eigenvec
        res.grad = grad
        res.rms = rms
        res.nsteps = i
        res.success = success
        res.nfev = self.nfev
        return res
Ejemplo n.º 15
0
def ode_scipy_naive(coords,
                    pot,
                    t_bound=1000,
                    tol=1e-4,
                    nsteps=20000,
                    convergence_check=None,
                    solver_type='rk45',
                    **kwargs):
    """ This uses rk45 in a minimizer like approach to find the ode
        The idea is to solve for the path
         dx/dt = - \\grad{E}
    Parameters
    ----------
    coords: array
        coords input for the quench
    pot: array
        potential class that has a
        getenergygradient function that we need for stepping
    nsteps: int
        maximum number of steps
    tol: float
        gives a tolerance for making sure that we have found the minima
    kwargs: ...
        extra arguments for the integrator
    Return
    ----------
    result container with steps and function evaluations
    """
    class feval_pot:
        """ wrapper class that calculates function evaluations
            and makes sure to point the gradient in the right direction
        """
        def __init__(self):
            self.nfev = 0

        def get_gradient(self, t, x):
            self.nfev += 1
            return -pot.getEnergyGradient(x.copy())[1]

        def get_energy_gradient(self, x):
            self.nfev += 1
            return pot.getEnergyGradient(x.copy())

    # This code seems to work for these two solvers
    # if solver_type == 'RK23':
    #     solver = RK23_LS
    # else:
    #     solver = RK23_LS
    solver = Radau
    # def get_gradient(t, x):
    #     # gets just the gradient from the potential
    #     return pot.getEnergyGradient(x)[1]
    function_evaluate_pot = feval_pot()
    solver = solver(
        function_evaluate_pot.get_gradient,
        # function_evaluate_pot.get_energy_gradient,
        0,
        coords,
        t_bound,
        rtol=1e-3,
        atol=1e-3)
    # min_step = 10 * np.abs(np.nextafter(t, self.direction * np.inf) - t)
    converged = False
    n = 0
    if convergence_check == None:
        convergence_check = lambda g: np.linalg.norm(g) < tol

    x_ = np.full(len(coords), np.nan)
    while not converged and n < nsteps:
        xold = x_
        solver.step()
        x_ = solver.dense_output().y_old
        n += 1
        converged = convergence_check(pot.getEnergyGradient(x_)[1])
    res = Result()
    res.coords = x_
    res.nfev = function_evaluate_pot.nfev
    print(res.nfev)
    res.coords = x_
    res.energy = pot.getEnergy(x_)
    res.rms = 0
    res.grad = 0
    res.nsteps = n
    res.success = converged
    return res