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 property_derivatives(mvals,h,FF,xyz,tky,kT,property_driver,property_kwargs,AGrad=True): G = np.zeros(FF.np) if not AGrad: return G ED0 = energy_driver(mvals, FF, xyz=xyz, tky=tky, dipole=True) E0 = ED0[:,0] D0 = ED0[:,1:] P0 = property_driver(b=None, **property_kwargs) if 'h_' in property_kwargs: H0 = property_kwargs['h_'].copy() for i in range(FF.np): ED1 = fdwrap(energy_driver,mvals,i,FF=FF,xyz=xyz,tky=tky,dipole=True)(h) E1 = ED1[:,0] D1 = ED1[:,1:] b = np.exp(-(E1-E0)/kT) b /= np.sum(b) if 'h_' in property_kwargs: property_kwargs['h_'] = H0.copy() + (E1-E0) if 'd_' in property_kwargs: property_kwargs['d_'] = D1.copy() S = -1*np.dot(b,np.log(b)) InfoContent = np.exp(S) if InfoContent / len(E0) < 0.1: print "Warning: Effective number of snapshots: % .1f (out of %i)" % (InfoContent, len(E0)) P1 = property_driver(b, **property_kwargs) EDM1 = fdwrap(energy_driver,mvals,i,FF=FF,xyz=xyz,tky=tky,dipole=True)(-h) EM1 = EDM1[:,0] DM1 = EDM1[:,1:] b = np.exp(-(EM1-E0)/kT) b /= np.sum(b) if 'h_' in property_kwargs: property_kwargs['h_'] = H0.copy() + (EM1-E0) if 'd_' in property_kwargs: property_kwargs['d_'] = DM1.copy() S = -1*np.dot(b,np.log(b)) InfoContent = np.exp(S) if InfoContent / len(E0) < 0.1: print "Warning: Effective number of snapshots: % .1f (out of %i)" % (InfoContent, len(E0)) PM1 = property_driver(b, **property_kwargs) G[i] = (P1-PM1)/(2*h) if 'h_' in property_kwargs: property_kwargs['h_'] = H0.copy() if 'd_' in property_kwargs: property_kwargs['d_'] = D0.copy() return G
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 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(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(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 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 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))} 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(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_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(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(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 FDCheckG(self): """ Finite-difference checker for the objective function gradient. For each element in the gradient, use a five-point finite difference stencil to compute a finite-difference derivative, and compare it to the analytic result. """ Adata = self.Objective.Full(self.mvals0,Order=1)['G'] Fdata = np.zeros(self.FF.np,dtype=float) printcool("Checking first derivatives by finite difference!\n%-8s%-20s%13s%13s%13s%13s" \ % ("Index", "Parameter ID","Analytic","Numerical","Difference","Fractional"),bold=1,color=5) for i in range(self.FF.np): Fdata[i] = f1d7p(fdwrap(self.Objective.Full,self.mvals0,i,'X',Order=0),self.h) Denom = max(abs(Adata[i]),abs(Fdata[i])) Denom = Denom > 1e-8 and Denom or 1e-8 D = Adata[i] - Fdata[i] Q = (Adata[i] - Fdata[i])/Denom cD = abs(D) > 0.5 and "\x1b[1;91m" or (abs(D) > 1e-2 and "\x1b[91m" or (abs(D) > 1e-5 and "\x1b[93m" or "\x1b[92m")) cQ = abs(Q) > 0.5 and "\x1b[1;91m" or (abs(Q) > 1e-2 and "\x1b[91m" or (abs(Q) > 1e-5 and "\x1b[93m" or "\x1b[92m")) logger.info("\r %-8i%-20s% 13.4e% 13.4e%s% 13.4e%s% 13.4e\x1b[0m\n" \ % (i, self.FF.plist[i][:20], Adata[i], Fdata[i], cD, D, cQ, Q))
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 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): """ 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 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): """ LPW 05-30-2012 This subroutine builds the objective function (and optionally its derivatives) from a general software. This subroutine interfaces with simulation software 'drivers'. The driver is expected to give exact values, fitting values, and weights. @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 """ global LAST_MVALS, CHECK_BASIS # print mvals # print LAST_MVALS # print mvals == LAST_MVALS if LAST_MVALS == None or not (mvals == LAST_MVALS).all(): CHECK_BASIS = False else: CHECK_BASIS = False Answer = {} Fac = 1000000 ## Dictionary for derivative terms dM = {} # Create the new force field!! np = len(mvals) G = zeros(np, dtype=float) H = zeros((np, np), dtype=float) pvals = self.FF.make(mvals) if float("Inf") in pvals: return {"X": 1e10, "G": G, "H": H} Ans = self.driver() W = Ans[:, 2] M = Ans[:, 1] Q = Ans[:, 0] D = M - Q self.MAQ = mean(abs(Q)) ns = len(M) # Wrapper to the driver, which returns just the part that changes. def callM(mvals_): self.FF.make(mvals_) Ans2 = self.driver() M_ = Ans2[:, 1] D_ = M_ - Q return Ans2[:, 1] if AGrad: # Leaving comment here if we want to reintroduce second deriv someday. # dM[p,:], ddM[p,:] = f12d3p(fdwrap(callM, mvals, p), h = self.h, f0 = M) for p in range(np): if self.call_derivatives[p] == False: continue dM_arr = f1d2p(fdwrap(callM, mvals, p), h=self.h, f0=M) if max(abs(dM_arr)) == 0.0 and Counter() == 0: print "\r Simulation %s will skip over parameter %i in subsequent steps" % (self.name, p) self.call_derivatives[p] = False else: dM[p] = dM_arr.copy() Objective = dot(W, D ** 2) * Fac if AGrad: for p in range(np): if self.call_derivatives[p] == False: continue G[p] = 2 * dot(W, D * dM[p]) if not AHess: continue H[p, p] = 2 * dot(W, dM[p] ** 2) for q in range(p): if self.call_derivatives[q] == False: continue GNP = 2 * dot(W, dM[p] * dM[q]) H[q, p] = GNP H[p, q] = GNP G *= Fac H *= Fac Answer = {"X": Objective, "G": G, "H": H} if not in_fd(): self.D = D self.objective = Answer["X"] LAST_MVALS = mvals.copy() 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): 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 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}
def func1(arg): mvals = list(mvals0) mvals[pidxj] += arg return f1d5p(fdwrap(self.Objective.Full,mvals,pidxi,'X',Order=0),self.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)) } # 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): """ 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): """ 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): 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 property_derivatives(engine, FF, mvals, h, pgrad, kT, property_driver, property_kwargs, AGrad=True): """ Function for double-checking property derivatives. This function is called to perform a more explicit numerical derivative of the property, rather than going through the fluctuation formula. It takes longer and is potentially less precise, which means it's here mainly as a sanity check. @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] property_driver The function that calculates the property @param[in] property_driver A dictionary of arguments that goes into calculating the property @param[in] AGrad Switch to turn derivatives on or off; if off, return all zeros @return G First derivative of the property """ G = np.zeros(FF.np) if not AGrad: return G def energy_driver(mvals_): FF.make(mvals_) return engine.energy_dipole() ED0 = energy_driver(mvals) E0 = ED0[:,0] D0 = ED0[:,1:] P0 = property_driver(None, **property_kwargs) if 'h_' in property_kwargs: H0 = property_kwargs['h_'].copy() for i in pgrad: logger.info("%s\n" % (FF.plist[i] + " "*30)) ED1 = fdwrap(energy_driver,mvals,i)(h) E1 = ED1[:,0] D1 = ED1[:,1:] b = np.exp(-(E1-E0)/kT) b /= np.sum(b) if 'h_' in property_kwargs: property_kwargs['h_'] = H0.copy() + (E1-E0) if 'd_' in property_kwargs: property_kwargs['d_'] = D1.copy() S = -1*np.dot(b,np.log(b)) InfoContent = np.exp(S) if InfoContent / len(E0) < 0.1: logger.warn("Warning: Effective number of snapshots: % .1f (out of %i)\n" % (InfoContent, len(E0))) P1 = property_driver(b=b,**property_kwargs) EDM1 = fdwrap(energy_driver,mvals,i)(-h) EM1 = EDM1[:,0] DM1 = EDM1[:,1:] b = np.exp(-(EM1-E0)/kT) b /= np.sum(b) if 'h_' in property_kwargs: property_kwargs['h_'] = H0.copy() + (EM1-E0) if 'd_' in property_kwargs: property_kwargs['d_'] = DM1.copy() S = -1*np.dot(b,np.log(b)) InfoContent = np.exp(S) if InfoContent / len(E0) < 0.1: logger.warn("Warning: Effective number of snapshots: % .1f (out of %i)\n" % (InfoContent, len(E0))) PM1 = property_driver(b=b,**property_kwargs) G[i] = (P1-PM1)/(2*h) if 'h_' in property_kwargs: property_kwargs['h_'] = H0.copy() if 'd_' in property_kwargs: property_kwargs['d_'] = D0.copy() return G
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): """ LPW 05-30-2012 This subroutine builds the objective function (and optionally its derivatives) from a general software. This subroutine interfaces with simulation software 'drivers'. The driver is expected to give exact values, fitting values, and weights. @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 """ global LAST_MVALS, CHECK_BASIS # print mvals # print LAST_MVALS # print mvals == LAST_MVALS if LAST_MVALS is None or not (mvals == LAST_MVALS).all(): CHECK_BASIS = False else: CHECK_BASIS = False Answer = {} Fac = 1000000 ## Dictionary for derivative terms dM = {} # Create the new force field!! NP = len(mvals) G = np.zeros(NP) H = np.zeros((NP,NP)) pvals = self.FF.make(mvals) if float('Inf') in pvals: return {'X' : 1e10, 'G' : G, 'H' : H} Ans = self.driver() W = Ans[:,2] M = Ans[:,1] Q = Ans[:,0] D = M - Q self.MAQ = np.mean(np.abs(Q)) ns = len(M) # Wrapper to the driver, which returns just the part that changes. def callM(mvals_): self.FF.make(mvals_) Ans2 = self.driver() M_ = Ans2[:,1] D_ = M_ - Q return Ans2[:,1] if AGrad: # Leaving comment here if we want to reintroduce second deriv someday. # dM[p,:], ddM[p,:] = f12d3p(fdwrap(callM, mvals, p), h = self.h, f0 = M) xgrad = [] for p in self.pgrad: dM_arr = f1d2p(fdwrap(callM, mvals, p), h = self.h, f0 = M) if np.max(np.abs(dM_arr)) == 0.0 and (not self.evaluated): logger.info("\r Simulation %s will skip over parameter %i in subsequent steps\n" % (self.name, p)) xgrad.append(p) else: dM[p] = dM_arr.copy() for p in xgrad: self.pgrad.remove(p) Objective = np.dot(W, D**2) * Fac if AGrad: for p in self.pgrad: G[p] = 2 * np.dot(W, D*dM[p]) if not AHess: continue H[p, p] = 2 * np.dot(W, dM[p]**2) for q in range(p): if q not in self.pgrad: continue GNP = 2 * np.dot(W, dM[p] * dM[q]) H[q,p] = GNP H[p,q] = GNP G *= Fac H *= Fac Answer = {'X':Objective, 'G':G, 'H':H} if not in_fd(): self.D = D self.objective = Answer['X'] LAST_MVALS = mvals.copy() return Answer
def get(self, mvals, AGrad=False, AHess=False): """ LPW 05-30-2012 This subroutine builds the objective function (and optionally its derivatives) from a general software. This subroutine interfaces with simulation software 'drivers'. The driver is expected to give exact values, fitting values, and weights. @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 """ global LAST_MVALS, CHECK_BASIS # print mvals # print LAST_MVALS # print mvals == LAST_MVALS if LAST_MVALS is None or not (mvals == LAST_MVALS).all(): CHECK_BASIS = False else: CHECK_BASIS = False Answer = {} Fac = 1000000 ## Dictionary for derivative terms dM = {} # Create the new force field!! NP = len(mvals) G = np.zeros(NP) H = np.zeros((NP, NP)) pvals = self.FF.make(mvals) if float('Inf') in pvals: return {'X': 1e10, 'G': G, 'H': H} Ans = self.driver() W = Ans[:, 2] M = Ans[:, 1] Q = Ans[:, 0] D = M - Q self.MAQ = np.mean(np.abs(Q)) ns = len(M) # Wrapper to the driver, which returns just the part that changes. def callM(mvals_): self.FF.make(mvals_) Ans2 = self.driver() M_ = Ans2[:, 1] D_ = M_ - Q return Ans2[:, 1] if AGrad: # Leaving comment here if we want to reintroduce second deriv someday. # dM[p,:], ddM[p,:] = f12d3p(fdwrap(callM, mvals, p), h = self.h, f0 = M) xgrad = [] for p in self.pgrad: dM_arr = f1d2p(fdwrap(callM, mvals, p), h=self.h, f0=M) if np.max(np.abs(dM_arr)) == 0.0 and (not self.evaluated): logger.info( "\r Simulation %s will skip over parameter %i in subsequent steps\n" % (self.name, p)) xgrad.append(p) else: dM[p] = dM_arr.copy() for p in xgrad: self.pgrad.remove(p) Objective = np.dot(W, D**2) * Fac if AGrad: for p in self.pgrad: G[p] = 2 * np.dot(W, D * dM[p]) if not AHess: continue H[p, p] = 2 * np.dot(W, dM[p]**2) for q in range(p): if q not in self.pgrad: continue GNP = 2 * np.dot(W, dM[p] * dM[q]) H[q, p] = GNP H[p, q] = GNP G *= Fac H *= Fac Answer = {'X': Objective, 'G': G, 'H': H} if not in_fd(): self.D = D self.objective = Answer['X'] LAST_MVALS = mvals.copy() 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