Example #1
0
    def performSVD(self, coordsets):
        """Calculate principal modes using singular value decomposition (SVD).
        *coordsets* argument may be a :class:`~prody.atomic.Atomic`, 
        :class:`~prody.ensemble.Ensemble`, or :class:`numpy.ndarray` instance.
        If *coordsets* is a numpy array it must have the shape 
        ``(n_coordsets, n_atoms, 3)``.  :class:`numpy.ndarray` instances are 
        accepted as *coordsets* argument.
        
        This is a considerably faster way of performing PCA calculations 
        compared to eigenvalue decomposition of covariance matrix, but is
        an approximate method when heterogeneous datasets are analyzed. 
        Covariance method should be preferred over this one for analysis of 
        ensembles with missing atomic data.  See :ref:`pca-xray-calculations`
        example for comparison of results from SVD and covariance methods."""

        linalg = importLA()

        start = time.time()
        if not isinstance(coordsets, (Ensemble, Atomic, np.ndarray)):
            raise TypeError('coordsets must be an Ensemble, Atomic, Numpy '
                            'array instance')
        if isinstance(coordsets, np.ndarray):
            if coordsets.ndim != 3 or coordsets.shape[2] != 3 or \
                coordsets.dtype not in (np.float32, float):
                raise ValueError('coordsets is not a valid coordinate array')
            deviations = coordsets - coordsets.mean(0)
        else:
            if isinstance(coordsets, Ensemble):
                deviations = coordsets.getDeviations()
            elif isinstance(coordsets, Atomic):
                deviations = coordsets._getCoordsets() - \
                             coordsets._getCoords()

        n_confs = deviations.shape[0]
        if n_confs < 3:
            raise ValueError('coordsets must have more than 3 coordinate sets')
        n_atoms = deviations.shape[1]
        if n_atoms < 3:
            raise ValueError('coordsets must have more than 3 atoms')

        dof = n_atoms * 3        
        deviations = deviations.reshape((n_confs, dof)).T

        vectors, values, self._temp = linalg.svd(deviations, 
                                                 full_matrices=False)
        values = (values ** 2) / n_confs
        self._dof = dof
        self._n_atoms = n_atoms
        which = values > 1e-18
        self._eigvals = values[which]
        self._array = vectors[:, which]
        self._vars = self._eigvals
        self._trace = self._vars.sum()
        self._n_modes = len(self._eigvals)
        LOGGER.debug('{0:d} modes were calculated in {1:.2f}s.'
                         .format(self._n_modes, time.time()-start))
Example #2
0
 def calcModes(self, n_modes=20, turbo=True):
     """Calculate principal (or essential) modes.  This method uses 
     :func:`scipy.linalg.eigh` function to diagonalize covariance matrix. 
     When Scipy is not found, :func:`numpy.linalg.eigh` is used.
     
     :arg n_modes: number of non-zero eigenvalues/vectors to calculate. 
                   If ``None`` is given, all modes will be calculated. 
     :type n_modes: int or None, default is 20
     
     :arg turbo: Use a memory intensive, but faster way to calculate modes.
     :type turbo: bool, default is ``True``
     """
     
     linalg = importLA()
     if self._cov is None:
         raise ProDyException('covariance matrix is not built or set')
     start = time.time()
     dof = self._dof
     if linalg.__package__.startswith('scipy'):        
         if n_modes is None:
             eigvals = None
             n_modes = dof
         else:
             n_modes = int(n_modes)
             if n_modes >= self._dof:
                 eigvals = None
                 n_modes = dof
             else:
                 eigvals = (dof - n_modes, dof - 1)
         values, vectors = linalg.eigh(self._cov, turbo=turbo, 
                                       eigvals=eigvals)
     else:
         values, vectors = linalg.eigh(self._cov)
     # Order by descending SV
     revert = range(len(values)-1, -1, -1)
     values = values[revert]
     vectors = vectors[:, revert]
     which = values > 1e-8
     self._eigvals = values[which]
     self._array = vectors[:, which]
     self._vars = self._eigvals
     self._n_modes = len(self._eigvals)
     LOGGER.debug('{0:d} modes were calculated in {1:.2f}s.'
                  .format(self._n_modes, time.time()-start))
