Пример #1
0
def anufft(sig_f, fourier_pts, sz, real=False):
    """
    Wrapper for 1, 2, and 3 dimensional Non Uniform FFT Adjoint.
    Dimension is based on the dimension of fourier_pts and checked against sig_f.

    Selects best available package from `nfft` `backends` configuration list.

    :param sig_f: Array representing the signal(s) in Fourier space to be transformed. \
    sig_f either matches length of fourier_pts or sig_f.shape is stack of (`ntransforms`, ...).
    :param fourier_pts: The points in Fourier space where the Fourier transform is to be calculated,
            arranged as a dimension-by-K array. These need to be in the range [-pi, pi] in each dimension.
    :param sz: A tuple indicating the geometry of the signal.
    :param real: Optional Bool indicating if you would like only the real components, Defaults False.
    :return: The Non Uniform FFT adjoint transform.

    """

    if fourier_pts.dtype != real_type(sig_f.dtype):
        logger.warning("anufft passed inconsistent dtypes."
                       f" fourier_pts: {fourier_pts.dtype}"
                       f" forcing precision of signal data: {sig_f.dtype}.")
        fourier_pts = fourier_pts.astype(real_type(sig_f.dtype))

    if sig_f.dtype != complex_type(sig_f.dtype):
        logger.debug("anufft passed real_type for signal, converting")
        sig_f = sig_f.astype(complex_type(sig_f.dtype))

    ntransforms = 1
    if len(sig_f.shape) == 2:
        ntransforms = sig_f.shape[0]

    plan = Plan(sz=sz, fourier_pts=fourier_pts, ntransforms=ntransforms)
    adjoint = plan.adjoint(sig_f)
    return np.real(adjoint) if real else adjoint
Пример #2
0
    def _evaluate_pswf2d_all(self, r, theta, max_ns):
        """
        Evaluate the numerical values of PSWF functions for all N's, up to given n for each N

        :param r: Radial part to evaluate
        :param theta: Phase part to evaluate
        :param max_ns: List of ints max_ns[i] is max n to to use for N=i, not included.
            If max_ns[i]<1 N=i won't be used
        :return: (len(r), sum(max_ns)) ndarray
            Indices are corresponding to the list (N, n)
            (0, 0),..., (0, max_ns[0]), (1, 0),..., (1, max_ns[1]),... , (len(max_ns)-1, 0),
            (len(max_ns)-1, max_ns[-1])
        """
        max_ns_ints = [int(max_n) for max_n in max_ns]
        out_mat = []
        for i, max_n in enumerate(max_ns_ints):
            if max_n < 1:
                continue

            d_vec = self.d_vec_all[i]

            phase_part = np.exp(1j * i * theta) / np.sqrt(2 * np.pi)
            range_array = np.arange(len(d_vec))
            r_radial_part_mat = t_radial_part_mat(
                r, i, range_array, len(d_vec)).dot(d_vec[:, :max_n])

            pswf_n_n_mat = phase_part * r_radial_part_mat.T

            out_mat.extend(pswf_n_n_mat)
        out_mat = np.array(out_mat, dtype=complex_type(self.dtype)).T
        return out_mat
Пример #3
0
    def evaluate(self, coefficients):
        """
        Evaluate coefficients in standard 2D coordinate basis from those in PSWF basis

        :param coeffcients: A coefficient vector (or an array of coefficient
            vectors) in PSWF basis to be evaluated.
        :return : The evaluation of the coefficient vector(s) in standard 2D
            coordinate basis.
        """
        coefficients = coefficients.T  # RCOPT

        # if we got only one vector
        if len(coefficients.shape) == 1:
            coefficients = coefficients[:, np.newaxis]

        angular_is_zero = np.absolute(self.ang_freqs) == 0
        flatten_images = self.samples[:, angular_is_zero].dot(
            coefficients[angular_is_zero]) + 2.0 * np.real(
                self.samples[:, ~angular_is_zero].dot(
                    coefficients[~angular_is_zero]))

        n_images = int(flatten_images.shape[1])
        images = np.zeros((self._image_height, self._image_height,
                           n_images)).astype(complex_type(self.dtype))
        images[self._disk_mask, :] = flatten_images
        images = np.transpose(images, axes=(1, 0, 2))
        return np.real(images).T  # RCOPT
Пример #4
0
    def testComplexCoversionErrorsToComplex(self):
        # Load a reasonable input
        x = np.load(os.path.join(DATA_DIR, "fbbasis_coefficients_8_8.npy"))

        # Express in an FB basis
        v1 = self.basis.expand(x.astype(self.dtype))

        # Test catching Errors
        with raises(TypeError):
            # Pass complex into `to_complex`
            _ = self.basis.to_complex(v1.astype(np.complex64))

        # Test casting case, where basis and coef don't match
        if self.basis.dtype == np.float32:
            test_dtype = np.float64
        elif self.basis.dtype == np.float64:
            test_dtype = np.float32
        # Result should be same precision as coef input, just complex.
        result_dtype = complex_type(test_dtype)

        v3 = self.basis.to_complex(v1.astype(test_dtype))
        self.assertTrue(v3.dtype == result_dtype)

        # Try 0d vector, should not crash.
        _ = self.basis.to_complex(v1.reshape(-1))
