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