Statistiques
| Révision :

root / ase / optimize / oldqn.py @ 3

Historique | Voir | Annoter | Télécharger (14,44 ko)

1 1 tkerber
# Copyright (C) 2003  CAMP
2 1 tkerber
# Please see the accompanying LICENSE file for further information.
3 1 tkerber
4 1 tkerber
"""
5 1 tkerber
Quasi-Newton algorithm
6 1 tkerber
"""
7 1 tkerber
8 1 tkerber
__docformat__ = 'reStructuredText'
9 1 tkerber
10 1 tkerber
import numpy as np
11 1 tkerber
import weakref,time,sys
12 1 tkerber
13 1 tkerber
14 1 tkerber
def f(lamda,Gbar,b,radius):
15 1 tkerber
        b1 = b - lamda
16 1 tkerber
        g = radius**2 - np.dot(Gbar/b1, Gbar/b1)
17 1 tkerber
        return g
18 1 tkerber
19 1 tkerber
20 1 tkerber
21 1 tkerber
def scale_radius_energy(f,r):
22 1 tkerber
        scale = 1.0
23 1 tkerber
#       if(r<=0.01):
24 1 tkerber
#               return scale
25 1 tkerber
26 1 tkerber
        if f<0.01: scale*=1.4
27 1 tkerber
        if f<0.05: scale*=1.4
28 1 tkerber
        if f<0.10: scale*=1.4
29 1 tkerber
        if f<0.40: scale*=1.4
30 1 tkerber
31 1 tkerber
        if f>0.5: scale *= 1./1.4
32 1 tkerber
        if f>0.7: scale *= 1./1.4
33 1 tkerber
        if f>1.0: scale *= 1./1.4
34 1 tkerber
35 1 tkerber
        return scale
36 1 tkerber
37 1 tkerber
def scale_radius_force(f,r):
38 1 tkerber
        scale = 1.0
39 1 tkerber
#       if(r<=0.01):
40 1 tkerber
#               return scale
41 1 tkerber
        g = abs(f -1)
42 1 tkerber
        if g<0.01: scale*=1.4
43 1 tkerber
        if g<0.05: scale*=1.4
44 1 tkerber
        if g<0.10: scale*=1.4
45 1 tkerber
        if g<0.40: scale*=1.4
46 1 tkerber
47 1 tkerber
        if g>0.5: scale *= 1./1.4
48 1 tkerber
        if g>0.7: scale *= 1./1.4
49 1 tkerber
        if g>1.0: scale *= 1./1.4
50 1 tkerber
51 1 tkerber
        return scale
52 1 tkerber
53 1 tkerber
def find_lamda(upperlimit,Gbar,b,radius):
54 1 tkerber
        lowerlimit = upperlimit
55 1 tkerber
        eps = 1e-12
56 1 tkerber
        step = 0.1
57 1 tkerber
        while  f(lowerlimit,Gbar,b,radius) < 0:
58 1 tkerber
                lowerlimit -= step
59 1 tkerber
60 1 tkerber
        converged = False
61 1 tkerber
62 1 tkerber
        while not converged:
63 1 tkerber
64 1 tkerber
                midt = (upperlimit+lowerlimit)/2.
65 1 tkerber
                lamda = midt
66 1 tkerber
                fmidt = f(midt,Gbar,b,radius)
67 1 tkerber
                fupper = f(upperlimit,Gbar,b,radius)
68 1 tkerber
                flower = f(lowerlimit,Gbar,b,radius)
69 1 tkerber
70 1 tkerber
                if fupper*fmidt<0:
71 1 tkerber
                        lowerlimit = midt
72 1 tkerber
                else:
73 1 tkerber
                        upperlimit = midt
74 1 tkerber
75 1 tkerber
                if abs(upperlimit-lowerlimit)<1e-6:
76 1 tkerber
                        converged = True
77 1 tkerber
78 1 tkerber
        return lamda