Пример #5
0
    def evaluate(self, coefficients):
        """
        Evaluate coefficients in standard 2D coordinate basis from those in PSWF basis

        :param coefficients: A coefficient vector (or an array of coefficient vectors)
            in PSWF basis to be evaluated.
        :return : The evaluation of the coefficient vector(s) in standard 2D
            coordinate basis.
        """

        coefficients = coefficients.T  # RCOPT

        # if we got only one vector
        if len(coefficients.shape) == 1:
            coefficients = coefficients.reshape((len(coefficients), 1))

        angular_is_zero = np.absolute(self.ang_freqs) == 0
        flatten_images = self.samples[:, angular_is_zero].dot(
            coefficients[angular_is_zero]
        ) + (
            2.0
            * np.real(
                self.samples[:, ~angular_is_zero].dot(coefficients[~angular_is_zero])
            )
        )

        n_images = int(flatten_images.shape[1])
        images = np.zeros((self._image_height, self._image_height, n_images)).astype(
            complex_type(self.dtype)
        )
        images[self._disk_mask, :] = flatten_images
        # TODO: no need to switch x and y any more, need to make consistent with direct method
        return np.real(images).T  # RCOPT
Пример #6
0
def nufft(sig_f, fourier_pts, real=False):
    """
    Wrapper for 1, 2, and 3 dimensional Non Uniform FFT
    Dimension is based on the dimension of fourier_pts and checked against sig_f.

    Selects best available package from `nfft` `backends` configuration list.

    :param sig_f: Array representing the signal(s) in real space to be transformed. \
    sig_f either matches `sz` or sig_f.shape is stack of (..., `ntransforms`).
    :param fourier_pts: The points in Fourier space where the Fourier transform is to be calculated,
            arranged as a dimension-by-K array. These need to be in the range [-pi, pi] in each dimension.
    :param real: Optional Bool indicating if you would like only the real components, Defaults False.
    :return: The Non Uniform FFT transform.

    """

    if fourier_pts.dtype != real_type(sig_f.dtype):
        logger.warning("nufft passed inconsistent dtypes."
                       f" fourier_pts: {fourier_pts.dtype}"
                       f" forcing precision of signal data: {sig_f.dtype}.")
        fourier_pts = fourier_pts.astype(real_type(sig_f.dtype))

    if sig_f.dtype != complex_type(sig_f.dtype):
        logger.debug("nufft passed real_type for signal, converting")
        sig_f = sig_f.astype(complex_type(sig_f.dtype))

    # Unpack the dimension of the signal
    #   Note, infer dimension from fourier_pts because signal might be a stack.
    dimension = fourier_pts.shape[0]

    # Unpack the resolution of the signal
    resolution = sig_f.shape[1]

    # Construct tuple describing geometry of signal
    sz = (resolution, ) * dimension

    ntransforms = 1
    if len(sig_f.shape) == dimension + 1:
        ntransforms = sig_f.shape[0]

    plan = Plan(sz=sz, fourier_pts=fourier_pts, ntransforms=ntransforms)
    transform = plan.transform(sig_f)
    return np.real(transform) if real else transform
Пример #7
0
    def estimate_psd(self, blocks, tapers_1d):
        """
        Estimate the power spectrum of the micrograph using the multi-taper method

        :param blocks: 3-D NumPy array containing windows extracted from the micrograph in the preprocess function.
        :param tapers_1d: NumPy array of data tapers.
        :return: NumPy array of estimated power spectrum.
        """

        num_1d_tapers = tapers_1d.shape[-1]
        tapers_1d = tapers_1d.astype(complex_type(self.dtype), copy=False)

        blocks_mt = np.zeros(blocks[0, :, :].shape, dtype=self.dtype)

        blocks_tapered = np.zeros(blocks[0, :, :].shape,
                                  dtype=complex_type(self.dtype))

        taper_2d = np.zeros((blocks.shape[1], blocks.shape[2]),
                            dtype=complex_type(self.dtype))

        for ax1 in range(num_1d_tapers):
            for ax2 in range(num_1d_tapers):
                np.matmul(
                    tapers_1d[:, ax1, np.newaxis],
                    tapers_1d[:, ax2, np.newaxis].T,
                    out=taper_2d,
                )

                for m in range(blocks.shape[0]):
                    np.multiply(blocks[m, :, :], taper_2d, out=blocks_tapered)
                    blocks_mt_post_fft = fft.fftn(blocks_tapered,
                                                  axes=(-2, -1))
                    blocks_mt += abs2(blocks_mt_post_fft)

        blocks_mt /= blocks.shape[0]**2
        blocks_mt /= tapers_1d.shape[0]**2

        amplitude_spectrum = fft.fftshift(
            blocks_mt)  # max difference 10^-13, max relative difference 10^-14

        return Image(amplitude_spectrum)
Пример #8
0
    def testSmallFitTransform(self):
        # Reals
        pca = PCA(n_components=self.components_small)
        Y1 = pca.fit_transform(self.X_small)

        # Complex
        cpca = ComplexPCA(n_components=self.components_small)
        Y2 = cpca.fit_transform(self.X_small.astype(complex_type(self.dtype)))

        # Real part should be the same.
        self.assertTrue(np.allclose(np.real(Y2), Y1))
        # Imag part should be zero.
        self.assertTrue(np.allclose(np.imag(Y2), 0))
