root / ase / optimize / bfgs.py @ 14
Historique | Voir | Annoter | Télécharger (3,61 ko)
| 1 | 1 | tkerber | # -*- coding: utf-8 -*-
|
|---|---|---|---|
| 2 | 1 | tkerber | import numpy as np |
| 3 | 1 | tkerber | from numpy.linalg import eigh, solve |
| 4 | 1 | tkerber | |
| 5 | 1 | tkerber | from ase.optimize.optimize import Optimizer |
| 6 | 1 | tkerber | |
| 7 | 1 | tkerber | |
| 8 | 1 | tkerber | class BFGS(Optimizer): |
| 9 | 1 | tkerber | def __init__(self, atoms, restart=None, logfile='-', trajectory=None, |
| 10 | 1 | tkerber | maxstep=None):
|
| 11 | 1 | tkerber | """BFGS optimizer.
|
| 12 | 1 | tkerber |
|
| 13 | 1 | tkerber | Parameters:
|
| 14 | 1 | tkerber |
|
| 15 | 1 | tkerber | restart: string
|
| 16 | 1 | tkerber | Pickle file used to store hessian matrix. If set, file with
|
| 17 | 1 | tkerber | such a name will be searched and hessian matrix stored will
|
| 18 | 1 | tkerber | be used, if the file exists.
|
| 19 | 1 | tkerber | trajectory: string
|
| 20 | 1 | tkerber | Pickle file used to store trajectory of atomic movement.
|
| 21 | 1 | tkerber | maxstep: float
|
| 22 | 1 | tkerber | Used to set the maximum distance an atom can move per
|
| 23 | 1 | tkerber | iteration (default value is 0.04 Å).
|
| 24 | 1 | tkerber | """
|
| 25 | 1 | tkerber | |
| 26 | 1 | tkerber | Optimizer.__init__(self, atoms, restart, logfile, trajectory)
|
| 27 | 1 | tkerber | |
| 28 | 1 | tkerber | if maxstep is not None: |
| 29 | 1 | tkerber | if maxstep > 1.0: |
| 30 | 1 | tkerber | raise ValueError('You are using a much too large value for ' + |
| 31 | 1 | tkerber | 'the maximum step size: %.1f Å' % maxstep)
|
| 32 | 1 | tkerber | self.maxstep = maxstep
|
| 33 | 1 | tkerber | |
| 34 | 1 | tkerber | def initialize(self): |
| 35 | 1 | tkerber | self.H = None |
| 36 | 1 | tkerber | self.r0 = None |
| 37 | 1 | tkerber | self.f0 = None |
| 38 | 1 | tkerber | self.maxstep = 0.04 |
| 39 | 1 | tkerber | |
| 40 | 1 | tkerber | def read(self): |
| 41 | 1 | tkerber | self.H, self.r0, self.f0, self.maxstep = self.load() |
| 42 | 1 | tkerber | |
| 43 | 1 | tkerber | def step(self, f): |
| 44 | 1 | tkerber | atoms = self.atoms
|
| 45 | 1 | tkerber | r = atoms.get_positions() |
| 46 | 1 | tkerber | f = f.reshape(-1)
|
| 47 | 1 | tkerber | self.update(r.flat, f, self.r0, self.f0) |
| 48 | 1 | tkerber | omega, V = eigh(self.H)
|
| 49 | 1 | tkerber | dr = np.dot(V, np.dot(f, V) / np.fabs(omega)).reshape((-1, 3)) |
| 50 | 1 | tkerber | #dr = solve(self.H, f).reshape((-1, 3))
|
| 51 | 1 | tkerber | steplengths = (dr**2).sum(1)**0.5 |
| 52 | 1 | tkerber | dr = self.determine_step(dr, steplengths)
|
| 53 | 1 | tkerber | atoms.set_positions(r + dr) |
| 54 | 1 | tkerber | self.r0 = r.flat.copy()
|
| 55 | 1 | tkerber | self.f0 = f.copy()
|
| 56 | 1 | tkerber | self.dump((self.H, self.r0, self.f0, self.maxstep)) |
| 57 | 1 | tkerber | |
| 58 | 1 | tkerber | def determine_step(self, dr, steplengths): |
| 59 | 1 | tkerber | """Determine step to take according to maxstep
|
| 60 | 1 | tkerber |
|
| 61 | 1 | tkerber | Normalize all steps as the largest step. This way
|
| 62 | 1 | tkerber | we still move along the eigendirection.
|
| 63 | 1 | tkerber | """
|
| 64 | 1 | tkerber | maxsteplength = np.max(steplengths) |
| 65 | 1 | tkerber | if maxsteplength >= self.maxstep: |
| 66 | 1 | tkerber | dr *= self.maxstep / maxsteplength
|
| 67 | 1 | tkerber | |
| 68 | 1 | tkerber | return dr
|
| 69 | 1 | tkerber | |
| 70 | 1 | tkerber | def update(self, r, f, r0, f0): |
| 71 | 1 | tkerber | if self.H is None: |
| 72 | 1 | tkerber | self.H = np.eye(3 * len(self.atoms)) * 70.0 |
| 73 | 1 | tkerber | return
|
| 74 | 1 | tkerber | dr = r - r0 |
| 75 | 1 | tkerber | |
| 76 | 1 | tkerber | if np.abs(dr).max() < 1e-7: |
| 77 | 1 | tkerber | # Same configuration again (maybe a restart):
|
| 78 | 1 | tkerber | return
|
| 79 | 1 | tkerber | |
| 80 | 1 | tkerber | df = f - f0 |
| 81 | 1 | tkerber | a = np.dot(dr, df) |
| 82 | 1 | tkerber | dg = np.dot(self.H, dr)
|
| 83 | 1 | tkerber | b = np.dot(dr, dg) |
| 84 | 1 | tkerber | self.H -= np.outer(df, df) / a + np.outer(dg, dg) / b
|
| 85 | 1 | tkerber | |
| 86 | 1 | tkerber | def replay_trajectory(self, traj): |
| 87 | 1 | tkerber | """Initialize hessian from old trajectory."""
|
| 88 | 1 | tkerber | if isinstance(traj, str): |
| 89 | 1 | tkerber | from ase.io.trajectory import PickleTrajectory |
| 90 | 1 | tkerber | traj = PickleTrajectory(traj, 'r')
|
| 91 | 1 | tkerber | self.H = None |
| 92 | 1 | tkerber | atoms = traj[0]
|
| 93 | 1 | tkerber | r0 = atoms.get_positions().ravel() |
| 94 | 1 | tkerber | f0 = atoms.get_forces().ravel() |
| 95 | 1 | tkerber | for atoms in traj: |
| 96 | 1 | tkerber | r = atoms.get_positions().ravel() |
| 97 | 1 | tkerber | f = atoms.get_forces().ravel() |
| 98 | 1 | tkerber | self.update(r, f, r0, f0)
|
| 99 | 1 | tkerber | r0 = r |
| 100 | 1 | tkerber | f0 = f |
| 101 | 1 | tkerber | |
| 102 | 1 | tkerber | self.r0 = r0
|
| 103 | 1 | tkerber | self.f0 = f0
|
| 104 | 1 | tkerber | |
| 105 | 1 | tkerber | class oldBFGS(BFGS): |
| 106 | 1 | tkerber | def determine_step(self, dr, steplengths): |
| 107 | 1 | tkerber | """Old BFGS behaviour for scaling step lengths
|
| 108 | 1 | tkerber |
|
| 109 | 1 | tkerber | This keeps the behaviour of truncating individual steps. Some might
|
| 110 | 1 | tkerber | depend of this as some absurd kind of stimulated annealing to find the
|
| 111 | 1 | tkerber | global minimum.
|
| 112 | 1 | tkerber | """
|
| 113 | 1 | tkerber | dr /= np.maximum(steplengths / self.maxstep, 1.0).reshape(-1, 1) |
| 114 | 1 | tkerber | return dr |