root / ase / optimize / bfgs.py @ 1
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 |