Пример #9
0
    def _pswf_integration(self, images_nufft):
        """
        Perform integration part for rotational invariant property.
        """
        num_images = images_nufft.shape[1]
        n_max_float = float(self.n_max) / 2
        r_n_eval_mat = np.zeros(
            (len(self.radial_quad_pts), self.n_max, num_images),
            dtype=complex_type(self.dtype),
        )

        for i in range(len(self.radial_quad_pts)):
            curr_r_mat = images_nufft[
                self.r_quad_indices[i] : self.r_quad_indices[i]
                + self.num_angular_pts[i],
                :,
            ]
            curr_r_mat = np.concatenate((curr_r_mat, np.conj(curr_r_mat)))
            fft_plan = xp.asnumpy(fft.fft(xp.asarray(curr_r_mat), axis=0))
            angular_eval = fft_plan * self.quad_rule_radial_wts[i]

            r_n_eval_mat[i, :, :] = np.tile(
                angular_eval,
                (int(max(1, np.ceil(n_max_float / self.num_angular_pts[i]))), 1),
            )[: self.n_max, :]

        r_n_eval_mat = r_n_eval_mat.reshape(
            (len(self.radial_quad_pts) * self.n_max, num_images), order="F"
        )
        coeff_vec_quad = np.zeros(
            (len(self.ang_freqs), num_images), dtype=complex_type(self.dtype)
        )
        m = len(self.pswf_radial_quad)
        for i in range(self.n_max):
            coeff_vec_quad[
                self.indices_for_n[i] + np.arange(self.numel_for_n[i]), :
            ] = np.dot(self.blk_r[i], r_n_eval_mat[i * m : (i + 1) * m, :])

        return coeff_vec_quad
Пример #10
0
    def to_complex(self, coef):
        """
        Return complex valued representation of coefficients.
        This can be useful when comparing or implementing methods
        from literature.

        There is a corresponding method, to_real.

        :param coef: Coefficients from this basis.
        :return: Complex coefficent representation from this basis.
        """

        if coef.ndim == 1:
            coef = coef.reshape(1, -1)

        if coef.dtype not in (np.float64, np.float32):
            raise TypeError("coef provided to to_complex should be real.")

        # Pass through dtype precions, but check and warn if mismatched.
        dtype = complex_type(coef.dtype)
        if coef.dtype != self.dtype:
            logger.warning(
                f"coef dtype {coef.dtype} does not match precision of basis.dtype {self.dtype}, returning {dtype}."
            )

        # Return the same precision as coef
        imaginary = dtype(1j)

        ccoef = np.zeros((coef.shape[0], self.complex_count), dtype=dtype)

        ccoef_d = OrderedDict()

        for i in range(self.count):
            ell = self.angular_indices[i]
            q = self.radial_indices[i]
            sgn = self.signs_indices[i]

            ccoef_d.setdefault((ell, q), 0 + 0j)
            if ell == 0:
                ccoef_d[(ell, q)] = coef[:, i]
            elif sgn == 1:
                ccoef_d[(ell, q)] += coef[:, i] / 2.0
            elif sgn == -1:
                ccoef_d[(ell, q)] -= imaginary * coef[:, i] / 2.0
            else:
                raise ValueError("sgns should be +-1")

        for i, k in enumerate(ccoef_d.keys()):
            ccoef[:, i] = ccoef_d[k]

        return ccoef
Пример #11
0
    def __init__(self, sz, fourier_pts, epsilon=1e-8, ntransforms=1, **kwargs):
        """
        A plan for non-uniform FFT in 2D or 3D.

        :param sz: A tuple indicating the geometry of the signal.
        :param fourier_pts: The points in Fourier space where the Fourier
        transform is to be calculated, arranged as a dimension-by-K array.
        These need to be in the range [-pi, pi] in each dimension.
        :param epsilon: The desired precision of the NUFFT.
        :param ntransforms: Optional integer indicating if you would like
        to compute a batch of `ntransforms`.
        transforms.  Implies vol_f.shape is (`ntransforms`, ...).
        """

        self.ntransforms = ntransforms

        self.sz = sz
        self.dim = len(sz)

        self.dtype = fourier_pts.dtype

        self.complex_dtype = complex_type(self.dtype)

        self.fourier_pts = np.ascontiguousarray(
            np.mod(fourier_pts + np.pi, 2 * np.pi) - np.pi)

        self.num_pts = fourier_pts.shape[1]

        self.epsilon = max(epsilon, np.finfo(self.dtype).eps)
        if self.epsilon != epsilon:
            logger.debug(f"FinufftPlan adjusted eps={self.epsilon}"
                         f" from requested {epsilon}.")

        self._transform_plan = finufft.Plan(
            nufft_type=2,
            n_modes_or_dim=self.sz,
            eps=self.epsilon,
            n_trans=self.ntransforms,
            dtype=self.dtype,
        )

        self._adjoint_plan = finufft.Plan(
            nufft_type=1,
            n_modes_or_dim=self.sz,
            eps=self.epsilon,
            n_trans=self.ntransforms,
            dtype=self.dtype,
        )

        self._transform_plan.setpts(*self.fourier_pts)
        self._adjoint_plan.setpts(*self.fourier_pts)
Пример #12
0
    def _compute_nfft_potts(self, images, start, finish):
        """
        Perform NuFFT transform for images in rectangular coordinates
        """
        x = self.us_fft_pts
        num_images = finish - start

        m = x.shape[0]

        images_nufft = np.zeros((m, num_images), dtype=complex_type(self.dtype))
        for i in range(start, finish):
            images_nufft[:, i - start] = nufft(images[..., i], 2 * pi * x.T)

        return images_nufft