Example #3
0
    def calcModes(self, n_modes=20, zeros=False, turbo=True):
        """Calculate normal modes.  This method uses :func:`scipy.linalg.eigh` 
        function to diagonalize the Hessian matrix. When Scipy is not found, 
        :func:`numpy.linalg.eigh` is used.

        :arg n_modes: number of non-zero eigenvalues/vectors to calculate. 
            If ``None`` is given, all modes will be calculated. 
        :type n_modes: int or None, default is 20
        
        :arg zeros: If ``True``, modes with zero eigenvalues will be kept.
        :type zeros: bool, default is ``False``
        
        :arg turbo: Use a memory intensive, but faster way to calculate modes.
        :type turbo: bool, default is ``True``
        """

        if self._hessian is None:
            raise ValueError("Hessian matrix is not built or set")
        assert n_modes is None or isinstance(n_modes, int) and n_modes > 0, "n_modes must be a positive integer"
        assert isinstance(zeros, bool), "zeros must be a boolean"
        assert isinstance(turbo, bool), "turbo must be a boolean"
        linalg = importLA()
        start = time.time()
        shift = 5
        if linalg.__package__.startswith("scipy"):
            if n_modes is None:
                eigvals = None
                n_modes = self._dof
            else:
                if n_modes >= self._dof:
                    eigvals = None
                    n_modes = self._dof
                else:
                    eigvals = (0, n_modes + shift)
            if eigvals:
                turbo = False
            if isinstance(self._hessian, np.ndarray):
                values, vectors = linalg.eigh(self._hessian, turbo=turbo, eigvals=eigvals)
            else:
                try:
                    from scipy.sparse import linalg as scipy_sparse_la
                except ImportError:
                    raise ImportError(
                        "failed to import scipy.sparse.linalg, " "which is required for sparse matrix " "decomposition"
                    )
                try:
                    values, vectors = scipy_sparse_la.eigsh(self._hessian, k=n_modes + 6, which="SA")
                except:
                    values, vectors = scipy_sparse_la.eigen_symmetric(self._hessian, k=n_modes + 6, which="SA")

        else:
            values, vectors = linalg.eigh(self._hessian)
        n_zeros = sum(values < ZERO)
        if n_zeros < 6:
            LOGGER.warning("Less than 6 zero eigenvalues are calculated.")
            shift = n_zeros - 1
        elif n_zeros > 6:
            LOGGER.warning("More than 6 zero eigenvalues are calculated.")
            shift = n_zeros - 1
        if zeros:
            shift = -1
        self._eigvals = values[1 + shift :]
        self._vars = 1 / self._eigvals
        self._trace = self._vars.sum()
        self._array = vectors[:, 1 + shift :]
        self._n_modes = len(self._eigvals)
        LOGGER.debug("{0:d} modes were calculated in {1:.2f}s." "".format(self._n_modes, time.time() - start))
Example #4
0
def reduceModel(model, atoms, selstr):
    """Return reduced NMA model.
    
    Reduces a :class:`NMA` model to a subset of *atoms* matching a selection 
    *selstr*.  This function behaves differently depending on the type of the 
    *model* argument.  For ANM and GNM or other NMA models, this functions 
    derives the force constant matrix for system of interest (specified by the 
    *selstr*) from the force constant matrix for the *model* by assuming that 
    for any given displacement of the system of interest, the other atoms move 
    along in such a way as to minimize the potential energy.  This is based on 
    the formulation in in [KH00]_.  For PCA models, this function simply takes 
    the sub-covariance matrix for the selected atoms.

    :arg model: dynamics model
    :type model: :class:`ANM`, :class:`GNM`, or :class:`PCA`
    :arg atoms: atoms that were used to build the model
    :arg selstr: a selection string specifying subset of atoms"""

    linalg = importLA()

    if not isinstance(model, NMA):
        raise TypeError("model must be an NMA instance, not {0:s}".format(type(model)))
    if not isinstance(atoms, (AtomGroup, AtomSubset, AtomMap)):
        raise TypeError("atoms type is not valid")
    if len(atoms) <= 1:
        raise TypeError("atoms must contain more than 1 atoms")

    if isinstance(model, GNM):
        matrix = model._kirchhoff
    elif isinstance(model, ANM):
        matrix = model._hessian
    elif isinstance(model, PCA):
        matrix = model._cov
    else:
        raise TypeError("model does not have a valid type derived from NMA")
    if matrix is None:
        raise ValueError("model matrix (Hessian/Kirchhoff/Covariance) is not " "built")

    system = SELECT.getBoolArray(atoms, selstr)
    other = np.invert(system)
    n_sel = sum(system)
    if n_sel == 0:
        LOGGER.warning("selection has 0 atoms")
        return None
    if len(atoms) == n_sel:
        LOGGER.warning("selection results in same number of atoms, " "model is not reduced")
        return None

    if model.is3d():
        system = np.tile(system, (3, 1)).transpose().flatten()
        other = np.tile(other, (3, 1)).transpose().flatten()
    ss = matrix[system, :][:, system]
    if isinstance(model, PCA):
        eda = PCA(model.getTitle() + " reduced")
        eda.setCovariance(ss)
        return eda, system
    so = matrix[system, :][:, other]
    os = matrix[other, :][:, system]
    oo = matrix[other, :][:, other]
    matrix = ss - np.dot(so, np.dot(linalg.inv(oo), os))

    if isinstance(model, GNM):
        gnm = GNM(model.getTitle() + " reduced")
        gnm.setKirchhoff(matrix)
        return gnm, system
    elif isinstance(model, ANM):
        anm = ANM(model.getTitle() + " reduced")
        anm.setHessian(matrix)
        return anm, system
    elif isinstance(model, PCA):
        eda = PCA(model.getTitle() + " reduced")
        eda.setCovariance(matrix)
        return eda, system