def get_G(self, mvals=None, customdir=None): """Computes the objective function contribution and its gradient. First the low-level 'get' method is called with the analytic gradient switch turned on. Then we loop through the fd1_pids and compute the corresponding elements of the gradient by finite difference, if the 'fdgrad' switch is turned on. Alternately we can compute the gradient elements and diagonal Hessian elements at the same time using central difference if 'fdhessdiag' is turned on. In this function we also record which parameters cause a nonzero change in the objective function contribution. Parameters which do not change the objective function will not be differentiated in subsequent calculations. This is recorded in a text file in the targets directory. """ Ans = self.meta_get(mvals, 1, 0, customdir=customdir) for i in self.pgrad: if any([j in self.FF.plist[i] for j in self.fd1_pids]) or 'ALL' in self.fd1_pids: if self.fdhessdiag: Ans['G'][i], Ans['H'][i, i] = f12d3p(fdwrap_G(self, mvals, i), self.h, f0=Ans['X']) elif self.fdgrad: Ans['G'][i] = f1d2p(fdwrap_G(self, mvals, i), self.h, f0=Ans['X']) self.gct += 1 if Counter() == self.zerograd and self.zerograd >= 0: self.write_0grads(Ans) return Ans
def get(self, mvals, AGrad=False, AHess=False): """ Evaluate objective function. """ Answer = {'X':0.0, 'G':np.zeros(self.FF.np), 'H':np.zeros((self.FF.np, self.FF.np))} if not hasattr(self, 'ref_eigvecs_nrm'): self.ref_eigvecs_nrm, self.ref_eigvecs_nrm_mw = self.process_vectors(self.ref_eigvecs) def get_eigvals(mvals_): self.FF.make(mvals_) eigvals, eigvecs = self.vibration_driver() eigvecs_nrm, eigvecs_nrm_mw = self.process_vectors(eigvecs) # The overlap metric may take into account some frequency differences dev = np.array([[(np.abs(i-j)/1000)/(1.0+np.abs(i-j)/1000) for j in self.ref_eigvals] for i in eigvals]) for i in range(dev.shape[0]): dev[i, :] /= max(dev[i, :]) if self.reassign in ['permute', 'overlap']: # In the matrix that we constructed, these are the column numbers (reference mode numbers) # that are mapped to the row numbers (calculated mode numbers) if self.reassign == 'permute': a = np.array([[int(1e6*(1.0-np.dot(v1.flatten(),v2.flatten())**2)) for v2 in self.ref_eigvecs_nrm] for v1 in eigvecs_nrm_mw]) row, c2r = optimize.linear_sum_assignment(a) # Commented out dependency on assignment code # c2r = Assign(a) eigvals = eigvals[c2r] elif self.reassign == 'overlap': a = np.array([[(1.0-np.dot(v1.flatten(),v2.flatten())**2) for v2 in self.ref_eigvecs_nrm] for v1 in eigvecs_nrm_mw]) a += dev c2r = np.argmin(a, axis=0) eigvals_p = [] for j in c2r: eigvals_p.append(eigvals[j]) eigvals = np.array(eigvals_p) if not in_fd(): if self.reassign == 'permute': eigvecs_nrm_mw = eigvecs_nrm_mw[c2r] elif self.reassign == 'overlap': self.c2r = c2r eigvecs_nrm_mw_p = [] for j in c2r: eigvecs_nrm_mw_p.append(eigvecs_nrm_mw[j]) eigvecs_nrm_mw = np.array(eigvecs_nrm_mw_p) self.overlaps = np.array([np.abs(np.dot(v1.flatten(),v2.flatten())) for v1, v2 in zip(self.ref_eigvecs_nrm, eigvecs_nrm_mw)]) return eigvals calc_eigvals = get_eigvals(mvals) D = calc_eigvals - self.ref_eigvals dV = np.zeros((self.FF.np,len(calc_eigvals))) if AGrad or AHess: for p in self.pgrad: dV[p,:], _ = f12d3p(fdwrap(get_eigvals, mvals, p), h = self.h, f0 = calc_eigvals) Answer['X'] = np.dot(D,D) / self.denom**2 / (len(D) if self.normalize else 1) for p in self.pgrad: Answer['G'][p] = 2*np.dot(D, dV[p,:]) / self.denom**2 / (len(D) if self.normalize else 1) for q in self.pgrad: Answer['H'][p,q] = 2*np.dot(dV[p,:], dV[q,:]) / self.denom**2 / (len(D) if self.normalize else 1) if not in_fd(): self.calc_eigvals = calc_eigvals self.objective = Answer['X'] return Answer
def energy_dipole_derivatives(mvals,h,FF,xyz,tky,AGrad=True): """ Compute the first and second derivatives of a set of snapshot energies with respect to the force field parameters. This basically calls the finite difference subroutine on the energy_driver subroutine also in this script. @todo This is a sufficiently general function to be merged into openmmio.py? @param[in] mvals Mathematical parameter values @param[in] pdb OpenMM PDB object @param[in] FF ForceBalance force field object @param[in] xyzs List of OpenMM positions @param[in] settings OpenMM settings for creating the System @param[in] boxes Periodic box vectors @return G First derivative of the energies in a N_param x N_coord array """ ED0 = energy_driver(mvals, FF, xyz=xyz, tky=tky, dipole=True) ns = ED0.shape[0] G = np.zeros((FF.np,ns)) GDx = np.zeros((FF.np,ns)) GDy = np.zeros((FF.np,ns)) GDz = np.zeros((FF.np,ns)) if not AGrad: return G, GDx, GDy, GDz CheckFDPts = False for i in range(FF.np): EDG, _ = f12d3p(fdwrap(energy_driver,mvals,i,FF=FF,xyz=xyz,tky=tky,dipole=True),h,f0=ED0) G[i,:] = EDG[:,0] GDx[i,:] = EDG[:,1] GDy[i,:] = EDG[:,2] GDz[i,:] = EDG[:,3] return G, GDx, GDy, GDz
def get(self, mvals, AGrad=False, AHess=False): """ Evaluate objective function. """ Answer = {'X':0.0, 'G':np.zeros(self.FF.np), 'H':np.zeros((self.FF.np, self.FF.np))} def get_momvals(mvals_): self.FF.make(mvals_) moments = self.engine.multipole_moments(polarizability='polarizability' in self.ref_moments, optimize=self.optimize_geometry) # Unpack from dictionary. return self.unpack_moments(moments) self.FF.make(mvals) ref_momvals = self.unpack_moments(self.ref_moments) calc_moments = self.engine.multipole_moments(polarizability='polarizability' in self.ref_moments, optimize=self.optimize_geometry) calc_momvals = self.unpack_moments(calc_moments) D = calc_momvals - ref_momvals dV = np.zeros((self.FF.np,len(calc_momvals))) if AGrad or AHess: for p in range(self.FF.np): dV[p,:], _ = f12d3p(fdwrap(get_momvals, mvals, p), h = self.h, f0 = calc_momvals) Answer['X'] = np.dot(D,D) for p in range(self.FF.np): Answer['G'][p] = 2*np.dot(D, dV[p,:]) for q in range(self.FF.np): Answer['H'][p,q] = 2*np.dot(dV[p,:], dV[q,:]) if not in_fd(): self.calc_moments = calc_moments self.objective = Answer['X'] return Answer
def get_H(self,mvals=None): """Computes the objective function contribution and its gradient / Hessian. First the low-level 'get' method is called with the analytic gradient and Hessian both turned on. Then we loop through the fd1_pids and compute the corresponding elements of the gradient by finite difference, if the 'fdgrad' switch is turned on. This is followed by looping through the fd2_pids and computing the corresponding Hessian elements by finite difference. Forward finite difference is used throughout for the sake of speed. """ Ans = self.meta_get(mvals,1,1) if self.fdhess: for i in self.pgrad: if any([j in self.FF.plist[i] for j in self.fd1_pids]) or 'ALL' in self.fd1_pids: Ans['G'][i] = f1d2p(fdwrap_G(self,mvals,i),self.h,f0 = Ans['X']) for i in self.pgrad: if any([j in self.FF.plist[i] for j in self.fd2_pids]) or 'ALL' in self.fd2_pids: FDSlice = f1d2p(fdwrap_H(self,mvals,i),self.h,f0 = Ans['G']) Ans['H'][i,:] = FDSlice Ans['H'][:,i] = FDSlice elif self.fdhessdiag: for i in self.pgrad: if any([j in self.FF.plist[i] for j in self.fd2_pids]) or 'ALL' in self.fd2_pids: Ans['G'][i], Ans['H'][i,i] = f12d3p(fdwrap_G(self,mvals,i),self.h, f0 = Ans['X']) self.write_0grads(Ans) self.hct += 1 return Ans
def get(self, mvals, AGrad=False, AHess=False): """ Evaluate objective function. """ Answer = {'X':0.0, 'G':np.zeros(self.FF.np), 'H':np.zeros((self.FF.np, self.FF.np))} def get_momvals(mvals_): self.FF.make(mvals_) moments = self.engine.multipole_moments(polarizability='polarizability' in self.ref_moments, optimize=self.optimize_geometry) # Unpack from dictionary. return self.unpack_moments(moments) self.FF.make(mvals) ref_momvals = self.unpack_moments(self.ref_moments) calc_moments = self.engine.multipole_moments(polarizability='polarizability' in self.ref_moments, optimize=self.optimize_geometry) calc_momvals = self.unpack_moments(calc_moments) D = calc_momvals - ref_momvals dV = np.zeros((self.FF.np,len(calc_momvals))) if AGrad or AHess: for p in self.pgrad: dV[p,:], _ = f12d3p(fdwrap(get_momvals, mvals, p), h = self.h, f0 = calc_momvals) Answer['X'] = np.dot(D,D) for p in self.pgrad: Answer['G'][p] = 2*np.dot(D, dV[p,:]) for q in self.pgrad: Answer['H'][p,q] = 2*np.dot(dV[p,:], dV[q,:]) if not in_fd(): self.calc_moments = calc_moments self.objective = Answer['X'] return Answer
def get(self, mvals, AGrad=False, AHess=False): """ Evaluate objective function. """ Answer = {'X':0.0, 'G':zeros(self.FF.np, dtype=float), 'H':zeros((self.FF.np, self.FF.np), dtype=float)} def get_momvals(mvals_): self.FF.make(mvals_) moments = self.moments_driver() # Unpack from dictionary. return self.unpack_moments(moments) self.FF.make(mvals) ref_momvals = self.unpack_moments(self.ref_moments) calc_moments = self.moments_driver() calc_momvals = self.unpack_moments(calc_moments) D = calc_momvals - ref_momvals dV = zeros((self.FF.np,len(calc_momvals)),dtype=float) if AGrad or AHess: for p in range(self.FF.np): dV[p,:], _ = f12d3p(fdwrap(get_momvals, mvals, p), h = self.h, f0 = calc_momvals) Answer['X'] = dot(D,D) for p in range(self.FF.np): Answer['G'][p] = 2*dot(D, dV[p,:]) for q in range(self.FF.np): Answer['H'][p,q] = 2*dot(dV[p,:], dV[q,:]) if not in_fd(): self.FF.make(mvals) self.calc_moments = calc_moments self.objective = Answer['X'] return Answer
def get_H(self,mvals=None,customdir=None): """Computes the objective function contribution and its gradient / Hessian. First the low-level 'get' method is called with the analytic gradient and Hessian both turned on. Then we loop through the fd1_pids and compute the corresponding elements of the gradient by finite difference, if the 'fdgrad' switch is turned on. This is followed by looping through the fd2_pids and computing the corresponding Hessian elements by finite difference. Forward finite difference is used throughout for the sake of speed. """ Ans = self.meta_get(mvals,1,1,customdir=customdir) if self.fdhess: for i in self.pgrad: if any([j in self.FF.plist[i] for j in self.fd1_pids]) or 'ALL' in self.fd1_pids: Ans['G'][i] = f1d2p(fdwrap_G(self,mvals,i),self.h,f0 = Ans['X']) for i in self.pgrad: if any([j in self.FF.plist[i] for j in self.fd2_pids]) or 'ALL' in self.fd2_pids: FDSlice = f1d2p(fdwrap_H(self,mvals,i),self.h,f0 = Ans['G']) Ans['H'][i,:] = FDSlice Ans['H'][:,i] = FDSlice elif self.fdhessdiag: for i in self.pgrad: if any([j in self.FF.plist[i] for j in self.fd2_pids]) or 'ALL' in self.fd2_pids: Ans['G'][i], Ans['H'][i,i] = f12d3p(fdwrap_G(self,mvals,i),self.h, f0 = Ans['X']) if Counter() == self.zerograd and self.zerograd >= 0: self.write_0grads(Ans) self.hct += 1 return Ans
def get(self, mvals, AGrad=False, AHess=False): """ Evaluate objective function. """ Answer = {'X':0.0, 'G':zeros(self.FF.np, dtype=float), 'H':zeros((self.FF.np, self.FF.np), dtype=float)} def get_eigvals(mvals_): self.FF.make(mvals_) eigvals, eigvecs = self.vibration_driver() # Put reference eigenvectors in the rows and calculated eigenvectors in the columns. # Square the dot product (pointing in opposite direction is still the same eigenvector) # Convert to integer for the "Assign" subroutine, subtract from a million. a = array([[int(1e6*(1.0-min(1.0,dot(v1.flatten(),v2.flatten())**2))) for v2 in self.ref_eigvecs] for v1 in eigvecs]) # In the matrix that we constructed, these are the column numbers (reference mode numbers) # that are mapped to the row numbers (calculated mode numbers) c2r = Assign(a) return eigvals[c2r] calc_eigvals = get_eigvals(mvals) D = calc_eigvals - self.ref_eigvals dV = zeros((self.FF.np,len(calc_eigvals)),dtype=float) if AGrad or AHess: for p in range(self.FF.np): dV[p,:], _ = f12d3p(fdwrap(get_eigvals, mvals, p), h = self.h, f0 = calc_eigvals) Answer['X'] = dot(D,D) / self.denom**2 for p in range(self.FF.np): Answer['G'][p] = 2*dot(D, dV[p,:]) / self.denom**2 for q in range(self.FF.np): Answer['H'][p,q] = 2*dot(dV[p,:], dV[q,:]) / self.denom**2 if not in_fd(): self.calc_eigvals = calc_eigvals self.objective = Answer['X'] return Answer
def energy_derivatives(mvals,h,FF,xyz,tky,AGrad=True): """ Compute the first and second derivatives of a set of snapshot energies with respect to the force field parameters. This basically calls the finite difference subroutine on the energy_driver subroutine also in this script. @todo This is a sufficiently general function to be merged into openmmio.py? @param[in] mvals Mathematical parameter values @param[in] pdb OpenMM PDB object @param[in] FF ForceBalance force field object @param[in] xyzs List of OpenMM positions @param[in] settings OpenMM settings for creating the System @param[in] boxes Periodic box vectors @return G First derivative of the energies in a N_param x N_coord array """ E0 = energy_driver(mvals, FF, xyz, tky) ns = len(E0) G = np.zeros((FF.np,ns)) if not AGrad: return G CheckFDPts = False for i in range(FF.np): G[i,:], _ = f12d3p(fdwrap(energy_driver,mvals,i,FF=FF,xyz=xyz,tky=tky),h,f0=E0) if CheckFDPts: # Check whether the number of finite difference points is sufficient. Forward difference still gives residual error of a few percent. G1 = f1d7p(fdwrap(energy_driver,mvals,i,FF=FF,xyz=xyz,tky=tky),h) dG = G1 - G[i,:] dGfrac = (G1 - G[i,:]) / G[i,:] print "Parameter %3i 7-pt vs. central derivative : RMS, Max error (fractional) = % .4e % .4e (% .4e % .4e)" % (i, np.sqrt(np.mean(dG**2)), max(np.abs(dG)), np.sqrt(np.mean(dGfrac**2)), max(np.abs(dGfrac))) return G
def get_G(self,mvals=None): """Computes the objective function contribution and its gradient. First the low-level 'get' method is called with the analytic gradient switch turned on. Then we loop through the fd1_pids and compute the corresponding elements of the gradient by finite difference, if the 'fdgrad' switch is turned on. Alternately we can compute the gradient elements and diagonal Hessian elements at the same time using central difference if 'fdhessdiag' is turned on. In this function we also record which parameters cause a nonzero change in the objective function contribution. Parameters which do not change the objective function will not be differentiated in subsequent calculations. This is recorded in a text file in the targets directory. """ Ans = self.meta_get(mvals,1,0) for i in self.pgrad: if any([j in self.FF.plist[i] for j in self.fd1_pids]) or 'ALL' in self.fd1_pids: if self.fdhessdiag: Ans['G'][i], Ans['H'][i,i] = f12d3p(fdwrap_G(self,mvals,i),self.h,f0 = Ans['X']) elif self.fdgrad: Ans['G'][i] = f1d2p(fdwrap_G(self,mvals,i),self.h,f0 = Ans['X']) self.gct += 1 self.write_0grads(Ans) return Ans
def get(self, mvals, AGrad=False, AHess=False): """ Evaluate objective function. """ Answer = {'X':0.0, 'G':np.zeros(self.FF.np), 'H':np.zeros((self.FF.np, self.FF.np))} if not hasattr(self, 'ref_eigvecs_nrm'): self.ref_eigvecs_nrm, self.ref_eigvecs_nrm_mw = self.process_vectors(self.ref_eigvecs) def get_eigvals(mvals_): self.FF.make(mvals_) eigvals, eigvecs = self.vibration_driver() eigvecs_nrm, eigvecs_nrm_mw = self.process_vectors(eigvecs) # The overlap metric may take into account some frequency differences dev = np.array([[(np.abs(i-j)/1000)/(1.0+np.abs(i-j)/1000) for j in self.ref_eigvals] for i in eigvals]) for i in range(dev.shape[0]): dev[i, :] /= max(dev[i, :]) if self.reassign in ['permute', 'overlap']: # In the matrix that we constructed, these are the column numbers (reference mode numbers) # that are mapped to the row numbers (calculated mode numbers) if self.reassign == 'permute': a = np.array([[int(1e6*(1.0-np.dot(v1.flatten(),v2.flatten())**2)) for v2 in self.ref_eigvecs_nrm] for v1 in eigvecs_nrm_mw]) c2r = Assign(a) eigvals = eigvals[c2r] elif self.reassign == 'overlap': a = np.array([[(1.0-np.dot(v1.flatten(),v2.flatten())**2) for v2 in self.ref_eigvecs_nrm] for v1 in eigvecs_nrm_mw]) a += dev c2r = np.argmin(a, axis=0) eigvals_p = [] for j in c2r: eigvals_p.append(eigvals[j]) eigvals = np.array(eigvals_p) if not in_fd(): if self.reassign == 'permute': eigvecs_nrm_mw = eigvecs_nrm_mw[c2r] elif self.reassign == 'overlap': self.c2r = c2r eigvecs_nrm_mw_p = [] for j in c2r: eigvecs_nrm_mw_p.append(eigvecs_nrm_mw[j]) eigvecs_nrm_mw = np.array(eigvecs_nrm_mw_p) self.overlaps = np.array([np.abs(np.dot(v1.flatten(),v2.flatten())) for v1, v2 in zip(self.ref_eigvecs_nrm, eigvecs_nrm_mw)]) return eigvals calc_eigvals = get_eigvals(mvals) D = calc_eigvals - self.ref_eigvals dV = np.zeros((self.FF.np,len(calc_eigvals))) if AGrad or AHess: for p in self.pgrad: dV[p,:], _ = f12d3p(fdwrap(get_eigvals, mvals, p), h = self.h, f0 = calc_eigvals) Answer['X'] = np.dot(D,D) / self.denom**2 / (len(D) if self.normalize else 1) for p in self.pgrad: Answer['G'][p] = 2*np.dot(D, dV[p,:]) / self.denom**2 / (len(D) if self.normalize else 1) for q in self.pgrad: Answer['H'][p,q] = 2*np.dot(dV[p,:], dV[q,:]) / self.denom**2 / (len(D) if self.normalize else 1) if not in_fd(): self.calc_eigvals = calc_eigvals self.objective = Answer['X'] return Answer
def get(self, mvals, AGrad=False, AHess=False): """ Evaluate objective function. """ Answer = {'X':0.0, 'G':np.zeros(self.FF.np), 'H':np.zeros((self.FF.np, self.FF.np))} def get_eigvals(mvals_): self.FF.make(mvals_) eigvals, eigvecs = self.vibration_driver() # The overlap metric may take into account some frequency differences. # Here, an element of dev is equal to 2/3 if (for example) the frequencies differ by 1000. dev = np.array([[(np.abs(i-j)/1000)/(1.0+np.abs(i-j)/1000) for j in eigvals] for i in self.ref_eigvals]) for i in range(dev.shape[0]): dev[i, :] /= max(dev[i, :]) if self.reassign in ['permute', 'overlap']: # The elements of "a" matrix are the column numbers (reference mode numbers) # that are mapped to the row numbers (calculated mode numbers). # Highly similar eigenvectors are assigned small values because # the assignment problem is a cost minimization problem. a = np.array([[(1.0-vib_overlap(self.engine, v1, v2)) for v2 in eigvecs] for v1 in self.ref_eigvecs]) a += dev if self.reassign == 'permute': row, c2r = optimize.linear_sum_assignment(a) eigvals = eigvals[c2r] elif self.reassign == 'overlap': c2r = np.argmin(a, axis=0) eigvals_p = [] for j in c2r: eigvals_p.append(eigvals[j]) eigvals = np.array(eigvals_p) if not in_fd(): if self.reassign == 'permute': eigvecs = eigvecs[c2r] elif self.reassign == 'overlap': self.c2r = c2r eigvecs_p = [] for j in c2r: eigvecs_p.append(eigvecs[j]) eigvecs = np.array(eigvecs_p) self.overlaps = np.array([vib_overlap(self.engine, v1, v2) for v1, v2 in zip(self.ref_eigvecs, eigvecs)]) return eigvals calc_eigvals = get_eigvals(mvals) D = calc_eigvals - self.ref_eigvals dV = np.zeros((self.FF.np,len(calc_eigvals))) if AGrad or AHess: for p in self.pgrad: dV[p,:], _ = f12d3p(fdwrap(get_eigvals, mvals, p), h = self.h, f0 = calc_eigvals) Answer['X'] = np.dot(D,D) / self.denom**2 / (len(D) if self.normalize else 1) for p in self.pgrad: Answer['G'][p] = 2*np.dot(D, dV[p,:]) / self.denom**2 / (len(D) if self.normalize else 1) for q in self.pgrad: Answer['H'][p,q] = 2*np.dot(dV[p,:], dV[q,:]) / self.denom**2 / (len(D) if self.normalize else 1) if not in_fd(): self.calc_eigvals = calc_eigvals self.objective = Answer['X'] return Answer
def energy_derivatives(engine, FF, mvals, h, pgrad, length, AGrad=True, dipole=False): """ Compute the first and second derivatives of a set of snapshot energies with respect to the force field parameters. This basically calls the finite difference subroutine on the energy_driver subroutine also in this script. @param[in] mvals Mathematical parameter values @param[in] h Finite difference step size @param[in] phase The phase (liquid, gas) to perform the calculation on @param[in] AGrad Switch to turn derivatives on or off; if off, return all zeros @param[in] dipole Switch for dipole derivatives. @return G First derivative of the energies in a N_param x N_coord array @return GDx First derivative of the box dipole moment x-component in a N_param x N_coord array @return GDy First derivative of the box dipole moment y-component in a N_param x N_coord array @return GDz First derivative of the box dipole moment z-component in a N_param x N_coord array """ G = np.zeros((FF.np, length)) GDx = np.zeros((FF.np, length)) GDy = np.zeros((FF.np, length)) GDz = np.zeros((FF.np, length)) if not AGrad: return G, GDx, GDy, GDz def energy_driver(mvals_): FF.make(mvals_) if dipole: return engine.energy_dipole() else: return engine.energy() ED0 = energy_driver(mvals) for i in pgrad: logger.info("%i %s\r" % (i, (FF.plist[i] + " " * 30))) EDG, _ = f12d3p(fdwrap(energy_driver, mvals, i), h, f0=ED0) if dipole: G[i, :] = EDG[:, 0] GDx[i, :] = EDG[:, 1] GDy[i, :] = EDG[:, 2] GDz[i, :] = EDG[:, 3] else: G[i, :] = EDG[:] #reset FF parameters FF.make(mvals) return G, GDx, GDy, GDz
def get_sp(self, mvals, AGrad=False, AHess=False): """ Get the hydration free energy and first parameteric derivatives using single point energy evaluations. """ def get_hfe(mvals_): self.FF.make(mvals_) self.hfe_dict = self.hydration_driver_sp() return np.array(self.hfe_dict.values()) calc_hfe = get_hfe(mvals) D = calc_hfe - np.array(self.expval.values()) dD = np.zeros((self.FF.np,len(self.IDs))) if AGrad or AHess: for p in self.pgrad: dD[p,:], _ = f12d3p(fdwrap(get_hfe, mvals, p), h = self.h, f0 = calc_hfe) return D, dD
def get_sp(self, mvals, AGrad=False, AHess=False): """ Get the hydration free energy and first parameteric derivatives using single point energy evaluations. """ def get_hfe(mvals_): self.FF.make(mvals_) self.hfe_dict = self.hydration_driver_sp() return np.array(list(self.hfe_dict.values())) calc_hfe = get_hfe(mvals) D = calc_hfe - np.array(list(self.expval.values())) dD = np.zeros((self.FF.np,len(self.IDs))) if AGrad or AHess: for p in self.pgrad: dD[p,:], _ = f12d3p(fdwrap(get_hfe, mvals, p), h = self.h, f0 = calc_hfe) return D, dD
def get(self, mvals, AGrad=False, AHess=False): """ Evaluate objective function. """ Answer = {'X':0.0, 'G':np.zeros(self.FF.np), 'H':np.zeros((self.FF.np, self.FF.np))} # If the weight is zero, turn all derivatives off. if (self.weight == 0.0): AGrad = False AHess = False def callM(mvals_, dielectric=False): logger.info("\r") pvals = self.FF.make(mvals_) return self.engine.interaction_energy(self.select1, self.select2) logger.info("Executing\r") emm = callM(mvals) D = emm - self.eqm dV = np.zeros((self.FF.np,len(emm))) # Dump interaction energies to disk. np.savetxt('M.txt',emm) np.savetxt('Q.txt',self.eqm) # Do the finite difference derivative. if AGrad or AHess: for p in self.pgrad: dV[p,:], _ = f12d3p(fdwrap(callM, mvals, p), h = self.h, f0 = emm) # Create the force field one last time. pvals = self.FF.make(mvals) Answer['X'] = np.dot(self.prefactor*D/self.divisor,D/self.divisor) for p in self.pgrad: Answer['G'][p] = 2*np.dot(self.prefactor*D/self.divisor, dV[p,:]/self.divisor) for q in self.pgrad: Answer['H'][p,q] = 2*np.dot(self.prefactor*dV[p,:]/self.divisor, dV[q,:]/self.divisor) if not in_fd(): self.emm = emm self.objective = Answer['X'] ## QYD: try to clean up OpenMM engine.simulation objects to free up GPU memory try: if self.engine.name == 'openmm': if hasattr(self.engine, 'simulation'): del self.engine.simulation if hasattr(self.engine, 'A'): del self.engine.A if hasattr(self.engine, 'B'): del self.engine.B except: pass return Answer
def get(self, mvals, AGrad=False, AHess=False): """ Evaluate objective function. """ Answer = {'X':0.0, 'G':np.zeros(self.FF.np), 'H':np.zeros((self.FF.np, self.FF.np))} if not hasattr(self, 'ref_eigvecs_nrm'): self.ref_eigvecs_nrm, self.ref_eigvecs_nrm_mw = self.process_vectors(self.ref_eigvecs) def get_eigvals(mvals_): self.FF.make(mvals_) eigvals, eigvecs = self.vibration_driver() eigvecs_nrm, eigvecs_nrm_mw = self.process_vectors(eigvecs) if self.reassign in ['permute', 'overlap']: a = np.array([[int(1e6*(1.0-np.dot(v1.flatten(),v2.flatten())**2)) for v2 in self.ref_eigvecs_nrm] for v1 in eigvecs_nrm_mw]) # In the matrix that we constructed, these are the column numbers (reference mode numbers) # that are mapped to the row numbers (calculated mode numbers) if self.reassign == 'permute': c2r = Assign(a) eigvals = eigvals[c2r] elif self.reassign == 'overlap': c2r = np.argmin(a, axis=0) eigvals_p = [] for j in c2r: eigvals_p.append(eigvals[j]) eigvals = np.array(eigvals_p) if not in_fd(): if self.reassign == 'permute': eigvecs_nrm_mw = eigvecs_nrm_mw[c2r] elif self.reassign == 'overlap': self.c2r = c2r eigvecs_nrm_mw_p = [] for j in c2r: eigvecs_nrm_mw_p.append(eigvecs_nrm_mw[j]) eigvecs_nrm_mw = np.array(eigvecs_nrm_mw_p) self.overlaps = np.array([np.abs(np.dot(v1.flatten(),v2.flatten())) for v1, v2 in zip(self.ref_eigvecs_nrm, eigvecs_nrm_mw)]) return eigvals calc_eigvals = get_eigvals(mvals) D = calc_eigvals - self.ref_eigvals dV = np.zeros((self.FF.np,len(calc_eigvals))) if AGrad or AHess: for p in range(self.FF.np): dV[p,:], _ = f12d3p(fdwrap(get_eigvals, mvals, p), h = self.h, f0 = calc_eigvals) Answer['X'] = np.dot(D,D) / self.denom**2 for p in range(self.FF.np): Answer['G'][p] = 2*np.dot(D, dV[p,:]) / self.denom**2 for q in range(self.FF.np): Answer['H'][p,q] = 2*np.dot(dV[p,:], dV[q,:]) / self.denom**2 if not in_fd(): self.calc_eigvals = calc_eigvals self.objective = Answer['X'] return Answer
def get(self, mvals, AGrad=False, AHess=False): """ Evaluate objective function. """ Answer = {'X':0.0, 'G':np.zeros(self.FF.np), 'H':np.zeros((self.FF.np, self.FF.np))} # If the weight is zero, turn all derivatives off. if (self.weight == 0.0): AGrad = False AHess = False def callM(mvals_, dielectric=False): logger.info("\r") pvals = self.FF.make(mvals_) return self.engine.interaction_energy(self.select1, self.select2) logger.info("Executing\r") emm = callM(mvals) D = emm - self.eqm dV = np.zeros((self.FF.np,len(emm))) # Dump interaction energies to disk. np.savetxt('M.txt',emm) np.savetxt('Q.txt',self.eqm) # Do the finite difference derivative. if AGrad or AHess: for p in self.pgrad: dV[p,:], _ = f12d3p(fdwrap(callM, mvals, p), h = self.h, f0 = emm) # Create the force field one last time. pvals = self.FF.make(mvals) Answer['X'] = np.dot(self.prefactor*D/self.divisor,D/self.divisor) for p in self.pgrad: Answer['G'][p] = 2*np.dot(self.prefactor*D/self.divisor, dV[p,:]/self.divisor) for q in self.pgrad: Answer['H'][p,q] = 2*np.dot(self.prefactor*dV[p,:]/self.divisor, dV[q,:]/self.divisor) if not in_fd(): self.emm = emm self.objective = Answer['X'] ## QYD: try to clean up OpenMM engine.simulation objects to free up GPU memory try: if self.engine.name == 'openmm': if hasattr(self.engine, 'simulation'): del self.engine.simulation if hasattr(self.engine, 'A'): del self.engine.A if hasattr(self.engine, 'B'): del self.engine.B except: pass return Answer
def energy_derivatives(engine, FF, mvals, h, pgrad, length, AGrad=True, dipole=False): """ Compute the first and second derivatives of a set of snapshot energies with respect to the force field parameters. This basically calls the finite difference subroutine on the energy_driver subroutine also in this script. @param[in] mvals Mathematical parameter values @param[in] h Finite difference step size @param[in] phase The phase (liquid, gas) to perform the calculation on @param[in] AGrad Switch to turn derivatives on or off; if off, return all zeros @param[in] dipole Switch for dipole derivatives. @return G First derivative of the energies in a N_param x N_coord array @return GDx First derivative of the box dipole moment x-component in a N_param x N_coord array @return GDy First derivative of the box dipole moment y-component in a N_param x N_coord array @return GDz First derivative of the box dipole moment z-component in a N_param x N_coord array """ G = np.zeros((FF.np,length)) GDx = np.zeros((FF.np,length)) GDy = np.zeros((FF.np,length)) GDz = np.zeros((FF.np,length)) if not AGrad: return G, GDx, GDy, GDz def energy_driver(mvals_): FF.make(mvals_) if dipole: return engine.energy_dipole() else: return engine.energy() ED0 = energy_driver(mvals) for i in pgrad: logger.info("%i %s\r" % (i, (FF.plist[i] + " "*30))) EDG, _ = f12d3p(fdwrap(energy_driver,mvals,i),h,f0=ED0) if dipole: G[i,:] = EDG[:,0] GDx[i,:] = EDG[:,1] GDy[i,:] = EDG[:,2] GDz[i,:] = EDG[:,3] else: G[i,:] = EDG[:] #reset FF parameters FF.make(mvals) return G, GDx, GDy, GDz
def energy_derivatives(engine, FF, mvals, h, pgrad, dipole=False): """ Compute the first and second derivatives of a set of snapshot energies with respect to the force field parameters. This basically calls the finite difference subroutine on the energy_driver subroutine also in this script. In the future we may need to be more sophisticated with controlling the quantities which are differentiated, but for now this is okay.. @param[in] engine Engine object for calculating energies @param[in] FF Force field object @param[in] mvals Mathematical parameter values @param[in] h Finite difference step size @param[in] pgrad List of active parameters for differentiation @param[in] dipole Switch for dipole derivatives. @return G First derivative of the energies in a N_param x N_coord array @return GDx First derivative of the box dipole moment x-component in a N_param x N_coord array @return GDy First derivative of the box dipole moment y-component in a N_param x N_coord array @return GDz First derivative of the box dipole moment z-component in a N_param x N_coord array """ def single_point(mvals_): FF.make(mvals_) if dipole: return engine.energy_dipole() else: return engine.energy() ED0 = single_point(mvals) G = OrderedDict() G['potential'] = np.zeros((FF.np, ED0.shape[0])) if dipole: G['dipole'] = np.zeros((FF.np, ED0.shape[0], 3)) for i in pgrad: logger.info("%i %s\r" % (i, (FF.plist[i] + " "*30))) edg, _ = f12d3p(fdwrap(single_point,mvals,i),h,f0=ED0) if dipole: G['potential'][i] = edg[:,0] G['dipole'][i] = edg[:,1:] else: G['potential'][i] = edg[:] return G
def energy_derivatives(engine, FF, mvals, h, pgrad, length, AGrad=True): """Compute the first derivatives of a set of snapshot energies with respect to the force field parameters. The function calls the finite difference subroutine on the energy_driver subroutine also in this script. Parameters ---------- engine : Engine Use this Engine (`GMX`,`TINKER`,`OPENMM` etc.) object to get the energy snapshots. FF : FF Force field object. mvals : list Mathematical parameter values. h : float Finite difference step size. length : int Number of snapshots (length of energy trajectory). AGrad : Boolean Switch that turns derivatives on or off; if off, return all zeros. Returns ------- G : np.array Derivative of the energy in a FF.np x length array. """ G = np.zeros((FF.np, length)) if not AGrad: return G def energy_driver(mvals_): FF.make(mvals_) return engine.energy() ED0 = energy_driver(mvals) for i in pgrad: logger.info("%i %s\r" % (i, (FF.plist[i] + " " * 30))) EDG, _ = f12d3p(fdwrap(energy_driver, mvals, i), h, f0=ED0) G[i, :] = EDG[:] return G
def energy_derivatives(engine, FF, mvals, h, pgrad, length, AGrad=True): """Compute the first derivatives of a set of snapshot energies with respect to the force field parameters. The function calls the finite difference subroutine on the energy_driver subroutine also in this script. Parameters ---------- engine : Engine Use this Engine (`GMX`,`TINKER`,`OPENMM` etc.) object to get the energy snapshots. FF : FF Force field object. mvals : list Mathematical parameter values. h : float Finite difference step size. length : int Number of snapshots (length of energy trajectory). AGrad : Boolean Switch that turns derivatives on or off; if off, return all zeros. Returns ------- G : np.array Derivative of the energy in a FF.np x length array. """ G = np.zeros((FF.np, length)) if not AGrad: return G def energy_driver(mvals_): FF.make(mvals_) return engine.energy() ED0 = energy_driver(mvals) for i in pgrad: logger.info("%i %s\r" % (i, (FF.plist[i] + " "*30))) EDG, _ = f12d3p(fdwrap(energy_driver, mvals, i), h, f0=ED0) G[i,:] = EDG[:] return G
def get_G(self,mvals=None): """Computes the objective function contribution and its gradient. First the low-level 'get' method is called with the analytic gradient switch turned on. Then we loop through the fd1_pids and compute the corresponding elements of the gradient by finite difference, if the 'fdgrad' switch is turned on. Alternately we can compute the gradient elements and diagonal Hessian elements at the same time using central difference if 'fdhessdiag' is turned on. """ Ans = self.sget(mvals,1,0) for i in range(self.FF.np): if any([j in self.FF.plist[i] for j in self.fd1_pids]) or 'ALL' in self.fd1_pids: if self.fdhessdiag: Ans['G'][i], Ans['H'][i,i] = f12d3p(fdwrap_G(self,mvals,i),self.h,f0 = Ans['X']) elif self.fdgrad: Ans['G'][i] = f1d2p(fdwrap_G(self,mvals,i),self.h,f0 = Ans['X']) self.gct += 1 return Ans
def get(self, mvals, AGrad=False, AHess=False): """ Evaluate objective function. """ Answer = {'X':0.0, 'G':zeros(self.FF.np, dtype=float), 'H':zeros((self.FF.np, self.FF.np), dtype=float)} # If the weight is zero, turn all derivatives off. if (self.weight == 0.0): AGrad = False AHess = False def callM(mvals_, dielectric=False): logger.info("\r") pvals = self.FF.make(mvals_) return self.interaction_driver_all(dielectric) logger.info("Executing\r") emm = callM(mvals) D = emm - self.eqm dV = zeros((self.FF.np,len(emm)),dtype=float) # Dump interaction energies to disk. savetxt('M.txt',emm) savetxt('Q.txt',self.eqm) # Do the finite difference derivative. if AGrad or AHess: for p in range(self.FF.np): dV[p,:], _ = f12d3p(fdwrap(callM, mvals, p), h = self.h, f0 = emm) # Create the force field one last time. pvals = self.FF.make(mvals) Answer['X'] = dot(self.prefactor*D/self.divisor,D/self.divisor) for p in range(self.FF.np): Answer['G'][p] = 2*dot(self.prefactor*D/self.divisor, dV[p,:]/self.divisor) for q in range(self.FF.np): Answer['H'][p,q] = 2*dot(self.prefactor*dV[p,:]/self.divisor, dV[q,:]/self.divisor) if not in_fd(): self.emm = emm self.objective = Answer['X'] return Answer
def rpmd_energy_derivatives(engine, FF, mvals, h, pgrad, length, AGrad=True, dipole=False): """ Compute the first and second derivatives of a set of snapshot energies with respect to the force field parameters. This is analagous to the energy_derivatives function above, but is specific for calculating derivatives for rpmd, which has an additional term due to the force term in the centroid virial estimator. """ G = np.zeros((FF.np,length)) GDx = np.zeros((FF.np,length)) GDy = np.zeros((FF.np,length)) GDz = np.zeros((FF.np,length)) RPMDG = np.zeros((FF.np,length)) RPMDG_frc_term = np.zeros((FF.np,length)) if not AGrad: return G, GDx, GDy, GDz, RPMDG, RPMDG_frc_term def rpmd_energy_driver(mvals_): FF.make(mvals_) if dipole: return engine.energy_dipole_rpmd() else: return engine.energy_rpmd() ED0 = rpmd_energy_driver(mvals) for i in pgrad: logger.info("%i %s\r" % (i, (FF.plist[i] + " "*30))) ERPMDG, _ = f12d3p(fdwrap(rpmd_energy_driver,mvals,i),h,f0=ED0) if dipole: G[i,:] = ERPMDG[:,0] GDx[i,:] = ERPMDG[:,1] GDy[i,:] = ERPMDG[:,2] GDz[i,:] = ERPMDG[:,3] RPMDG[i,:] = ERPMDG[:,4] RPMDG_frc_term[i,:] = ERPMDG[:,5] else: G[i,:] = ERPMDG[:,0] RPMDG[i,:] = ERPMDG[:,1] RPMDG_frc_term[i,:] = ERPMDG[:,2] return G, GDx, GDy, GDz, RPMDG, RPMDG_frc_term
def get(self, mvals, AGrad=False, AHess=False): """ Evaluate objective function. """ Answer = {'X':0.0, 'G':np.zeros(self.FF.np), 'H':np.zeros((self.FF.np, self.FF.np))} # If the weight is zero, turn all derivatives off. if (self.weight == 0.0): AGrad = False AHess = False def callM(mvals_, dielectric=False): logger.info("\r") pvals = self.FF.make(mvals_) return self.engine.interaction_energy(self.select1, self.select2) logger.info("Executing\r") emm = callM(mvals) D = emm - self.eqm dV = np.zeros((self.FF.np,len(emm))) if self.writelevel > 0: # Dump interaction energies to disk. np.savetxt('M.txt',emm) np.savetxt('Q.txt',self.eqm) import pickle pickle.dump((self.name, self.label, self.prefactor, self.eqm, emm), open("qm_vs_mm.p",'w')) # select the qm and mm data that has >0 weight to plot qm_data, mm_data = [], [] for i in xrange(len(self.eqm)): if self.prefactor[i] != 0: qm_data.append(self.eqm[i]) mm_data.append(emm[i]) plot_interaction_qm_vs_mm(qm_data, mm_data, title="Interaction Energy "+self.name) # Do the finite difference derivative. if AGrad or AHess: for p in self.pgrad: dV[p,:], _ = f12d3p(fdwrap(callM, mvals, p), h = self.h, f0 = emm) # Create the force field one last time. pvals = self.FF.make(mvals) Answer['X'] = np.dot(self.prefactor*D/self.divisor,D/self.divisor) for p in self.pgrad: Answer['G'][p] = 2*np.dot(self.prefactor*D/self.divisor, dV[p,:]/self.divisor) for q in self.pgrad: Answer['H'][p,q] = 2*np.dot(self.prefactor*dV[p,:]/self.divisor, dV[q,:]/self.divisor) if not in_fd(): self.emm = emm self.objective = Answer['X'] ## QYD: try to clean up OpenMM engine.simulation objects to free up GPU memory try: if self.engine.name == 'openmm': if hasattr(self.engine, 'simulation'): del self.engine.simulation if hasattr(self.engine, 'A'): del self.engine.A if hasattr(self.engine, 'B'): del self.engine.B except: pass return Answer
def get(self, mvals, AGrad=False, AHess=False): Answer = {'X':0.0, 'G':np.zeros(self.FF.np), 'H':np.zeros((self.FF.np, self.FF.np))} self.PrintDict = OrderedDict() self.RMSDDict = OrderedDict() #pool = Pool(processes=4) def compute(mvals_): # This function has automatically assigned variable names from the interaction master file # Thus, all variable names in here are protected using an underscore. self.FF.make(mvals_) VectorD_ = [] # The following code was odified by Chengwen Liu to accelarate the tinker analyze and optimize jobs def Energy_RMSD(systems): tinkerhome = os.environ["TINKERPATH"] f1 = open("runAna.sh", "w") f2 = open("runMin.sh", "w") i = 0 for sys_ in systems: opts = systems[sys_] optimize = (opts['optimize'] if 'optimize' in opts else False) if not optimize: if (i+1)%24 == 0: cmd = "rm -f %s.out\n%s/analyze %s.xyz -k %s.key E > %s.out \n"%(sys_, os.environ["TINKERPATH"], sys_, sys_, sys_) i += 1 else: cmd = "rm -f %s.out\n%s/analyze %s.xyz -k %s.key E > %s.out & \n"%(sys_, os.environ["TINKERPATH"], sys_, sys_, sys_) i += 1 f1.write(cmd) else: if (i+1)%24 == 0: cmd = "rm -f %s.xyz_2 %s.out \n%s/optimize %s.xyz -k %s.key 0.0001 > %s.out \n"%(sys_, sys_, os.environ["TINKERPATH"], sys_, sys_, sys_) i += 1 else: cmd = "rm -f %s.xyz_2 %s.out \n%s/optimize %s.xyz -k %s.key 0.0001 > %s.out & \n"%(sys_, sys_, os.environ["TINKERPATH"], sys_, sys_, sys_) i += 1 f2.write(cmd) f1.write("wait\n") f2.write("wait\n") f1.close() f2.close() os.system("sh runAna.sh") os.system("sh runMin.sh") for sys_ in systems: while not os.path.isfile(os.path.join(os.getcwd(), sys_ + ".out")): time.sleep(1.0) Es = {} RMSDs = {} for sys_ in systems: energ = 0.0 rmsd = 0.0 for line in open("%s.out"%sys_).readlines(): if "Total Potential Energy" in line: energ = float(line.split()[-2].replace('D','e')) Es[sys_] = energ RMSDs[sys_] = 0.0 if "Final Function Value :" in line: energ = float(line.split()[-1].replace('D','e')) Es[sys_] = energ M1 = Molecule("%s.xyz" % sys_, ftype="tinker") M2 = Molecule("%s.xyz_2" % sys_, ftype="tinker") M1 += M2 RMSDs[sys_] = M1.ref_rmsd(0)[1] return Es,RMSDs Es, RMSDs = Energy_RMSD(self.sys_opts) for sys_ in self.sys_opts: Energy_ = Es[sys_] RMSD_ = RMSDs[sys_] exec("%s = Energy_" % sys_) in locals() RMSDNrm_ = RMSD_ / self.rmsd_denom w_ = self.sys_opts[sys_]['rmsd_weight'] if 'rmsd_weight' in self.sys_opts[sys_] else 1.0 VectorD_.append(np.sqrt(w_)*RMSDNrm_) if not in_fd() and RMSD_ != 0.0: self.RMSDDict[sys_] = "% 9.3f % 12.5f" % (RMSD_, w_*RMSDNrm_**2) VectorE_ = [] for inter_ in self.inter_opts: Calculated_ = eval(self.inter_opts[inter_]['equation']) Reference_ = self.inter_opts[inter_]['reference_physical'] Delta_ = Calculated_ - Reference_ Denom_ = self.energy_denom if self.cauchy: Divisor_ = np.sqrt(Denom_**2 + Reference_**2) elif self.attenuate: if Reference_ < Denom_: Divisor_ = Denom_ else: Divisor_ = np.sqrt(Denom_**2 + (Reference_-Denom_)**2) else: Divisor_ = Denom_ DeltaNrm_ = Delta_ / Divisor_ w_ = self.inter_opts[inter_]['weight'] if 'weight' in self.inter_opts[inter_] else 1.0 VectorE_.append(np.sqrt(w_)*DeltaNrm_) if not in_fd(): self.PrintDict[inter_] = "% 9.3f % 9.3f % 9.3f % 12.5f" % (Calculated_, Reference_, Delta_, w_*DeltaNrm_**2) # print "%-20s" % inter_, "Calculated:", Calculated_, "Reference:", Reference_, "Delta:", Delta_, "DeltaNrm:", DeltaNrm_ # The return value is an array of normalized interaction energy differences. if not in_fd(): self.rmsd_part = np.dot(np.array(VectorD_),np.array(VectorD_)) if len(VectorE_) > 0: self.energy_part = np.dot(np.array(VectorE_),np.array(VectorE_)) else: self.energy_part = 0.0 if len(VectorE_) > 0 and len(VectorD_) > 0: return np.array(VectorD_ + VectorE_) elif len(VectorD_) > 0: return np.array(VectorD_) elif len(VectorE_) > 0: return np.array(VectorE_) V = compute(mvals) dV = np.zeros((self.FF.np,len(V))) if AGrad or AHess: for p in self.pgrad: dV[p,:], _ = f12d3p(fdwrap(compute, mvals, p), h = self.h, f0 = V) Answer['X'] = np.dot(V,V) for p in self.pgrad: Answer['G'][p] = 2*np.dot(V, dV[p,:]) for q in self.pgrad: Answer['H'][p,q] = 2*np.dot(dV[p,:], dV[q,:]) if not in_fd(): self.objective = Answer['X'] self.FF.make(mvals) return Answer
def get(self, mvals, AGrad=False, AHess=False): Answer = {'X':0.0, 'G':np.zeros(self.FF.np), 'H':np.zeros((self.FF.np, self.FF.np))} self.PrintDict = OrderedDict() self.RMSDDict = OrderedDict() #pool = Pool(processes=4) def compute(mvals_): # This function has automatically assigned variable names from the interaction master file # Thus, all variable names in here are protected using an underscore. self.FF.make(mvals_) VectorD_ = [] for sys_ in self.sys_opts: Energy_, RMSD_ = self.system_driver(sys_) #print "Setting %s to" % sys_, Energy_ exec("%s = Energy_" % sys_) in locals() RMSDNrm_ = RMSD_ / self.rmsd_denom w_ = self.sys_opts[sys_]['rmsd_weight'] if 'rmsd_weight' in self.sys_opts[sys_] else 1.0 VectorD_.append(np.sqrt(w_)*RMSDNrm_) if not in_fd() and RMSD_ != 0.0: self.RMSDDict[sys_] = "% 9.3f % 12.5f" % (RMSD_, w_*RMSDNrm_**2) VectorE_ = [] for inter_ in self.inter_opts: Calculated_ = eval(self.inter_opts[inter_]['equation']) Reference_ = self.inter_opts[inter_]['reference_physical'] Delta_ = Calculated_ - Reference_ Denom_ = self.energy_denom if self.cauchy: Divisor_ = np.sqrt(Denom_**2 + Reference_**2) elif self.attenuate: if Reference_ < Denom_: Divisor_ = Denom_ else: Divisor_ = np.sqrt(Denom_**2 + (Reference_-Denom_)**2) else: Divisor_ = Denom_ DeltaNrm_ = Delta_ / Divisor_ w_ = self.inter_opts[inter_]['weight'] if 'weight' in self.inter_opts[inter_] else 1.0 VectorE_.append(np.sqrt(w_)*DeltaNrm_) if not in_fd(): self.PrintDict[inter_] = "% 9.3f % 9.3f % 9.3f % 12.5f" % (Calculated_, Reference_, Delta_, w_*DeltaNrm_**2) # print "%-20s" % inter_, "Calculated:", Calculated_, "Reference:", Reference_, "Delta:", Delta_, "DeltaNrm:", DeltaNrm_ # The return value is an array of normalized interaction energy differences. if not in_fd(): self.rmsd_part = np.dot(np.array(VectorD_),np.array(VectorD_)) if len(VectorE_) > 0: self.energy_part = np.dot(np.array(VectorE_),np.array(VectorE_)) else: self.energy_part = 0.0 if len(VectorE_) > 0 and len(VectorD_) > 0: return np.array(VectorD_ + VectorE_) elif len(VectorD_) > 0: return np.array(VectorD_) elif len(VectorE_) > 0: return np.array(VectorE_) V = compute(mvals) dV = np.zeros((self.FF.np,len(V))) if AGrad or AHess: for p in range(self.FF.np): dV[p,:], _ = f12d3p(fdwrap(compute, mvals, p), h = self.h, f0 = V) Answer['X'] = np.dot(V,V) for p in range(self.FF.np): Answer['G'][p] = 2*np.dot(V, dV[p,:]) for q in range(self.FF.np): Answer['H'][p,q] = 2*np.dot(dV[p,:], dV[q,:]) if not in_fd(): self.objective = Answer['X'] self.FF.make(mvals) return Answer
def get(self, mvals, AGrad=False, AHess=False): """ LPW 04-17-2013 This subroutine builds the objective function from Psi4. @param[in] mvals Mathematical parameter values @param[in] AGrad Switch to turn on analytic gradient @param[in] AHess Switch to turn on analytic Hessian @return Answer Contribution to the objective function """ Answer = {} Fac = 1000000 n = len(mvals) X = 0.0 G = np.zeros(n,dtype=float) H = np.zeros((n,n),dtype=float) pvals = self.FF.make(mvals) self.tdir = os.getcwd() self.objd = OrderedDict() self.gradd = OrderedDict() self.hdiagd = OrderedDict() bidirect = False def fdwrap2(func,mvals0,pidx,qidx,key=None,**kwargs): def func2(arg1,arg2): mvals = list(mvals0) mvals[pidx] += arg1 mvals[qidx] += arg2 print "\rfdwrap2:", func.__name__, "[%i] = % .1e , [%i] = % .1e" % (pidx, arg1, qidx, arg2), ' '*50, if key != None: return func(mvals,**kwargs)[key] else: return func(mvals,**kwargs) return func2 def f2d5p(f, h): fpp, fpm, fmp, fmm = [f(i*h,j*h) for i,j in [(1,1),(1,-1),(-1,1),(-1,-1)]] fpp = (fpp-fpm-fmp+fmm)/(4*h*h) return fpp def f2d4p(f, h, f0 = None): if f0 == None: fpp, fp0, f0p, f0 = [f(i*h,j*h) for i,j in [(1,1),(1,0),(0,1),(0,0)]] else: fpp, fp0, f0p = [f(i*h,j*h) for i,j in [(1,1),(1,0),(0,1)]] fpp = (fpp-fp0-f0p+f0)/(h*h) return fpp for d in self.objfiles: print "\rNow working on", d, 50*' ','\r', x = self.driver(mvals, d) grad = np.zeros(n,dtype=float) hdiag = np.zeros(n,dtype=float) hess = np.zeros((n,n),dtype=float) for p in range(self.FF.np): if self.callderivs[d][p]: if AHess: grad[p], hdiag[p] = f12d3p(fdwrap(self.driver, mvals, p, d=d), h = self.h, f0 = x) hess[p,p] = hdiag[p] # for q in range(p): # if self.callderivs[d][q]: # if bidirect: # hessentry = f2d5p(fdwrap2(self.driver, mvals, p, q, d=d), h = self.h) # else: # hessentry = f2d4p(fdwrap2(self.driver, mvals, p, q, d=d), h = self.h, f0 = x) # hess[p,q] = hessentry # hess[q,p] = hessentry elif AGrad: if bidirect: grad[p], _ = f12d3p(fdwrap(self.driver, mvals, p, d=d), h = self.h, f0 = x) else: grad[p] = f1d2p(fdwrap(self.driver, mvals, p, d=d), h = self.h, f0 = x) self.objd[d] = x self.gradd[d] = grad self.hdiagd[d] = hdiag X += x G += grad #H += np.diag(hdiag) H += hess if not in_fd(): self.objective = X self.objvals = self.objd # print self.objd # print self.gradd # print self.hdiagd if float('Inf') in pvals: return {'X' : 1e10, 'G' : G, 'H' : H} return {'X' : X, 'G' : G, 'H' : H}
def get(self, mvals, AGrad=False, AHess=False): """ Evaluate objective function. """ Answer = {'X':0.0, 'G':np.zeros(self.FF.np), 'H':np.zeros((self.FF.np, self.FF.np))} def compute(mvals_): self.FF.make(mvals_) Xx, Gx, Hx, freqs, normal_modes, M_opt = self.hessian_driver() # convert into internal hessian Xx *= 1/ Bohr2nm Gx *= Bohr2nm/ Hartree2kJmol Hx *= Bohr2nm**2/ Hartree2kJmol Hq = self.IC.calcHess(Xx, Gx, Hx) compute.Hq_flat = Hq.flatten() compute.freqs = freqs compute.normal_modes = normal_modes compute.M_opt = M_opt diff = Hq - self.ref_Hq return (np.sqrt(self.wts)/self.denom) * (compute.Hq_flat - self.ref_Hq_flat) V = compute(mvals) Answer['X'] = np.dot(V,V) * len(compute.freqs) # HJ: len(compute.freqs) is multiplied to match the scale of X2 with vib freq target X2 # compute gradients and hessian dV = np.zeros((self.FF.np,len(V))) if AGrad or AHess: for p in self.pgrad: dV[p,:], _ = f12d3p(fdwrap(compute, mvals, p), h = self.h, f0 = V) for p in self.pgrad: Answer['G'][p] = 2*np.dot(V, dV[p,:]) * len(compute.freqs) for q in self.pgrad: Answer['H'][p,q] = 2*np.dot(dV[p,:], dV[q,:]) * len(compute.freqs) if not in_fd(): self.Hq_flat = compute.Hq_flat self.Hq = self.Hq_flat.reshape(self.ref_Hq.shape) self.objective = Answer['X'] self.FF.make(mvals) if self.writelevel > 0: # 1. write HessianCompare.txt hessian_comparison = np.array([ self.ref_Hq_flat, compute.Hq_flat, compute.Hq_flat - self.ref_Hq_flat, np.sqrt(self.wts)/self.denom ]).T np.savetxt("HessianCompare.txt", hessian_comparison, header="%11s %12s %12s %12s" % ("QMHessian", "MMHessian", "Delta(MM-QM)", "Weight"), fmt="% 12.6e") # 2. rearrange MM vibrational frequencies using overlap between normal modes in redundant internal coordinates ref_int_normal_modes = self.calc_int_normal_mode(self.ref_xyz, self.ref_eigvecs) int_normal_modes = self.calc_int_normal_mode(np.array(compute.M_opt.xyzs[0]), compute.normal_modes) a = np.array([[(1.0-np.abs(np.dot(v1/np.linalg.norm(v1),v2/np.linalg.norm(v2)))) for v2 in int_normal_modes] for v1 in ref_int_normal_modes]) row, c2r = optimize.linear_sum_assignment(a) # old arrangement method, which uses overlap between mass weighted vibrational modes in cartesian coordinates # a = np.array([[(1.0-self.vib_overlap(v1, v2)) for v2 in compute.normal_modes] for v1 in self.ref_eigvecs]) # row, c2r = optimize.linear_sum_assignment(a) freqs_rearr = compute.freqs[c2r] normal_modes_rearr = compute.normal_modes[c2r] # 3. Save rearranged frequencies and normal modes into a file for post-analysis with open('mm_vdata.txt', 'w') as outfile: outfile.writelines('%s\n' % line for line in compute.M_opt.write_xyz([0])) outfile.write('\n') for freq, normal_mode in zip(freqs_rearr, normal_modes_rearr): outfile.write(f'{freq}\n') for nx, ny, nz in normal_mode: outfile.write(f'{nx:13.4f} {ny:13.4f} {nz:13.4f}\n') outfile.write('\n') outfile.close() # 4. draw a scatter plot of vibrational frequencies and an overlap matrix of normal modes in cartessian coordinates draw_vibfreq_scatter_plot_n_overlap_matrix(self.name, self.engine, self.ref_eigvals, self.ref_eigvecs, freqs_rearr, normal_modes_rearr) return Answer
def get(self, mvals, AGrad=False, AHess=False): """ LPW 04-17-2013 This subroutine builds the objective function from Psi4. @param[in] mvals Mathematical parameter values @param[in] AGrad Switch to turn on analytic gradient @param[in] AHess Switch to turn on analytic Hessian @return Answer Contribution to the objective function """ Answer = {} Fac = 1000000 n = len(mvals) X = 0.0 G = np.zeros(n) H = np.zeros((n, n)) pvals = self.FF.make(mvals) self.tdir = os.getcwd() self.objd = OrderedDict() self.gradd = OrderedDict() self.hdiagd = OrderedDict() wq = getWorkQueue() def fdwrap2(func, mvals0, pidx, qidx, key=None, **kwargs): def func2(arg1, arg2): mvals = list(mvals0) mvals[pidx] += arg1 mvals[qidx] += arg2 logger.info("\rfdwrap2:" + func.__name__ + "[%i] = % .1e , [%i] = % .1e" % (pidx, arg1, qidx, arg2) + ' ' * 50) if key is not None: return func(mvals, **kwargs)[key] else: return func(mvals, **kwargs) return func2 def f2d5p(f, h): fpp, fpm, fmp, fmm = [ f(i * h, j * h) for i, j in [(1, 1), (1, -1), (-1, 1), (-1, -1)] ] fpp = (fpp - fpm - fmp + fmm) / (4 * h * h) return fpp def f2d4p(f, h, f0=None): if f0 is None: fpp, fp0, f0p, f0 = [ f(i * h, j * h) for i, j in [(1, 1), (1, 0), (0, 1), (0, 0)] ] else: fpp, fp0, f0p = [ f(i * h, j * h) for i, j in [(1, 1), (1, 0), (0, 1)] ] fpp = (fpp - fp0 - f0p + f0) / (h * h) return fpp for d in self.objfiles: logger.info("\rNow working on" + str(d) + 50 * ' ' + '\r') if wq is None: x = self.driver(mvals, d) grad = np.zeros(n) hdiag = np.zeros(n) hess = np.zeros((n, n)) apath = os.path.join(self.tdir, d, "current") x = float( open(os.path.join( apath, 'objective.out')).readlines()[0].split()[1]) * self.factor for p in range(self.FF.np): if self.callderivs[d][p]: def reader(mvals_, h): apath = os.path.join(self.tdir, d, str(p), str(h)) answer = float( open(os.path.join(apath, 'objective.out')). readlines()[0].split()[1]) * self.factor return answer if AHess: if wq is not None: apath = os.path.join(self.tdir, d, "current") x = float( open(os.path.join(apath, 'objective.out')). readlines()[0].split()[1]) * self.factor grad[p], hdiag[p] = f12d3p(fdwrap(reader, mvals, p, h=self.h), h=self.h, f0=x) else: grad[p], hdiag[p] = f12d3p(fdwrap(self.driver, mvals, p, d=d), h=self.h, f0=x) hess[p, p] = hdiag[p] elif AGrad: if self.bidirect: if wq is not None: apath = os.path.join(self.tdir, d, "current") x = float( open(os.path.join(apath, 'objective.out')). readlines()[0].split()[1]) * self.factor grad[p], _ = f12d3p(fdwrap(reader, mvals, p, h=self.h), h=self.h, f0=x) else: grad[p], _ = f12d3p(fdwrap(self.driver, mvals, p, d=d), h=self.h, f0=x) else: if wq is not None: # Since the calculations are submitted as 3-point finite difference, this part of the code # actually only reads from half of the completed calculations. grad[p] = f1d2p(fdwrap(reader, mvals, p, h=self.h), h=self.h, f0=x) else: grad[p] = f1d2p(fdwrap(self.driver, mvals, p, d=d), h=self.h, f0=x) self.objd[d] = x self.gradd[d] = grad self.hdiagd[d] = hdiag X += x G += grad #H += np.diag(hdiag) H += hess if not in_fd(): self.objective = X self.objvals = self.objd # print self.objd # print self.gradd # print self.hdiagd if float('Inf') in pvals: return {'X': 1e10, 'G': G, 'H': H} return {'X': X, 'G': G, 'H': H}
def submit_jobs(self, mvals, AGrad=True, AHess=True): # This routine is called by Objective.stage() will run before "get". # It submits the jobs to the Work Queue and the stage() function will wait for jobs to complete. # self.tdir = os.getcwd() wq = getWorkQueue() if wq is None: return def submit_psi(this_apath, dname, these_mvals): """ Create a grid file and a psi4 input file in the absolute path and submit it to the work queue. """ cwd = os.getcwd() if not os.path.exists(this_apath): os.makedirs(this_apath) os.chdir(this_apath) self.FF.make(these_mvals) o = wopen('objective.dat') for line in self.objfiles[d]: s = line.split() if len(s) > 2 and s[0] == 'path' and s[1] == '=': print("path = '%s'" % os.getcwd(), file=o) elif len(s) > 2 and s[0] == 'set' and s[1] == 'objective_path': print("opath = '%s'" % os.getcwd(), file=o) print("set objective_path $opath", file=o) else: print(line, end=' ', file=o) o.close() os.system("rm -f objective.out") if wq is None: logger.info("There is no Work Queue!!!\n") sys.exit() else: input_files = [(os.path.join(this_apath, i), i) for i in glob.glob("*")] input_files += [(os.path.join(self.root, self.tgtdir, dname, "build.dat"), "build.dat")] input_files += [(os.path.join( os.path.split(__file__)[0], "data", "run_psi_rdvr3_objective.sh"), "run_psi_rdvr3_objective.sh")] logger.info("\r") queue_up_src_dest( wq, "sh run_psi_rdvr3_objective.sh -c %s &> run_psi_rdvr3_objective.log" % os.path.join(self.root, self.tgtdir, dname), input_files=input_files, output_files=[ (os.path.join(this_apath, i), i) for i in ["run_psi_rdvr3_objective.log", "output.dat"] ], verbose=False) os.chdir(cwd) for d in self.objfiles: logger.info("\rNow working on" + str(d) + 50 * ' ' + '\r') odir = os.path.join(os.getcwd(), d) #if os.path.exists(odir): # shutil.rmtree(odir) if not os.path.exists(odir): os.makedirs(odir) apath = os.path.join(odir, "current") submit_psi(apath, d, mvals) for p in range(self.FF.np): def subjob(mvals_, h): apath = os.path.join(odir, str(p), str(h)) submit_psi(apath, d, mvals_) #logger.info("Will set up a job for %s, parameter %i\n" % (d, p)) return 0.0 if self.callderivs[d][p]: if AHess: f12d3p(fdwrap(subjob, mvals, p, h=self.h), h=self.h, f0=0.0) elif AGrad: if self.bidirect: f12d3p(fdwrap(subjob, mvals, p, h=self.h), h=self.h, f0=0.0) else: f1d2p(fdwrap(subjob, mvals, p, h=self.h), h=self.h, f0=0.0)
def get(self, mvals, AGrad=False, AHess=False): Answer = {'X':0.0, 'G':np.zeros(self.FF.np), 'H':np.zeros((self.FF.np, self.FF.np))} self.PrintDict = OrderedDict() self.RMSDDict = OrderedDict() EnergyDict = OrderedDict() #pool = Pool(processes=4) def compute(mvals_): # This function has automatically assigned variable names from the interaction master file # Thus, all variable names in here are protected using an underscore. self.FF.make(mvals_) VectorD_ = [] for sys_ in self.sys_opts: Energy_, RMSD_ = self.system_driver(sys_) # Energies are stored in a dictionary. EnergyDict[sys_] = Energy_ RMSDNrm_ = RMSD_ / self.rmsd_denom w_ = self.sys_opts[sys_]['rmsd_weight'] if 'rmsd_weight' in self.sys_opts[sys_] else 1.0 VectorD_.append(np.sqrt(w_)*RMSDNrm_) if not in_fd() and RMSD_ != 0.0: self.RMSDDict[sys_] = "% 9.3f % 12.5f" % (RMSD_, w_*RMSDNrm_**2) VectorE_ = [] for inter_ in self.inter_opts: def encloseInDictionary(matchobj): return 'EnergyDict["' + matchobj.group(0)+'"]' # Here we need to evaluate a mathematical expression of the stored variables in EnergyDict. # We start by enclosing every variable in EnergyDict[""] and then calling eval on it. evalExpr = re.sub('[A-Za-z_][A-Za-z0-9_]*', encloseInDictionary, self.inter_opts[inter_]['equation']) Calculated_ = eval(evalExpr) Reference_ = self.inter_opts[inter_]['reference_physical'] Delta_ = Calculated_ - Reference_ Denom_ = self.energy_denom if self.cauchy: Divisor_ = np.sqrt(Denom_**2 + Reference_**2) elif self.attenuate: if Reference_ < Denom_: Divisor_ = Denom_ else: Divisor_ = np.sqrt(Denom_**2 + (Reference_-Denom_)**2) else: Divisor_ = Denom_ DeltaNrm_ = Delta_ / Divisor_ w_ = self.inter_opts[inter_]['weight'] if 'weight' in self.inter_opts[inter_] else 1.0 VectorE_.append(np.sqrt(w_)*DeltaNrm_) if not in_fd(): self.PrintDict[inter_] = "% 9.3f % 9.3f % 9.3f % 12.5f" % (Calculated_, Reference_, Delta_, w_*DeltaNrm_**2) # print "%-20s" % inter_, "Calculated:", Calculated_, "Reference:", Reference_, "Delta:", Delta_, "DeltaNrm:", DeltaNrm_ # The return value is an array of normalized interaction energy differences. if not in_fd(): self.rmsd_part = np.dot(np.array(VectorD_),np.array(VectorD_)) if len(VectorE_) > 0: self.energy_part = np.dot(np.array(VectorE_),np.array(VectorE_)) else: self.energy_part = 0.0 if len(VectorE_) > 0 and len(VectorD_) > 0: return np.array(VectorD_ + VectorE_) elif len(VectorD_) > 0: return np.array(VectorD_) elif len(VectorE_) > 0: return np.array(VectorE_) V = compute(mvals) dV = np.zeros((self.FF.np,len(V))) if AGrad or AHess: for p in self.pgrad: dV[p,:], _ = f12d3p(fdwrap(compute, mvals, p), h = self.h, f0 = V) Answer['X'] = np.dot(V,V) for p in self.pgrad: Answer['G'][p] = 2*np.dot(V, dV[p,:]) for q in self.pgrad: Answer['H'][p,q] = 2*np.dot(dV[p,:], dV[q,:]) if not in_fd(): self.objective = Answer['X'] self.FF.make(mvals) return Answer
def get(self, mvals, AGrad=False, AHess=False): Answer = { 'X': 0.0, 'G': np.zeros(self.FF.np), 'H': np.zeros((self.FF.np, self.FF.np)) } self.PrintDict = OrderedDict() def compute(mvals_, indicate=False): self.FF.make(mvals_) M_opts = None compute.emm = [] compute.rmsd = [] for i in range(self.ns): energy, rmsd, M_opt = self.engine.optimize(shot=i, align=False) # Create a molecule object to hold the MM-optimized structures compute.emm.append(energy) compute.rmsd.append(rmsd) if M_opts is None: M_opts = deepcopy(M_opt) else: M_opts += M_opt compute.emm = np.array(compute.emm) compute.emm -= compute.emm[self.smin] compute.rmsd = np.array(compute.rmsd) if indicate: if self.writelevel > 0: M_opts.write('mm_minimized.xyz') if self.ndim == 1: import matplotlib.pyplot as plt plt.switch_backend('agg') fig, ax = plt.subplots() dihedrals = np.array( [i[0] for i in self.metadata['torsion_grid_ids']]) dsort = np.argsort(dihedrals) ax.plot(dihedrals[dsort], self.eqm[dsort], label='QM') if hasattr(self, 'emm_orig'): ax.plot(dihedrals[dsort], compute.emm[dsort], label='MM Current') ax.plot(dihedrals[dsort], self.emm_orig[dsort], label='MM Initial') else: ax.plot(dihedrals[dsort], compute.emm[dsort], label='MM Initial') self.emm_orig = compute.emm.copy() ax.legend() ax.set_xlabel('Dihedral (degree)') ax.set_ylabel('Energy (kcal/mol)') fig.suptitle( 'Torsion profile: iteration %i\nSystem: %s' % (Counter(), self.name)) fig.savefig('plot_torsion.pdf') return (np.sqrt(self.wts) / self.energy_denom) * (compute.emm - self.eqm) compute.emm = None compute.rmsd = None V = compute(mvals, indicate=True) Answer['X'] = np.dot(V, V) # Energy RMSE e_rmse = np.sqrt(np.dot(self.wts, (compute.emm - self.eqm)**2)) self.PrintDict[ self. name] = '%10s %10s %6.3f - %-6.3f % 6.3f - %-6.3f %6.3f %7.4f % 7.4f' % ( ','.join([ '%i' % i for i in self.metadata['torsion_grid_ids'][self.smin] ]), ','.join([ '%i' % i for i in self.metadata['torsion_grid_ids'][ np.argmin(compute.emm)] ]), min(self.eqm), max(self.eqm), min(compute.emm), max(compute.emm), max(compute.rmsd), e_rmse, Answer['X']) # compute gradients and hessian dV = np.zeros((self.FF.np, len(V))) if AGrad or AHess: for p in self.pgrad: dV[p, :], _ = f12d3p(fdwrap(compute, mvals, p), h=self.h, f0=V) for p in self.pgrad: Answer['G'][p] = 2 * np.dot(V, dV[p, :]) for q in self.pgrad: Answer['H'][p, q] = 2 * np.dot(dV[p, :], dV[q, :]) if not in_fd(): self.objective = Answer['X'] self.FF.make(mvals) return Answer
def get(self, mvals, AGrad=False, AHess=False): Answer = { 'X': 0.0, 'G': np.zeros(self.FF.np), 'H': np.zeros((self.FF.np, self.FF.np)) } self.PrintDict = OrderedDict() # enable self.system_mval_masks (supported by OptGeoTarget_SMIRNOFF) enable_system_mval_mask = hasattr(self, 'system_mval_masks') def compute(mvals, p_idx=None): ''' Compute total objective value for each system ''' self.FF.make(mvals) v_obj_list = [] for sysname, sysopt in self.sys_opts.items(): # ref values of each type vref_bonds = self.internal_coordinates[sysname]['vref_bonds'] vref_angles = self.internal_coordinates[sysname]['vref_angles'] vref_dihedrals = self.internal_coordinates[sysname][ 'vref_dihedrals'] vref_impropers = self.internal_coordinates[sysname][ 'vref_impropers'] # counts of each type n_bonds = len(vref_bonds) n_angles = len(vref_angles) n_dihedrals = len(vref_dihedrals) n_impropers = len(vref_impropers) # use self.system_mval_masks to skip evaluations when computing gradients if enable_system_mval_mask and in_fd() and ( p_idx is not None) and ( self.system_mval_masks[sysname][p_idx] == False): v_obj_list += [0] * (n_bonds + n_angles + n_dihedrals + n_impropers) continue # read denominators from system options bond_denom = sysopt['bond_denom'] angle_denom = sysopt['angle_denom'] dihedral_denom = sysopt['dihedral_denom'] improper_denom = sysopt['improper_denom'] # inverse demon to be scaling factors, 0 for denom 0 scale_bond = 1.0 / bond_denom if bond_denom != 0 else 0.0 scale_angle = 1.0 / angle_denom if angle_denom != 0 else 0.0 scale_dihedral = 1.0 / dihedral_denom if dihedral_denom != 0 else 0.0 scale_improper = 1.0 / improper_denom if improper_denom != 0 else 0.0 # calculate new internal coordinates v_ic = self.system_driver(sysname) # objective contribution from bonds vtar_bonds = v_ic['bonds'] diff_bond = ((vref_bonds - vtar_bonds) * scale_bond).tolist() if n_bonds > 0 else [] # objective contribution from angles vtar_angles = v_ic['angles'] diff_angle = (periodic_diff(vref_angles, vtar_angles, 360) * scale_angle).tolist() if n_angles > 0 else [] # objective contribution from dihedrals vtar_dihedrals = v_ic['dihedrals'] diff_dihedral = ( periodic_diff(vref_dihedrals, vtar_dihedrals, 360) * scale_dihedral).tolist() if n_dihedrals > 0 else [] # objective contribution from improper dihedrals vtar_impropers = v_ic['impropers'] diff_improper = ( periodic_diff(vref_impropers, vtar_impropers, 360) * scale_improper).tolist() if n_impropers > 0 else [] # combine objective values into a big result list sys_obj_list = diff_bond + diff_angle + diff_dihedral + diff_improper # extend the result v_obj_list by individual terms in this system v_obj_list += sys_obj_list # save print string if not in_fd(): # For printing, we group the RMSD by type rmsd_bond = compute_rmsd(vref_bonds, vtar_bonds) rmsd_angle = compute_rmsd(vref_angles, vtar_angles, v_periodic=360) rmsd_dihedral = compute_rmsd(vref_dihedrals, vtar_dihedrals, v_periodic=360) rmsd_improper = compute_rmsd(vref_impropers, vtar_impropers, v_periodic=360) obj_total = sum(v**2 for v in sys_obj_list) self.PrintDict[sysname] = "% 9.3f % 7.2f % 9.3f % 7.2f % 9.3f % 7.2f % 9.3f % 7.2f %17.3f" % (rmsd_bond, \ bond_denom, rmsd_angle, angle_denom, rmsd_dihedral, dihedral_denom, rmsd_improper, improper_denom, obj_total) return np.array(v_obj_list, dtype=float) V = compute(mvals) Answer['X'] = np.dot(V, V) # write objective decomposition if wanted if self.writelevel > 0: # recover mvals self.FF.make(mvals) with open('rmsd_decomposition.txt', 'w') as fout: for sysname in self.internal_coordinates: fout.write("\n[ %s ]\n" % sysname) fout.write('%-25s %15s %15s %15s\n' % ("Internal Coordinate", "Ref QM Value", "Cur MM Value", "Difference")) # reference data sys_data = self.internal_coordinates[sysname] sys_data['ic_bonds'] # compute all internal coordinate values again v_ic = self.system_driver(sysname) for p in ['bonds', 'angles', 'dihedrals', 'impropers']: fout.write('--- ' + p + ' ---\n') ic_list = sys_data['ic_' + p] ref_v = sys_data['vref_' + p] tar_v = v_ic[p] # print each value for ic, v1, v2 in zip(ic_list, ref_v, tar_v): diff = periodic_diff( v1, v2, v_periodic=360) if p != 'bonds' else v1 - v2 fout.write('%-25s %15.5f %15.5f %+15.3e\n' % (ic, v1, v2, diff)) # compute gradients and hessian dV = np.zeros((self.FF.np, len(V))) if AGrad or AHess: for p in self.pgrad: dV[p, :], _ = f12d3p(fdwrap(compute, mvals, p, p_idx=p), h=self.h, f0=V) for p in self.pgrad: Answer['G'][p] = 2 * np.dot(V, dV[p, :]) for q in self.pgrad: Answer['H'][p, q] = 2 * np.dot(dV[p, :], dV[q, :]) if not in_fd(): self.objective = Answer['X'] self.FF.make(mvals) return Answer
def get(self, mvals, AGrad=False, AHess=False): """ Evaluate objective function. """ Answer = { 'X': 0.0, 'G': np.zeros(self.FF.np), 'H': np.zeros((self.FF.np, self.FF.np)) } # If the weight is zero, turn all derivatives off. if (self.weight == 0.0): AGrad = False AHess = False def callM(mvals_, dielectric=False): logger.info("\r") pvals = self.FF.make(mvals_) return self.engine.interaction_energy(self.select1, self.select2) logger.info("Executing\r") emm = callM(mvals) D = emm - self.eqm dV = np.zeros((self.FF.np, len(emm))) if self.writelevel > 0: # Dump interaction energies to disk. np.savetxt('M.txt', emm) np.savetxt('Q.txt', self.eqm) import pickle pickle.dump((self.name, self.label, self.prefactor, self.eqm, emm), open("qm_vs_mm.p", 'w')) # select the qm and mm data that has >0 weight to plot qm_data, mm_data = [], [] for i in range(len(self.eqm)): if self.prefactor[i] != 0: qm_data.append(self.eqm[i]) mm_data.append(emm[i]) plot_interaction_qm_vs_mm(qm_data, mm_data, title="Interaction Energy " + self.name) # Do the finite difference derivative. if AGrad or AHess: for p in self.pgrad: dV[p, :], _ = f12d3p(fdwrap(callM, mvals, p), h=self.h, f0=emm) # Create the force field one last time. pvals = self.FF.make(mvals) Answer['X'] = np.dot(self.prefactor * D / self.divisor, D / self.divisor) for p in self.pgrad: Answer['G'][p] = 2 * np.dot(self.prefactor * D / self.divisor, dV[p, :] / self.divisor) for q in self.pgrad: Answer['H'][p, q] = 2 * np.dot( self.prefactor * dV[p, :] / self.divisor, dV[q, :] / self.divisor) if not in_fd(): self.emm = emm self.objective = Answer['X'] ## QYD: try to clean up OpenMM engine.simulation objects to free up GPU memory try: if self.engine.name == 'openmm': if hasattr(self.engine, 'simulation'): del self.engine.simulation if hasattr(self.engine, 'A'): del self.engine.A if hasattr(self.engine, 'B'): del self.engine.B except: pass return Answer
def get(self, mvals, AGrad=False, AHess=False): Answer = { 'X': 0.0, 'G': np.zeros(self.FF.np), 'H': np.zeros((self.FF.np, self.FF.np)) } self.PrintDict = OrderedDict() self.RMSDDict = OrderedDict() EnergyDict = OrderedDict() #pool = Pool(processes=4) def compute(mvals_): # This function has automatically assigned variable names from the interaction master file # Thus, all variable names in here are protected using an underscore. self.FF.make(mvals_) VectorD_ = [] for sys_ in self.sys_opts: Energy_, RMSD_ = self.system_driver(sys_) # Energies are stored in a dictionary. EnergyDict[sys_] = Energy_ RMSDNrm_ = RMSD_ / self.rmsd_denom w_ = self.sys_opts[sys_][ 'rmsd_weight'] if 'rmsd_weight' in self.sys_opts[ sys_] else 1.0 VectorD_.append(np.sqrt(w_) * RMSDNrm_) if not in_fd() and RMSD_ != 0.0: self.RMSDDict[sys_] = "% 9.3f % 12.5f" % (RMSD_, w_ * RMSDNrm_**2) VectorE_ = [] for inter_ in self.inter_opts: def encloseInDictionary(matchobj): return 'EnergyDict["' + matchobj.group(0) + '"]' # Here we need to evaluate a mathematical expression of the stored variables in EnergyDict. # We start by enclosing every variable in EnergyDict[""] and then calling eval on it. evalExpr = re.sub('[A-Za-z_][A-Za-z0-9_]*', encloseInDictionary, self.inter_opts[inter_]['equation']) Calculated_ = eval(evalExpr) Reference_ = self.inter_opts[inter_]['reference_physical'] Delta_ = Calculated_ - Reference_ Denom_ = self.energy_denom if self.cauchy: Divisor_ = np.sqrt(Denom_**2 + Reference_**2) elif self.attenuate: if Reference_ < Denom_: Divisor_ = Denom_ else: Divisor_ = np.sqrt(Denom_**2 + (Reference_ - Denom_)**2) else: Divisor_ = Denom_ DeltaNrm_ = Delta_ / Divisor_ w_ = self.inter_opts[inter_][ 'weight'] if 'weight' in self.inter_opts[inter_] else 1.0 VectorE_.append(np.sqrt(w_) * DeltaNrm_) if not in_fd(): self.PrintDict[inter_] = "% 9.3f % 9.3f % 9.3f % 12.5f" % ( Calculated_, Reference_, Delta_, w_ * DeltaNrm_**2) # print "%-20s" % inter_, "Calculated:", Calculated_, "Reference:", Reference_, "Delta:", Delta_, "DeltaNrm:", DeltaNrm_ # The return value is an array of normalized interaction energy differences. if not in_fd(): self.rmsd_part = np.dot(np.array(VectorD_), np.array(VectorD_)) if len(VectorE_) > 0: self.energy_part = np.dot(np.array(VectorE_), np.array(VectorE_)) else: self.energy_part = 0.0 if len(VectorE_) > 0 and len(VectorD_) > 0: return np.array(VectorD_ + VectorE_) elif len(VectorD_) > 0: return np.array(VectorD_) elif len(VectorE_) > 0: return np.array(VectorE_) V = compute(mvals) dV = np.zeros((self.FF.np, len(V))) if AGrad or AHess: for p in self.pgrad: dV[p, :], _ = f12d3p(fdwrap(compute, mvals, p), h=self.h, f0=V) Answer['X'] = np.dot(V, V) for p in self.pgrad: Answer['G'][p] = 2 * np.dot(V, dV[p, :]) for q in self.pgrad: Answer['H'][p, q] = 2 * np.dot(dV[p, :], dV[q, :]) if not in_fd(): self.objective = Answer['X'] self.FF.make(mvals) return Answer
def submit_jobs(self, mvals, AGrad=True, AHess=True): # This routine is called by Objective.stage() will run before "get". # It submits the jobs to the Work Queue and the stage() function will wait for jobs to complete. # self.tdir = os.getcwd() wq = getWorkQueue() if wq == None: return def submit_psi(this_apath, mname, these_mvals): """ Create a grid file and a psi4 input file in the absolute path and submit it to the work queue. """ cwd = os.getcwd() if not os.path.exists(this_apath) : os.makedirs(this_apath) os.chdir(this_apath) self.FF.make(these_mvals) o = wopen('objective.dat') for line in self.objfiles[d]: s = line.split() if len(s) > 2 and s[0] == 'path' and s[1] == '=': print >> o, "path = '%s'" % os.getcwd() elif len(s) > 2 and s[0] == 'set' and s[1] == 'objective_path': print >> o, "opath = '%s'" % os.getcwd() print >> o, "set objective_path $opath" else: print >> o, line, o.close() os.system("rm -f objective.out") if wq == None: logger.info("There is no Work Queue!!!\n") sys.exit() else: input_files = [(os.path.join(this_apath, i), i) for i in glob.glob("*")] # input_files += [(os.path.join(self.tgtdir,d,"build.dat"), "build.dat")] input_files += [(os.path.join(os.path.split(__file__)[0],"data","run_psi_rdvr3_objective.sh"), "run_psi_rdvr3_objective.sh")] logger.info("\r") queue_up_src_dest(wq,"sh run_psi_rdvr3_objective.sh %s &> run_psi_rdvr3_objective.log" % mname, input_files=input_files, output_files=[(os.path.join(this_apath, i),i) for i in ["run_psi_rdvr3_objective.log", "output.dat"]], verbose=False) os.chdir(cwd) for d in self.objfiles: logger.info("\rNow working on" + str(d) + 50*' ' + '\r') odir = os.path.join(os.getcwd(),d) #if os.path.exists(odir): # shutil.rmtree(odir) if not os.path.exists(odir): os.makedirs(odir) apath = os.path.join(odir, "current") submit_psi(apath, d, mvals) for p in range(self.FF.np): def subjob(mvals_,h): apath = os.path.join(odir, str(p), str(h)) submit_psi(apath, d, mvals_) #logger.info("Will set up a job for %s, parameter %i\n" % (d, p)) return 0.0 if self.callderivs[d][p]: if AHess: f12d3p(fdwrap(subjob, mvals, p, h=self.h), h = self.h, f0 = 0.0) elif AGrad: if self.bidirect: f12d3p(fdwrap(subjob, mvals, p, h=self.h), h = self.h, f0 = 0.0) else: f1d2p(fdwrap(subjob, mvals, p, h=self.h), h = self.h, f0 = 0.0)
def get(self, mvals, AGrad=False, AHess=False): """ LPW 04-17-2013 This subroutine builds the objective function from Psi4. @param[in] mvals Mathematical parameter values @param[in] AGrad Switch to turn on analytic gradient @param[in] AHess Switch to turn on analytic Hessian @return Answer Contribution to the objective function """ Answer = {} Fac = 1000000 n = len(mvals) X = 0.0 G = np.zeros(n) H = np.zeros((n,n)) pvals = self.FF.make(mvals) self.tdir = os.getcwd() self.objd = OrderedDict() self.gradd = OrderedDict() self.hdiagd = OrderedDict() wq = getWorkQueue() def fdwrap2(func,mvals0,pidx,qidx,key=None,**kwargs): def func2(arg1,arg2): mvals = list(mvals0) mvals[pidx] += arg1 mvals[qidx] += arg2 logger.info("\rfdwrap2:" + func.__name__ + "[%i] = % .1e , [%i] = % .1e" % (pidx, arg1, qidx, arg2) + ' '*50) if key != None: return func(mvals,**kwargs)[key] else: return func(mvals,**kwargs) return func2 def f2d5p(f, h): fpp, fpm, fmp, fmm = [f(i*h,j*h) for i,j in [(1,1),(1,-1),(-1,1),(-1,-1)]] fpp = (fpp-fpm-fmp+fmm)/(4*h*h) return fpp def f2d4p(f, h, f0 = None): if f0 == None: fpp, fp0, f0p, f0 = [f(i*h,j*h) for i,j in [(1,1),(1,0),(0,1),(0,0)]] else: fpp, fp0, f0p = [f(i*h,j*h) for i,j in [(1,1),(1,0),(0,1)]] fpp = (fpp-fp0-f0p+f0)/(h*h) return fpp for d in self.objfiles: logger.info("\rNow working on" + str(d) + 50*' ' + '\r') if wq == None: x = self.driver(mvals, d) grad = np.zeros(n) hdiag = np.zeros(n) hess = np.zeros((n,n)) apath = os.path.join(self.tdir, d, "current") x = float(open(os.path.join(apath,'objective.out')).readlines()[0].split()[1])*self.factor for p in range(self.FF.np): if self.callderivs[d][p]: def reader(mvals_,h): apath = os.path.join(self.tdir, d, str(p), str(h)) answer = float(open(os.path.join(apath,'objective.out')).readlines()[0].split()[1])*self.factor return answer if AHess: if wq != None: apath = os.path.join(self.tdir, d, "current") x = float(open(os.path.join(apath,'objective.out')).readlines()[0].split()[1])*self.factor grad[p], hdiag[p] = f12d3p(fdwrap(reader, mvals, p, h=self.h), h = self.h, f0 = x) else: grad[p], hdiag[p] = f12d3p(fdwrap(self.driver, mvals, p, d=d), h = self.h, f0 = x) hess[p,p] = hdiag[p] elif AGrad: if self.bidirect: if wq != None: apath = os.path.join(self.tdir, d, "current") x = float(open(os.path.join(apath,'objective.out')).readlines()[0].split()[1])*self.factor grad[p], _ = f12d3p(fdwrap(reader, mvals, p, h=self.h), h = self.h, f0 = x) else: grad[p], _ = f12d3p(fdwrap(self.driver, mvals, p, d=d), h = self.h, f0 = x) else: if wq != None: # Since the calculations are submitted as 3-point finite difference, this part of the code # actually only reads from half of the completed calculations. grad[p] = f1d2p(fdwrap(reader, mvals, p, h=self.h), h = self.h, f0 = x) else: grad[p] = f1d2p(fdwrap(self.driver, mvals, p, d=d), h = self.h, f0 = x) self.objd[d] = x self.gradd[d] = grad self.hdiagd[d] = hdiag X += x G += grad #H += np.diag(hdiag) H += hess if not in_fd(): self.objective = X self.objvals = self.objd # print self.objd # print self.gradd # print self.hdiagd if float('Inf') in pvals: return {'X' : 1e10, 'G' : G, 'H' : H} return {'X' : X, 'G' : G, 'H' : H}