Пример #13
0
    def to_complex(self, coef):
        """
        Return complex valued representation of coefficients.
        This can be useful when comparing or implementing methods
        from literature.

        There is a corresponding method, to_real.

        :param coef: Coefficients from this basis.
        :return: Complex coefficent representation from this basis.
        """

        if coef.ndim == 1:
            coef = coef.reshape(1, -1)

        if coef.dtype not in (np.float64, np.float32):
            raise TypeError("coef provided to to_complex should be real.")

        # Pass through dtype precions, but check and warn if mismatched.
        dtype = complex_type(coef.dtype)
        if coef.dtype != self.dtype:
            logger.warning(
                f"coef dtype {coef.dtype} does not match precision of basis.dtype {self.dtype}, returning {dtype}."
            )

        # Return the same precision as coef
        imaginary = dtype(1j)

        ccoef = np.zeros((coef.shape[0], self.complex_count), dtype=dtype)

        ind = 0
        idx = np.arange(self.k_max[0], dtype=int)
        ind += np.size(idx)

        ccoef[:, idx] = coef[:, idx]

        for ell in range(1, self.ell_max + 1):
            idx = ind + np.arange(self.k_max[ell], dtype=int)
            ccoef[:, idx] = (
                coef[:, self._pos[idx]] - imaginary * coef[:, self._neg[idx]]
            ) / 2.0

            ind += np.size(idx)

        return ccoef
Пример #14
0
    def evaluate(self, v):
        """
        Evaluate coefficients in standard 2D coordinate basis from those in FB basis

        :param v: A coefficient vector (or an array of coefficient vectors)
            in FB basis to be evaluated. The last dimension must equal `self.count`.
        :return x: The evaluation of the coefficient vector(s) `x` in standard 2D
            coordinate basis. This is Image instance with resolution of `self.sz`
            and the first dimension correspond to remaining dimension of `v`.
        """

        if v.dtype != self.dtype:
            logger.debug(
                f"{self.__class__.__name__}::evaluate"
                f" Inconsistent dtypes v: {v.dtype} self: {self.dtype}")

        sz_roll = v.shape[:-1]
        v = v.reshape(-1, self.count)

        # number of 2D image samples
        n_data = v.shape[0]

        # get information on polar grids from precomputed data
        n_theta = np.size(self._precomp["freqs"], 2)
        n_r = np.size(self._precomp["freqs"], 1)

        # go through  each basis function and find corresponding coefficient
        pf = np.zeros((n_data, 2 * n_theta, n_r),
                      dtype=complex_type(self.dtype))
        mask = self._indices["ells"] == 0

        ind = 0

        idx = ind + np.arange(self.k_max[0], dtype=int)

        # include the normalization factor of angular part into radial part
        radial_norm = self._precomp["radial"] / np.expand_dims(
            self.angular_norms, 1)
        pf[:, 0, :] = v[:, mask] @ radial_norm[idx]
        ind = ind + np.size(idx)

        ind_pos = ind

        for ell in range(1, self.ell_max + 1):
            idx = ind + np.arange(self.k_max[ell], dtype=int)
            idx_pos = ind_pos + np.arange(self.k_max[ell], dtype=int)
            idx_neg = idx_pos + self.k_max[ell]

            v_ell = (v[:, idx_pos] - 1j * v[:, idx_neg]) / 2.0

            if np.mod(ell, 2) == 1:
                v_ell = 1j * v_ell

            pf_ell = v_ell @ radial_norm[idx]
            pf[:, ell, :] = pf_ell

            if np.mod(ell, 2) == 0:
                pf[:, 2 * n_theta - ell, :] = pf_ell.conjugate()
            else:
                pf[:, 2 * n_theta - ell, :] = -pf_ell.conjugate()

            ind = ind + np.size(idx)
            ind_pos = ind_pos + 2 * self.k_max[ell]

        # 1D inverse FFT in the degree of polar angle
        pf = 2 * pi * xp.asnumpy(fft.ifft(xp.asarray(pf), axis=1))

        # Only need "positive" frequencies.
        hsize = int(np.size(pf, 1) / 2)
        pf = pf[:, 0:hsize, :]

        for i_r in range(0, n_r):
            pf[..., i_r] = pf[..., i_r] * (self._precomp["gl_weights"][i_r] *
                                           self._precomp["gl_nodes"][i_r])

        pf = np.reshape(pf, (n_data, n_r * n_theta))

        # perform inverse non-uniformly FFT transform back to 2D coordinate basis
        freqs = m_reshape(self._precomp["freqs"], (2, n_r * n_theta))

        x = 2 * anufft(pf, 2 * pi * freqs, self.sz, real=True)

        # Return X as Image instance with the last two dimensions as *self.sz
        x = x.reshape((*sz_roll, *self.sz))

        return Image(x)
