def buildHessian(self, coords, cutoff=15.0, gamma=1.0, **kwargs): """Build Hessian matrix for given coordinate set. :arg coords: a coordinate set or anything with getCoordinates method :type coords: :class:`~numpy.ndarray` or :class:`~prody.atomic.Atomic` :arg cutoff: cutoff distance (Å) for pairwise interactions, default is 15.0 Å, minimum is 4.0 Å :type cutoff: float :arg gamma: spring constant, default is 1.0 :type gamma: float, :class:`Gamma` :arg sparse: slect to use sparse matrices. Default is ``False``. If Scipy is not found, :class:`ImportError` is raised. :type sparse: bool Instances of :class:`Gamma` classes and custom functions are accepted as *gamma* argument. When Scipy is available, user can select to use sparse matrices for efficient usage of memory at the cost of computation speed.""" slow = kwargs.get("slow", False) try: from KDTree import KDTree except ImportError: KDTree = False if not slow and not KDTree: LOGGER.info("Using a slower method for building the Hessian " "matrix.") if not isinstance(coords, np.ndarray): try: coords = coords.getCoords() except AttributeError: raise TypeError("coords must be a Numpy array or must have " "getCoordinates attribute") coords = checkCoords(coords, "coords") cutoff, g, gamma = checkENMParameters(cutoff, gamma) self._reset() self._cutoff = cutoff self._gamma = g n_atoms = coords.shape[0] dof = n_atoms * 3 start = time.time() if kwargs.get("sparse", False): try: from scipy import sparse as scipy_sparse except ImportError: raise ImportError("failed to import scipy.sparse, which is " "required for sparse matrix calculations") kirchhoff = scipy_sparse.lil_matrix((n_atoms, n_atoms)) hessian = scipy_sparse.lil_matrix((dof, dof)) else: kirchhoff = np.zeros((n_atoms, n_atoms), "d") hessian = np.zeros((dof, dof), float) if not slow and KDTree: kdtree = getKDTree(coords) kdtree.all_search(cutoff) for i, j in kdtree.all_get_indices(): i2j = coords[j] - coords[i] dist2 = np.dot(i2j, i2j) g = gamma(dist2, i, j) super_element = np.outer(i2j, i2j) * (-g / dist2) res_i3 = i * 3 res_i33 = res_i3 + 3 res_j3 = j * 3 res_j33 = res_j3 + 3 hessian[res_i3:res_i33, res_j3:res_j33] = super_element hessian[res_j3:res_j33, res_i3:res_i33] = super_element hessian[res_i3:res_i33, res_i3:res_i33] = hessian[res_i3:res_i33, res_i3:res_i33] - super_element hessian[res_j3:res_j33, res_j3:res_j33] = hessian[res_j3:res_j33, res_j3:res_j33] - super_element kirchhoff[i, j] = -g kirchhoff[j, i] = -g kirchhoff[i, i] = kirchhoff[i, i] - g kirchhoff[j, j] = kirchhoff[j, j] - g else: cutoff2 = cutoff * cutoff for i in range(n_atoms): res_i3 = i * 3 res_i33 = res_i3 + 3 xyz_i = coords[i, :] for j in range(i + 1, n_atoms): i2j = coords[j, :] - xyz_i dist2 = np.dot(i2j, i2j) if dist2 > cutoff2: continue g = gamma(dist2, i, j) res_j3 = j * 3 res_j33 = res_j3 + 3 super_element = np.outer(i2j, i2j) * (-g / dist2) hessian[res_i3:res_i33, res_j3:res_j33] = super_element hessian[res_j3:res_j33, res_i3:res_i33] = super_element hessian[res_i3:res_i33, res_i3:res_i33] = hessian[res_i3:res_i33, res_i3:res_i33] - super_element hessian[res_j3:res_j33, res_j3:res_j33] = hessian[res_j3:res_j33, res_j3:res_j33] - super_element kirchhoff[i, j] = -g kirchhoff[j, i] = -g kirchhoff[i, i] = kirchhoff[i, i] - g kirchhoff[j, j] = kirchhoff[j, j] - g LOGGER.info("Hessian was built in {0:.2f}s.".format(time.time() - start)) self._kirchhoff = kirchhoff self._hessian = hessian self._n_atoms = n_atoms self._dof = dof
def buildHessian(self, coords, cutoff=15., gamma=1., **kwargs): """Build Hessian matrix for given coordinate set. :arg coords: a coordinate set or an object with ``getCoords`` method :type coords: :class:`numpy.ndarray` :arg cutoff: cutoff distance (Å) for pairwise interactions, default is 15.0 Å, minimum is 4.0 Å :type cutoff: float :arg gamma: spring constant, default is 1.0 :type gamma: float, :class:`Gamma` :arg sparse: elect to use sparse matrices, default is **False**. If Scipy is not found, :class:`ImportError` is raised. :type sparse: bool :arg kdtree: elect to use KDTree for building Hessian matrix, default is **False** since KDTree method is slower :type kdtree: bool Instances of :class:`Gamma` classes and custom functions are accepted as *gamma* argument. When Scipy is available, user can select to use sparse matrices for efficient usage of memory at the cost of computation speed.""" try: coords = (coords._getCoords() if hasattr(coords, '_getCoords') else coords.getCoords()) except AttributeError: try: checkCoords(coords) except TypeError: raise TypeError('coords must be a Numpy array or an object ' 'with `getCoords` method') cutoff, g, gamma = checkENMParameters(cutoff, gamma) self._reset() self._cutoff = cutoff self._gamma = g n_atoms = coords.shape[0] dof = n_atoms * 3 start = time.time() if kwargs.get('sparse', False): try: from scipy import sparse as scipy_sparse except ImportError: raise ImportError('failed to import scipy.sparse, which is ' 'required for sparse matrix calculations') kirchhoff = scipy_sparse.lil_matrix((n_atoms, n_atoms)) hessian = scipy_sparse.lil_matrix((dof, dof)) else: kirchhoff = np.zeros((n_atoms, n_atoms), 'd') hessian = np.zeros((dof, dof), float) if kwargs.get('kdtree', False): LOGGER.info('Using KDTree for building the Hessian.') kdtree = KDTree(coords) kdtree.search(cutoff) for i, j in kdtree.getIndices(): i2j = coords[j] - coords[i] dist2 = np.dot(i2j, i2j) g = gamma(dist2, i, j) super_element = np.outer(i2j, i2j) * (- g / dist2) res_i3 = i*3 res_i33 = res_i3+3 res_j3 = j*3 res_j33 = res_j3+3 hessian[res_i3:res_i33, res_j3:res_j33] = super_element hessian[res_j3:res_j33, res_i3:res_i33] = super_element hessian[res_i3:res_i33, res_i3:res_i33] = \ hessian[res_i3:res_i33, res_i3:res_i33] - super_element hessian[res_j3:res_j33, res_j3:res_j33] = \ hessian[res_j3:res_j33, res_j3:res_j33] - super_element kirchhoff[i, j] = -g kirchhoff[j, i] = -g kirchhoff[i, i] = kirchhoff[i, i] - g kirchhoff[j, j] = kirchhoff[j, j] - g else: cutoff2 = cutoff * cutoff for i in range(n_atoms): res_i3 = i*3 res_i33 = res_i3+3 i_p1 = i+1 i2j_all = coords[i_p1:, :] - coords[i] for j, dist2 in enumerate((i2j_all ** 2).sum(1)): if dist2 > cutoff2: continue i2j = i2j_all[j] j += i_p1 g = gamma(dist2, i, j) res_j3 = j*3 res_j33 = res_j3+3 super_element = np.outer(i2j, i2j) * (- g / dist2) hessian[res_i3:res_i33, res_j3:res_j33] = super_element hessian[res_j3:res_j33, res_i3:res_i33] = super_element hessian[res_i3:res_i33, res_i3:res_i33] = \ hessian[res_i3:res_i33, res_i3:res_i33] - super_element hessian[res_j3:res_j33, res_j3:res_j33] = \ hessian[res_j3:res_j33, res_j3:res_j33] - super_element kirchhoff[i, j] = -g kirchhoff[j, i] = -g kirchhoff[i, i] = kirchhoff[i, i] - g kirchhoff[j, j] = kirchhoff[j, j] - g LOGGER.info('Hessian was built in {0:.2f}s.'.format(time.time()-start)) self._kirchhoff = kirchhoff self._hessian = hessian self._n_atoms = n_atoms self._dof = dof