Пример #1
0
    def eigs(self):
        """
        Eigendecomposition of volume covariance matrix of simulation
        :return: A 2-tuple:
            eigs_true: The eigenvectors of the volume covariance matrix in the form of an L-by-L-by-L-by-(C-1) array,
            where C is the number of distinct states in the simulation
            lambdas_true: The eigenvalues of the covariance matrix in the form of a (C-1)-by-(C-1) diagonal matrix.
        """
        C = self.C
        vols_c = self.vols - np.expand_dims(self.mean_true(), 3)
        p = np.ones(C) / C
        vols_c = vol_to_vec(vols_c)
        Q, R = qr(vols_c, mode='economic')

        # Rank is at most C-1, so remove last vector
        Q = Q[:, :-1]
        R = R[:-1, :]

        w, v = eigh(make_symmat(R @ np.diag(p) @ R.T))
        eigs_true = vec_to_vol(Q @ v)

        # Arrange in descending order (flip column order in eigenvector matrix)
        w = w[::-1]
        eigs_true = np.flip(eigs_true, axis=-1)

        return eigs_true, np.diag(w)
Пример #2
0
    def expand_t(self, v):
        """
        Expand array in dual basis

        This is a similar function to `evaluate` but with more accuracy by
         using the cg optimizing of linear equation, Ax=b.

        If `v` is a matrix of size `basis.ct`-by-..., `B` is the change-of-basis
        matrix of this basis, and `x` is a matrix of size `self.sz`-by-...,
        the function calculates x = (B * B')^(-1) * B * v, where the rows of `B`
        and columns of `x` are read as vectorized arrays.

        :param v: An array whose first dimension is to be expanded in this
            basis's dual. This dimension must be equal to `self.count`.
        :return: The coefficients of `v` expanded in the dual of `basis`. If more
            than one vector is supplied in `v`, the higher dimensions of the return
            value correspond to second and higher dimensions of `v`.

        .. seealso:: expand
        """
        ensure(v.shape[0] == self.count,
               f'First dimension of v must be {self.count}')

        v, sz_roll = unroll_dim(v, 2)
        b = vol_to_vec(self.evaluate(v))

        operator = LinearOperator(
            shape=(self.nres ** 3, self.nres ** 3),
            matvec=lambda x: vol_to_vec(self.evaluate(self.evaluate_t(vec_to_vol(x))))
        )

        # TODO: (from MATLAB implementation) - Check that this tolerance make sense for multiple columns in v
        tol = 10 * np.finfo(v.dtype).eps
        logger.info('Expanding array in dual basis')
        v, info = cg(operator, b, tol=tol)

        v = v[..., np.newaxis]

        if info != 0:
            raise RuntimeError('Unable to converge!')

        v = roll_dim(v, sz_roll)
        x = vec_to_vol(v)

        return x
Пример #3
0
    def expand_t(self, v):
        ensure(v.shape[0] == self.basis_count,
               f'First dimension of v must be {self.basis_count}')

        v, sz_roll = unroll_dim(v, 2)
        b = vol_to_vec(self.evaluate(v))

        operator = LinearOperator(
            shape=(self.N**3, self.N**3),
            matvec=lambda x: vol_to_vec(
                self.evaluate(self.evaluate_t(vec_to_vol(x)))))

        # TODO: (from MATLAB implementation) - Check that this tolerance make sense for multiple columns in v
        tol = 10 * np.finfo(v.dtype).eps
        logger.info('Expanding array in dual basis')
        v, info = cg(operator, b, tol=tol)

        if info != 0:
            raise RuntimeError('Unable to converge!')

        v = roll_dim(v, sz_roll)
        x = vec_to_vol(v)

        return x
Пример #4
0
    def toeplitz(self, L=None):
        """
        Compute the 3D Toeplitz matrix corresponding to this Fourier Kernel
        :param L: The size of the volumes to be convolved (default M/2, where the dimensions of this Fourier Kernel
            are MxMxM
        :return: An six-dimensional Toeplitz matrix of size L describing the convolution of a volume with this kernel
        """
        if L is None:
            L = int(self.M / 2)

        A = np.eye(L**3, dtype=self.as_type)
        for i in range(L**3):
            A[:, i] = vol_to_vec(self.convolve_volume(vec_to_vol(A[:, i])))

        A = vecmat_to_volmat(A)
        return A
Пример #5
0
    def vol_coords(self, mean_vol=None, eig_vols=None):
        """
        Coordinates of simulation volumes in a given basis
        :param mean_vol: A mean volume in the form of an L-by-L-by-L array (default `mean_true`).
        :param eig_vols: A set of eigenvolumes in an L-by-L-by-L-by-K array (default `eigs`).
        :return:
        """
        if mean_vol is None:
            mean_vol = self.mean_true()
        if eig_vols is None:
            eig_vols = self.eigs()[0]

        vols = self.vols - np.expand_dims(mean_vol, 3)
        coords = vol_to_vec(eig_vols).T @ vol_to_vec(vols)
        res = vols - vec_to_vol(vol_to_vec(eig_vols) @ coords)
        res_norms = np.diag(anorm(res, (0, 1, 2)))
        res_inners = vol_to_vec(mean_vol).T @ vol_to_vec(res)

        return coords.squeeze(), res_norms, res_inners