Пример #15
0
    def testPolarBasis2DEvaluate(self):
        v = np.array(
            [
                0.38243133 - 6.66608316e-18j,
                0.3249317 + 1.47839074e-01j,
                0.14819172 - 3.78171168e-03j,
                -0.22808599 - 5.29338933e-02j,
                0.38243133 - 6.66608316e-18j,
                0.34595014 + 1.06355385e-01j,
                0.15519289 + 4.75602164e-02j,
                -0.22401193 - 4.33128746e-03j,
                0.38243133 - 6.66608316e-18j,
                0.36957165 + 5.69575709e-02j,
                0.17389327 + 5.53498385e-02j,
                -0.11601473 + 1.35405676e-02j,
                0.38243133 - 6.66608316e-18j,
                0.39045046 + 2.17911945e-03j,
                0.18146449 + 1.37089189e-02j,
                -0.02110144 - 6.65071497e-03j,
                0.38243133 - 6.66608316e-18j,
                0.4063995 - 5.21354967e-02j,
                0.15674204 - 3.85815662e-02j,
                -0.02886296 - 3.91489615e-02j,
                0.38243133 - 6.66608316e-18j,
                0.41872477 - 9.98946906e-02j,
                0.11862477 - 5.15231952e-02j,
                -0.05298751 - 1.95319478e-02j,
                0.38243133 - 6.66608316e-18j,
                0.43013599 - 1.38307796e-01j,
                0.10075763 - 1.25689289e-02j,
                -0.04052728 + 5.66863498e-02j,
                0.38243133 - 6.66608316e-18j,
                0.44144497 - 1.68826980e-01j,
                0.11446016 + 4.53003874e-02j,
                -0.03546515 + 1.13544145e-01j,
                0.38243133 - 6.66608316e-18j,
                0.44960099 - 1.94794929e-01j,
                0.15053714 + 8.11915305e-02j,
                -0.04800556 + 1.15828804e-01j,
                0.38243133 - 6.66608316e-18j,
                0.44872328 - 2.17957567e-01j,
                0.19116871 + 7.99536373e-02j,
                -0.05683092 + 9.72225058e-02j,
                0.38243133 - 6.66608316e-18j,
                0.43379428 - 2.36681249e-01j,
                0.21025378 + 5.48466438e-02j,
                -0.05318826 + 8.54948014e-02j,
                0.38243133 - 6.66608316e-18j,
                0.40485577 - 2.47073481e-01j,
                0.18680217 + 3.31766116e-02j,
                -0.06674163 + 7.94216591e-02j,
                0.38243133 - 6.66608316e-18j,
                0.36865853 - 2.45913767e-01j,
                0.13660805 + 3.68947359e-02j,
                -0.11467046 + 8.49198927e-02j,
                0.38243133 - 6.66608316e-18j,
                0.33597018 - 2.32971425e-01j,
                0.1072859 + 6.24686168e-02j,
                -0.12932565 + 1.06139634e-01j,
                0.38243133 - 6.66608316e-18j,
                0.31616666 - 2.10791785e-01j,
                0.11876919 + 7.93812474e-02j,
                -0.1094488 + 1.20159845e-01j,
                0.38243133 - 6.66608316e-18j,
                0.31313975 - 1.82190396e-01j,
                0.14075481 + 5.85637416e-02j,
                -0.15198775 + 1.02156797e-01j,
                0.38243133 + 6.66608316e-18j,
                0.3249317 - 1.47839074e-01j,
                0.14819172 + 3.78171168e-03j,
                -0.22808599 + 5.29338933e-02j,
                0.38243133 + 6.66608316e-18j,
                0.34595014 - 1.06355385e-01j,
                0.15519289 - 4.75602164e-02j,
                -0.22401193 + 4.33128746e-03j,
                0.38243133 + 6.66608316e-18j,
                0.36957165 - 5.69575709e-02j,
                0.17389327 - 5.53498385e-02j,
                -0.11601473 - 1.35405676e-02j,
                0.38243133 + 6.66608316e-18j,
                0.39045046 - 2.17911945e-03j,
                0.18146449 - 1.37089189e-02j,
                -0.02110144 + 6.65071497e-03j,
                0.38243133 + 6.66608316e-18j,
                0.4063995 + 5.21354967e-02j,
                0.15674204 + 3.85815662e-02j,
                -0.02886296 + 3.91489615e-02j,
                0.38243133 + 6.66608316e-18j,
                0.41872477 + 9.98946906e-02j,
                0.11862477 + 5.15231952e-02j,
                -0.05298751 + 1.95319478e-02j,
                0.38243133 + 6.66608316e-18j,
                0.43013599 + 1.38307796e-01j,
                0.10075763 + 1.25689289e-02j,
                -0.04052728 - 5.66863498e-02j,
                0.38243133 + 6.66608316e-18j,
                0.44144497 + 1.68826980e-01j,
                0.11446016 - 4.53003874e-02j,
                -0.03546515 - 1.13544145e-01j,
                0.38243133 + 6.66608316e-18j,
                0.44960099 + 1.94794929e-01j,
                0.15053714 - 8.11915305e-02j,
                -0.04800556 - 1.15828804e-01j,
                0.38243133 + 6.66608316e-18j,
                0.44872328 + 2.17957567e-01j,
                0.19116871 - 7.99536373e-02j,
                -0.05683092 - 9.72225058e-02j,
                0.38243133 + 6.66608316e-18j,
                0.43379428 + 2.36681249e-01j,
                0.21025378 - 5.48466438e-02j,
                -0.05318826 - 8.54948014e-02j,
                0.38243133 + 6.66608316e-18j,
                0.40485577 + 2.47073481e-01j,
                0.18680217 - 3.31766116e-02j,
                -0.06674163 - 7.94216591e-02j,
                0.38243133 + 6.66608316e-18j,
                0.36865853 + 2.45913767e-01j,
                0.13660805 - 3.68947359e-02j,
                -0.11467046 - 8.49198927e-02j,
                0.38243133 + 6.66608316e-18j,
                0.33597018 + 2.32971425e-01j,
                0.1072859 - 6.24686168e-02j,
                -0.12932565 - 1.06139634e-01j,
                0.38243133 + 6.66608316e-18j,
                0.31616666 + 2.10791785e-01j,
                0.11876919 - 7.93812474e-02j,
                -0.1094488 - 1.20159845e-01j,
                0.38243133 + 6.66608316e-18j,
                0.31313975 + 1.82190396e-01j,
                0.14075481 - 5.85637416e-02j,
                -0.15198775 - 1.02156797e-01j,
            ],
            dtype=complex_type(self.dtype),
        )

        x = self.basis.evaluate(v)
        result = np.array(
            [
                [
                    9.8593804,
                    7.94242903,
                    7.23336975,
                    7.33314303,
                    7.41260132,
                    7.59483694,
                    7.94830958,
                    9.47324547,
                ],
                [
                    7.27801941,
                    8.29797686,
                    7.17234599,
                    7.31082685,
                    7.04347376,
                    6.91956664,
                    8.12234596,
                    8.36258646,
                ],
                [
                    8.76188511,
                    10.69546884,
                    8.37029969,
                    9.87512737,
                    9.73946157,
                    6.56646752,
                    5.69555713,
                    8.77758976,
                ],
                [
                    10.42069436,
                    12.3649092,
                    14.23951952,
                    20.41736454,
                    22.32664939,
                    18.11535113,
                    7.95059873,
                    8.79515046,
                ],
                [
                    11.23152882,
                    12.61468396,
                    17.92585027,
                    25.82097043,
                    26.4633412,
                    25.11167661,
                    11.90634511,
                    9.05131389,
                ],
                [
                    10.7048523,
                    11.73534566,
                    16.53838035,
                    25.13242621,
                    23.58037996,
                    21.37129485,
                    12.1024389,
                    10.26313743,
                ],
                [
                    8.24162377,
                    11.90490143,
                    14.82292441,
                    19.50174891,
                    17.69291969,
                    15.06781768,
                    10.4669263,
                    10.2082326,
                ],
                [
                    5.26532858,
                    9.60999648,
                    12.68642275,
                    12.42354237,
                    10.87648517,
                    10.60647963,
                    9.11026567,
                    8.53250276,
                ],
            ],
            dtype=complex_type(self.dtype),
        ).T  # RCOPT

        self.assertTrue(np.allclose(x.asnumpy(), result))