79 1 tkerber
80 1 tkerber
def get_hessian_inertia(eigenvalues):
81 1 tkerber
        # return number of negative modes
82 1 tkerber
        n = 0
83 1 tkerber
        print 'eigenvalues ',eigenvalues[0],eigenvalues[1],eigenvalues[2]
84 1 tkerber
        while eigenvalues[n]<0:
85 1 tkerber
                n+=1
86 1 tkerber
        return n
87 1 tkerber
88 1 tkerber
89 1 tkerber
from numpy.linalg import eigh, solve
90 1 tkerber
91 1 tkerber
from ase.optimize.optimize import Optimizer
92 1 tkerber
93 1 tkerber
94 1 tkerber
95 1 tkerber
class GoodOldQuasiNewton(Optimizer):
96 1 tkerber
97 1 tkerber
    def __init__(self, atoms, restart=None, logfile='-', trajectory=None,
98 1 tkerber
                 fmax=None, converged=None,
99 1 tkerber
                hessianupdate='BFGS',hessian=None,forcemin=True,
100 1 tkerber
                verbosity=None,maxradius=None,
101 1 tkerber
                diagonal=20.,radius=None,
102 1 tkerber
                transitionstate = False):
103 1 tkerber
104 1 tkerber
        Optimizer.__init__(self, atoms, restart, logfile, trajectory)
105 1 tkerber
106 1 tkerber
        self.eps = 1e-12
107 1 tkerber
        self.hessianupdate = hessianupdate
108 1 tkerber
        self.forcemin = forcemin
109 1 tkerber
        self.verbosity = verbosity
110 1 tkerber
        self.diagonal = diagonal
111 1 tkerber
112 1 tkerber
        self.atoms = atoms
113 1 tkerber
114 1 tkerber
        n = len(self.atoms) * 3
115 1 tkerber
        if radius is None:
116 1 tkerber
                self.radius = 0.05*np.sqrt(n)/10.0
117 1 tkerber
        else:
118 1 tkerber
                self.radius = radius
119 1 tkerber
120 1 tkerber
        if maxradius is None:
121 1 tkerber
                self.maxradius = 0.5*np.sqrt(n)
122 1 tkerber
        else:
123 1 tkerber
                self.maxradius = maxradius
124 1 tkerber
125 1 tkerber
        # 0.01 < radius < maxradius
126 1 tkerber
        self.radius = max(min( self.radius, self.maxradius ), 0.0001)
127 1 tkerber
128 1 tkerber
        self.transitionstate = transitionstate
129 1 tkerber
130 1 tkerber
        # check if this is a nudged elastic band calculation
131 1 tkerber
        if hasattr(atoms,'springconstant'):
132 1 tkerber
                self.forcemin=False
133 1 tkerber
134 1 tkerber
        self.t0 = time.time()
135 1 tkerber
136 1 tkerber
    def initialize(self):pass
137 1 tkerber
138 1 tkerber
    def write_log(self,text):
139 1 tkerber
        if self.logfile is not None:
140 1 tkerber
            self.logfile.write(text + '\n')
141 1 tkerber
            self.logfile.flush()
142 1 tkerber
143 1 tkerber
    def set_max_radius(self, maxradius):
144 1 tkerber
                self.maxradius = maxradius
145 1 tkerber
                self.radius = min(self.maxradius, self.radius)
146 1 tkerber
147 1 tkerber
    def set_hessian(self,hessian):
148 1 tkerber
        self.hessian = hessian
149 1 tkerber
150 1 tkerber
    def get_hessian(self):
151 1 tkerber
        if not hasattr(self,'hessian'):
152 1 tkerber
                self.set_default_hessian()
153 1 tkerber
        return self.hessian
154 1 tkerber
155 1 tkerber
    def set_default_hessian(self):
156 1 tkerber
        # set unit matrix
157 1 tkerber
        n = len(self.atoms) * 3
158 1 tkerber
        hessian = np.zeros((n,n))
