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
Exemple #2
0
    def evaluate(self, v):
        """
        Evaluate coefficients in standard 2D coordinate basis from those in polar Fourier basis

        :param v: A coefficient vector (or an array of coefficient vectors)
            in polar Fourier basis to be evaluated. The last dimension must equal to
            `self.count`.
        :return x: Image instance in standard 2D coordinate basis with
            resolution of `self.sz`.
        """
        if self.dtype != real_type(v.dtype):
            msg = (f"Input data type, {v.dtype}, is not consistent with"
                   f" type defined in the class {self.dtype}.")
            logger.error(msg)
            raise TypeError(msg)

        v = v.reshape(-1, self.ntheta, self.nrad)

        nimgs = v.shape[0]

        half_size = self.ntheta // 2

        v = v[:, :half_size, :] + v[:, half_size:, :].conj()

        v = v.reshape(nimgs, self.nrad * half_size)

        x = anufft(v, self.freqs, self.sz, real=True)

        return Image(x)
    def testComplexCoversionErrorsToReal(self):
        # Load a reasonable input
        x = np.load(os.path.join(DATA_DIR, "fbbasis_coefficients_8_8.npy"))

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

        # Test catching Errors
        with raises(TypeError):
            # Pass real into `to_real`
            _ = self.basis.to_real(cv1.real.astype(np.float32))

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

        v3 = self.basis.to_real(cv1.astype(test_dtype))
        self.assertTrue(v3.dtype == result_dtype)

        # Try a 0d vector, should not crash.
        _ = self.basis.to_real(cv1.reshape(-1))
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
Exemple #5
0
 def calculate_bispectrum(self,
                          coef,
                          flatten=False,
                          filter_nonzero_freqs=False,
                          freq_cutoff=None):
     if coef.dtype == real_type(self.dtype):
         coef = self.to_complex(coef)
     return super().calculate_bispectrum(
         coef,
         flatten=flatten,
         filter_nonzero_freqs=filter_nonzero_freqs,
         freq_cutoff=freq_cutoff,
     )
Exemple #6
0
    def to_real(self, complex_coef):
        """
        Return real valued representation of complex coefficients.
        This can be useful when comparing or implementing methods
        from literature.

        There is a corresponding method, to_complex.

        :param complex_coef: Complex coefficients from this basis.
        :return: Real coefficent representation from this basis.
        """
        if complex_coef.ndim == 1:
            complex_coef = complex_coef.reshape(1, -1)

        if complex_coef.dtype not in (np.complex128, np.complex64):
            raise TypeError("coef provided to to_real should be complex.")

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

        coef = np.zeros((complex_coef.shape[0], self.count), dtype=dtype)

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

        coef[:, idx] = complex_coef[:, idx].real

        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]

            c = complex_coef[:, idx]
            coef[:, idx_pos] = 2.0 * np.real(c)
            coef[:, idx_neg] = -2.0 * np.imag(c)

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

        return coef
Exemple #7
0
    def to_real(self, complex_coef):
        """
        Return real valued representation of complex coefficients.
        This can be useful when comparing or implementing methods
        from literature.

        There is a corresponding method, to_complex.

        :param complex_coef: Complex coefficients from this basis.
        :return: Real coefficent representation from this basis.
        """

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

        if complex_coef.dtype not in (np.complex128, np.complex64):
            raise TypeError("coef provided to to_real should be complex.")

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

        coef = np.zeros((complex_coef.shape[0], self.count), dtype=dtype)

        # map ordered index to (ell, q) key in dict
        keymap = list(self.complex_indices_map.keys())
        for i in range(self.complex_count):
            # retreive index into reals
            pos_i, neg_i = self.complex_indices_map[keymap[i]]
            if self.complex_angular_indices[i] == 0:
                coef[:, pos_i] = complex_coef[:, i].real
            else:
                coef[:, pos_i] = 2.0 * complex_coef[:, i].real
                coef[:, neg_i] = -2.0 * complex_coef[:, i].imag

        return coef