Пример #16
0
    def testPolarBasis2DEvaluate_t(self):
        x = Image(
            np.array(
                [
                    [
                        0.00000000e00,
                        0.00000000e00,
                        0.00000000e00,
                        0.00000000e00,
                        -1.08106869e-17,
                        0.00000000e00,
                        0.00000000e00,
                        0.00000000e00,
                    ],
                    [
                        0.00000000e00,
                        0.00000000e00,
                        -6.40456062e-03,
                        -3.32961020e-03,
                        -1.36887927e-02,
                        -5.42770488e-03,
                        7.63680861e-03,
                        0.00000000e00,
                    ],
                    [
                        0.00000000e00,
                        3.16377602e-03,
                        -9.31273350e-03,
                        9.46128404e-03,
                        1.93239220e-02,
                        3.79891953e-02,
                        1.06841173e-02,
                        -2.36467925e-03,
                    ],
                    [
                        0.00000000e00,
                        1.72736955e-03,
                        -1.00710814e-02,
                        4.93520304e-02,
                        3.77702656e-02,
                        6.57365438e-02,
                        3.94739462e-03,
                        -4.41228496e-03,
                    ],
                    [
                        4.01551066e-18,
                        -3.08071647e-03,
                        -1.61670565e-02,
                        8.66886286e-02,
                        5.09898409e-02,
                        7.19313349e-02,
                        1.68313715e-02,
                        5.19180892e-03,
                    ],
                    [
                        0.00000000e00,
                        2.87262215e-03,
                        -3.37732956e-02,
                        4.51706505e-02,
                        5.72215879e-02,
                        4.63553081e-02,
                        1.86552175e-03,
                        1.12608805e-02,
                    ],
                    [
                        0.00000000e00,
                        2.77905016e-03,
                        -2.77499404e-02,
                        -4.02645374e-02,
                        -1.54969139e-02,
                        -1.66229153e-02,
                        -2.07389259e-02,
                        6.64060546e-03,
                    ],
                    [
                        0.00000000e00,
                        0.00000000e00,
                        5.20080934e-03,
                        -1.06788196e-02,
                        -1.14761672e-02,
                        -1.27443126e-02,
                        -1.15563484e-02,
                        0.00000000e00,
                    ],
                ],
                dtype=self.dtype,
            ).T)  # RCOPT

        pf = self.basis.evaluate_t(x)
        result = np.array(
            [
                0.38243133 + 6.66608316e-18j,
                0.3249317 - 1.47839074e-01j,
                0.14819172 + 3.78171168e-03j,
                -0.22808599 + 5.29338933e-02j,
                0.38243133 + 6.66608316e-18j,
                0.34595014 - 1.06355385e-01j,
                0.15519289 - 4.75602164e-02j,
                -0.22401193 + 4.33128746e-03j,
                0.38243133 + 6.66608316e-18j,
                0.36957165 - 5.69575709e-02j,
                0.17389327 - 5.53498385e-02j,
                -0.11601473 - 1.35405676e-02j,
                0.38243133 + 6.66608316e-18j,
                0.39045046 - 2.17911945e-03j,
                0.18146449 - 1.37089189e-02j,
                -0.02110144 + 6.65071497e-03j,
                0.38243133 + 6.66608316e-18j,
                0.4063995 + 5.21354967e-02j,
                0.15674204 + 3.85815662e-02j,
                -0.02886296 + 3.91489615e-02j,
                0.38243133 + 6.66608316e-18j,
                0.41872477 + 9.98946906e-02j,
                0.11862477 + 5.15231952e-02j,
                -0.05298751 + 1.95319478e-02j,
                0.38243133 + 6.66608316e-18j,
                0.43013599 + 1.38307796e-01j,
                0.10075763 + 1.25689289e-02j,
                -0.04052728 - 5.66863498e-02j,
                0.38243133 + 6.66608316e-18j,
                0.44144497 + 1.68826980e-01j,
                0.11446016 - 4.53003874e-02j,
                -0.03546515 - 1.13544145e-01j,
                0.38243133 + 6.66608316e-18j,
                0.44960099 + 1.94794929e-01j,
                0.15053714 - 8.11915305e-02j,
                -0.04800556 - 1.15828804e-01j,
                0.38243133 + 6.66608316e-18j,
                0.44872328 + 2.17957567e-01j,
                0.19116871 - 7.99536373e-02j,
                -0.05683092 - 9.72225058e-02j,
                0.38243133 + 6.66608316e-18j,
                0.43379428 + 2.36681249e-01j,
                0.21025378 - 5.48466438e-02j,
                -0.05318826 - 8.54948014e-02j,
                0.38243133 + 6.66608316e-18j,
                0.40485577 + 2.47073481e-01j,
                0.18680217 - 3.31766116e-02j,
                -0.06674163 - 7.94216591e-02j,
                0.38243133 + 6.66608316e-18j,
                0.36865853 + 2.45913767e-01j,
                0.13660805 - 3.68947359e-02j,
                -0.11467046 - 8.49198927e-02j,
                0.38243133 + 6.66608316e-18j,
                0.33597018 + 2.32971425e-01j,
                0.1072859 - 6.24686168e-02j,
                -0.12932565 - 1.06139634e-01j,
                0.38243133 + 6.66608316e-18j,
                0.31616666 + 2.10791785e-01j,
                0.11876919 - 7.93812474e-02j,
                -0.1094488 - 1.20159845e-01j,
                0.38243133 + 6.66608316e-18j,
                0.31313975 + 1.82190396e-01j,
                0.14075481 - 5.85637416e-02j,
                -0.15198775 - 1.02156797e-01j,
                0.38243133 - 6.66608316e-18j,
                0.3249317 + 1.47839074e-01j,
                0.14819172 - 3.78171168e-03j,
                -0.22808599 - 5.29338933e-02j,
                0.38243133 - 6.66608316e-18j,
                0.34595014 + 1.06355385e-01j,
                0.15519289 + 4.75602164e-02j,
                -0.22401193 - 4.33128746e-03j,
                0.38243133 - 6.66608316e-18j,
                0.36957165 + 5.69575709e-02j,
                0.17389327 + 5.53498385e-02j,
                -0.11601473 + 1.35405676e-02j,
                0.38243133 - 6.66608316e-18j,
                0.39045046 + 2.17911945e-03j,
                0.18146449 + 1.37089189e-02j,
                -0.02110144 - 6.65071497e-03j,
                0.38243133 - 6.66608316e-18j,
                0.4063995 - 5.21354967e-02j,
                0.15674204 - 3.85815662e-02j,
                -0.02886296 - 3.91489615e-02j,
                0.38243133 - 6.66608316e-18j,
                0.41872477 - 9.98946906e-02j,
                0.11862477 - 5.15231952e-02j,
                -0.05298751 - 1.95319478e-02j,
                0.38243133 - 6.66608316e-18j,
                0.43013599 - 1.38307796e-01j,
                0.10075763 - 1.25689289e-02j,
                -0.04052728 + 5.66863498e-02j,
                0.38243133 - 6.66608316e-18j,
                0.44144497 - 1.68826980e-01j,
                0.11446016 + 4.53003874e-02j,
                -0.03546515 + 1.13544145e-01j,
                0.38243133 - 6.66608316e-18j,
                0.44960099 - 1.94794929e-01j,
                0.15053714 + 8.11915305e-02j,
                -0.04800556 + 1.15828804e-01j,
                0.38243133 - 6.66608316e-18j,
                0.44872328 - 2.17957567e-01j,
                0.19116871 + 7.99536373e-02j,
                -0.05683092 + 9.72225058e-02j,
                0.38243133 - 6.66608316e-18j,
                0.43379428 - 2.36681249e-01j,
                0.21025378 + 5.48466438e-02j,
                -0.05318826 + 8.54948014e-02j,
                0.38243133 - 6.66608316e-18j,
                0.40485577 - 2.47073481e-01j,
                0.18680217 + 3.31766116e-02j,
                -0.06674163 + 7.94216591e-02j,
                0.38243133 - 6.66608316e-18j,
                0.36865853 - 2.45913767e-01j,
                0.13660805 + 3.68947359e-02j,
                -0.11467046 + 8.49198927e-02j,
                0.38243133 - 6.66608316e-18j,
                0.33597018 - 2.32971425e-01j,
                0.1072859 + 6.24686168e-02j,
                -0.12932565 + 1.06139634e-01j,
                0.38243133 - 6.66608316e-18j,
                0.31616666 - 2.10791785e-01j,
                0.11876919 + 7.93812474e-02j,
                -0.1094488 + 1.20159845e-01j,
                0.38243133 - 6.66608316e-18j,
                0.31313975 - 1.82190396e-01j,
                0.14075481 + 5.85637416e-02j,
                -0.15198775 + 1.02156797e-01j,
            ],
            dtype=complex_type(self.dtype),
        )
        self.assertTrue(np.allclose(pf, result))