159 1 tkerber
        for i in range(n):
160 1 tkerber
                        hessian[i][i] = self.diagonal
161 1 tkerber
        self.set_hessian(hessian)
162 1 tkerber
163 1 tkerber
    def read_hessian(self,filename):
164 1 tkerber
        import cPickle
165 1 tkerber
        f = open(filename,'r')
166 1 tkerber
        self.set_hessian(cPickle.load(f))
167 1 tkerber
        f.close()
168 1 tkerber
169 1 tkerber
    def write_hessian(self,filename):
170 1 tkerber
        import cPickle
171 1 tkerber
        f = paropen(filename,'w')
172 1 tkerber
        cPickle.dump(self.get_hessian(),f)
173 1 tkerber
        f.close()
174 1 tkerber
175 1 tkerber
    def write_to_restartfile(self):
176 1 tkerber
        import cPickle
177 1 tkerber
        f = paropen(self.restartfile,'w')
178 1 tkerber
        cPickle.dump((self.oldpos,
179 1 tkerber
                      self.oldG,
180 1 tkerber
                      self.oldenergy,
181 1 tkerber
                      self.radius,
182 1 tkerber
                      self.hessian,
183 1 tkerber
                      self.energy_estimate),f)
184 1 tkerber
        f.close()
185 1 tkerber
186 1 tkerber
187 1 tkerber
188 1 tkerber
    def update_hessian(self,pos,G):
189 1 tkerber
        import copy
190 1 tkerber
        if hasattr(self,'oldG'):
191 1 tkerber
                if self.hessianupdate=='BFGS':
192 1 tkerber
                        self.update_hessian_bfgs(pos,G)
193 1 tkerber
                elif self.hessianupdate== 'Powell':
194 1 tkerber
                        self.update_hessian_powell(pos,G)
195 1 tkerber
                else:
196 1 tkerber
                        self.update_hessian_bofill(pos,G)
197 1 tkerber
        else:
198 1 tkerber
                if not hasattr(self,'hessian'):
199 1 tkerber
                        self.set_default_hessian()
200 1 tkerber
201 1 tkerber
        self.oldpos = copy.copy(pos)
202 1 tkerber
        self.oldG = copy.copy(G)
203 1 tkerber
204 1 tkerber
        if self.verbosity:
205 1 tkerber
                print 'hessian ',self.hessian
206 1 tkerber
207 1 tkerber
208 1 tkerber
209 1 tkerber
    def update_hessian_bfgs(self,pos,G):
210 1 tkerber
        n = len(self.hessian)
211 1 tkerber
        dgrad = G - self.oldG
212 1 tkerber
        dpos  = pos - self.oldpos
213 1 tkerber
        absdpos = np.sqrt(np.dot(dpos, dpos))
214 1 tkerber
        dotg  = np.dot(dgrad,dpos)
215 1 tkerber
        tvec  = np.dot(dpos,self.hessian)
216 1 tkerber
        dott  = np.dot(dpos,tvec)
217 1 tkerber
        if (abs(dott)>self.eps) and (abs(dotg)>self.eps):
218 1 tkerber
                for i in range(n):
219 1 tkerber
                        for j in range(n):
220 1 tkerber
                                h = dgrad[i]*dgrad[j]/dotg - tvec[i]*tvec[j]/dott
221 1 tkerber
                                self.hessian[i][j] += h
222 1 tkerber
223 1 tkerber
224 1 tkerber
225 1 tkerber
    def update_hessian_powell(self,pos,G):
226 1 tkerber
        n = len(self.hessian)
227 1 tkerber
        dgrad = G - self.oldG
228 1 tkerber
        dpos  = pos - self.oldpos
229 1 tkerber
        absdpos = np.dot(dpos, dpos)
230 1 tkerber
        if absdpos<self.eps:
231 1 tkerber
                return
232 1 tkerber
233 1 tkerber
        dotg  = np.dot(dgrad,dpos)
