Statistiques
| Révision :

root / ase / optimize / bfgs.py @ 3

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