Пример #17
0
    def calculate_bispectrum(self,
                             complex_coef,
                             flatten=False,
                             filter_nonzero_freqs=False,
                             freq_cutoff=None):
        """
        Calculate bispectrum for a set of coefs in this basis.

        The Bispectum matrix is of shape:
            (count, count, unique_radial_indices)

        where count is the number of complex coefficients.

        :param coef: Coefficients representing a (single) image expanded in this basis.
        :param flatten: Optionally extract symmetric values (tril) and then flatten.
        :param filter_nonzero_freqs: Remove indices corresponding to zero frequency (defaults False).
        :param freq_cutoff: Truncate (zero) high k frequecies above (int) value, defaults off (None).
        :return: Bispectum matrix (complex valued).
        """

        # Check shape
        if complex_coef.shape[0] != 1:
            raise ValueError(
                "Due to potentially large sizes, bispectrum is limited to a single set of coefs."
                f"  Passed shape {complex_coef.shape}")

        if complex_coef.shape[1] != self.complex_count:
            raise ValueError(
                "Basis.calculate_bispectrum coefs expected"
                f" to have (complex) count {self.complex_count}, received {complex_coef.shape}."
            )

        # From here just treat complex_coef as 1d vector instead of 1 by count 2d array.
        complex_coef = complex_coef[0]

        if freq_cutoff and freq_cutoff > np.max(self.complex_angular_indices):
            logger.warning(
                f"Bispectrum frequency cutoff {freq_cutoff} outside max {np.max(self.complex_angular_indices)}"
            )

        # Notes, regarding the naming:
        # radial freq indices q in paper/slides, _indices["ks"] in code
        radial_indices = self.complex_radial_indices  # q
        # angular freq indices k in paper/slides, _indices["ells"] in code
        angular_indices = self.complex_angular_indices  # k
        # Compute the set of all unique q in the compressed basis
        #   Note that np.unique is also sorted.
        unique_radial_indices = np.unique(radial_indices)

        # When compressed, we need maps between the basis and uncompressed set of q
        #   to a reduced set of q that remain after compression.
        # One map is provided by self.complex_radial_indices
        #   which maps an index in the basis remaining after compression to a q value.
        # The following code computes a similar but inverted map,
        #   given a q value, find an index into the set of unique q after compression.
        # Also, it is known that the set of q gets sparser with increasing k
        #   but that's ignored here, instead construct a dense
        #   array and filter it later.
        #  The plan is to revisit this code after appropriate coef classes are derived.

        # Default array to fill_value, we can use a value
        # k should never achieve..
        fill_value = self.complex_count**2
        compressed_radial_map = (
            np.ones(np.max(unique_radial_indices) + 1, dtype=int) * fill_value)
        for uniq_q_index, q_value in enumerate(unique_radial_indices):
            compressed_radial_map[q_value] = uniq_q_index

        B = np.zeros(
            (self.complex_count, self.complex_count,
             unique_radial_indices.shape[0]),
            dtype=complex_type(self.dtype),
        )

        logger.info(f"Calculating bispectrum matrix with shape {B.shape}.")

        for ind1 in range(self.complex_count):

            k1 = angular_indices[ind1]
            if freq_cutoff and k1 > freq_cutoff:
                continue
            coef1 = complex_coef[ind1]

            for ind2 in range(self.complex_count):

                k2 = angular_indices[ind2]
                if freq_cutoff and k2 > freq_cutoff:
                    continue
                coef2 = complex_coef[ind2]

                k3 = k1 + k2
                intermodulated_coef_inds = angular_indices == k3

                if np.any(intermodulated_coef_inds):
                    # Get the specific q indices related to feasible k3 angular_indices
                    Q3_ind = radial_indices[intermodulated_coef_inds]

                    if hasattr(self, "compressed") and self.compressed:
                        # Map those Q3_ind values to indices into compressed unique_radial_indices
                        #  by using the compressed_radial_map prepared above.
                        Q3_ind = compressed_radial_map[Q3_ind]

                    Coef3 = complex_coef[intermodulated_coef_inds]

                    B[ind1, ind2, Q3_ind] = coef1 * coef2 * np.conj(Coef3)

        if filter_nonzero_freqs:
            non_zero_freqs = angular_indices != 0
            B = B[non_zero_freqs][:, non_zero_freqs]

        if flatten:
            # B is sym, start by taking lower triangle.
            tril = np.tri(B.shape[0], dtype=bool)
            B = B[tril, :]
            # Then flatten
            B = B.flatten()

        return B