234 1 tkerber
        tvec  = dgrad-np.dot(dpos,self.hessian)
235 1 tkerber
        tvecdot = np.dot(tvec,tvec)
236 1 tkerber
        tvecdpos = np.dot(tvec,dpos)
237 1 tkerber
        ddot = tvecdpos/absdpos
238 1 tkerber
239 1 tkerber
        dott  = np.dot(dpos,tvec)
240 1 tkerber
        if (abs(dott)>self.eps) and (abs(dotg)>self.eps):
241 1 tkerber
                for i in range(n):
242 1 tkerber
                        for j in range(n):
243 1 tkerber
                                h = tvec[i]*dpos[j] + dpos[i]*tvec[j]-ddot*dpos[i]*dpos[j]
244 1 tkerber
                                h *= 1./absdpos
245 1 tkerber
                                self.hessian[i][j] += h
246 1 tkerber
247 1 tkerber
248 1 tkerber
    def update_hessian_bofill(self,pos,G):
249 1 tkerber
        print 'update Bofill'
250 1 tkerber
        n = len(self.hessian)
251 1 tkerber
        dgrad = G - self.oldG
252 1 tkerber
        dpos  = pos - self.oldpos
253 1 tkerber
        absdpos = np.dot(dpos, dpos)
254 1 tkerber
        if absdpos<self.eps:
255 1 tkerber
                return
256 1 tkerber
        dotg  = np.dot(dgrad,dpos)
257 1 tkerber
        tvec  = dgrad-np.dot(dpos,self.hessian)
258 1 tkerber
        tvecdot = np.dot(tvec,tvec)
259 1 tkerber
        tvecdpos = np.dot(tvec,dpos)
260 1 tkerber
        ddot = tvecdpos/absdpos
261 1 tkerber
262 1 tkerber
        coef1 = 1. - tvecdpos*tvecdpos/(absdpos*tvecdot)
263 1 tkerber
        coef2 = (1. - coef1)*absdpos/tvecdpos
264 1 tkerber
        coef3 = coef1*tvecdpos/absdpos
265 1 tkerber
266 1 tkerber
        dott  = np.dot(dpos,tvec)
267 1 tkerber
        if (abs(dott)>self.eps) and (abs(dotg)>self.eps):
268 1 tkerber
                for i in range(n):
269 1 tkerber
                        for j in range(n):
270 1 tkerber
                                h = coef1*(tvec[i]*dpos[j] + dpos[i]*tvec[j])-dpos[i]*dpos[j]*coef3 + coef2*tvec[i]*tvec[j]
271 1 tkerber
                                h *= 1./absdpos
272 1 tkerber
                                self.hessian[i][j] += h
273 1 tkerber
274 1 tkerber
275 1 tkerber
276 1 tkerber
    def step(self, f):
277 1 tkerber
        """ Do one QN step
278 1 tkerber
        """
279 1 tkerber
280 1 tkerber
        pos = self.atoms.get_positions().ravel()
281 1 tkerber
        G = -self.atoms.get_forces().ravel()
282 1 tkerber
        energy = self.atoms.get_potential_energy()
283 1 tkerber
284 1 tkerber
285 1 tkerber
        self.write_iteration(energy,G)
286 1 tkerber
287 1 tkerber
        if hasattr(self,'oldenergy'):
288 1 tkerber
289 1 tkerber
                self.write_log('energies ' + str(energy) + ' ' + str(self.oldenergy))
290 1 tkerber
291 1 tkerber
                if self.forcemin:
292 1 tkerber
                        de = 1e-4
293 1 tkerber
                else:
294 1 tkerber
                        de = 1e-2
295 1 tkerber
296 1 tkerber
                if self.transitionstate:
297 1 tkerber
                        de = 0.2
298 1 tkerber
299 1 tkerber
                if (energy-self.oldenergy)>de:
300 1 tkerber
                        self.write_log('reject step')
301 1 tkerber
                        self.atoms.set_positions(self.oldpos.reshape((-1, 3)))
302 1 tkerber
                        G = self.oldG
303 1 tkerber
                        energy = self.oldenergy
304 1 tkerber
                        self.radius *= 0.5
305 1 tkerber
                else:
306 1 tkerber
                        self.update_hessian(pos,G)
307 1 tkerber
                        de = energy - self.oldenergy
308 1 tkerber
                        f = 1.0
309 1 tkerber
                        if self.forcemin:
310 1 tkerber
                                self.write_log("energy change; actual: %f estimated: %f "%(de,self.energy_estimate))
311 1 tkerber
                                if abs(self.energy_estimate)>self.eps:
312 1 tkerber
                                        f = abs((de/self.energy_estimate)-1)
313 1 tkerber
                                        self.write_log('Energy prediction factor ' + str(f))
314 1 tkerber
                                        # fg = self.get_force_prediction(G)
315 1 tkerber
                                        self.radius *= scale_radius_energy(f,self.radius)
316 1 tkerber
317 1 tkerber
                        else:
318 1 tkerber
                                self.write_log("energy change; actual: %f "%(de))
319 1 tkerber
                                self.radius*=1.5
320 1 tkerber
321 1 tkerber
                        fg = self.get_force_prediction(G)
322 1 tkerber
                        self.write_log("Scale factors %f %f "%(scale_radius_energy(f,self.radius),
323 1 tkerber
                                                                scale_radius_force(fg,self.radius)))
324 1 tkerber
325 1 tkerber
326 1 tkerber
                self.radius = max(min(self.radius,self.maxradius), 0.0001)
327 1 tkerber
        else:
328 1 tkerber
                self.update_hessian(pos,G)
329 1 tkerber
330 1 tkerber
        self.write_log("new radius %f "%(self.radius))
331 1 tkerber
        self.oldenergy = energy
332 1 tkerber
333 1 tkerber
        b,V = eigh(self.hessian)
334 1 tkerber
        V=V.T.copy()
335 1 tkerber
        self.V = V
336 1 tkerber
337 1 tkerber
        # calculate projection of G onto eigenvectors V
338 1 tkerber
        Gbar = np.dot(G,np.transpose(V))
339 1 tkerber
340 1 tkerber
        lamdas = self.get_lambdas(b,Gbar)
341 1 tkerber
342 1 tkerber
        D = -Gbar/(b-lamdas)
343 1 tkerber
        n = len(D)
344 1 tkerber
        step = np.zeros((n))
345 1 tkerber
        for i in range(n):
346 1 tkerber
                step += D[i]*V[i]
347 1 tkerber
348 1 tkerber
        pos = self.atoms.get_positions().ravel()
349 1 tkerber
        pos += step
350 1 tkerber
351 1 tkerber
        energy_estimate = self.get_energy_estimate(D,Gbar,b)
352 1 tkerber
        self.energy_estimate = energy_estimate
353 1 tkerber
        self.gbar_estimate = self.get_gbar_estimate(D,Gbar,b)
354 1 tkerber
        self.old_gbar = Gbar
355 1 tkerber
356 1 tkerber
        self.atoms.set_positions(pos.reshape((-1, 3)))
357 1 tkerber
358 1 tkerber
359 1 tkerber
360 1 tkerber
361 1 tkerber
    def get_energy_estimate(self,D,Gbar,b):
362 1 tkerber
363 1 tkerber
        de = 0.0
364 1 tkerber
        for n in range(len(D)):
365 1 tkerber
                de += D[n]*Gbar[n] + 0.5*D[n]*b[n]*D[n]
366 1 tkerber
        return de
367 1 tkerber
368 1 tkerber
    def get_gbar_estimate(self,D,Gbar,b):
369 1 tkerber
        gbar_est = (D*b) + Gbar
370 1 tkerber
        self.write_log('Abs Gbar estimate ' + str(np.dot(gbar_est,gbar_est)))
371 1 tkerber
        return gbar_est
372 1 tkerber
373 1 tkerber
    def get_lambdas(self,b,Gbar):
374 1 tkerber
        lamdas = np.zeros((len(b)))
375 1 tkerber
376 1 tkerber
        D = -Gbar/b
377 1 tkerber
        #absD = np.sqrt(np.sum(D**2))
378 1 tkerber
        absD = np.sqrt(np.dot(D, D))
379 1 tkerber
380 1 tkerber
        eps = 1e-12
381 1 tkerber
        nminus = self.get_hessian_inertia(b)
382 1 tkerber
383 1 tkerber
        if absD < self.radius:
384 1 tkerber
                if not self.transitionstate:
385 1 tkerber
                        self.write_log('Newton step')
386 1 tkerber
                        return lamdas
387 1 tkerber
                else:
388 1 tkerber
                        if nminus==1:
389 1 tkerber
                                self.write_log('Newton step')
390 1 tkerber
                                return lamdas
391 1 tkerber
                        else:
392 1 tkerber
                                self.write_log("Wrong inertia of Hessian matrix: %2.2f %2.2f "%(b[0],b[1]))
393 1 tkerber
394 1 tkerber
        else:
395 1 tkerber
                self.write_log("Corrected Newton step: abs(D) = %2.2f "%(absD))
396 1 tkerber
397 1 tkerber
        if not self.transitionstate:
398 1 tkerber
                # upper limit
399 1 tkerber
                upperlimit = min(0,b[0])-eps
400 1 tkerber
                lowerlimit = upperlimit
401 1 tkerber
                lamda = find_lamda(upperlimit,Gbar,b,self.radius)
402 1 tkerber
                lamdas += lamda
403 1 tkerber
        else:
404 1 tkerber
                # upperlimit
405 1 tkerber
                upperlimit = min(-b[0],b[1],0)-eps
406 1 tkerber
                lamda = find_lamda(upperlimit,Gbar,b,self.radius)
407 1 tkerber
                lamdas += lamda
408 1 tkerber
                lamdas[0] -= 2*lamda
409 1 tkerber
410 1 tkerber
        return lamdas
411 1 tkerber
412 1 tkerber
413 1 tkerber
414 1 tkerber
    def print_hessian(self):
415 1 tkerber
        hessian = self.get_hessian()
416 1 tkerber
        n = len(hessian)
417 1 tkerber
        for i in range(n):
418 1 tkerber
            for j in range(n):
419 1 tkerber
                print "%2.4f " %(hessian[i][j]),
420 1 tkerber
            print " "
421 1 tkerber
422 1 tkerber
423 1 tkerber
424 1 tkerber
425 1 tkerber
    def get_hessian_inertia(self,eigenvalues):
426 1 tkerber
        # return number of negative modes
427 1 tkerber
        self.write_log("eigenvalues %2.2f %2.2f %2.2f "%(eigenvalues[0],
428 1 tkerber
                                                        eigenvalues[1],
429 1 tkerber
                                                        eigenvalues[2]))
430 1 tkerber
        n = 0
431 1 tkerber
        while eigenvalues[n]<0:
432 1 tkerber
                n+=1
433 1 tkerber
        return n
434 1 tkerber
435 1 tkerber
    def get_force_prediction(self,G):
436 1 tkerber
        # return measure of how well the forces are predicted
437 1 tkerber
        Gbar = np.dot(G,np.transpose(self.V))
438 1 tkerber
        dGbar_actual = Gbar-self.old_gbar
439 1 tkerber
        dGbar_predicted = Gbar-self.gbar_estimate
440 1 tkerber
441 1 tkerber
        f = np.dot(dGbar_actual,dGbar_predicted)/np.dot(dGbar_actual,dGbar_actual)
442 1 tkerber
        self.write_log('Force prediction factor ' + str(f))
443 1 tkerber
        return f
444 1 tkerber
445 1 tkerber
    def write_iteration(self,energy,G):pass